[Перевод] Pillow 2.7 — Существенные улучшения качества и производительности

652911ebbe334680a1baf15dd6815a29.png Первого января 2015 года, по расписанию, вышла новая версия библиотеки для работы с изображениями Pillow 2.7. Так как многие изменения в ней были сделаны командой Uploadcare, мы рады представить вам расширенную версию заметок о релизе этой версии.Для начала вспомним, с чего все началось. Pillow — дружественный форк (как называют его авторы) популярной библиотеки PIL, Python Imaging Library. Последняя версия PIL 1.1.7 вышла в 2009 году и в основном содержала исправления ошибок. Изначально Pillow задумывался как проект только по приведению в порядок сборки PIL, и разработчики рекомендовал отправлять все баги, не связанные со сборкой, в оригинальный PIL. Но время шло, PIL стремительно устаревала, багов не уменьшалось, тут еще Python 3 маячил на горизонте. Поэтому с версией Pillow 2.0 все изменилось. «Pillow 2.0.0 добавляет поддержку Python 3 и включает много багфиксов со всего интернета» гласит описание проекта на PyPI. И с тех пор понеслось. Каждые три месяца выходили версии с огромных количеством багфиксов и другими улучшениями от различных разработчиков. Самое значительное нововведение за это время было, пожалуй, поддержка форматов WebP и JPEG2000. Теперь пришло время следующего большого шага.

Фильтры ресайза изображенийФункции ресайза изображений Image.resize () и Image.thumbnail () в качестве одного из аргументов принимают resample — фильтр, использующийся для ресайза. Его возможные значения: NEAREST, BILINEAR, BICUBIC и ANTIALIAS. Поведение практически каждого из них изменилось в новой версии.Уменьшение изображения с билинейным и бикубическим фильтрами Одной из проблем в PIL, а потом и в Pillow, было то, что для ресайза с помощью билинейного и бикубического фильтра использовался метод аффинных преобразований, который использует одно и то же количество пикселей исходного изображения для формирования одного пикселя конечного (2×2 пикселя для билинейного, 4×4 для бикубического) и фиксированный размер фильтра. Это приводило к неудовлетворительным результатам для уменьшения изображения, практически не отличавшимся от метода ближайшего соседа.nearestaffinenearestaffine

Слева метод ближайшего соседа, справа бикубический фильтр аффинных преобразований. Первый образец — уменьшение в 5,8 раз, различий практически нет. Второй — в 1,8 раз, отличия минимальные, на резких диагональных линиях видна лесенка.

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

Начиная с Pillow 2.7.0, высококачественный алгоритм на основе сверток используется для всех трех фильтров.

affineconvolutionaffineconvolution

Слева бикубический фильтр на основе аффинных преобразований, справа сверток. Свертки определенно выигрывают.

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

Antialias переименован в Lanczos Новая константа Image.LANCZOS была добавлена взамен Image.ANTIALIAS.Когда метод ANTIALIAS был впервые представлен, он был единственным высококачественным методом, основаны на свертках. И его имя отражало этот факт. Теперь, когда все методы основаны на свертках, они все стали «сглаживающими». А настоящее название фильтра, которое использовалось раньше для этой константы — фильтр Ланцоша.

Само собой, старая константа оставлена для обратной совместимости и является псевдонимом для новой. Юмор для лингвистов: Antialias is alias now.

Качество фильтра Ланцоша при увеличении Как ни странно, с качеством сверок тоже было не все в порядке. В предыдущих версиях был баг, из-за которого качество фильтра Ланцоша при увеличении было практически таким же, как у фильтра BILINEAR. Этот баг был исправлен.beforeafterbeforeafter

Слева результат увеличения в 4,3 раза предыдущей версии, справа — Pillow 2.7.0. Картинки слева одновременно более размытые и пикселизированные.

Качество бикубического фильтра при увеличении Бикубический фильтр, реализованный для аффинных преобразований, давал резкую, слегка пикселизованную картинку при увеличении. Бикубический фильтр, реализованный для сверток немного мягче.beforeafterbeforeafter

Слева результат увеличения в 4,3 раза предыдущей версии, справа — Pillow 2.7.0. Картинки слева более пикселизированные (имеют более ощутимые границы пикселей). В то же время, диагональные линии на первом примере более четкие и менее подвержены эффекту лесенки. И то и другое — влияние параметра «a» в бикубическом уравнении. Избежать обоих эффектов можно только с помощью более качественного фильтра Ланцоша.

