Estoy trabajando en mí mismo la enseñanza de los conceptos básicos de la informática procesamiento de la imagen, y me estoy enseñando a Python en el mismo tiempo.

Dado una imagen x de dimensiones 2048×1354 con 3 canales, de manera eficiente calcular el histograma de las intensidades de los píxeles.

import numpy as np, cv2 as cv

img = cv.imread("image.jpg")
bins = np.zeros(256, np.int32)

for i in range(0, img.shape[0]):
    for j in range(0, img.shape[1]):

        intensity = 0
        for k in range(0, len(img[i][j])):
            intensity += img[i][j][k]

        bins[intensity/3] += 1

print bins

Mi problema es que este código se ejecuta muy lentamente, como en ~30 segundos. ¿Cómo puedo acelerar este proceso y ser más Python?

  • stackoverflow.com/a/14728935/995394 tal vez esto es útil.
  • Con 3 anidada for bucles, el algoritmo se ejecuta en O(n^3) el tiempo, que es muy lento.
  • Esto no es exactamente a tu pregunta original, pero considere el uso de un mejor algoritmo para generar el histograma. Puesto que usted está probablemente interesado en el percibidas de colores, usted podría tratar de usar una luminancia de cálculo: stackoverflow.com/questions/596216/…
  • el tercer bucle sólo se ejecuta un número constante de veces, probablemente 3. No es proporcional al tamaño de la imagen.
  • Una pequeña cosa sería sustituir el bucle interno: for k in img[i][j]: intensity += k. Un bucle por range(len(...)) nunca es una buena señal. Aún mejor sería utilizar sum(img[i][j]) y eliminar el bucle completo.
InformationsquelleAutor Jason | 2014-03-03

4 Comentarios

  1. 5

    Usted puede utilizar los nuevos OpenCV interfaz de python que utiliza de forma nativa arrays de numpy y graficar el histograma de las intensidades de los píxeles mediante matplotlib hist. Se tarda menos de un segundo en mi equipo.

    import matplotlib.pyplot as plt
    import cv2
    
    im = cv2.imread('image.jpg')
    # calculate mean value from RGB channels and flatten to 1D array
    vals = im.mean(axis=2).flatten()
    # plot histogram with 255 bins
    b, bins, patches = plt.hist(vals, 255)
    plt.xlim([0,255])
    plt.show()

    Python - Calcular el histograma de la imagen

    ACTUALIZACIÓN:
    Anteriormente especificado número de ubicaciones que no siempre da resultado deseado min y max son calculados a partir de los valores reales. Por otra parte, cuenta de valores 254 y 255 se suman en la última de reciclaje. Aquí se actualizó el código que siempre parcela histograma correctamente con bares centrada en los valores 0..255

    import numpy as np
    import matplotlib.pyplot as plt
    import cv2
    
    # read image
    im = cv2.imread('image.jpg')
    # calculate mean value from RGB channels and flatten to 1D array
    vals = im.mean(axis=2).flatten()
    # calculate histogram
    counts, bins = np.histogram(vals, range(257))
    # plot histogram centered on values 0..255
    plt.bar(bins[:-1] - 0.5, counts, width=1, edgecolor='none')
    plt.xlim([-0.5, 255.5])
    plt.show()

    Python - Calcular el histograma de la imagen

  2. 3

    es imposible hacer esto(he.e sin quitar el bucle for) en python puro. Python para la construcción de bucle tiene demasiadas cosas pasando a ser rápido. Si usted realmente quiere mantener el bucle for, la única solución es numba o cython, pero estos tienen su propio conjunto de problemas. Normalmente, estas líneas están escritas en c/c++(lo más sencillo en mi opinión) y, a continuación, llama desde python, es la principal función de un lenguaje de secuencias de comandos.

    Habiendo dicho eso, opencv+numpy proporciona suficiente rutinas útiles para que en el 90% de los casos, es posible utilizar simplemente construido en funciones sin tener que recurrir a la escritura de su propio nivel de píxel código.

    Aquí tienes la solución en numba sin cambiar su código bucle. en mi equipo es de aproximadamente 150 veces más rápido que python puro.

    import numpy as np, cv2 as cv
    
    from time import time
    from numba import jit,int_,uint8 
    
    @jit(argtypes=(uint8[:,:,:],int_[:]),
        locals=dict(intensity=int_),
        nopython=True
        )
    def numba(img,bins):
        for i in range(0, img.shape[0]):
            for j in range(0, img.shape[1]):
                intensity = 0
                for k in range(0, len(img[i][j])):
                    intensity += img[i][j][k]
                bins[intensity/3] += 1
    
    
    def python(img,bins):
        for i in range(0, img.shape[0]):
            for j in range(0, img.shape[1]):
                intensity = 0
                for k in range(0, len(img[i][j])):
                    intensity += img[i][j][k]
                bins[intensity/3] += 1
    
    img = cv.imread("image.jpg")
    bins = np.zeros(256, np.int32)
    
    t0 = time()
    numba(img,bins)
    t1 = time()
    #print bins
    print t1 - t0
    
    bins[...]=0
    t0 = time()
    python(img,bins)
    t1 = time()
    #print bins
    print t1 - t0    
  3. 2

    Si usted sólo desea contar el número de ocurrencias de cada valor en una matriz, numpy puede hacer eso para usted, utilizando numpy.bincount. En su caso:

    arr  = numpy.asarray(img)
    flat = arr.reshape(numpy.prod(arr.shape[:2]),-1)
    bins = numpy.bincount(np.sum(flat,1)/flat.shape[1],minsize=256)

    Estoy usando numpy.asarray aquí para asegurarse de que img es una colección de la matriz, así que se pueden acoplar a la matriz unidimensional bincount necesidades. Si img ya es una matriz, puede saltarse este paso. El recuento de sí mismo va a ser muy rápido. La mayoría del tiempo aquí probablemente será gastado en la conversión de la cv de la matriz a una matriz.

    Edit: Según esta respuesta, puede que necesite utilizar numpy.asarray(img[:,:]) (o posiblemente img[:,:,:]), para convertir la imagen en una matriz. Por otro lado, de acuerdo a este, lo que se obtiene de las versiones más recientes de openCV es ya una colección de la matriz. Así que en ese caso se puede omitir el asarray completamente.

    • Esta es la mejor respuesta. El uso de CV2 es como usar un cañón para matar a un mosquito. No hay necesidad de usar openCV para todo, cuando no es pura numpy o numpy basada en librerías como scikit-imagen
  4. 1

    Echar un vistazo en MatPlotLib. Esto lo llevará a través de todo lo que quieras hacer, y sin los bucles for.

    • Sé herramientas que ya existen. Sin embargo, quiero usar esto como una oportunidad de aprendizaje tanto de la lengua y de los algoritmos.
    • Una gran parte de python es aprender cuáles son las herramientas disponibles y matplotlib es una enorme biblioteca que voy a usar en casi todos los de mi código. Yo entiendo que usted quiera aprender el idioma, pero Python utilidad es que hay muchas herramientas que permiten hacer todo tipo de cosas con facilidad y de manera eficiente.
    • Otra manera de acelerar sería el uso de numpy, pero una vez más usted está utilizando una biblioteca para ayudar a usted. Python no es el mejor para ‘para’ bucles. Usted puede vectorizar este código con Numpy, o el uso de Matplotlib para hacerlo en un modo más simple.
    • Lo principal que hace que python grande no el lenguaje en sí (aunque eso es muy bonito (si lento)). Es su gran conjunto de librerías estándar, y si no hace uso de ellos, usted está paralizando python.
    • de acuerdo. esto es muy rápido. Es interesante la forma en que matplotlib y cv2 importar imágenes, conduce a los diferentes valores de la img variable. si importa el uso de cv2 los valores de los píxeles que habrá entre [0, 255]. Si la importación de la imagen utilizando matplotlib, los valores entre [0,1]

Dejar respuesta

Please enter your comment!
Please enter your name here