2009-10-24 12 views
14

Sto cercando di rimuovere un determinato colore dalla mia immagine, tuttavia non funziona come avrei sperato. Ho provato a fare la stessa cosa vista qui Using PIL to make all white pixels transparent? ma la qualità dell'immagine è un po 'sminuita, quindi lascia un po' di fantasmi di strani pixel colorati attorno a ciò che è stato rimosso. Ho provato a fare qualcosa come cambiare pixel se tutti e tre i valori sono inferiori a 100 ma poiché l'immagine era di scarsa qualità, i pixel circostanti non erano nemmeno neri.PIL Il modo migliore per sostituire il colore?

Qualcuno sa di un modo migliore con PIL in Python per sostituire un colore e tutto ciò che lo circonda? Questo è probabilmente l'unico modo sicuro in cui posso pensare di rimuovere completamente gli oggetti, ma non riesco a pensare a un modo per farlo.

L'immagine ha uno sfondo bianco e il testo è nero. Diciamo solo che voglio rimuovere il testo interamente dall'immagine senza lasciare alcun artefatto alle spalle.

Gradirei davvero l'aiuto di qualcuno! Grazie

risposta

5

Dovrai rappresentare l'immagine come una matrice bidimensionale. Ciò significa creare un elenco di liste di pixel o visualizzare l'array 1-dimensionale come un 2D con una matematica intelligente. Quindi, per ciascun pixel targetizzato, dovrai trovare tutti i pixel circostanti. Si potrebbe fare questo con un generatore di pitone così:

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 

Quindi, si dovrebbe utilizzare in questo modo:

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

Se i pixel non sono facilmente identificabili per esempio si dice (r < 100 g < 100 e b < 100) inoltre non corrisponde correttamente alla regione nera, significa che hai un sacco di rumore.

Il modo migliore sarebbe identificare una regione e riempirla con il colore desiderato, è possibile identificare la regione manualmente o essere rilevata dal bordo, ad es. http://bitecode.co.uk/2008/07/edge-detection-in-python/

o un approccio più sofisticato sarebbe utilizzare la libreria come opencv (http://opencv.willowgarage.com/wiki/) per identificare gli oggetti.

22

Il modo migliore per farlo è utilizzare l'algoritmo "colore in alfa" utilizzato in Gimp per sostituire un colore. Funzionerà perfettamente nel tuo caso. Ho reimplementato questo algoritmo utilizzando PIL per un processore di foto python open source phatch. È possibile trovare l'intera implementazione here. Questa è una pura implementazione del PIL e non ha altre dipendenze. È possibile copiare il codice funzione e usarlo. Ecco un esempio utilizzando Gimp:

alt text a alt text

È possibile applicare la funzione color_to_alpha sulle immagini utilizzando il nero come colore. Quindi incollare l'immagine su un colore di sfondo diverso per fare la sostituzione.

A proposito, questa implementazione utilizza il modulo ImageMath in PIL. È molto più efficiente dell'accesso ai pixel usando i getdata.

EDIT: Ecco il codice 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

Ho cercato di arrivare a questo lavoro, ma ha detto di no nucleo modulo chiamato e cose del genere , era solo un casino. Sono probabilmente un idiota ma non riuscivo proprio a farlo funzionare. Grazie comunque sono sicuro che la tua risposta aiuterà qualcun altro. – Cookies

+0

Non si dovrebbe provare a eseguire l'intero file. Basta copiare la funzione color_to_alpha stessa. Ad ogni modo, sono felice che tu abbia trovato una soluzione che funziona per te. Se hai bisogno di una soluzione più efficiente, sai dove guardare;) –

+0

ho fatto, e prima ho detto che il nome globale "OPZIONI" non è definito, quindi ho copiato quella parte e poi ha detto che _t non è definito, ma era un modulo che non avevo. Questo è ciò che intendevo per pasticcio, ho cercato di farlo funzionare ma non potevo, il metodo suggerito di seguito che ha funzionato per me va bene, ma se la tua funzione potesse davvero eliminare tutti i pixel di sfondo nell'immagine sarebbe fantastico. Ne rimangono ancora alcuni che confondono il tesseract. – Cookies

11

Uso NumPy e PIL:

Questa carica l'immagine in una matrice NumPy di ​​forma (W,H,3), dove W è la larghezza e H è l'altezza. Il terzo asse dell'array rappresenta i 3 canali , 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() 

Da orig_color è una tupla di lunghezza 3, e presenta data forma (W,H,3), NumPy broadcasts orig_color a una matrice di forma (W,H,3) per eseguire il confronto data == orig_color. Il risultato in una matrice booleana di forma (W,H,3).

(data == orig_color).all(axis = -1) è una matrice booleana di forma (W,H) cui è vero ovunque il colore RGB in data è 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

Questo fa parte del mio codice, il risultato vorrebbe: 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") 
Problemi correlati