Подтвердить что ты не робот

Использование метода 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() ожидает таблицу?
  • Может ли кто-то выполнить примерную реализацию? На каждой итерации, которую я пробовал до сих пор, оказалось прямое черное изображение.
4b9b3361

Ответ 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 textalt 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)