Использование метода Image.point() в PIL для управления пиксельными данными
Я использую Python Imaging Library, чтобы раскрасить черно-белое изображение с помощью таблицы поиска, которая определяет отношения цвета. Таблица поиска - это всего лишь 256-элементный список кортежей RGB:
>>> len(colors)
256
>>> colors[0]
(255, 237, 237)
>>> colors[127]
(50, 196, 33)
>>>
Моя первая версия использовала методы getpixel()
и putpixel()
:
for x in range(w):
for y in range(h):
pix = img.getpixel((x,y))
img.putpixel((x,y), colors[pix[0]])
Это было ужасно медленно. Отчет A profile
указывал на методы putpixel
и getpixel
в качестве виновников. Небольшое исследование (т.е. Чтение документов), и я нахожу "Обратите внимание, что этот метод относительно медленный". re: putpixel
. (фактическое время выполнения: 53 с в putpixel
и 50 с getpixel
для изображения 1024x1024)
Основываясь на предположении в документах, я использовал im.load()
и прямой доступ к пикселям:
pixels = img.load()
for x in range(w):
for y in range(h):
pix = pixels[x, y]
pixels[x, y] = colors[pix[0]]
Обработка ускоряется на порядок, но все еще медленная: около 3,5 секунд для обработки изображения 1024x1024.
Более тщательное изучение документов PIL, по-видимому, указывает, что Image.point()
точно предназначено для этой цели:
im.point(table)
= > изображение
im.point(function)
= > изображение
Возвращает копию изображения, в котором каждый пиксель был сопоставлен в данной таблице. Таблица должна содержать 256 значений для диапазона в изображении. Если вместо этого используется функция, она должна принимать один аргумент. Функция вызывается один раз для каждого возможного значения пикселя, и результирующая таблица применяется ко всем полосам изображения.
Я потратил некоторое время на хакерство с интерфейсом, но, похоже, не совсем правильно понял. Простите мое невежество, но документы PIL короткие, и у меня мало опыта обработки изображений. Я немного поработал с поиском, и я привел несколько примеров, но ничего, что сделало использование "click" для меня. Таким образом, наконец, мои вопросы:
- Является ли
Image.point()
правильным инструментом для этого задания?
- Какой формат/структура
Image.point()
ожидает таблицу?
- Может ли кто-то выполнить примерную реализацию? На каждой итерации, которую я пробовал до сих пор, оказалось прямое черное изображение.
Ответы
Ответ 1
Является ли Image.point() правильным инструментом для эта работа?
Да, действительно, Image.point()
идеально подходит для этой работы
Какой формат/структура Image.point() ожидает таблицу?
Вы должны сгладить список, поэтому вместо [(12, 140, 10), (10, 100, 200), ...]
используйте:
[12, 140, 10, 10, 100, 200, ...]
Вот быстрый пример, который я только что пробовал:
im = im.point(range(256, 0, -1) * 3)
![alt text]()
![alt text]()
И, кстати, если вам нужно больше контролировать цвета, и вы чувствуете, что Image.point не для вас, вы также можете использовать Image.getdata
и Image.putdata
для более быстрого изменения цветов, чем load
и putpixel
, Он медленнее, чем Image.point
.
Image.getdata
дает вам список всех пикселей, модифицирует их и записывает обратно с помощью Image.putdata
. Это так просто. Но сначала попробуйте сделать это, используя Image.point
.
ИЗМЕНИТЬ
Я ошибся в первом объяснении, я сейчас правильно объясню:
Таблица цветов на самом деле похожа на это
[0, 1, 2, 3, 4, 5, ...255, 0, 1, 2, 3, ....255, 0, 1, 2, 3, ...255]
Каждая полоса находится рядом друг с другом.
Чтобы изменить цвет (0, 0, 0) на (10, 100, 10), он должен выглядеть следующим образом:
[10, 1, 2, 3, 4, 5, ...255, 100, 1, 2, 3, ....255, 10, 1, 2, 3, ...255]
Чтобы преобразовать список цветов в нужном формате, попробуйте следующее:
table = sum(zip(*colors), ())
Я думаю, что мой первый пример должен продемонстрировать форму для вас.
Ответ 2
Я думаю, что это было бы более типично для point
для поэтапного подхода (например, снятого непосредственно из 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)