Pillow для обработки изображений: все фильтры от А до Я

—

от автора

Фильтры

Это вторая часть разбора библиотеки Python Pillow. В прошлой статье был сделан упор на общее направление и примеры, теперь же давайте разберём конкретный инструментарий: отвечу на вопросы, что вообще можно использовать и каким образом. Можно назвать это так: прошлая статья – введение в ремонт, эта – хранилище инструментов. Стоит учесть, что всю более подробную информацию можно найти в сети, а с целями (зачем, для чего) придётся определяться самим. Впрочем, это и так очевидно.

Вся суть, благодаря которой нога моя ступила на территорию Pillow, можно описать следующим образом: желание автоматизации однотипной обработки нескольких файлов подряд. Через Photoshop можно обработать одно фото, но если у вас есть определённый шаблон, придётся долго возиться с каждым отдельным изображением. Нужен был инструмент, который был бы достаточно гибким, чтобы при желании и менять параметры, и создавать из последовательностей – функции. Таким инструментом оказалась библиотека Python, форк PIL, – Python Pillow.

Как и в программах для редактирования визуальных объектов данная библиотека работает по принципу фильтров: есть определённые фильтры, которые делают что-то с изображением, и можно задать определённое значение таким образом, чтобы указать программе, насколько сильно стоит это применить. И да, абсолютно весь тот шикарный визуал, который вы когда-либо видели в сети, был обработан, описан и создан именно по такой цепочке: ввод, наложение некоторых фильтров в каком-то соотношении, вывод.

Разобьём инструментарий на типы: равно как и отвёртки лежат отдельно от болтов… В Pillow есть три типа фильтров: готовые пресеты (те, что не требуют указания дополнительных значений), регулируемые фильтры (те, что требуют указания), и «ImageEnhance» (для регуляции контраста, яркости, резкости). Первые два типа объединены в единое семейство: «ImageFilter». Рассмотрим сначала их.

Есть три типа эффектов: блюр, выделение границ, увеличение чёткости. Разумеется, в каждом конкретном – свои нюансы, но для запоминания пойдёт. Далее буду перечислять и названия данных эффектов в Pillow, и представлю возможность увидеть эффект своими глазами. Понятное дело, с примерами кода!

Сами описания будут строиться по принципу: перевод, краткое описание применяемого эффекта и, где это уместно, сравнение с другими похожими фильтрами.

Перед тем, как применить данную библиотеку на практике, необходимо её инициализировать:

from PIL import Image, ImageFilter

«BLUR». Blur переводится как «размытие», и, как и все остальные названия в данном списке, оно хорошо отражает результат применения фильтра. Действительно, при наложении картинка как бы «размывается»: цвет каждого пикселя усредняется по отношению к своим соседям, что и даёт эффект размытой картинки. Интересный инструмент, например, для обработки фона: когда нам нужно немного выделить объекты переднего плана, можно чуть подразмыть объекты заднего.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.BLUR)

result.save("output_blur.jpg")

BLUR

BLUR

«SMOOTH» и «SMOOTH_MORE». Это два фильтра, которые дают сглаживание, просто тот, что с припиской more, даёт ещё более заметный эффект. More так и переводится – «более». Smooth, кстати, можно перевести как «гладкий», «нетерпкий». Сглаживание чем-то похоже на блюр, но оно не так сильно даёт мыльную картинку, как это делает предыдущий эффект. Конечно, каждый предназначен под свои цели и задачи.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.SMOOTH)

result.save("output_smooth.jpg")

SMOOTH

SMOOTH

image = Image.open("input.jpg")

result = image.filter(ImageFilter.SMOOTH_MORE)

result.save("output_smooth_more.jpg")

SMOOTH_MORE

SMOOTH_MORE

«DETAIL». А вот это уже противоположность размытию. Переводится как «детально» или «подробнее». Сам по себе фильтр делает изображение хрустящее, выделяет детали за счёт обратного воздействия: усиления центрального пикселя и ослабления соседних. Правда не так агрессивно, как следующий эффект в этом списке.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.DETAIL)

result.save("output_detail.jpg")

DETAIL

DETAIL

«SHARPEN». Переводится как «точить» или «оттачивать». Сильно повышает резкость, делая изображение чётким. Усиливает контраст на границах, что также множит шум.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.SHARPEN)

result.save("output_sharpen.jpg")

SHARPEN

SHARPEN

«CONTOUR». Переводится как «контур»: выделяет контуры изображения, оставляя только линии переходов между цветами. Это чем-то напоминает мне карандашный рисунок.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.CONTOUR)

result.save("output_contour.jpg")

CONTOUR

CONTOUR

«FIND_EDGES». Дословно: «поиск границы». Находит и оставляет только границы объектов. Во многом мне это напомнило как бы «тёмную версию» CONTOUR, либо же, если увеличить границы, будет косвенно напоминать комиксный чёрно-белый стиль.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.FIND_EDGES)

result.save("output_find_edges.jpg")

FIND_EDGES

FIND_EDGES

Кстати, по поводу увеличения границ.

Стоит упомянуть «EDGE_ENHANCE» и «EDGE_ENHANCE_MORE»: фильтры, увеличивающие данные границы. Опять же «more» обозначает «сильнее», а «enhance» переводится как «улучшать» (улучшать выделение).

image = Image.open("input.jpg")

result = image.filter(ImageFilter.EDGE_ENHANCE)

result.save("output_edge_enhance.jpg")

EDGE_ENHANCE

EDGE_ENHANCE

image = Image.open("input.jpg")

result = image.filter(ImageFilter.EDGE_ENHANCE_MORE)

result.save("output_edge_enhance_more.jpg")

EDGE_ENHANCE_MORE

EDGE_ENHANCE_MORE

И один прикольный, не вписывающийся ни в один из перечисленных типов:

«EMBOSS». Переводится как «выбивать» и действительно создаёт эффект как бы выдавленной картинки.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.EMBOSS)

result.save("output_emboss.jpg")

EMBOSS

EMBOSS

Регулируемые показатели

Если базовые фильтры были освоены на достаточном уровне, то со временем начинает приходить желание большего контроля. Хочется самому задавать параметры, комбинировать и осуществлять свои дизайнерские эксперименты. И данная библиотека сполна это позволяет.

Для начала посмотрим на ранговые и статистические фильтры. Смысл один и тот же: существует определённое окно (обычно 3×3, то есть 9 значений), и мы можем что-то с этим окном сделать. А вариантов множество: взять минимальное значение и притянуть все остальные значения в данном окне к нему, либо же наоборот – взять максимальное, или медианное значение. В общем, всё, что можно сделать с массивом, можно осуществлять с этими окнами, что сильно упрощает работу со всем изображением, создавая конкретную механику работы с крупной картинкой. Тут буду максимально кратким – просто объясню, что делает каждый из инструментов, и мы перейдём уже к продвинутым фильтрам.

MinFilter – заменяет каждый пиксель на минимальное значение в его окне. Изображение темнеет. Также мне нравится, как оно немного пикселизирует картинку, по крайней мере так получилось с текущим исходником.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.MinFilter(size=3))

result.save("output_min_filter.jpg")

MinFilter

MinFilter

MaxFilter – заменяет каждый пиксель на максимальное значение в его окне. Изображение светлеет.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.MaxFilter(size=3))

result.save("output_max_filter.jpg")

MaxFilter

MaxFilter

MedianFilter – заменяет каждый пиксель на медианное значение в его окне.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.MedianFilter(size=3))

result.save("output_median_filter.jpg")

MedianFilter

MedianFilter

ModeFilter – заменяет каждый пиксель на наиболее частое значение в его окне. Интересный факт: код с данным фильтром выполнялся у меня целых 10 секунд, что многовато – остальные в данной категории выполнялись за 2, а базовые (без указания параметров) – вовсе за 1. Но именно ModeFilter, как мне кажется, дал тот самый долгожданный мною эффект – создал нормальную чёткую картинку, чётче оригинального изображения. Это может создать условия для использования его не только в виде промежуточного этапа в цепочке эффектов под определённым def (define).

image = Image.open("input.jpg")

result = image.filter(ImageFilter.ModeFilter(size=3))

result.save("output_mode_filter.jpg")

ModeFilter

ModeFilter

RankFilter – что-то вроде конструктора: можно самостоятельно задать размер окна и ранг.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.RankFilter(size=3, rank=4)) # size=3, rank=4 — это медиана для окна 3x3

result.save("output_rank_filter.jpg")

RankFilter

RankFilter

А теперь продвинутые фильтры! В предыдущей статье их было, мягко скажем, в достатке, теперь пришло время разобрать подробнее.

BoxBlur (размытие по коробке) – обычный блюр, только теперь вы можете его регулировать.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.BoxBlur(radius=5))

result.save("output_box_blur.jpg")

BoxBlur

BoxBlur

GaussianBlur (размытие по Гауссу) – данный блюр выглядит естественнее, чем обычный, и именно его лично я чаще всего использую для выделения переднего плана по отношению к заднему, к которому он и применяется. Суть в том, что размытие по Гауссу не оставляет столь резких границ на краях объектов после своего применения. Ну а также это более гибкий инструмент в контексте Pillow, ведь можно самостоятельно устанавливать значение, насколько именно нужно размыть. В общем, сказка.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.GaussianBlur(radius=5))

result.save("output_gaussian_blur.jpg")

GaussianBlur

GaussianBlur

UnsharpMask (контурная резкость) – повышение резкости. Тут следует объяснить механику: более резкое изображение есть разница между классическим изображением и размытым. Следовательно, его можно вывести как данную разницу, чем и занимается «контурная резкость». Сначала мы задаём параметр, насколько сильно заблюрена будет размытая копия, а потом – насколько сильно стоило бы усилить детали (в процентах), а третьим параметром задаётся минимальный перепад, который стоило бы усиливать (это защищает гладкие области от усиления шума). Самый гибкий инструмент для повышения резкости у изображения.

image = Image.open("input.jpg")

result = image.filter(ImageFilter.UnsharpMask(radius=2, percent=150, threshold=3))

result.save("output_unsharp_mask.jpg")

UnsharpMask

UnsharpMask

Kernel (матричные фильтры) – тоже своего рода конструктор. Помните, говорил вам про матрицы, называемые ранее окнами? Так вот, благодаря этому конструктору можно задать размер матрицы (рекомендуется только до 5×5, но больше обычно и не нужно) и как бы передать её фильтру. Kernel применяет размер матрицы и ваше ядро к изображению, создавая эффект в зависимости от установленных в матрице коэффициентов.

from PIL import Image, ImageFilter

image = Image.open("input.jpg")

kernel = ImageFilter.Kernel(

size=(3, 3),

kernel=[

0, -1, 0,

-1, 5, -1,

0, -1, 0

],

scale=1,

offset=0

)

result = image.filter(kernel)

result.save("output_kernel.jpg")

Kernel

Kernel

Общая регуляция

Перейдём к третьему типу фильтров – ImageEnhance. Он работает не через матрицы (окошки), а с помощью регуляции яркости, контрастности и резкости сразу на всём изображении. Можно менять «Color» (дословно – цвета, но в этом контексте обозначает насыщенность), «Contrast» (контраст), «Brightness» (яркость), «Sharpness» (резкость).

Для этого типа синтаксис будет чуть-чуть иным, но теперь вместо нескольких примеров кода покажу один уникальный рецепт:

image = Image.open("input.jpg")

enhancer = ImageEnhance.SOMETHING(image)

result = enhancer.enhance(1.5)

result.save("output_SOMETHING.jpg")

На месте «SOMETHING» можно вставить: Color, Contrast, Brightness, Sharpness.

Теперь весь инструментарий прямо перед глазами, и лишь руки радуются и тянутся, как вдруг разум осознаёт необходимость системы. Да, именно это отличает хорошее нагромождение эффектов от последовательного добавления ради какой-то цели. Как вы уже понимаете, система возникает только с определением цели, поэтому прежде следует разобраться, какой результат нужен, потом продумать способы решения, и только после – реализовывать).

Одна и та же категория может изменить свою суть при смене набора. Все категории в каждом наборе же должны быть заданы в рамках смыслового диапазона, то есть связаны логически. Множество наборов – становится функцией.

ссылка на оригинал статьи https://habr.com/ru/articles/1087768/