Производительность ресайза В общем случае свертки — более затратный алгоритм для уменьшения, потому что в отличии от аффинных преобразований, он учитывает все пиксели исходного изображения. Из-за этого чистая производительность билинейного и бикубического фильтров может быть ниже, чем раньше. С другой стороны, если вы до этого были удовлетворены билинейным и бикубическим фильтрами, которые дают качество для уменьшения, схожее с ближайшим соседом, возможно вам стоит подумать над использованием самого NEAREST фильтра. Это существенно увеличит производительность.В то же время, одно из существенных улучшений Pillow 2.7.0 в том, что производительность сверток для уменьшения была увеличена в среднем в 2 раза по сравнению с предыдущей версией и даже по сравнению с ImageMagick. Производительность при увеличении реализации на свертках для фильтра BILINEAR оказалась быстрее в полтора раза, для BICUBIC в четыре, а для LANCZOS осталась на том же уровне.

Т.к. скорее всего вы не использовали в своем приложении ничего, кроме LANCZOS (бывший ANTIALIAS), то производительность при уменьшении для вас должна увеличиться в среднем в два раза. Если, например, использование Ланцоша для вас было вынужденной мерой из-за низкого качества остальных фильтров, то теперь вы можете перейти, например, на билинейный фильтр. Это увеличит производительность еще примерно в 2 раза для уменьшения и примерно на 30% для увеличения.

Фильтр по умолчанию для Image.thumbnail () В Pillow 2.5 фильтр по умолчанию для Image.thumbnail () был изменен с NEAREST на ANTIALIAS. Этот фильтр был выбран по причинам неоднократно озвученным выше — низкое качество остальных фильтров. В Pillow 2.7.0 фильтр по умолчанию вновь изменен, в этот раз на BICUBIC, потому что он немного быстрее. На самом деле Ланцош не дает каких-либо преимуществ после использования метода Image.draft () внутри Image.thumbnail (), который уменьшает изображение с помощью библиотеки libjpeg и использует для этого суперсемплинг, а не свертки.Транспонирование изображений Новый метод Image.TRANSPOSE был добавлен для функции Image.transpose () в дополнение к уже существующим FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, ROTATE_270. TRANSPOSE — это алгебраическое транспонирование, т.е. отражение изображение относительно её основной диагонали.Производительность методов ROTATE_90, ROTATE_270 и TRANSPOSE была существенно увеличена для больших изображений, не помещающихся в кэш процессора.

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

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

Гауссово размытие и контурная резкость Реализация ImageFilter.GaussianBlur была заменена на последовательное применение бокс-фильтров. Новая реализация основана на работе Theoretical foundations of Gaussian convolution by extended box filtering от Mathematical Image Analysis Group. Так как реализация ImageFilter.UnsharpMask базируется на Гауссовом размытии, все что описано в этой секции, также применимо и к ней.Радиус размытия В предыдущих версиях Pillow была ошибка, из-за которой радиус размытия (стандартное отклонение Гауссианы) на самом деле задавал его диаметр. Поэтому, например, чтобы размыть изображение на радиус 5, нужно было указывать значение 10. Ошибка была исправлена, и теперь значение радиуса интерпретируется так же, как во всем остальном программном обеспечении.Если до этого вы использовали Гауссово размытие с определенным радиусом, вам нужно поделить его значение на два.

Производительность размытия Время вычисления бокс-фильтра постоянно относительно его радиуса и зависит только от размеров входного изображения. Т.к. новая реализация Гауссового размытия основана на бокс-фильтре, её вычисление также не зависит от радиуса размытия.Для радиуса в 1 пиксель новая реализация работает 5 раз быстрее, для радиуса 10 — в 18 раз, для радиуса 50 уже в 85 раз. Ваш дизайнер, рисующий интерфейсы в стиле iOS 8, должен быть доволен.

Качество размытия Теоретически при Гауссовом размытии в вычислении каждой точки конечного изображения должны участвовать все точки исходного с определенными коэффициентами. На практике коэффициенты точек дальше 3×стандартное отклонение настолько малы, что учитывать их нет смысла.Предыдущая реализация учитывала только пиксели в радиусе 2×стандартное отклонение для каждого конечного пикселя. Это было недостаточно, поэтому качество было хуже в сравнении с другими реализациями Гауссова размытия.

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

beforeafterbeforeafter

Слева результат размытия с радиусом 5 в предыдущей версии (с учетом бага с удвоением радиуса), справа в новой. Слева видны резкие границы объектов.

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

© Habrahabr.ru