code blog

Начало работы с изображениями с помощью Python и Pillow

Автор телегам-канала CODE BLOG — Вадим Шванов написал для нашего блога статью о работе с изображениями c использованием Python и Pillow.

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

Использовать для этого мы будем Pillow, NumPy (т. к. мы будем преобразовывать изображение в массив) и matplotlib для настраиваемого вывода результата на экран.

Установка пакетов

NumPy и matplotlib можно легко установить с помощью пакетного менеджера pip, просто запустив следующие команды в терминале:

pip install numpy
pip install matplotlib

С Pillow всё тяжелее. Python версии => 3.6 поддерживает установку этого пакета через pip. Однако при возникновении ошибки при других версиях Pillow и «питона» проверяем соответствие версий здесь.

Также я предлагаю изучить ответы на вопрос по поводу установки pillow, а также как установить .whl файл с помощью pip.

В качестве среды разработки был выбран всем известный Jupyter Notebook.

Начало работы

Для начала импортируем все необходимые нам модули, а также загрузим изображение в объект Python:

Примечание. Никто не заставляет вас импортировать модули с помощью конструкции import … as …, давая им такие же имена, как и я. Однако почти во всех «литературных» источниках NumPy импортируется с псевдонимом np, а matplotlib.pyplotplt.

По поводу кода не беспокойтесь, в конце статьи будет указана ссылка на .ipynb документ, который вы сможете легко загрузить и изменить под своё усмотрение.

Загрузка изображения происходит с помощью функции open() под-модуля Image:

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

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

Вывод изображений с matplotlib

Вот функция, которую мы будем использовать далее. Она принимает два параметра: img — массив, который мы получили из исходной фотографии и titleзаголовок, который по умолчанию имеет значение None. Мы изменяем размеры фигуры на 6×6, чтобы сделать вывод изображения больше, и выводим его на экран. Также мы проверяем, был ли указан заголовок, и в таком случае устанавливаем его с помощью функции title(). Выключим пометки на осях, указав ‘off’ в функцию axis().

Преобразование в «черный-белый»

Сейчас мы будем преобразовывать изображение в черно-белые тона. Пока что мы не будем преобразовывать загруженное изображение в массив, ибо здесь мы будем использовать метод convert() классаизображения PIL.

Функция выглядит очень просто:

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

Мне подсказали хорошую статью насчет «правильности» этой формулы, и я с радостью поделюсь ею с вами: «Об относительной яркости, или насколько живучим бывает легаси».

PIL использует свои коэффициенты, потому просто давайте просмотрим результат:

Да, мы немного потеряли в качестве из-за того, что растянули изображение с помощью matplotlib. Однако инструмент действительно работает (для сравнения можете реализовать мою первоначальную идею про среднее арифметическое).

Что мы хотим? Массив!

Далее мы будем использовать инструменты NumPy, потому желательно конвертировать изображение в np.array:

Да, конструктор массива принимает изображение в качестве аргумента и создаёт вот такой массив. Узнаем форму массива с помощью поля shape.

224 x 225 — размеры нашего изображения. 3 появляется из-за того, что каждый пиксель представлен тремя значениями.

Читайте в блоге: Как стать веб-разработчиком? Самый полный гид

Негатив

Значения цветов могут меняться от 0 до 255. Негатив — эффект «переворачивания» цветов. Достигается он довольно просто: от 255 мы просто отнимаем значение цвета. Т. к. все операции над массивом numpy выполняются векторизированно (поэлементно), то мы просто отнимем от 255 текущий массив:

С помощью нашей функции show() оценим результат:

Swap «left-right»

Следующей задачей у нас является «разворот» изображения слева направо (т. е. правая и левая стороны изображения меняются местами). В NumPy есть замечательная функция fliplr и её «близкий друг» flipud

Про их применение можно найти информацию здесь.

Нас интересует только функция fliplr, в которую мы и передадим исходный массив: 

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

Добавим синего

Последний эффект я случайно обнаружил у себя в телефоне, после чего мне показалось хорошей идеей реализовать его. Идея проста — нам нужно сделать изображение как можно синее. Для этого я предлагаю заменить каждое третье «значение пикселя» на максимальное — 255. 

Обратите внимание. Здесь нам нужно было поменять сам массив, однако я не хотел терять исходное изображение. Потому мы копируем последовательность с помощью np.copy()

Также имеет место быть другая конструкция:

arr[:, :, 2] = 255

И, по традиции, выводим результат:

Заключение 

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

Весь код из статьи вы сможете найти в этом документе.

Больше статей автора на сайте shwanoff.ru

Также рекомендуем подписаться на группу ВКонтакте, Телеграм и YouTube-канал. Там еще больше полезного и интересного для разработчиков.

Поделиться: