Фильтры
Это вторая часть разбора библиотеки 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")
«SMOOTH» и «SMOOTH_MORE». Это два фильтра, которые дают сглаживание, просто тот, что с припиской more, даёт ещё более заметный эффект. More так и переводится – «более». Smooth, кстати, можно перевести как «гладкий», «нетерпкий». Сглаживание чем-то похоже на блюр, но оно не так сильно даёт мыльную картинку, как это делает предыдущий эффект. Конечно, каждый предназначен под свои цели и задачи.
image = Image.open("input.jpg")
result = image.filter(ImageFilter.SMOOTH)
result.save("output_smooth.jpg")
image = Image.open("input.jpg")
result = image.filter(ImageFilter.SMOOTH_MORE)
result.save("output_smooth_more.jpg")
«DETAIL». А вот это уже противоположность размытию. Переводится как «детально» или «подробнее». Сам по себе фильтр делает изображение хрустящее, выделяет детали за счёт обратного воздействия: усиления центрального пикселя и ослабления соседних. Правда не так агрессивно, как следующий эффект в этом списке.
image = Image.open("input.jpg")
result = image.filter(ImageFilter.DETAIL)
result.save("output_detail.jpg")
«SHARPEN». Переводится как «точить» или «оттачивать». Сильно повышает резкость, делая изображение чётким. Усиливает контраст на границах, что также множит шум.
image = Image.open("input.jpg")
result = image.filter(ImageFilter.SHARPEN)
result.save("output_sharpen.jpg")
«CONTOUR». Переводится как «контур»: выделяет контуры изображения, оставляя только линии переходов между цветами. Это чем-то напоминает мне карандашный рисунок.
image = Image.open("input.jpg")
result = image.filter(ImageFilter.CONTOUR)
result.save("output_contour.jpg")
«FIND_EDGES». Дословно: «поиск границы». Находит и оставляет только границы объектов. Во многом мне это напомнило как бы «тёмную версию» CONTOUR, либо же, если увеличить границы, будет косвенно напоминать комиксный чёрно-белый стиль.
image = Image.open("input.jpg")
result = image.filter(ImageFilter.FIND_EDGES)
result.save("output_find_edges.jpg")
Кстати, по поводу увеличения границ.
Стоит упомянуть «EDGE_ENHANCE» и «EDGE_ENHANCE_MORE»: фильтры, увеличивающие данные границы. Опять же «more» обозначает «сильнее», а «enhance» переводится как «улучшать» (улучшать выделение).
image = Image.open("input.jpg")
result = image.filter(ImageFilter.EDGE_ENHANCE)
result.save("output_edge_enhance.jpg")
image = Image.open("input.jpg")
result = image.filter(ImageFilter.EDGE_ENHANCE_MORE)
result.save("output_edge_enhance_more.jpg")
И один прикольный, не вписывающийся ни в один из перечисленных типов:
«EMBOSS». Переводится как «выбивать» и действительно создаёт эффект как бы выдавленной картинки.
image = Image.open("input.jpg")
result = image.filter(ImageFilter.EMBOSS)
result.save("output_emboss.jpg")
Регулируемые показатели
Если базовые фильтры были освоены на достаточном уровне, то со временем начинает приходить желание большего контроля. Хочется самому задавать параметры, комбинировать и осуществлять свои дизайнерские эксперименты. И данная библиотека сполна это позволяет.
Для начала посмотрим на ранговые и статистические фильтры. Смысл один и тот же: существует определённое окно (обычно 3×3, то есть 9 значений), и мы можем что-то с этим окном сделать. А вариантов множество: взять минимальное значение и притянуть все остальные значения в данном окне к нему, либо же наоборот – взять максимальное, или медианное значение. В общем, всё, что можно сделать с массивом, можно осуществлять с этими окнами, что сильно упрощает работу со всем изображением, создавая конкретную механику работы с крупной картинкой. Тут буду максимально кратким – просто объясню, что делает каждый из инструментов, и мы перейдём уже к продвинутым фильтрам.
MinFilter – заменяет каждый пиксель на минимальное значение в его окне. Изображение темнеет. Также мне нравится, как оно немного пикселизирует картинку, по крайней мере так получилось с текущим исходником.
image = Image.open("input.jpg")
result = image.filter(ImageFilter.MinFilter(size=3))
result.save("output_min_filter.jpg")
MaxFilter – заменяет каждый пиксель на максимальное значение в его окне. Изображение светлеет.
image = Image.open("input.jpg")
result = image.filter(ImageFilter.MaxFilter(size=3))
result.save("output_max_filter.jpg")
MedianFilter – заменяет каждый пиксель на медианное значение в его окне.
image = Image.open("input.jpg")
result = image.filter(ImageFilter.MedianFilter(size=3))
result.save("output_median_filter.jpg")
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")
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")
А теперь продвинутые фильтры! В предыдущей статье их было, мягко скажем, в достатке, теперь пришло время разобрать подробнее.
BoxBlur (размытие по коробке) – обычный блюр, только теперь вы можете его регулировать.
image = Image.open("input.jpg")
result = image.filter(ImageFilter.BoxBlur(radius=5))
result.save("output_box_blur.jpg")
GaussianBlur (размытие по Гауссу) – данный блюр выглядит естественнее, чем обычный, и именно его лично я чаще всего использую для выделения переднего плана по отношению к заднему, к которому он и применяется. Суть в том, что размытие по Гауссу не оставляет столь резких границ на краях объектов после своего применения. Ну а также это более гибкий инструмент в контексте Pillow, ведь можно самостоятельно устанавливать значение, насколько именно нужно размыть. В общем, сказка.
image = Image.open("input.jpg")
result = image.filter(ImageFilter.GaussianBlur(radius=5))
result.save("output_gaussian_blur.jpg")
UnsharpMask (контурная резкость) – повышение резкости. Тут следует объяснить механику: более резкое изображение есть разница между классическим изображением и размытым. Следовательно, его можно вывести как данную разницу, чем и занимается «контурная резкость». Сначала мы задаём параметр, насколько сильно заблюрена будет размытая копия, а потом – насколько сильно стоило бы усилить детали (в процентах), а третьим параметром задаётся минимальный перепад, который стоило бы усиливать (это защищает гладкие области от усиления шума). Самый гибкий инструмент для повышения резкости у изображения.
image = Image.open("input.jpg")
result = image.filter(ImageFilter.UnsharpMask(radius=2, percent=150, threshold=3))
result.save("output_unsharp_mask.jpg")
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")
Общая регуляция
Перейдём к третьему типу фильтров – 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/