[Перевод] Обработка изображений с помощью библиотеки Python Pillow

5ffdb2aac93fc5baadcfea6b9b3edc09.webp

Данный туториал является переводом статьи, написанной Stephen Grupetta. Все изображения и коды скопированы без изменений. В конце вы найдете примечания относительно данной информации, а также ссылку на github с работающим кодом. Если код, приведенный автором не запускается, переходите в примечания и, возможно, сможете найти решение вашей ошибки.

Оглавление

  • Основные операции с изображениями в библиотеке Python Pillow

    • Модуль Image и класс Image в Pillow

    • Основные манипуляции с использованием модуля Image

    • Градации и режимы модуля Image в библиотеке Python Pillow

  • Обработка изображений с помощью Pillow в Python

    • Фильтры изображений с использованием Convolution Kernels (ядра свертки)

    • Размытие изображения, повышение резкости и сглаживание

    • Обнаружение краев, приданием им четкости и тиснение

  • Сегментация и наложение изображений. Пример:

    • Порог изображения

    • Сжатие (erosion) и расширение (dilation)

    • Сегментация изображения с использованием порогового значения

    • Наложение изображений с использованием Image.paste ()

    • Создание водяного знака

  • Операции с изображениями с помощью NumPy и Pillow

    • Использование NumPy для вычитания изображений друг из друга (поиск разницы)

    • Использование NumPy для создания изображений

    • Создание анимации

  • Заключение

Когда вы смотрите на изображение, вы видите на нем предметы и людей. Однако, когда вы программно читаете изображение с помощью Python или любого другого языка, компьютер видит массив чисел. В этом руководстве вы узнаете, как манипулировать изображениями и выполнять базовую обработку с помощью библиотеки Python Pillow.

Pillow и его предшественник PIL — это оригинальные библиотеки Python для работы с изображениями. Несмотря на то, что существуют другие библиотеки Python для обработки изображений, Pillow остается важным инструментом для понимания и работы в целом.

Для манипулирования и обработки изображений Pillow предоставляет инструменты, аналогичные тем, которые можно найти в программном обеспечении, таком как Photoshop. Некоторые из более современных библиотек обработки изображений Python построены на основе Pillow и часто предоставляют более продвинутую функциональность.

В этом уроке вы узнаете, как:

  • Читать изображения с помощью Pillow

  • Выполнять основные операции с изображениями

  • Использовать Pilow для обработки изображений

  • Используйте NumPy с Pillow для дальнейшей обработки

  • Создавать анимации с помощью Pillow

В этом руководстве представлен обзор возможностей библиотеки Python Pillow с помощью наиболее распространенных методов. Как только вы освоитесь в использовании этих методов, вы сможете использовать документацию Pillow для изучения остальных методов в библиотеке. Если вы никогда раньше не работали с изображениями в Python, это отличная возможность сразу приступить!

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

Основные операции с изображениями в библиотеке Python Pillow

Библиотека Python Pillow является ответвлением более старой библиотеки под названием PIL. PIL расшифровывается как Python Imaging Library, и она позволяла Python работать с изображениями. PIL приостановила работу в 2011 году и поддерживается только Python 2. Если использовать описание разработчиков, то Pillow — это удобный «переходник» PIL, который поддерживает жизнь библиотеки и включает поддержку Python 3.

В Python есть много модулей для работы с изображениями и их обработки. Если вы хотите работать с изображениями напрямую, манипулируя их пикселями, вы можете использовать NumPy и SciPy. Другими популярными библиотеками для обработки изображений являются OpenCV, scikit-image и Mahotas. Некоторые из этих библиотек быстрее и мощнее, чем Pillow.

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

Pillow также часто используется для исследовательской работы при работе с изображениями. У Pillow есть то преимущество, что она широко используется сообществом Python, и её не так тяжело изучить, как некоторые другие библиотеки обработки изображений.

Вам нужно будет установить библиотеку, прежде чем вы сможете ее использовать. Вы можете установить Pillow с помощью pip в виртуальной среде:

