Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

163
Vistas
¿Cómo mapear la función directamente sobre la lista de listas?

He creado un clasificador de píxeles para imágenes y, para cada píxel de la imagen, quiero definir a qué grupo de colores predefinido pertenece. Funciona, pero a unos 5 minutos por imagen, creo que estoy haciendo algo no pitónico que seguramente se puede optimizar.

¿Cómo podemos mapear la función directamente sobre la lista de listas?

 #First I convert my image to a list #Below list represents a true image size list1=[[255, 114, 70], [120, 89, 15], [247, 190, 6], [41, 38, 37], [102, 102, 10], [255,255,255]]*3583180

Luego definimos los grupos para mapear los colores y la función para hacerlo (que se toma de la biblioteca PIL )

 #Define colors of interest #Colors of interest RED=[255, 114, 70] DARK_YELLOW=[120, 89, 15] LIGHT_YELLOW=[247, 190, 6] BLACK=[41, 38, 37] GREY=[102, 102, 10] WHITE=[255,255,255] Colors=[RED, DARK_YELLOW, LIGHT_YELLOW, GREY, BLACK, WHITE] #Function to find closes cluster by root and squareroot distance of RGB def distance(c1, c2): (r1,g1,b1) = c1 (r2,g2,b2) = c2 return math.sqrt((r1 - r2)**2 + (g1 - g2) ** 2 + (b1 - b2) **2)

Lo que queda es hacer coincidir todos los colores y hacer una nueva lista con índices coincidentes de los colores originales:

 Filt_lab=[] #Match colors and make new list with indexed colors for pixel in tqdm(list1): closest_colors = sorted(Colors, key=lambda color: distance(color, pixel)) closest_color = closest_colors[0] for num, clust in enumerate(Colors): if list(clust) == list(closest_color): Filt_lab.append(num)

La ejecución de una sola imagen lleva aproximadamente 5 minutos, lo cual está bien, pero es probable que haya un método en el que este tiempo se pueda reducir considerablemente.

36%|███▌ | 7691707/21499080 [01:50<03:18, 69721.86it/s]

Resultado esperado de Filt_lab:

 [0, 1, 2, 4, 3, 5]*3583180
over 4 years ago · Santiago Trujillo
7 Respuestas
Responde la pregunta

0

Solo aceleraciones rápidas:

  1. Puede omitir math.sqrt()
  2. Cree un diccionario de colores en lugar de una lista (de esa manera no tiene que buscar el índice en cada iteración)
  3. use min() en lugar de sorted()
 from tqdm import tqdm list1 = [ [255, 114, 70], [120, 89, 15], [247, 190, 6], [41, 38, 37], [102, 102, 10], [255, 255, 255], ] * 3583180 RED = [255, 0, 0] DARK_YELLOW = [120, 89, 15] LIGHT_YELLOW = [247, 190, 6] BLACK = [41, 38, 37] GREY = [102, 102, 10] WHITE = [255, 255, 255] # create a dictionary instead of a list: Colors = { i: c for i, c in enumerate([RED, DARK_YELLOW, LIGHT_YELLOW, GREY, BLACK, WHITE]) } # Function to find closes cluster by root and squareroot distance of RGB - EDIT: squareroot omitted def distance(c1, c2): (r1, g1, b1) = c1 (r2, g2, b2) = c2 return (r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2 # <-- you can ommit math.sqrt Filt_lab = [] # Match colors and make new list with indexed colors for pixel in tqdm(list1): # use min() instead of sorted: closest_color = min( Colors, key=lambda color: distance(Colors[color], pixel) ) Filt_lab.append(closest_color)

En mi computadora, la velocidad subió de ~108000.0it/s a ~155000.00it/s.


Nota: para este tipo de tareas es mejor usar la biblioteca numpy .

over 4 years ago · Santiago Trujillo Denunciar

0

Apliqué la comprensión de listas, teniendo la impresión de que esto debería haber sido mucho más rápido, pero al final fue incluso un poco más lento:

 from tqdm import tqdm #Representative image list1 = [[255, 114, 70], [120, 89, 15], [247, 190, 6], [41, 38, 37], [102, 102, 10], [255, 255, 255],] * 3583180 #Colors of interest RED = [255, 0, 0] DARK_YELLOW = [120, 89, 15] LIGHT_YELLOW = [247, 190, 6] BLACK = [41, 38, 37] GREY = [102, 102, 10] WHITE = [255, 255, 255] # create a dictionary instead of a list: Colors = {i: c for i, c in enumerate([RED, DARK_YELLOW, LIGHT_YELLOW, GREY, BLACK, WHITE])} # Function to find closes cluster by root and squareroot distance of RGB - EDIT: squareroot omitted def distance(c1, c2): (r1, g1, b1) = c1 (r2, g2, b2) = c2 return (r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2 #Attempt with list comprehension Filt_lab = tqdm([(min(Colors, key=lambda color: distance(Colors[color], x))) for x in tqdm(list1)])

Antes de la comprensión de la lista: 59%|█████▉ | 12738323/21499080 [01:49<01:15, 116309.97it/s ]

Después de la lista de comprensión 100%|██████████| 21499080/21499080 [03:10<00:00, 112848.76it/s ]

over 4 years ago · Santiago Trujillo Denunciar

0

Básicamente, tomé en serio los comentarios de @GPI: "No soy un experto en computación de alto rendimiento, sin embargo, mi corazonada es: descartar la lista de listas. Deberían ser arreglos planos. Su imagen debería ser [r1, g1, b1, r2, g2, b2, ...] o 3 arreglos [r1, r2, ...], [g1, g2, ...], [b1, b2, ...] o un mux (use los primeros bits de un int para r, los siguientes 8 para g...). Entonces NO debe calcular las diferencias píxel por píxel en un bucle for porque entonces está utilizando regiones de memoria altamente no consecutivas. Para cada clase, calcule la diferencia sobre las matrices rojas, luego verde, luego azul. Luego sume las diferencias, luego clasifique. Y use numpy para hacer todo vectorizado. – GPI"

He redibujado completamente el guión y dejo la imagen como está, sin remodelarla primero en una lista. Solo se aplican operaciones numpy, y ahora se tarda un par de segundos en procesar una imagen completa (en comparación con los 5 minutos anteriores). Probablemente aún se puedan realizar algunos pequeños pasos de optimización.

 from tqdm import tqdm import numpy as np #Representative image imarray = np.random.rand(3650,2000,3) * 255 image=imarray.astype(np.uint64) #Colors of interest RED = [255, 0, 0] DARK_YELLOW = [120, 89, 15] LIGHT_YELLOW = [247, 190, 6] BLACK = [41, 38, 37] GREY = [102, 102, 10] WHITE = [255, 255, 255] # create a dictionary instead of a list: Colors = {i: c for i, c in enumerate([RED, DARK_YELLOW, LIGHT_YELLOW, GREY, BLACK, WHITE])} #Create a dictionary which will be filled with key of color and value of cumulative array with differences for every color img_dictionary={} for key, value in Colors.items(): R=(image[:,:,0].astype(np.uint64)-value[0])**2 G=(image[:,:,1].astype(np.uint64)-value[1])**2 B=(image[:,:,2].astype(np.uint64)-value[2])**2 Total=R+G+B img_dictionary[key] = Total #Stack all arrays from dictionary arr = np.stack(list(img_dictionary.values())) #Find array with lowest difference and map it to new array classified_pixels=np.argmin(arr, axis=0) #Convert array to list cl_pixel_list = classified_pixels.reshape((classified_pixels.shape[0] * classified_pixels.shape[1])).tolist() #Print print(cl_pixel_list[0:10]) >>>[4, 1, 4, 3, 3, 4, 0, 2, 4, 4]
over 4 years ago · Santiago Trujillo Denunciar

0

Puede usar el JIT de Numba para acelerar el código por un amplio margen. La idea es construir classified_pixels sobre la marcha iterando sobre los colores de cada píxel. Los colores se almacenan en una matriz Numpy donde el índice es la clave de color. Todo el cálculo puede ejecutarse en paralelo . Esto evita que se creen y se escriban o lean muchas matrices temporales en la memoria y que se asigne mucha memoria. Además, los tipos de datos se pueden adaptar para que la matriz resultante sea más pequeña en la memoria (por lo que se escribe/lee más rápido). Aquí está el guión final:

 import numpy as np import numba as nb @nb.njit('int32[:,::1](int32[:,:,::1], int32[:,::1])', parallel=True) def classify(image, colors): classified_pixels = np.empty((image.shape[0], image.shape[1]), dtype=np.int32) for i in nb.prange(image.shape[0]): for j in range(image.shape[1]): minId = -1 minValue = 256*256 # The initial value is the maximum possible value ir, ig, ib = image[i, j] # Find the color index with the minimum difference for k in range(len(colors)): cr, cg, cb = colors[k] total = (ir-cr)**2 + (ig-cg)**2 + (ib-cb)**2 if total < minValue: minValue = total minId = k classified_pixels[i, j] = minId return classified_pixels # Representative image np.random.seed(42) imarray = np.random.rand(3650,2000,3) * 255 image = imarray.astype(np.int32) # Colors of interest RED = [255, 0, 0] DARK_YELLOW = [120, 89, 15] LIGHT_YELLOW = [247, 190, 6] BLACK = [41, 38, 37] GREY = [102, 102, 10] WHITE = [255, 255, 255] # Build a Numpy array rather than a dict colors = np.array([RED, DARK_YELLOW, LIGHT_YELLOW, GREY, BLACK, WHITE], dtype=np.int32) # Actual classification classified_pixels = classify(image, colors) # Convert array to list cl_pixel_list = classified_pixels.reshape(classified_pixels.shape[0] * classified_pixels.shape[1]).tolist() # Print print(cl_pixel_list[0:10])

Esta implementación toma alrededor de 0,19 segundos en mi máquina de 6 núcleos. Es unas 15 veces más rápido que la última respuesta proporcionada hasta el momento y más de mil veces más rápido que la implementación inicial. Tenga en cuenta que aproximadamente la mitad del tiempo se dedica a tolist() ya que la función de classify es muy rápida.

over 4 years ago · Santiago Trujillo Denunciar

0

Usando numpy:

 import numpy as np #Representative image imarray = np.uint64(np.random.rand(3583180*6,3) * 255) #Or make with np.uint64(your_list_of_lists) if you already have that list lists; Axes: pixel, color_channels RED=[255, 114, 70] DARK_YELLOW=[120, 89, 15] LIGHT_YELLOW=[247, 190, 6] BLACK=[41, 38, 37] GREY=[102, 102, 10] WHITE=[255,255,255] #your list of colors Colors=[RED, DARK_YELLOW, LIGHT_YELLOW, GREY, BLACK, WHITE] #again converted to numpy Colors_np = np.uint64(Colors) #axes: colors, color_channels #Compute all distance, or rather the squares at that has no #effect on which is minimal and we can drop the sqrt computation then #Extend both numpy arrays to be haves axes [pixel, color, color_channels] with `np.newaxis`, #take the difference, #then the square, #and then the sum across color channels distances = np.sum((imarray[:,np.newaxis, :] - Colors_np[np.newaxis, :, :])**2, 2) #difference has axes [pixel, color, color_channels], summed over axes 2 => [pixel, color] axes remain #You want index of minimum over color axis, so: closest_color_indices = np.argmin(distances, 1) #written as one line and timed with %timeit in ipython (on a single core): #%timeit np.argmin(np.sum((imarray[:,np.newaxis, :] - Colors_np[np.newaxis, :, :])**2, 2), 1) #6.11 s +- 79.4 ms per loop (mean +- std. dev. of 7 runs, 1 loop each)

