Фильтры
Это вторая часть разбора библиотеки 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.
Теперь весь инструментарий прямо перед глазами, и лишь руки радуются и тянутся, как вдруг разум осознаёт необходимость системы. Да, именно это отличает хорошее нагромождение эффектов от последовательного добавления ради какой‑то цели. Как вы уже понимаете, система возникает только с определением цели, поэтому прежде следует разобраться, какой результат нужен, потом продумать способы решения, и только после — реализовывать).
Одна и та же категория может изменить свою суть при смене набора. Все категории в каждом наборе же должны быть заданы в рамках смыслового диапазона, то есть связаны логически. Множество наборов — становится функцией.
Комментарии (8)

alexeymogulev
28.09.2026 17:50Автору поста спасибо! Вы в последних статьях сместились в сторону окружения для Питона, это интересно. Да я понимаю, что можно у ИИ спросить, но блин! Приятно именно зайти случайно наткнутся на чтиво за чаем и желанием потестить чего-то.

U235U235
28.09.2026 17:50Статья слабенькая, много воды, на мой взгяд, рассчитаная на совсем уж новичков. Все это есть в официальной документации к PIL.
Если нужен консольный "фотошоп" для автоматизации, то есть смысл обратить внимание на замечательный ImageMagick. Возможности которого гораздо больше чем у PIL.
Если нужна серьезная обработка изображений в Python, то стоит сразу начинать с OpenCV и/или scikit-image. Вот такое мнение.

ivanstor
28.09.2026 17:50ImageMagick/GraphicsMagick. Если что-то совсем хитрое — G'mic, у него есть и GUI, и плагины и под Gimp, и под PS.
dyadyaSerezha
Первая очевидная мысль - почему бы не сделать, если ещё никто не сделал, GUI-приложение с этой библиотекой? Хороший опыт для AI, кстати.
Ну и Lightroom, насколько я помню, именно на это и заточен - запоминается последовательность фильтров и применяется пакетно или не очень ко многим изображениям. Да и неужели для Фотошоп нет таких же плагинов?
А то как-то уж слишком “на коленке” всё это выглядит.
alexeymogulev
У фотошопа возможности практически не ограничены и в пакетной обработке и в плане алгоритмов. Если через интерфейс уже не достаточно, то:
- Базовый GUI и скриптинг через UXP
- Можно ещё дальше -> ExtendScript устаревает, но пашет везде вроде
- Можно вообще в Плюсы и СДК залезть, API вроде всё ещё открыт
Но если по простому -> Экшены мощная штука!
П.С. По теме GUI обёртки над Pillow. Одновременно невозможно и не имеет смысла. Тот же Фотошоп очень заморочен на точности, например вроде простое наложение, но я около полугода парился в Сях пытаясь воспроизвести идентичный результат без накоплений погрешности. Не тут то было, там доволно специфичные алгоритмы. Человек или маленькая команда просто не вывезет и 10% работы с цветом на том же уровне.
Немного вникал в это, вот и изрёк простыню текста. Вот.
dyadyaSerezha
Ну вот, я примерно так и думал. Тогда вопрос: а на фига изобретать велосипед?
dom1n1k
Много лет назад я интереса ради решил отреверсить фотошоповский фильтр яркости-контраста. Не прям в деталях, но хотя бы общее понимание. Стал разбираться и получились у меня странноватые графики. "Какой-то ерунды напридумывали лишней" - подумал я - можно же проще.
Взялся делать свою реализацию, она вроде бы и работает, но плохо. Стал постепенно дорабатывать, там допилю, то докручу. И со временем понимаю, что у меня получается что-то примерно похожее на ту самую лишнюю ерунду.
Оказывается, в Адобе не дураки сидят, кто бы мог подумать.