Разработчик представил Quite OK Image, алгоритм сжатия без потерь со сложностью O(n)

—

от автора

Разработчик Доминик Саблевски (Dominic Szablewski) представил алгоритм QOI (Quite OK Image), который позволяет без потерь сжимать RGB и RGBA изображения до размера файла, аналогичного для формата PNG, но в 20-50 раз быстрее. Автор отметил у себя в блоге, что алгоритм оказался «до глупости простым». Код проекта доступен на GitHub.

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

Особенность Quite OK Image в том, что кодирование и декодирование происходит за один проход по изображению — алгоритм касается каждого пикселя только один раз и при этом каждый пиксель кодируется одним из четырех различных способов.

Результирующие значения записываются в чанки, начиная с 2..4 tag-бита, который указывает на способ кодирования, а затем следует количество данных в битах. Все чанки побайтово выровнены. 

Как уже было сказано выше, алгоритм включает в себя 4 разных типа кодирования пикселей:

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

QOI_RUN_8 {     u8 tag  :  3;   // b010     u8 run  :  5;   // 5-битовый интервал повторения предыдущего пикселя: 1..32 }  QOI_RUN_16 {     u8 tag  :  3;   // b011     u16 run : 13;   // 13-битовый интервал повторения предыдущего пикселя: 33..8224 }
  • индексирование ранее просмотренного пикселя: кодировщик хранит массив из 64 ранее просмотренных пикселей и если обнаруживает пиксель, до сих пор хранящийся в массиве, то сохраняет его индекс в поток.

    Для сохранения сложности O(n) в массиве используется только один вид поиска по хешам значения rgba (r^g^b^a). Линейный поиск или другие алгоритмы усложнили бы кодировщик и замедлили бы его.

QOI_INDEX {     u8 tag  :  2;   // b00     u8 idx  :  6;   // 6-битный индекс в массиве цветов: 0..63 }
  • отличие от предыдущего пикселя: если цвет текущего пикселя не критично отличается от предыдущего, то разница между этими пикселями записывается в поток. Есть три вида записи, которые выбираются в зависимости от того, насколько велика разница. Примечательно, что обработка альфа-канала занимает больше времени, чем RGB.

QOI_DIFF_8 {     u8 tag  :  2;   // b10     u8 dr   :  2;   // 2-битная разница красного канала: -1..2     u8 dg   :  2;   // 2-битная разница зеленого канала: -1..2     u8 db   :  2;   // 2-битная разница синего канала: -1..2 }  QOI_DIFF_16 {     u8 tag  :  3;   // b110     u8 dr   :  5;   // 5-битная разница красного канала: -15..16     u8 dg   :  4;   // 4-битная разница зеленого канала:  -7.. 8     u8 db   :  4;   // 4-битная разница синего канала:  -7.. 8 }  QOI_DIFF_24 {     u8 tag  :  4;   // b1110     u8 dr   :  5;   // 5-битная разница красного канала: -15..16     u8 dg   :  5;   // 5-битная разница зеленого канала: -15..16     u8 db   :  5;   // 5-битная разница синего канала: -15..16     u8 da   :  5;   // 5-битная разница альфа-канала: -15..16 }
  • целые значение RGBA: если три предыдущих способов не удается применить, то в поток сохраняются целые значения пикселя.

QOI_COLOR {     u8 tag  :  4;   // b1111     u8 has_r:  1;   // красный байт     u8 has_g:  1;   // зеленый байт     u8 has_b:  1;   // синий байт     u8 has_a:  1;   // альфа-байт     u8 r;           // значение красного байта, если has_r == 1: 0..255     u8 g;           // значение зеленого байта, если has_g == 1: 0..255     u8 b;           // значение синего байта, если has_b == 1: 0..255     u8 a;           // значение альфа-байта, если has_a == 1: 0..255 }

Автор сравнил работу своего алгоритма с libpng и stb_image. Тесты показали, что qoi обгоняет популярные библиотеки по скорости кодирования и декодирования в 20-50 раз, но при этом размер итогового файла получается таким же или незначительно меньше. Также разработчик заявил о том, что планирует применить полученный опыт для создания эффективного видеокодека.


ссылка на оригинал статьи https://habr.com/ru/articles/591577/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *