shikhalev.org
Исходный скриншот диалога изменения размеров в GIMP

Недавно мне задали такой вопрос:

Иван, как-то довелось делать resize png-картинки в Gimp. По сравнению с Photoshop качество хуже. Можешь тут подсказать?

Фотошопа у меня нет, сравнить не могу, поэтому попробую рассмотреть вопрос несколько по другому — какие способы ресайза мы имеем в Linux, пусть не «из коробки», но с минимальными трудозатратами. И что нам со всем этим богатством делать…

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

Для начала, чтобы много не писать о сути проблемы вообще, сошлюсь на хабрастатью «Ликбез: методы ресайза изображений». Она старая, но в плане основ и теории достаточно хорошо всё описывает. Более подробно, но на английском, можно почитать на сайте ImageMagick: «Resizing or Scaling», «Resampling Filters» и «Re­sam­p­ling by Nicolas Robidoux». Если Photoshop не использует новейшие достижения искусственного интеллекта (это не шутка, различные AI-методы сейчас активно применяются в обработке изображений), то правильный выбор фильтров и параметров, будем надеяться, позволит получить результат не хуже.

Что же касается формата PNG, то тут есть два соображения: во-первых, область применения — как правило, в PNG сохраняют не фотографии, а изображения с чистыми цветами и четкими границами, а во-вторых, применимость его к финальному результату — запросто можно при уменьшении картинки получить файл большего объема…

В общем, я взял два типичных, как мне кажется, случая, когда применяется именно этот формат: уменьшение скриншота (небольшое, чтобы можно было говорить о читаемости) и увеличение иконки (тут — в разы). Экспериментировать я буду с при­ме­не­ни­ем GIMP и ImageMagick.

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

Уменьшение скриншота

Вот такой скриншот диалогового окна собственно GIMP и собственно изменения размеров. В оригинальном разрешении:

Исходный скриншот диалога изменения размеров в GIMP

Ширина в оригинале — 415px. Ужимать будем до 320px, то есть, если очень грубо, на 20%. Точно такая же картинка вверху этого поста, но сделанная движком браузера автоматически, что там, какие алгоритмы использованы — то мне неведомо.

GIMP

GIMP предлагает пять вариантов интерполяции:

  • Нет
  • Линейная
  • Кубическая
  • Без гало
  • Мало гало

Обратившись к документации, мы узнаем, что «Нет» — означает то, что обычно в источниках называется «метод ближайшего соседа», а «линейная» и «кубическая» чаще называются «билинейной» и «бикубической» соответственно. Какой алгоритм используется в двух последних вариантах — непонятно, судя по всему, подавление гало работает уже поверх собственно интерполяции… Так же возможно, это какие-то модификации метода Lanczos, который был на их месте в старых версиях GIMP.

Пять вариантов достаточно мало, для того, чтобы все попробовать. Что же мы получим?

«Нет»
«Нет»
«Линейная»
«Линейная»
«Кубическая»
«Кубическая»

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

$ optipng -dir ./opt1/ -o7 source.png gimp-*.png

Получилось:

Файл Пиксели Байты Опт.
source.png (исходный файл) 415x361 34K 25K
gimp-none.png (интерполяции нет) 320x278 25K 16K
gimp-linear.png 320x278 37K 24K
gimp-cubic.png 320x278 43K 28K
gimp-wo-galo.png (без гало) 320x278 40K 27K
gimp-s-galo.png (мало гало) 320x278 43K 28K

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

  • Метод ближайшего соседа для уменьшения скриншотов не годится совсем — надписи не читаемы.

  • Если качество линейной интерполяции нас устраивает, берем ее — она не только выполняется быстрее, но и дает результат меньшего объема.

  • Если нам нужно максимальное качество, в GIMP выбираем вариант «Без гало» — за счет более резкой картинки (а именно это и делает подавление гало) файл лучше сжимается.

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

ImageMagick

Хорошая новость заключается в том, что ImageMagick предоставляет больше вариантов ресайза. Плохая — в том, что их намного больше. Команда convert -list filter на моей системе выдает список из 31 пункта. Но это еще не все — помимо оператора -resize есть еще -distort Resize… Так что количество вариантов нужно умножать на два. А еще у многих фильтров есть настраиваемые параметры, а еще… В общем, при углублении в тему документация ImageMagick подбрасывает все новые и новые уровни. В рамках этого поста я пройдусь по верхам и параметры по умолчанию менять не буду.

Чтобы посмотреть все варианты, я воспользовался таким скриптом:

#!/bin/bash

filters=$(convert -list filter);

convert source.png -resize 320x ./im/0-resize-default.png
convert source.png -adaptive-resize 320x ./im/0-resize-adaptive.png
convert source.png -distort Resize 320x ./im/0-distort-default.png

for f in $filters; do
    convert source.png -filter $f -resize 320x ./im/1-resize-$f.png;
    convert source.png -filter $f -distort Resize 320x ./im/2-distort-$f.png
done;

На самом деле -resize без указания фильтра — это то же самое, что -filter Mitchell -resize, а -distort Resize — -filter Robidoux -distort Resize. А вот -adaptive-resize — это отдельный режим, где обработка должна базироваться на данных самой картинки (в документации тут не все ясно).

Итого у меня получилось 65(!) вариантов, все я, естественно, приводить не буду. Начнем с умолчательных:

-resize
-resize
-distort Resize
-distort Resize
-adaptive-resize
-adaptive-resize

Далее проверим алгоритмы, которые должны соответствовать GIMP’овским (на самом деле не совсем).

-filter Point -resize
-filter Point -resize
-filter Point -distort Resize
-filter Point -distort Resize

-filter Point — это метод ближайшего соседа, как из него -distort получил нечто приемлемое — мне не очень понятно.

-filter Triangle -resize
-filter Triangle -resize
-filter Triangle -distort Resize
-filter Triangle -distort Resize
-filter Cubic -resize
-filter Cubic -resize
-filter Cubic -distort Resize
-filter Cubic -distort Resize
-filter Lanczos2 -resize
-filter Lanczos2 -resize
-filter Lanczos2 -distort Resize
-filter Lanczos2 -distort Resize
-filter Lanczos2Sharp -resize
-filter Lanczos2Sharp -resize
-filter Lanczos2Sharp -distort Resize
-filter Lanczos2Sharp -distort Resize

Курьеза ради, и чтобы не создалось впечатления, что -distort всегда лучше, чем -resize, приведу варианты с фильтром Sinc:

-filter Sinc -resize
-filter Sinc -resize
-filter Sinc -distort Resize
-filter Sinc -distort Resize

Завершу этот парад почти одинаковых картинок еще двумя фильтрами, которые показали приемлемые результаты по размеру выходного файла.

-filter Box -resize
-filter Box -resize
-filter Box -distort Resize
-filter Box -distort Resize
-filter Hermite -resize
-filter Hermite -resize
-filter Hermite -distort Resize
-filter Hermite -distort Resize

Теперь о размерах выходных файлов подробно (напомню, что исходный файл был 34K/25K):

Фильтр -resize -distort
Байты Опт. Байты Опт.
-resize по умолчанию (Mitchell) 43K 29K
-distort по умолчанию (Robidoux) 46K 31K
-adaptive-resize 36K 23K
Point (ближайший сосед) 25K 17K 37K 24K
Triangle (линейная) 39K 26K 42K 28K
Cubic 41K 30K 41K 30K
Lanczos2 47K 31K 47K 32K
Lanczos2Sharp 46K 31K 47K 32K
Sinc 67K 46K 67K 39K
Box 30K 19K 38K 24K
Hermite 39K 25K 40K 26K

Выводы:

  1. Если очень нужно уменьшать разрешение скриншота в PNG, то -adaptive-resize;
  2. Но лучше вообще без этого обойтись, например, в вебе просто задав размеры у тега IMG и отдав масштабирование на откуп браузеру — трафик так можно сэкономить заметно.
Исходная картинка
Исходная картинка, отмасштабированная браузером

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

Увеличение иконки

Здесь я решил поиздеваться над иконкой Darktable, в оригинале — 64x64px.

Иконка Darktable

Будем увеличивать ее в 5 раз по линейным размерам, т.е. в 25 по площади — до 320x320px. Вот так это может сделать браузер:

Иконка Darktable

GIMP

А вот что нам предлагает GIMP:

«Нет»
«Нет»
«Линейная»
«Линейная»
«Без гало»
«Без гало»

В режиме «Нет интерполяции» мы ожидаемо видим увеличение каждого пикселя, а в режиме «Линейная» — какое-то мыло. «Без гало» дает нам мыло немножко по краям подшарпленое, что, кстати, наиболее похоже на вариант от браузера. Что же до оставшихся «Кубической» и «Мало гало» — я не смог их отличить от «Линейной», даже быстро переключаясь с одной на другую.

Файл Пиксели Байты Опт.
darktable.png (исходный файл) 64x64 6.2K 6.2K
gimp-none.png (интерполяции нет) 320x320 9.7K 9.4K
gimp-linear.png 320x320 68K 68K
gimp-cubic.png 320x320 75K 75K
gimp-wo-galo.png (без гало) 320x320 72K 72K
gimp-s-galo.png (мало гало) 320x320 76K 75K

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

ImageMagick

Скриптом, аналогичным предыдущему, удалось получить множество вариантов. Но ничего прямо так принципиально отличающегося в лучшую сторону. Вероятно, без применения каких-нибудь AI-методов (т.е. распознавания и перерисовывания по сути) ничего принципиально улучшить и не получится. Поэтому ограничусь только тремя примерами:

-adaptive-resize
-adaptive-resize
-filter Parzen -distort Resize
-filter Parzen -distort Resize
-filter Sinc -distort Resize
-filter Sinc -distort Resize

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

Как можно видеть, -adaptive-resize справляется, как минимум, не хуже GIMP’а… Что же по размерам файлов?

Фильтр -resize -distort
Байты Опт. Байты Опт.
-resize по умолчанию (Mitchell) 74K 70K
-distort по умолчанию (Robidoux) 77K 73K
-adaptive-resize 67K 61K
Point (ближайший сосед) 11K 9.6K 69K 66K
Parzen 88K 84K 78K 74K
Sinc 111K 107K 131K 130K

Я не стал вставлять картинку с -filter Point -resize, поскольку она ожидаемо неотличима от того, что делает GIMP без интерполяции. А еще такой же результат позволяют получить операторы -sample и -scale, только быстрее. Так же, как и -thumbnail — это максимально упрощенные и тупые варианты ресайза.

Итого

  • Во-первых, формат PNG применяется для тех случаев, когда нам важно сохранить каждый пиксель как он есть. Т.е. любое масштабирование для таких картинок противопоказано. Именно для картинок как таковых, а не для формата. Если мы храним в PNG что-то хорошо масштабируемое, возможно, мы что-то делаем не так.

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

  • Если всё же необходимо ресайзить PNG и именно в PNG, я бы, пожалуй, первым делом попробовал оператор -adaptive-resize пакета ImageMagick. А если результат не устраивает — прогнал бы скриптом прочие варианты фильтров. Можно, конечно, заморочиться и на подгонку их параметров, но боюсь, мои глаза не справятся с отловом тончайших отличий в огромном количестве файлов.

  • GIMP, честно говоря, удивил бедностью возможностей — уж с десяток алгоритмов могли бы и завезти…


PS. Для удобства оригиналы ссылкой: скрин и значок. Желающие могут попробовать поиграть с их размерами в разном другом софте, включая фотошоп.