Entonces esto toma alrededor de 6.11s para 3583180*6=21499080 píxeles y 6 colores posibles.

over 4 years ago · Santiago Trujillo Denunciar

0

Puede intentar crear y usar una tabla de búsqueda con 256 * 256 * 256 elementos.

 import numpy as np from scipy.spatial import cKDTree imarray = np.uint8(np.random.rand(3583180*6,3) * 255) code=np.array([1, 256, 256*256]) RED=[255, 114, 70] DARK_YELLOW=[120, 89, 15] LIGHT_YELLOW=[247, 190, 6] GREY=[102, 102, 10] BLACK=[41, 38, 37] WHITE=[255,255,255] #your list of colors Colors=[RED, DARK_YELLOW, LIGHT_YELLOW, GREY, BLACK, WHITE] #again converted to numpy Colors_np = np.uint8(Colors) #axes: colors, color_channels x=np.arange(256, dtype=np.uint8) rgb=np.array(np.meshgrid(x, x, x)).T.reshape(-1,3) rgb[:,[0, 1, 2]]=rgb[:,[1, 0, 2]] #swap columns # rgb is table all colors voronoi_kdtree = cKDTree(Colors_np) # Voronoi by base Colors _, test_point_regions = voronoi_kdtree.query(rgb) # test_point_regions is lookup table (LUT) result=test_point_regions[np.dot(imarray, code)] assert np.all(test_point_regions[np.dot(Colors_np, code)]==np.array([0, 1, 2, 3, 4, 5]))
over 4 years ago · Santiago Trujillo Denunciar

0

parece que tu computadora es muy rápida :)

esta es la salida a mitad de camino de su código en mi sistema:

 0%| | 5635/21499080 [00:44<46:51:14, 127.43it/s]

pero he reescrito tu código usando TensorFlow, y ahora se está ejecutando durante unos 3 segundos :)

 import math import os from time import time import numpy as np from tqdm import tqdm os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # or any {'0', '1', '2'} import tensorflow as tf list1 = [[255, 114, 70], [120, 89, 15], [247, 190, 6], [41, 38, 37], [102, 102, 10], [255, 255, 255]] * 3583180 list1_np = tf.constant(list1) RED = [255, 114, 70] DARK_YELLOW = [120, 89, 15] LIGHT_YELLOW = [247, 190, 6] BLACK = [41, 38, 37] GREY = [102, 102, 10] WHITE = [255, 255, 255] Colors = tf.constant([RED, DARK_YELLOW, LIGHT_YELLOW, GREY, BLACK, WHITE]) t = time() ans = tf.argmin(np.array([tf.math.reduce_sum((list1_np - c) ** 2, axis=1) for c in Colors]), axis=0) print(time() - t) print(ans) # and now your code def distance(c1, c2): (r1, g1, b1) = c1 (r2, g2, b2) = c2 return math.sqrt((r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2) t = time() Filt_lab = [] # Match colors and make new list with indexed colors for pixel in tqdm(list1): closest_colors = sorted(Colors, key=lambda color: distance(color, pixel)) closest_color = closest_colors[0] for num, clust in enumerate(Colors): if list(clust) == list(closest_color): Filt_lab.append(num) print(time() - t)

la salida es:

 3.1714584827423096 tf.Tensor([0 1 2 ... 4 3 5], shape=(21499080,), dtype=int64) 0%| | 951/21499080 [00:07<47:36:50, 125.42it/s]

NOTA 1: puede omitir algunas de las importaciones si elimina la segunda parte.

NOTA 2: cuando desee comparar distancias entre sí, no es necesario utilizar la raíz cuadrada.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda