Для начала вспомним, с чего все началось. 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, было то, что для ресайза с помощью билинейного и бикубического фильтра использовался метод аффинных преобразований, который использует одно и то же количество пикселей исходного изображения для формирования одного пикселя конечного (2x2 пикселя для билинейного, 4x4 для бикубического) и фиксированный размер фильтра. Это приводило к неудовлетворительным результатам для уменьшения изображения, практически не отличавшимся от метода ближайшего соседа.
Слева метод ближайшего соседа, справа бикубический фильтр аффинных преобразований. Первый образец — уменьшение в 5,8 раз, различий практически нет. Второй — в 1,8 раз, отличия минимальные, на резких диагональных линиях видна лесенка.
В тоже время, для фильтра ANTIALIAS
использовался высококачественный алгоритм на основе сверток, что давало одинаково хороший результат как для уменьшения, так и для увеличения.
Начиная с Pillow 2.7.0, высококачественный алгоритм на основе сверток используется для всех трех фильтров.
Слева бикубический фильтр на основе аффинных преобразований, справа сверток. Свертки определенно выигрывают.
Если до того вы использовали какие-то ухищрения для улучшения качества при использовании билинейного или бикубического фильтра (например, уменьшение изображения за несколько шагов или предварительное размытие), теперь в них нет необходимости.
Antialias переименован в Lanczos
Новая константа
Image.LANCZOS
была добавлена взамен Image.ANTIALIAS
.Когда метод ANTIALIAS
был впервые представлен, он был единственным высококачественным методом, основаны на свертках. И его имя отражало этот факт. Теперь, когда все методы основаны на свертках, они все стали «сглаживающими». А настоящее название фильтра, которое использовалось раньше для этой константы — фильтр Ланцоша.
Само собой, старая константа оставлена для обратной совместимости и является псевдонимом для новой. Юмор для лингвистов: Antialias is alias now.
Качество фильтра Ланцоша при увеличении
Как ни странно, с качеством сверок тоже было не все в порядке. В предыдущих версиях был баг, из-за которого качество фильтра Ланцоша при увеличении было практически таким же, как у фильтра
BILINEAR
. Этот баг был исправлен.
Слева результат увеличения в 4,3 раза предыдущей версии, справа — Pillow 2.7.0. Картинки слева одновременно более размытые и пикселизированные.
Качество бикубического фильтра при увеличении
Бикубический фильтр, реализованный для аффинных преобразований, давал резкую, слегка пикселизованную картинку при увеличении. Бикубический фильтр, реализованный для сверток немного мягче.
Слева результат увеличения в 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×стандартное отклонение для каждого конечного пикселя. Это было недостаточно, поэтому качество было хуже в сравнении с другими реализациями Гауссова размытия.
Несмотря на то, что новая реализация является лишь математической аппроксимацией, она не содержит такого бага.
Слева результат размытия с радиусом 5 в предыдущей версии (с учетом бага с удвоением радиуса), справа в новой. Слева видны резкие границы объектов.
Все эти изменения уже работают на наших серверах. Благодаря им мы повысили качество и скорость API для обработки картинок на лету. Также мы реализовали операцию быстрого блюра. Но это еще не все. Мы готовим следующий большой шаг для Pillow, о котором мы объявим чуть позже.
This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий