2009-10-24 24 views
14

Estoy tratando de eliminar un cierto color de mi imagen, sin embargo, no está funcionando tan bien como esperaba. Traté de hacer lo mismo que se ve aquí Using PIL to make all white pixels transparent?, sin embargo, la calidad de la imagen es un poco con pérdida por lo que deja un pequeño fantasma de píxeles de colores impares alrededor de lo que se eliminó. Intenté hacer algo así como cambiar el píxel si los tres valores están por debajo de 100, pero debido a que la imagen era de mala calidad, los píxeles circundantes ni siquiera eran negros.PIL ¿Mejor manera de reemplazar el color?

¿Alguien sabe de una mejor manera con PIL en Python para reemplazar un color y todo lo que lo rodea? Esta es probablemente la única forma segura de disparo que se me ocurre para eliminar completamente los objetos, sin embargo, no se me ocurre una manera de hacerlo.

La imagen tiene un fondo blanco y un texto que es negro. Digamos que quiero eliminar el texto completamente de la imagen sin dejar ningún artefacto detrás.

¡Realmente agradecería la ayuda de alguien! Gracias

Respuesta

5

Tendrá que representar la imagen como una matriz bidimensional. Esto significa hacer una lista de las listas de píxeles, o ver la matriz de 1 dimensión como una 2d con algunas matemáticas inteligentes. Luego, para cada píxel que esté orientado, deberá encontrar todos los píxeles circundantes. Usted puede hacer esto con un generador de pitón de este modo:

def targets(x,y): 
    yield (x,y) # Center 
    yield (x+1,y) # Left 
    yield (x-1,y) # Right 
    yield (x,y+1) # Above 
    yield (x,y-1) # Below 
    yield (x+1,y+1) # Above and to the right 
    yield (x+1,y-1) # Below and to the right 
    yield (x-1,y+1) # Above and to the left 
    yield (x-1,y-1) # Below and to the left 

Por lo tanto, debería utilizar de esta manera:

for x in range(width): 
    for y in range(height): 
     px = pixels[x][y] 
     if px[0] == 255 and px[1] == 255 and px[2] == 255: 
      for i,j in targets(x,y): 
       newpixels[i][j] = replacementColor 
4

Si los píxeles no son fácilmente identificables por ejemplo, se dice (r < 100 y g < 100 yb < 100) tampoco coincide correctamente con la región negra, significa que tiene mucho ruido.

La mejor manera sería identificar una región y llenarla con el color que desea, puede identificar la región manualmente o puede detectarla por flancos, p. http://bitecode.co.uk/2008/07/edge-detection-in-python/

o un enfoque más sofisticado sería utilizar la biblioteca como opencv (http://opencv.willowgarage.com/wiki/) para identificar objetos.

22

La mejor manera de hacerlo es utilizar el algoritmo "color a alfa" utilizado en Gimp para reemplazar un color. Funcionará perfectamente en tu caso. Reimplementé este algoritmo usando PIL para un procesador de fotografía python de código abierto phatch. Puede encontrar la implementación completa here. Esta es una implementación pura de PIL y no tiene otras dependencias. Puede copiar el código de función y usarlo. Aquí está una muestra utilizando Gimp:

alt text a alt text

Puede aplicar la función color_to_alpha en la imagen utilizando el negro como color. Luego pegue la imagen en un color de fondo diferente para hacer el reemplazo.

Por cierto, esta implementación utiliza el módulo ImageMath en PIL. Es mucho más eficiente que acceder a píxeles usando getdata.

EDIT: Aquí está el código completo:

from PIL import Image, ImageMath 

def difference1(source, color): 
    """When source is bigger than color""" 
    return (source - color)/(255.0 - color) 

def difference2(source, color): 
    """When color is bigger than source""" 
    return (color - source)/color 


def color_to_alpha(image, color=None): 
    image = image.convert('RGBA') 
    width, height = image.size 

    color = map(float, color) 
    img_bands = [band.convert("F") for band in image.split()] 

    # Find the maximum difference rate between source and color. I had to use two 
    # difference functions because ImageMath.eval only evaluates the expression 
    # once. 
    alpha = ImageMath.eval(
     """float(
      max(
       max(
        max(
         difference1(red_band, cred_band), 
         difference1(green_band, cgreen_band) 
        ), 
        difference1(blue_band, cblue_band) 
       ), 
       max(
        max(
         difference2(red_band, cred_band), 
         difference2(green_band, cgreen_band) 
        ), 
        difference2(blue_band, cblue_band) 
       ) 
      ) 
     )""", 
     difference1=difference1, 
     difference2=difference2, 
     red_band = img_bands[0], 
     green_band = img_bands[1], 
     blue_band = img_bands[2], 
     cred_band = color[0], 
     cgreen_band = color[1], 
     cblue_band = color[2] 
    ) 

    # Calculate the new image colors after the removal of the selected color 
    new_bands = [ 
     ImageMath.eval(
      "convert((image - color)/alpha + color, 'L')", 
      image = img_bands[i], 
      color = color[i], 
      alpha = alpha 
     ) 
     for i in xrange(3) 
    ] 

    # Add the new alpha band 
    new_bands.append(ImageMath.eval(
     "convert(alpha_band * alpha, 'L')", 
     alpha = alpha, 
     alpha_band = img_bands[3] 
    )) 

    return Image.merge('RGBA', new_bands) 

image = color_to_alpha(image, (0, 0, 0, 255)) 
background = Image.new('RGB', image.size, (255, 255, 255)) 
background.paste(image.convert('RGB'), mask=image) 
+0

Traté de conseguir que esto funcione, pero dijo que no núcleo módulo llamado y cosas por el estilo , fue solo un desastre. Probablemente soy un idiota, pero no pude hacer que funcionara. Gracias de todos modos, estoy seguro de que su respuesta ayudará a otra persona. – Cookies

+0

No intente ejecutar todo el archivo. Solo copie la función color_to_alpha. De todos modos, me alegra que hayas encontrado una solución que funcione para ti. Si necesita una solución más eficiente, sabe dónde buscar;) –

