PIL Лучший способ заменить цвет?
Я пытаюсь удалить определенный цвет из моего изображения, но он не работает так хорошо, как я надеялся. Я попытался сделать то же самое, что и здесь, Используя PIL, чтобы сделать все белые пиксели прозрачными? однако качество изображения немного потеряно, поэтому он оставляет немного призрака нечетного цвета пиксели вокруг того, что было удалено. Я попытался сделать что-то вроде изменения пикселя, если все три значения ниже 100, но из-за низкого качества изображения окружающие пиксели не были даже черными.
Кто-нибудь знает, как лучше с PIL в Python заменить цвет и что-нибудь вокруг него? Вероятно, это единственный верный способ, который я могу придумать, чтобы полностью удалить объекты, но я не могу придумать, как это сделать.
Изображение имеет белый фон и черный текст. Скажем, я хочу полностью удалить текст из изображения, не оставляя никаких артефактов.
Было бы действительно признательно, что кто-то поможет! Спасибо
Ответы
Ответ 1
Вам нужно представить изображение как двухмерный массив. Это означает либо составление списка списков пикселей, либо просмотр 1-мерного массива как 2d с некоторой умной математикой. Затем для каждого целевого пикселя вам нужно будет найти все окружающие пиксели. Вы можете сделать это с помощью генератора python, таким образом:
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
Итак, вы бы использовали его следующим образом:
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
Ответ 2
Лучший способ сделать это - использовать алгоритм "color to alpha", используемый в Gimp для замены цвета. Это будет отлично работать в вашем случае. Я повторил этот алгоритм с помощью PIL для открытого исходного кода python-фотопроцессора phatch. Здесь вы можете найти полную реализацию здесь. Это чистая реализация PIL, и у нее нет других зависимостей. Вы можете скопировать код функции и использовать его. Вот пример с использованием Gimp:
до ![alt text]()
Вы можете применить функцию color_to_alpha
на изображении, используя черный цвет. Затем вставьте изображение на другой цвет фона, чтобы выполнить замену.
Кстати, эта реализация использует модуль ImageMath в PIL. Это намного эффективнее, чем доступ к пикселям с использованием getdata.
EDIT: Вот полный код:
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)
Ответ 3
Использование numpy и PIL:
Это загружает изображение в массив numpy формы (W,H,3)
, где W
- это
ширина и H
- это высота. Третья ось массива представляет собой 3 цвета
каналов, 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()
Так как orig_color
является кортежем длины 3, а data
имеет
форма (W,H,3)
, NumPy
трансляции
orig_color
в массив формы (W,H,3)
для выполнения сравнения data ==
orig_color
. Результат в булевом массиве формы (W,H,3)
.
(data == orig_color).all(axis = -1)
является булевым массивом формы (W,H)
, который
True, где RGB-цвет в data
равен original_color
.
Ответ 4
#!/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)
Ответ 5
Если пиксели нелегко идентифицировать, например, вы говорите (r < 100 и g < 100 и b < 100) также неправильно соответствует черной области, это означает, что у вас много шума.
Лучшим способом было бы определить регион и заполнить его желаемым цветом, вы можете идентифицировать регион вручную или, возможно, путем обнаружения края, например. http://bitecode.co.uk/2008/07/edge-detection-in-python/
или более сложный подход заключается в использовании библиотеки, такой как opencv (http://opencv.willowgarage.com/wiki/) для идентификации объектов.
Ответ 6
Это часть моего кода, результат:
источник
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")