Введение в обработку изображений в Python при помощи Pillow

Автор оригинала: Kalebu Jordan
  • Перевод
  • Tutorial

Бывает такой заходишь на почту и видишь очередную подборку статей, которые никогда не прочитаешь, но тут так сошлись звезды, что и открыл статью, и прочел, и придумал, где на практике пригодится. А теперь хочу поделиться статьей с Хабром, чтобы если кому-то нужен будет простой и понятный гайд по работе с изображениями на Python — пожалуйста.


Pillow — это свободно распространяемая библиотека для работы с изображениями (далее Imaging Library) на Python с открытым исходным кодом, которая добавляет вашему коду поддержку открытия, изменения и сохранения изображений в различных расширениях.


Давайте начнем


Самый важный класс в Imaging Library Python — это класс Image, определенный в одноименном модуле. Мы используем open(), чтобы открыть изображение в нашей локальной директории, как показано ниже:


from PIL import Image
sample = Image.open('pena.jpg')

Это просто! Теперь вы умеете считывать изображения с помощью Pillow, а значит можно приступать к обработке изображения с его помощью. Вы также можете проверить тип изображения, которое мы только что загрузили.


type(sample)
PIL.JpegImagePlugin.JpegImageFile

Вы можете посмотреть свойства изображения, например:


  • формат
  • размер
  • цветовой режим

sample.format
'JPEG'
sample.size
(640, 640)
sample.mode
'RGB'

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


sample.show() #Открывает в новом окне, расширение изображения меняется на PNG


Конвертирование расширения изображения


Когда вы закончите работать с изображением при помощи библиотеки Pillow  в изначальном расширении, вы можете пересохранить его в других форматах, например, их jpg сделать png или многие другие.


Для примера, давайте попробуем написать простую программу на Python для преобразования всех изображений в каталоге вашего проекта, которые находятся в формате jpg, в формат png.


import os
import sys
from PIL import Image
jpg_images = [image for image in os.listdir() if image.endswith('.jpg')]
for jpg_image in jpg_images:
    try:
        new_name = jpg_image.split('.')[0] + '.png'
        Image.open(jpg_image).save(new_name)
    except IOError as error:
        print('Couldn\'t read {} '.format(jpg_image))


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


Обрезка изображения


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


from PIL import Image
picture = Image.open('pena.png')
cord = (10, 10, 640, 340) # лево, верх, право, низ
new_picture = picture.crop(cord)
new_picture.show()


Как мы видим, изображение было успешно обрезано. Координаты обрезанной поверхности представлены диагональными координатами.


При этом первые две точки находятся (x, y) от верхней левой диагональной точки, а следующие две точки (x2, y2) также являются диагональной точкой снизу справа.


Геометрическое преобразование


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


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


Изменение размера изображения


from PIL import Image
image = Image.open('pena.png')
resized_image = image.resize((320, 320))
resized_image.save('resized.png')

Когда вы запустите приведенный выше код, вы должны увидеть новое изображение с измененным размером в вашем каталоге с размером 320 на 320.


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


from PIL import Image
image = Image.open('pena.png')
rotated_img = image.rotate(80)
rotated_img.save('./rotation/rotated_img.png')


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


Генератор изображений


from PIL import Image 
images = ['pena.jpg']
for img in images:
    try:
        org_img = Image.open(img)
        for angle in range(1, 361):
            image_name = str(angle)+'.jpg'
            new_img = org_img.rotate(angle)
            new_img.save('./rotation/'+image_name)
    except IOError:
        print('Couldn\'t read {}'.format(img))

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



Фильтрация изображений


Фильтрация — это метод изменения или улучшения изображения. Например, вы можете отфильтровать изображение, чтобы выделить определенные особенности или удалить другие.


Фильтрация изображений используется для получения различных результатов, как, например,- сглаживание, повышение резкости, удаление шума и обнаружение краев.


В библиотеке Pillow доступно множество фильтров, включая BLUR, BoxBlur, CONTOUR, FIND_EDGES, Filter, GaussianBlur, Kernel, MaxFilter, MedianFilter, SHARPEN, SMOOTH и т.д.


Пример использования


Давайте попробуем найти края на изображении ниже, используя фильтр FIND_EDGES.


from PIL import Image
from PIL import Image, ImageFilter
image = Image.open('pena.jpg')
edges = image.filter(ImageFilter.FIND_EDGES)
edges.show()


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


Чтение изображения из открытого файла


Кроме того, вы можете использовать Pillow для чтения изображения из файлового объекта Python, как показано ниже


from PIL import Image
image = Image.open(open('pena.jpg', 'rb'))

Чтение изображения из URL


В этом случае вам придется использовать Pillow в сочетании с запросами. Запросы должны будут отправлять GET-request на сервер, чтобы получить необработанные байты изображения, а уже Pillow считает эти байты.


import requests
from PIL import Image
url = 'http://pena.marketing/images/Logo1.png'
raw = requests.get(url, stream=True).raw
Image.open(raw).show()


Создание новых изображений


С помощью Pillow вы также можете создать новое пустое изображение, которое может понадобиться для различных целей. Используйте Image.new() для создания совершенно нового изображения.


Синтаксис:


new = Image.new(mode, shape, color)

Пример использования:


from PIL import Image
new_img = Image.new('RGB', (500, 500), 'blue')
new_img.show()


Рисование прямоугольников на изображениях


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


Пример исползования


Давайте попробуем нарисовать прямоугольную рамку внутри пустого изображения.


from PIL import Image, ImageDraw
new_img = Image.new('RGB', (400, 400), 'black')
pencil = ImageDraw.Draw(new_img)
pencil.rectangle((200, 50, 300, 300), fill ='green')
new_img.show()


Первые две координаты представляют (x, y) левой верхней части, а следующие две (x2, y2) представляют координатную точку правой нижней части.


Рисование текста на изображениях


Мы также можем использовать библиотеку Pillow для рисования текста на изображениях.


from PIL import Image , ImageDraw, ImageFont 
new_img = Image.new('RGB', (200, 200), 'black')
font = ImageFont.load_default()
pencil = ImageDraw.Draw(new_img)
pencil.text((100,100),'Hello World',  font=font, fill='blue', size = 36)
new_img.show()


Ссылки:


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

Будешь пользоваться библиотекой?

  • 82,1%Да, спасибо!23
  • 17,9%Нет, использую другую (поделюсь в комментариях)5

Комментарии 14

    0

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

      +2
      Вроде если использовать Pillow-SIMD (https://github.com/uploadcare/pillow-simd), то иногда она обгоняет OpenCV на некоторых операциях (см. бенчмарки python-pillow.org/pillow-perf ).
      Вообще, библиотеки каждая хорошая по своему, и есть ситуации когда и Pillow очень удобен. Думаю, преимущество OpenCV все же в большей мене обусловлено более богатом наборе инструментов, в т.ч. для многих задач комп. зрения.
        +1
        мне всегда казалось, что OpenCV про работу с хардкорным компьютерным зрением, буду рад ошибиться
          +1

          На C++ и правда OpenCV можно назвать хардкорным, но вот питоновский интерфейс очень приятный для использования, и совместим с numpy.

          0

          Кто говорит, покажите?

            0

            Ну, например:
            https://github.com/experiencor/keras-yolo3/issues/105


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

              +4

              Код с Pillow не нашёл, из opencv там используются следующие возможности:


              • Загрузка/сохранение изображений
              • Ресайз
              • Некоторые операции в random_distort_image;
              • Кроме того есть нанесение текста и рисование прямоугольников, как я понял для дебага, то есть не самая критичная к производительности часть

              Загрузка/сохранение изображений по большей части выполняется готовыми библиотеками вроде libjpeg-turbo или libwebp, тут вряд ли есть существенные различия. Отмечу лишь, что во время создания тикета пакет opencv-contrib-python, который есть в requirements.txt, собирался с обычной версией libjpeg, которая в несколько раз медленнее. Кроме того, после декодирования меняется порядок байт с BGR на RGB, что не добавляет скорости.


              Про ресайз в OpenCV могу сказать, что он сломан для многих сочетаний размеров. Я к сожалению не понял из исходников, несколько сильный используется ресайз, в зависимости от этого результат OpenCV будет либо неверен и можно использовать супербыстрый фильтр nearest neighbor, либо результаты Pillow-SIMD с биллинейным фильтром будут не хуже.


              В функции random_distort_image происходят интересные вещи и казалось бы тут уж Pillow нечем ответить. Но нет, все это укладывается в рамки операций, которые можно сделать с помощью 3D color LUT, что будет работать с константной скоростью для любого сочетания параметров. А конкретнее 50 мегапикселей/cек для Pillow и почти 400 мегапикселей/cек для Pillow-SIMD. Сама же функция random_distort_image работает со скоростью 41 мегапиксель/сек.


              То есть по чистой производительности на ядро, Pillow, а тем более Pillow-SIMD для этих зада был бы быстрее. Единственное, с чем могли бы возникнуть проблемы — распараллеливание на несколько ядрер. Но тут я приверженец максимальной утилизации ресурсов, а для этого нужно запускать несколько задач параллельно, а не параллелить одну задачу внутри. А для этого в OpenCV тоже нет ничего встроенного.


              Получается, зря говорят?

                +1

                Спасибо за столь развернутый ответ, комментарий ценнее статьи в духе хабра!)) Призываю в тренд dkurt, может это будет тебе интересно))

                  +1

                  Если что-то сломано в OpenCV — просим открыть баг репорт. Вообще, билинейная интерполяция это одна из священных войн всего CV где решается вопрос непортируемости и нет победителей и не будет. В том же DL нельзя так просто исправить даже забагованную реализацию, потому что все натренированные к тому моменту сети разойдутся. Взять пример How Tensorflow’s tf.image.resize stole 60 days of my life. А для параллелизма есть встроенный в OpenCV parallel_for о применении которого можно почитать в нашей статье https://m.habr.com/ru/company/intel/blog/489884/.

                    +2
                    Если что-то сломано в OpenCV — просим открыть баг репорт. Вообще, билинейная интерполяция это одна из священных войн всего CV где решается вопрос непортируемости и нет победителей и не будет.

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


                    Можете как раз взять пример из статьи на которую вы дали ссылку, там очень показательный пример. Только отресайзить его не в 4x4, а даже 3x3. Как это может превратиться в это?


                           
                    оригинал, cv2.INTER_LINEAR, cv2.INTER_CUBIC


                    А должно получиться во всех случаях что-то вроде такого:


                           
                    cv2.INTER_AREA, PIL.Image.LINEAR, PIL.Image.LANCZOS


                    И с NEAREST такие же проблемы, что в той статье:




                    для параллелизма есть встроенный в OpenCV parallel_for

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

            +1
            Сколько уже базовых туториалов по этой библиотеке. А сама библиотека богаче чем кажется на первый взгляд, да и активно развивается.
            Например, в реальных проектах часто требуется поворачивать изображение согласно ориентации в EXIF (загрузил пользователь аватарку, которая снята на смартфон и бац, ориентация не верная).
            Начиная с 6-й версии это делает вот так:
            from PIL import ImageOps
            image = ImageOps.exif_transpose(image)
            

            и никакой больше возни. И таких «плюшек», про которых не говорят в подобных туториалах, очень много.
              +1

              Зачем нужны переводы про Pillow, если есть pillow-simd и оригинальные посты от homm на хабре?

                +1

                Но я же не пишу туториалы )

                0

                Использую opencv, хотя в некоторых случаях удобнее всё-таки использовать Pillow. Кстати, вот мой туториал по opencv: часть 1 и часть 2.

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

                Самое читаемое