+0

Lo hice, y primero dijo que el nombre global 'OPCIONES' no está definido, así que copié esa parte y luego dijo que _t no está definido, pero era un módulo que no tenía. Eso es lo que quise decir con desorden, traté de hacer que funcionara pero no pude, el método sugerido a continuación que funcionó para mí está bien, pero si tu función realmente podría eliminar todos los píxeles de fondo de la imagen sería genial. Todavía quedan algunos que confunden tesseract. – Cookies

11

Usando numpy y PIL:

Esto carga la imagen en una matriz de numpy de forma (W,H,3), donde W es la anchura y H es la altura El tercer eje de la matriz representa los 3 canales de color , R,G,B.

import Image 
import numpy as np 

orig_color = (255,255,255) 
replacement_color = (0,0,0) 
img = Image.open(filename).convert('RGB') 
data = np.array(img) 
data[(data == orig_color).all(axis = -1)] = replacement_color 
img2 = Image.fromarray(data, mode='RGB') 
img2.show() 

Desde orig_color es una tupla de longitud 3, y data tiene forma (W,H,3), NumPy broadcasts orig_color a una matriz de forma (W,H,3) para realizar la comparación data == orig_color. El resultado en una matriz booleana de la forma (W,H,3).

(data == orig_color).all(axis = -1) es una matriz booleana de forma (W,H) que es Verdadero siempre que sea el color RGB en data es original_color.

8
#!/usr/bin/python 
from PIL import Image 
import sys 

img = Image.open(sys.argv[1]) 
img = img.convert("RGBA") 

pixdata = img.load() 

# Clean the background noise, if color != white, then set to black. 
# change with your color 
for y in xrange(img.size[1]): 
    for x in xrange(img.size[0]): 
     if pixdata[x, y] == (255, 255, 255, 255): 
      pixdata[x, y] = (0, 0, 0, 255) 
0

Esto es parte de mi código, el resultado le gustaría: source

target

import os 
import struct 
from PIL import Image 
def changePNGColor(sourceFile, fromRgb, toRgb, deltaRank = 10): 
    fromRgb = fromRgb.replace('#', '') 
    toRgb = toRgb.replace('#', '') 

    fromColor = struct.unpack('BBB', bytes.fromhex(fromRgb)) 
    toColor = struct.unpack('BBB', bytes.fromhex(toRgb)) 

    img = Image.open(sourceFile) 
    img = img.convert("RGBA") 
    pixdata = img.load() 

    for x in range(0, img.size[0]): 
     for y in range(0, img.size[1]): 
      rdelta = pixdata[x, y][0] - fromColor[0] 
      gdelta = pixdata[x, y][0] - fromColor[0] 
      bdelta = pixdata[x, y][0] - fromColor[0] 
      if abs(rdelta) <= deltaRank and abs(gdelta) <= deltaRank and abs(bdelta) <= deltaRank: 
       pixdata[x, y] = (toColor[0] + rdelta, toColor[1] + gdelta, toColor[2] + bdelta, pixdata[x, y][3]) 

    img.save(os.path.dirname(sourceFile) + os.sep + "changeColor" + os.path.splitext(sourceFile)[1]) 

if __name__ == '__main__': 
    changePNGColor("./ok_1.png", "#000000", "#ff0000") 
Cuestiones relacionadas