«

»

Янв 11 2014

Первые результаты RnD-проекта по сжатию изображений

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

К создаваемому формату предъявлялись следующие требования:

  • Высокая скорость загрузки (не более 50мс на изображение 1024×1024 в формате RGB8)
  • Поддержка сжатия как без потерь, так и с потерями
  • Поддержка форматов изображения: G8, GA8, RGB8, RGBA8
  • Поддержка, при сжатии изображения RGBA8 с потерями, специального режима когда альфа-канал сжимается без потерь

В качестве основы для будущего формата решено было взять JPEG2000. На выбор в первую очередь повлияло то, что в своё время Джон Кармак использовал его модификацию для хранения данных мегатекстуры в своей игре Rage. Кроме того этот формат обладал почти всеми требуемыми возможностями. Использовать его напрямую не представлялось возможным, т.к. скорость всех существующих на текущий момент библиотек не удовлетворяла требованиям.

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

Сжатие без потерь состоит из 2-х основных этапов: преобразование (сюда входит импорт данных с опциональным RGB->YUV преобразованием, DWT и переупорядочивание коэффициентов) и сжатие. Сразу скажу что в моей реализации время затрачиваемое на преобразования в 4-6 раз меньше времени затрачиваемого на сжатие, и в 10-20 раз меньше времени затрачиваемого на разжатие. В JPEG2000 это соотношение ещё выше.

Вообще основная причина медленности JPEG2000 это бинарный арифметической кодер с PPM, который еще и кодирует коэффициенты плоскостями. Лично мне не удалось добиться от бинарного арифметика скорости выше 30МБ/с даже в статическом режиме (который для бинарных кодеров вообще бессмысленен) и т.к. бинарный арифметик сам по себе крайне прост, то я не думаю что кому-то удалось реализовать его намного быстрее. В тоже время DWT тратит около 1,19мс на 5-ти ступенчатое преобразование одного канала 1024×1024, т.е. его возможности находятся на уровне 840МБ/с.

Собственно поэтому в моей реализации используется реализация статического арифметика, оперирующего 12-ти битными словами, с некоторыми специфическими улучшениями. Вообще хоть DWT и декорелирует сигнал, но при сжатии без потерь в изображениях не так много нулевых коэффициентов как при сжатии с потерями, именно поэтому этот вариант оказался приемлемым даже без ухищрений вроде кодирования длин 0-й как в JPEG.

Теперь результаты тестов. В качестве противника своему алгоритму я выбрал известный JPEG2000-кодер Kakadu, т.к. он славится своей скоростью работы. Также для сравнения я приведу данные о степени сжатия для такого распространённого формата, как PNG. Тестирование проводилось на 7 изображениях 1024х1024 как в полноцветном варианте, так и в градациях серого:

Файл Kakadu G8 SLP G8 PNG G8 Kakadu RGB8 SLP RGB8 PNG RGB8
1 46,27% 48,03% 52,40% 41,42% 42,58% 53,03%
2 42,53% 45,18% 47,22% 33,72% 38,27% 43,28%
3 51,71% 54,82% 56,71% 43,52% 45,71% 55,53%
4 28,66% 29,82% 34,03% 19,38% 22,23% 24,42%
5 50,56% 51,53% 52,74% 44,58% 46,66% 54,23%
6 50,62% 53,21% 54,23% 48,86% 51,17% 56,30%
7 52,44% 55,56% 59,98% 41,19% 44,81% 55,37%

Как и ожидалось чуда не произошло. SLP сжимает хуже чем Kakaku, хотя и лучше чем PNG. Теперь о главном, о скорости работы:

Алгоритм Сжатие G8 Расжатие G8 Сжатие RGB8 Расжатие RGB8
Kakadu 12,17 МБ/с 13,77 МБ/с 14,61 МБ/с 16,35 МБ/с
SLP 121,88 МБ/с 52,87 МБ/с 120,77 МБ/с 53,54 МБ/с
PNG (FFmpeg) N/A 103,09 МБ/с N/A 179,04 МБ/с

P.S. Вообще-то SLP можно ускорить примерно в 2 раза заменив арифметик на коды Хаффмана, однако это приведёт к снижению уровня сжатия примерно на 1%. Однако в текущий момент это улучшение не приоритетно.

P.P.S. В плане стабильности времени работы SLP демонстрируем минимальные колебания в зависимости от изображения, что крайне удобно для расчётов нагрузки. У Kakadu эти колебания заметно больше.

Добавить комментарий