Использование метода Image.point () в PIL для манипулирования данными пикселей - PullRequest
19 голосов
/ 02 февраля 2010

Я использую Python Imaging Library , чтобы раскрасить черно-белое изображение с помощью справочной таблицы, которая определяет цветовые отношения. Таблица поиска - это просто список из RGB-кортежей из 256 элементов:

>>> 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]])

Это было ужасно медленно. В отчете 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) => image

im.point(function) => image

Возвращает копию изображения, где каждый пиксель был отображен в данной таблице. Таблица должна содержать 256 значений для каждой полосы в изображении. Если вместо этого используется функция, она должна принимать один аргумент. Функция вызывается один раз для каждого возможного значения пикселя, и результирующая таблица применяется ко всем полосам изображения.

Я провел некоторое время, разбираясь с интерфейсом, но, похоже, не совсем понял, как это правильно. Простите мое невежество, но документы PIL кратки, и у меня нет большого опыта обработки изображений. Я немного погуглил и нашел несколько примеров, но ничего, что сделало использование «щелчком» для меня. Итак, наконец, мои вопросы:

  • Является ли Image.point() подходящим инструментом для этой работы?
  • В каком формате / структуре Image.point() ожидает таблицу?
  • Может кто-нибудь набросать пример реализации? Каждая итерация, которую я пробовал до сих пор, заканчивалась прямым черным изображением.

Ответы [ 2 ]

15 голосов
/ 02 февраля 2010

Является ли 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.


EDIT

Я сделал ошибку в первом объяснении, сейчас я объясню правильно:

Таблица цветов на самом деле такова

[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), ())

Я думаю, мой первый пример должен продемонстрировать формат для вас.

3 голосов
/ 02 февраля 2010

Я думаю, что это может быть более типично для point для каждой полосы, например, так (взято непосредственно из учебника PIL ):

# 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)
...