18

Estoy usando el Python Imaging Library para colorear una imagen en blanco y negro con una tabla de búsqueda que define las relaciones de color. La tabla de búsqueda es simplemente una lista de 256 elementos de tuplas RGB:Usando el método Image.point() en PIL para manipular datos de píxeles

>>> len(colors) 
256 
>>> colors[0] 
(255, 237, 237) 
>>> colors[127] 
(50, 196, 33) 
>>> 

Mi primera versión utiliza los métodos getpixel() y putpixel():

for x in range(w): 
     for y in range(h): 
      pix = img.getpixel((x,y)) 
      img.putpixel((x,y), colors[pix[0]]) 

Este fue terriblemente lento. Un informe profile apuntó a los métodos putpixel y getpixel como los culpables. Una pequeña investigación (es decir, lea los documentos) y encuentro "Tenga en cuenta que este método es relativamente lento." re: putpixel. (tiempo de ejecución real: 53s en putpixel y 50 getpixel una imagen de 1024x1024 para)

Sobre la base de la sugerencia en la documentación, solía im.load() y el acceso de píxeles directa en su lugar:

pixels = img.load() 
    for x in range(w): 
     for y in range(h): 
      pix = pixels[x, y] 
      pixels[x, y] = colors[pix[0]]     

procesamiento acelerado por una orden de magnitud, pero sigue siendo lento: aproximadamente 3.5s para procesar una imagen de 1024x1024.

Un estudio más a fondo de los documentos PIL parece indicar Image.point() es exactamente previsto para este fin:

im.point(table) => imagen

im.point(function) => imagen

devuelve una copia de la imagen donde se ha mapeado cada píxel a través de la tabla dada. La tabla debe contener 256 valores por banda en la imagen. Si se usa una función en su lugar, debería tomar un solo argumento. La función se llama una vez para cada valor de píxel posible, y la tabla resultante se aplica a todas las bandas de la imagen.

He pasado algún tiempo pirateando la interfaz, pero parece que no puedo hacerlo bien. Perdona mi ignorancia, pero los documentos de PIL son escuetos y no tengo mucha experiencia en el procesamiento de imágenes. Busqué en Google un poco y encontré algunos ejemplos, pero nada que hiciera que el uso "haga clic" para mí. Por lo tanto, finalmente, mis preguntas:

  • ¿Es Image.point() la herramienta adecuada para este trabajo?
  • ¿Qué formato/estructura tiene Image.point() esperan la tabla?
  • ¿Alguien puede dar un paso atrás en una implementación de ejemplo? Cada iteración que he intentado hasta ahora ha terminado con una imagen negra recta.

Respuesta

15

Es Image.point() la herramienta adecuada para este trabajo?

Sí, efectivamente, Image.point() es perfecto para este trabajo

¿Qué formato/estructura hace Image.point() espera que la mesa?

debe acoplar la lista así que en vez de [(12, 140, 10), (10, 100, 200), ...] uso:

[12, 140, 10, 10, 100, 200, ...] 

Aquí está un ejemplo rápido he intentado:

im = im.point(range(256, 0, -1) * 3) 

alt text alt text

Y por el manera, si necesita más control sobre los colores y siente que Image.point no es para usted, también puede usar Image.getdata y Image.putdata para cambiar los colores más rápido que load y putpixel. Sin embargo, es más lento que Image.point.

Image.getdata le da la lista de todos los píxeles, los modifica y los vuelve a escribir usando Image.putdata. Es así de simple. Pero intente hacerlo primero usando Image.point.


EDITAR

he cometido un error en la primera explicación, voy a explicar ahora correctamente:

La tabla de colores en realidad es así

[0, 1, 2, 3, 4, 5, ...255, 0, 1, 2, 3, ....255, 0, 1, 2, 3, ...255] 

Cada banda rango al lado del otro. Para cambiar el color (0, 0, 0) a (10, 100, 10) que tiene que ser como esto:

[10, 1, 2, 3, 4, 5, ...255, 100, 1, 2, 3, ....255, 10, 1, 2, 3, ...255] 

para transformar su lista de colores en el formato correcto intentar esto:

table = sum(zip(*colors),()) 

Creo que mi primer ejemplo debería mostrarle el formato.

+0

aplanar la lista? vale, pero ¿cómo funciona eso? Quiero píxeles con valor 0 -> (12, 140, 10) y píxeles con valor 255 -> (254, 237, 220). –

+4

ya sabes, creo que este podría ser el único lugar en Internet donde se describe el formato esperado de esa tabla. // Acabo de obtener img.point() trabajando con la tabla de búsqueda, gracias a su descripción. Los resultados no son exactamente los que esperaba, pero tengo lo suficiente como para hackearlos y resolverlos. ¡Muchas gracias! –

+0

@ J.J., El formato no es realmente obvio. Cometí un error en mi primera explicación y lo arreglé ahora. Espero haberlo hecho bien esta vez. Me voy a dormir, así que no podré responder preguntas. –

3

yo creo que puede ser más típica a point sobre una base de banda por banda como así (levantada directamente desde el PIL tutorial):

# split the image into individual bands 
source = im.split() 

R, G, B = 0, 1, 2 

# select regions where red is less than 100 
mask = source[R].point(lambda i: i < 100 and 255) 

# process the green band 
out = source[G].point(lambda i: i * 0.7) 

# paste the processed band back, but only where red was < 100 
source[G].paste(out, None, mask) 

# build a new multiband image 
im = Image.merge(im.mode, source) 
+0

el formato de tabla tiene más sentido con una sola banda, eso es seguro. Puede que tenga que utilizar esta ruta para manejar el canal alfa con un estuche especial ... –

+0

es fácil y rápido gracias. – Conex

Cuestiones relacionadas