Фильтры

Это вторая часть разбора библиотеки 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.

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

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

Комментарии (8)


  1. dyadyaSerezha
    28.09.2026 17:50

    Первая очевидная мысль - почему бы не сделать, если ещё никто не сделал, GUI-приложение с этой библиотекой? Хороший опыт для AI, кстати.

    Ну и Lightroom, насколько я помню, именно на это и заточен - запоминается последовательность фильтров и применяется пакетно или не очень ко многим изображениям. Да и неужели для Фотошоп нет таких же плагинов?

    А то как-то уж слишком “на коленке” всё это выглядит.


    1. alexeymogulev
      28.09.2026 17:50

      У фотошопа возможности практически не ограничены и в пакетной обработке и в плане алгоритмов. Если через интерфейс уже не достаточно, то:
      - Базовый GUI и скриптинг через UXP
      - Можно ещё дальше -> ExtendScript устаревает, но пашет везде вроде
      - Можно вообще в Плюсы и СДК залезть, API вроде всё ещё открыт
      Но если по простому -> Экшены мощная штука!

      П.С. По теме GUI обёртки над Pillow. Одновременно невозможно и не имеет смысла. Тот же Фотошоп очень заморочен на точности, например вроде простое наложение, но я около полугода парился в Сях пытаясь воспроизвести идентичный результат без накоплений погрешности. Не тут то было, там доволно специфичные алгоритмы. Человек или маленькая команда просто не вывезет и 10% работы с цветом на том же уровне.

      Немного вникал в это, вот и изрёк простыню текста. Вот.


      1. dyadyaSerezha
        28.09.2026 17:50

        Ну вот, я примерно так и думал. Тогда вопрос: а на фига изобретать велосипед?


      1. dom1n1k
        28.09.2026 17:50

        Много лет назад я интереса ради решил отреверсить фотошоповский фильтр яркости-контраста. Не прям в деталях, но хотя бы общее понимание. Стал разбираться и получились у меня странноватые графики. "Какой-то ерунды напридумывали лишней" - подумал я - можно же проще.
        Взялся делать свою реализацию, она вроде бы и работает, но плохо. Стал постепенно дорабатывать, там допилю, то докручу. И со временем понимаю, что у меня получается что-то примерно похожее на ту самую лишнюю ерунду.
        Оказывается, в Адобе не дураки сидят, кто бы мог подумать.


  1. alexeymogulev
    28.09.2026 17:50

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


    1. ivoout Автор
      28.09.2026 17:50

      Вам спасибо, дорогой читатель!


  1. U235U235
    28.09.2026 17:50

    Статья слабенькая, много воды, на мой взгяд, рассчитаная на совсем уж новичков. Все это есть в официальной документации к PIL.

    Если нужен консольный "фотошоп" для автоматизации, то есть смысл обратить внимание на замечательный ImageMagick. Возможности которого гораздо больше чем у PIL.

    Если нужна серьезная обработка изображений в Python, то стоит сразу начинать с OpenCV и/или scikit-image. Вот такое мнение.


  1. ivanstor
    28.09.2026 17:50

    ImageMagick/GraphicsMagick. Если что-то совсем хитрое — G'mic, у него есть и GUI, и плагины и под Gimp, и под PS.