PIL Le meilleur moyen de remplacer la couleur?
-
06-07-2019 - |
Question
J'essaie de supprimer une certaine couleur de mon image, mais cela ne fonctionne pas aussi bien que je l'espérais. J'ai essayé de faire la même chose que celle décrite ici utiliser la LIP pour rendre tout blanc pixels transparents? Cependant, la qualité de l'image est un peu dégradée et laisse donc un petit fantôme de pixels de couleur impairs autour desquels les éléments ont été supprimés. J'ai essayé quelque chose comme changer de pixel si les trois valeurs sont inférieures à 100, mais parce que l'image était de mauvaise qualité, les pixels environnants n'étaient même pas noirs.
Quelqu'un connaît-il une meilleure façon, avec PIL en Python, de remplacer une couleur et tout ce qui l'entoure? C’est probablement le seul moyen infaillible auquel je puisse penser pour enlever complètement les objets, mais je ne vois pas comment le faire.
La photo a un arrière-plan blanc et un texte en noir. Disons simplement que je veux supprimer entièrement le texte de l'image sans laisser d'artefacts.
J'apprécierais vraiment l'aide de quelqu'un! Merci
La solution
Vous devrez représenter l'image sous forme de tableau à 2 dimensions. Cela signifie soit en faisant une liste de listes de pixels, soit en affichant le tableau à 1 dimension comme un tableau en 2D avec quelques astuces mathématiques. Ensuite, pour chaque pixel ciblé, vous devez rechercher tous les pixels environnants. Vous pouvez le faire avec un générateur de python ainsi:
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
Donc, vous l'utiliseriez comme ceci:
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
Autres conseils
La meilleure façon de le faire est d’utiliser la " couleur en alpha " algorithme utilisé pour Gimp remplacer une couleur. Cela fonctionnera parfaitement dans votre cas. J'ai réimplémenté cet algorithme en utilisant PIL pour un processeur de photo python open source phatch . Vous pouvez trouver la mise en œuvre complète ici . Ceci est une implémentation PIL pure et il n’a pas d’autres dépendances. Vous pouvez copier le code de fonction et l'utiliser. Voici un exemple utilisant Gimp:
à
Vous pouvez appliquer la fonction color_to_alpha
à l'image en utilisant le noir comme couleur. Collez ensuite l'image sur une couleur d'arrière-plan différente pour effectuer le remplacement.
À propos, cette implémentation utilise le module ImageMath dans PIL. C'est beaucoup plus efficace que d'accéder aux pixels avec getdata.
EDIT: voici le code complet:
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)
Utilisation de Numpy et de PIL:
Ceci charge l'image dans un tableau numpy de forme (W,H,3)
, où W
est le
width et H
est la hauteur. Le troisième axe du tableau représente la couleur 3
canaux, 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()
Étant donné que orig_color
est un tuple de longueur 3, et data
a
forme data ==
orig_color
, NumPy
diffusions
(data == orig_color).all(axis = -1)
à un tableau de forme (W,H)
pour effectuer la comparaison original_color
. Le résultat dans un tableau booléen de forme <=>.
<=> est un tableau booléen de forme <=> qui est vrai partout où la couleur RVB dans <=> est <=>.
#!/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)
Si les pixels ne sont pas facilement identifiables, par exemple, vous dites (r < 100 et g < 100 et b < 100) ne correspond pas non plus correctement à la région noire, cela signifie vous avez beaucoup de bruit.
Le meilleur moyen serait d’identifier une région et de la remplir avec la couleur de votre choix. Vous pouvez identifier la région manuellement ou par détection des contours, par exemple. http://bitecode.co.uk/2008/07/edge -detection-in-python /
ou une approche plus sophistiquée consisterait à utiliser une bibliothèque comme opencv ( http://opencv.willowgarage.com/ wiki / ) pour identifier les objets.
Cela fait partie de mon code, le résultat voudrait: source
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")