для Windows:

PS> python -m venv venv
PS> .\venv\Scripts\activate
(venv) PS> python -m pip install Pillow

для Linux+macOS:

$ python -m venv venv
$ source venv/bin/activate
(venv) $ python -m pip install Pillow

Теперь, когда вы установили пакет, вы готовы начать знакомство с библиотекой Python Pillow и выполнять основные операции с изображениями.

Модуль Image и класс Image в Pillow

Основным классом, определенным в Pillow, является класс Image. Когда вы читаете изображение с помощью Pillow, оно сохраняется в объекте типа Image.

Для написания программы в этом разделе вам понадобится файл изображения с именем building.jpg (изображение предоставлено), которое вы можете найти в репозитории изображений для данного руководства.

Вы можете поместить этот файл изображения в корневую папку проекта, в котором работаете. При изучении изображений с помощью Pillow лучше всего использовать интерактивную среду REPL. Начните с открытия изображения, которое вы только что скачали:

>>> from PIL import Image
>>> filename = "buildings.jpg"
>>> with Image.open(filename) as img:
...     img.load()
...

>>> type(img)


>>> isinstance(img, Image.Image)
True

Вы можете ожидать импорта из Pillow, а не из PIL. В конце концов, вы установили Pillow, а не PIL. Однако Pillow — это ответвление библиотеки PIL. Поэтому вам все равно придется использовать PIL при импорте в ваш код.

Вы вызываете функцию open () для чтения изображения из файла и .load () для чтения в память, чтобы теперь файл можно было закрыть. Вы используете оператор with для создания менеджера контекста, чтобы обеспечить закрытие файла, как только он больше не нужен. В этом примере объект представляет собой тип изображения JPEG, который является подклассом класса Image, что подтверждается вызовом isinstance (). Обратите внимание, что и класс, и модуль, в котором определен класс, имеют одно и то же имя Image. Вы можете отобразить изображение, используя .show ():

img.show()

Метод .show () сохраняет изображение как временный файл и отображает его, используя собственное программное обеспечение вашей операционной системы для работы с изображениями. Когда вы запустите приведенный выше код, вы увидите следующее изображение:

файл building.jpgфайл building.jpg

В некоторых системах вызов .show () блокирует REPL, пока вы не закроете изображение. Это зависит от операционной системы и программного обеспечения для просмотра изображений по умолчанию, которое вы используете.

Вам необходимо ознакомиться с тремя ключевыми свойствами при работе с изображениями в библиотеке Python Pillow. Вы можете изучить их, используя атрибуты класса Image .format, .size и .mode:

>>> img.format
'JPEG'

>>> img.size
(1920, 1273)

>>> img.mode
'RGB'

Формат изображения показывает, с каким типом изображения вы имеете дело. В этом случае формат изображения — «JPEG». Размер показывает ширину и высоту изображения в пикселях. Режим этого изображения — «RGB». Вскоре вы узнаете больше о режимах.

Часто вам может понадобиться обрезать и изменить размер изображения. Класс Image имеет два метода, которые можно использовать для выполнения этих операций: .crop () и .resize ():

>>> cropped_img = img.crop((300, 150, 700, 1000))
>>> cropped_img.size
(400, 850)

>>> cropped_img.show()

>>> low_res_img = cropped_img.resize(
...     (cropped_img.width // 4, cropped_img.height // 4)
... )
>>> low_res_img.show()

Аргумент .crop () должен состоять из четырех элементов, определяющих левый, верхний, правый и нижний края области, которую вы хотите обрезать. Система координат, используемая в Pillow, назначает координаты (0, 0) пикселю в верхнем левом углу. Это та же система координат, которая обычно используется для двумерных массивов. Кортеж из четырех элементов представляет собой следующую область изображения:

Область кадрированияОбласть кадрирования

Новое изображение, которое .crop () возвращает в приведенном выше коде, имеет размер 400×850 пикселей. На обрезанном изображении видно только одно из зданий с исходной картинки:

Обрезанная часть изображенияОбрезанная часть изображения

В приведенном выше коде вы также изменяете разрешение обрезанного изображения с помощью .resize (), которому требуется кортеж в качестве обязательного аргумента. Кортеж, который вы используете в качестве аргумента, определяет новую ширину и высоту изображения в пикселях.

В приведенном выше примере вы устанавливаете новую ширину и высоту в четверть их исходных значений, используя оператор деления без остатка (//) и атрибуты изображения .width и .height. Последний вызов show () отображает обрезанное изображение с измененным размером:

Сжатое изображениеСжатое изображение

Есть дополнительные необязательные параметры, которые можно использовать с .resize () для управления тем, как изображение масштабирутся. В качестве альтернативы вы можете использовать аналогичный метод масштабирования, используя .reduce ():

low_res_img = cropped_img.reduce(4)

Аргумент определяет коэффициент, на который вы уменьшаете масштаб изображения. Если вы хотите установить максимальный размер, а не коэффициент масштабирования, вы можете использовать .thumbnail (). Размер эскиза будет меньше или равен установленному вами размеру.

Примечание. Метод .thumbnail () изменяет объект изображения, но не возвращает новый объект. Однако методы .crop (), .resize () и .reduce () возвращают новый объект изображения. Не все методы в библиотеке Pillow ведут себя одинаково.

Когда вы довольны возвращенным изображением, вы можете сохранить любой из объектов Image в файл, используя .save ():

cropped_img.save("cropped_image.jpg")
low_res_img.save("low_resolution_cropped_image.png")

Как только вы вызываете метод, он создает файлы изображений в папке вашего проекта. В этом примере одно из изображений является изображением в формате JPEG, а другое — изображением в формате PNG. Расширение, которое вы используете в качестве имени файла, автоматически определяет формат файла. Также вы можете указать формат в качестве дополнительного необязательного аргумента.

Основные манипуляции с использованием модуля Image

Вы можете оперировать изображением не только с помощью обрезки и изменения размера. Другим распространенным методом является поворот или отражение изображения. Вы можете использовать метод .transpose () для некоторых преобразований. Продолжайте с той же сессией REPL, которую вы начали в предыдущем разделе:

converted_img = img.transpose(Image.FLIP_TOP_BOTTOM)
converted_img.show()

Этот код отображает следующее изображение:

Перевернутое изображниеПеревернутое изображние

Есть семь опций, которые вы можете передать в качестве аргументов .transpose ():

  1. Image.FLIP_LEFT_RIGHT: переворачивает изображение слева направо, в результате чего получается зеркальное отображение.

  2. Image.FLIP_TOP_BOTTOM: переворачивает изображение сверху вниз.

  3. Image.ROTATE_90: поворачивает изображение на 90 градусов против часовой стрелки.

  4. Image.ROTATE_180: поворачивает изображение на 180 градусов.

  5. Image.ROTATE_270: изображение поворачивается на 270 градусов против часовой стрелки, что соответствует повороту на 90 градусов по часовой стрелке.

  6. Image.TRANSPOSE: перемещает строки и столбцы, используя верхний левый пиксель в качестве источника, при этом верхний левый пиксель в перемещенном изображении такой же, как и в исходном изображении.

  7. Image.TRANSVERSE: перемещает строки и столбцы, используя нижний левый пиксель в качестве источника, при этом нижний левый пиксель остается фиксированным относительно исходной и измененной версиями.

Все параметры поворота, представленные выше, определяют повороты с шагом в 90 градусов. Если вам нужно повернуть изображение на другой угол, вы можете использовать .rotate ():

rotated_img = img.rotate(45)
rotated_img.show()

Этот метода поворачивает изображение на 45 градусов против часовой стрелки, давая следующее изображение:

Изображение повернуто на 45 градусовИзображение повернуто на 45 градусов

Возвращаемый объект Image имеет тот же размер, что и исходное изображение. Поэтому на дисплее отсутствуют углы изображения. Вы можете изменить это поведение с помощью расширенного именованного параметра:

rotated_img = img.rotate(45, expand=True)
rotated_img.show()

Этот метод возвращает увеличенное изображение, полностью содержащее повернутое изображение:

Повернутое изображение увеличенного размераПовернутое изображение увеличенного размера

Вы можете дополнительно настроить вращение с дополнительными необязательными параметрами. Теперь вы можете изменить размер и ориентацию изображения. В следующем разделе вы узнаете о различных типах изображений в библиотеке Python Pillow.

Градации и режимы модуля Image в библиотеке Python Pillow

Изображение представляет собой двумерный массив пикселей, где каждый пиксель соответствует цвету. Каждый пиксель может быть представлен одним или несколькими значениями. Например, в изображении RGB каждый пиксель представлен тремя значениями, соответствующими значениям красного, зеленого и синего.

Таким образом, объект Image для изображения RBG содержит три параметра, по одному для каждого цвета. Изображение RGB размером 100×100 пикселей представлено массивом значений 100×100x3.

Изображения RGBA также включают значение альфа-параметра, которое содержит информацию о прозрачности каждого пикселя. Изображение RGBA имеет четыре режима, по одному для каждого из цветов и четвертый, содержащую альфа-значения. Каждый режим имеет те же размеры, что и размеры изображения. Таким образом, RGBA-изображение размером 100×100 пикселей представляется массивом значений 100×100x4.

Режим изображения описывает, с каким типом изображения вы работаете. Pillow поддерживает большинство стандартных режимов, включая черно-белый (двоичный), оттенки серого, RGB, RGBA и CMYK. Вы можете увидеть полный список поддерживаемых режимов в документации Pillow. Также вы можете узнать, сколько полос в объекте изображения, используя метод .getbands (), и вы можете конвертировать между режимами, используя .convert (). Теперь вы будете использовать изображение с именем «strawberry.jpg» (изображение предоставлено) из репозитория изображений для этого урока:

strawberry.jpgstrawberry.jpg

Режим этого изображения также RGB. Вы можете преобразовать это изображение в другие режимы. Данный код использует тот же сеанс REPL, который вы использовали в предыдущих разделах:

>>> filename = "strawberry.jpg"
>>> with Image.open(filename) as img:
...     img.load()
...

>>> cmyk_img = img.convert("CMYK")
>>> gray_img = img.convert("L")  # Grayscale

>>> cmyk_img.show()
>>> gray_img.show()

>>> img.getbands()
('R', 'G', 'B')
>>> cmyk_img.getbands()
('C', 'M', 'Y', 'K')
>>> gray_img.getbands()
('L',)

Вы вызываете .convert () дважды, чтобы преобразовать изображение RGB в версию CMYK и в версию в градациях серого. Изображение CMYK похоже на исходное изображение, но закодировано с использованием режима, характерного для печатных материалов, а не для цифровых дисплеев. Преобразование в оттенки серого дает следующий результат:

Изображение в градациях серогоИзображение в градациях серого

Выходные данные вызовов .getbands () подтверждают наличие трех полос в изображении RGB, четырех полос в изображении CMYK и одной полосы в изображении в градациях серого.

Вы можете разделить изображение на его полосы, используя .split (), и объединить отдельные полосы обратно в объект изображения, используя merge (). Когда вы используете .split (), метод возвращает все полосы как отдельные объекты изображения. Вы можете подтвердить это, отобразив строковое представление одного из возвращенных объектов:

>>> red, green, blue = img.split()
>>> red

>>> red.mode
'L'

Режим объекта, который возвращает .split (), — «L», что указывает на то, что это изображение в градациях серого или же изображение отображает только значения яркости каждого пикселя.

Теперь вы можете создать три новых изображения RGB, отображенных к красном, зеленом и синем диапазоне, используя функцию merge (), которая является функцией модуля Image:

>>> zeroed_band = red.point(lambda _: 0)

>>> red_merge = Image.merge(
...     "RGB", (red, zeroed_band, zeroed_band)
... )

>>> green_merge = Image.merge(
...     "RGB", (zeroed_band, green, zeroed_band)
... )

>>> blue_merge = Image.merge(
...     "RGB", (zeroed_band, zeroed_band, blue)
... )

>>> red_merge.show()
>>> green_merge.show()
>>> blue_merge.show()

Первый аргумент в функции merge () определяет режим изображения, которое вы хотите создать. Второй аргумент содержит отдельные режимы, которые вы хотите объединить в одно изображение.

Один красный режим, сохраненный в переменной red, представляет собой изображение в градациях серого с режимом L. Чтобы создать изображение, показывающее только красный режим, вы объединяете красный диапазон из исходного изображения с зеленым и синем режимами, которые содержат только нули. Чтобы создать режим, содержащий повсюду нули, вы используете метод .point ().

Этому методу нужна функция в качестве аргумента. Используемая функция определяет, как преобразуется каждая точка. В данном случае вы используете лямбда-функцию, чтобы сопоставить каждую точку с 0.

Когда вы объединяете красный режим с зеленым и синим, содержащими нули, вы получаете изображение RGB с именем red_merge. Таким образом, созданное вами изображение RGB имеет ненулевые значения только в красном режиме, но, поскольку это все еще изображение RGB, оно будет отображаться в цвете.

Вы также повторяете аналогичный процесс, чтобы получить green_merge и blue_merge, которые содержат изображения RGB с зеленым и синим режимами из исходного изображения. Код отображает следующие три изображения:

9a067630e9cf00028d144252c76f2bd7.webp

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

Создание общего изображения из нескольких

Когда требуется вывести несколько изображений рядом друг с другом, сохраненных одним файлом, можно воспользоваться следующей функцией:

from PIL import Image

def tile(*images, vertical=False):
    width, height = images[0].width, images[0].height
    tiled_size = (
        (width, height * len(images))
        if vertical
        else (width * len(images), height)
    )
    tiled_img = Image.new(images[0].mode, tiled_size)
    row, col = 0, 0
    for image in images:
        tiled_img.paste(image, (row, col))
        if vertical:
            col += height
        else:
            row += width

    return tiled_img

Первый параметр в tile () использует оператор распаковки (*), поэтому в качестве входных аргументов можно использовать любое количество объектов типа PIL.Image. Для параметра ключевого слова vertical можно установить значение True, если вы хотите размещать изображения вертикально, а не горизонтально. Эта функция предполагает, что все изображения имеют одинаковый размер.

Общий размер окончательного изображения рассчитывается исходя из размера изображений и количества используемых изображений. Затем вы создаете новый объект изображения с тем же режимом, что и исходные изображения, и с размером общего экрана.

Цикл for вставляет изображения, которые вы вводите при вызове функции, в окончательное изображение. Функция возвращает окончательный объект Image, содержащий все изображения рядом.

Изображение в оданной статье, показывающее три цветовых режима для изображения клубники, было получено путем вызова функции tile () следующим образом:

strawberry_channels = tile(red_merge, green_merge, blue_merge)

Эта функция будет использоваться для создания всех изображений, которые состоят из нескольких в текущем руководстве.

Обработка изображений с помощью Pillow в Python

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

Фильтры изображений с использованием Convolution Kernels (ядра свертки)

Одним из методов, используемых при обработке изображений, является свертка изображений с использованием ядер (Convolution Kernels). Цель этого урока не в том, чтобы дать подробное объяснение теории обработки изображений. Если вас интересует наука об обработке изображений, одним из лучших ресурсов, который вы можете использовать, является Digital Image Processing by Gonzalez and Woods.

В этом разделе вы узнаете основы того, как можно использовать ядра свертки для обработки изображений. Но что такое ядро ​​свертки? Ядро представляет собой матрицу:

матрицаматрица

Вы можете рассмотреть простое изображение, чтобы понять процесс свертки с использованием ядер. Изображение имеет размер 30×30 пикселей и содержит вертикальную линию и точку. Линия имеет ширину четыре пикселя, а точка состоит из квадрата 4×4 пикселя. Изображение ниже увеличено для наглядности:

d308c6595934ddef19f804bbc97bdfd2.webp

Вы можете поместить ядро ​​в любое место на изображении и использовать расположение центральной ячейки ядра в качестве эталона. На приведенной ниже диаграмме представлена ​​верхняя левая часть изображения:

dfb631201cc2421d2f3acc760818d191.webp

Элементы на этой диаграмме представляют различные аспекты образа и ядра:

  • Белые квадраты представляют собой пиксели изображения со значением 0.

  • Красные квадраты представляют собой пиксели изображения со значением 255. Они составляют точку на изображении, показанном выше.

  • Каждая фиолетовая область представляет собой ядро. Это ядро ​​состоит из области 3×3, и каждая ячейка в ядре имеет значение 1/9. На диаграмме показано ядро ​​в трех разных положениях, обозначенных цифрами 1, 2 и 3.

Новое изображение может быть создано в результате свертки изображения с ядром. Вы можете понять процесс свертки, выполнив следующие шаги:

  1. Найдите ядро: рассмотрите одно из местоположений ядра и посмотрите на пиксели изображения, покрытые девятью ячейками ядра.

  2. Умножение значений ядра и пикселей: умножьте значения в каждой из ячеек ядра на соответствующие значения пикселей в изображении. У вас будет девять значений из девяти операций.

  3. Суммируйте результаты умножения: сложите эти девять значений вместе. Результатом будет значение пикселя в новом изображении, которое имеет те же координаты, что и центральный пиксель ядра.

  4. Повторите для всех пикселей: повторите процесс для каждого пикселя изображения, каждый раз перемещая ядро ​​так, чтобы центральная ячейка ядра каждый раз соответствовала другому пикселю изображения.

Вы можете увидеть этот процесс с тремя позициями ядра, помеченными 1, 2 и 3 на диаграмме выше. Рассмотрим положение ядра, обозначенное 1. Положение этого ядра равно (3, 2), что является положением его центральной ячейки, поскольку оно находится в четвертой строке (индекс = 3) и третьем столбце (индекс = 2). Каждый пиксель изображения в области, покрываемой ядром, имеет нулевое значение.

Следовательно, все умножения из шага 2 будут равны нулю, и их сложение тоже будет равно нулю. Новое изображение будет иметь нулевое значение в пикселе (3, 2).

Сценарий отличается для других показанных позиций ядра. Далее рассмотрим ядро ​​с номером 2, расположенное в точках (4, 7). Один из пикселей изображения, не равен нулю. Умножение этого значения пикселя на значение ядра даст 255 x (1/9) = 28,33. Восемь оставшихся умножений по-прежнему равны нулю, поскольку пиксели изображения равны нулю. Следовательно, значение пикселя в позиции (4, 7) на новом изображении будет 28,33.

Третья позиция ядра, показанная выше, находится в (8, 11). С этим ядром перекрываются четыре ненулевых пикселя изображения. Каждый из них имеет значение 255, поэтому результат умножения снова будет 28,33 для каждой из этих позиций пикселей. Общий результат для этой позиции ядра равен 28,33×4 = 113,33. Новое изображение будет иметь значение (8, 11).

В данном случае мы рассмотрели только три положения ядра. Процесс свертки повторяет этот процесс для каждой возможной позиции ядра в изображении. Это дает значение для каждой позиции пикселя в новом изображении. Результат свертки показан справа на следующем изображении, а исходное изображение слева:

7645e11059eec87f47ac713605cab51c.webp

Ядро, которое вы использовали, представляет собой ядро ​​размытия поля. Коэффициент 1/9 присутствует, так что общий вес ядра и равен 1. Результатом свертки является размытая версия исходного изображения. Существуют и другие ядра, выполняющие другие функции, в том числе различные методы размытия, определение границ, повышение резкости и многие другие.

Библиотека Python Pillow имеет несколько встроенных ядер и функций, которые будут выполнять свертки, описанные выше. Вам не нужно понимать математику фильтрации с помощью свертки, чтобы использовать эти фильтры, но всегда полезно знать, что происходит за кадром при использовании этих инструментов. В следующих разделах будут рассмотрены ядра и возможности фильтрации изображений, доступные в модуле ImageFilter в Pillow.

Размытие изображения, повышение резкости и сглаживание

Вернемся к изображению зданий, которое использовали в начале урока. Можно начать новую сессию REPL для этого раздела:

from PIL import Image, ImageFilter
filename = "buildings.jpg"
with Image.open(filename) as img:
    img.load()

В дополнение к модулю Image вы также импортируете модуль ImageFilter из Pillow. Вы можете использовать метод .filter () для применения фильтрации к изображению. Этот метод требует ядра свертки в качестве аргумента, и вы можете использовать одно из нескольких ядер, доступных в модуле ImageFilter в Pillow. Первый набор фильтров, о котором вы узнаете, касается размытия, повышения резкости и сглаживания изображения.

Вы можете размыть изображение с помощью фильтра ImageFilter.BLUR:

blur_img = img.filter(ImageFilter.BLUR)
blur_img.show()

Отображаемое изображение является размытой версией исходного изображения. Вы можете увеличить масштаб, чтобы увидеть разницу более подробно, используя .crop (), а затем снова отобразить изображения, используя .show ():

Два обрезанных изображения показывают разницу между двумя версиями:Два обрезанных изображения показывают разницу между двумя версиями:

Вы можете настроить необходимый тип и степень размытия, используя ImageFilter.BoxBlur () или ImageFilter.GaussianBlur ():

img.filter(ImageFilter.BoxBlur(5)).show()
img.filter(ImageFilter.BoxBlur(20)).show()
img.filter(ImageFilter.GaussianBlur(20)).show()

Теперь имеется три размытых изображения, показанные в том же порядке, что и в приведенном выше коде:

1396f726e29de938aa0e1445d700d64d.webp

Фильтр .BoxBlur () аналогичен фильтру, описанному в предыдущем разделе, где представлены ядра свертки. Аргументом является радиус фильтра размытия прямоугольника. В предыдущем разделе, посвященном ядрам, использовался фильтр рамочного размытия 3×3. Это означает, что он имел радиус 1, потому что фильтр простирается на один пиксель от центра.

Размытые изображения показывают, что прямоугольный фильтр размытия с радиусом 20 создает более размытое изображение, чем изображение, сгенерированное прямоугольным фильтром размытия с радиусом 5.

Вы также можете использовать фильтр .GaussianBlur (), который использует ядро ​​размытия по Гауссу. Ядро Гаусса придает больший вес пикселям в центре ядра, чем по краям, и это приводит к более плавному размытию, чем то, что получается с размытием прямоугольника. По этой причине во многих случаях размытие по Гауссу может дать лучшие результаты.

Что делать, если вы хотите повысить резкость изображения? В этом случае вы можете использовать фильтр ImageFilter.SHARPEN и сравнить результат с исходным изображением:

sharp_img = img.filter(ImageFilter.SHARPEN)
img.crop((300, 300, 500, 500)).show()
sharp_img.crop((300, 300, 500, 500)).show()

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

Изображение с резкостью справа Изображение с резкостью справа

Возможно, вместо повышения резкости, изображение нужно сгладить. Вы можете добиться этого, передав ImageFilter.SMOOTH в качестве аргумента для .filter ():

smooth_img = img.filter(ImageFilter.SMOOTH)
img.crop((300, 300, 500, 500)).show()
smooth_img.crop((300, 300, 500, 500)).show()

Ниже вы можете увидеть исходное изображение слева и сглаженное изображение справа:

e2ea4a0c56ac29ad6aa3694a74ce1638.jpg

Разберем применение сглаженного фильтра в следующем разделе, в котором вы узнаете о дополнительных фильтрах в модуле ImageFilter. Эти фильтры воздействуют на края объектов на изображении.

Обнаружение границ, приданием им четкости и тиснение

Когда вы смотрите на изображение, легко определить границы объектов на этом изображении. Алгоритм также может автоматически обнаруживать края, вызывая выявление границ.

Модуль ImageFilter в Pillow имеет предопределенное ядро ​​для достижения этой цели. В этом разделе вы снова используете изображения зданий и конвертируете его в оттенки серого, прежде чем применять фильтр обнаружения границ. Вы можете продолжить сеанс REPL из прошлого раздела:

img_gray = img.convert("L")
edges = img_gray.filter(ImageFilter.FIND_EDGES)
edges.show()

результат обнаружения границрезультат обнаружения границ

Данный фильтр идентифицирует границы изображения. Вы можете получить лучший результат, применив фильтр ImageFilter.SMOOTH перед поиском границ:

img_gray_smooth = img_gray.filter(ImageFilter.SMOOTH)
edges_smooth = img_gray_smooth.filter(ImageFilter.FIND_EDGES)
edges_smooth.show()

Изучите исходное изображение в градациях серого и два результата обнаружения границ ниже. Версия со сглаживанием перед обнаружением краев показана в самом низу:

b1ea01e6a3c6f9fa54a13d26a491e304.jpg

Вы также можете улучшить границы исходного изображения с помощью фильтра ImageFilter.EDGE_ENHANCE:

edge_enhance = img_gray_smooth.filter(ImageFilter.EDGE_ENHANCE)
edge_enhance.show()

В данном случае используется сглаженная версия изображения в градациях серого для очерчивания границ. Часть исходного изображения в градациях серого и изображение с очерченными границами показаны рядом ниже.

Изображение с очерченными границами находится справаИзображение с очерченными границами находится справа

Еще одним предопределенным фильтром в ImageFilter, который работает с границами объекта, является ImageFilter.EMBOSS. Вы можете передать его в качестве аргумента .filter (), как мы делали с другими фильтрами в этом разделе:

emboss = img_gray_smooth.filter(ImageFilter.EMBOSS)
emboss.show()

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

cc89a2fc1894097218ccf52ac592bbf5.jpg

В этом разделе вы узнали о нескольких доступных в модуле ImageFilter фильтрах, которые можно применять к изображениям. Существуют и другие фильтры. Вы можете увидеть список всех доступных фильтров в документации ImageFilter.

Сегментация и наложение изображений. Пример:

В этом разделе вы будете использовать файлы изображений с именами cat.jpg и monastery.jpg, которые вы можете найти в репозитории изображений для этого руководства. Вот два изображения:

cat.jpgcat.jpgmonastery.jpgmonastery.jpg

Вы можете использовать библиотеку Python Pillow, чтобы извлечь кота из первого изображения и разместить его на полу монастырского двора. Для этого потребуется ряд методов обработки изображений.

Порог изображения

Начнем работу с cat.jpg. Сначала нужно будет удалить изображение кошки с фона, используя методы сегментации изображения. В этом примере сегментируется изображение, используя методы пороговой обработки. Сначала обрезаем изображение до меньшего размера, чтобы удалить часть фона. Для данного проекта можно начать новую сессию REPL для этого проекта:

from PIL import Image
filename_cat = "cat.jpg"

with Image.open(filename_cat) as img_cat:
    img_cat.load()


img_cat = img_cat.crop((800, 0, 1650, 1281))
img_cat.show()

Обрезанное изображение содержит кошку и часть фона, который максимально близок к кошке:

164c588663660549f7d48d519e0c5c53.jpg

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

img_cat_gray = img_cat.convert("L"
    
            

© Habrahabr.ru