Сайт о телевидении

Сайт о телевидении

» » Алгоритмы квантования для полутоновых и цветных изображений

Алгоритмы квантования для полутоновых и цветных изображений


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

Рис.8.Функция, описывающая квантование
Задача построения квантователя состоит в определении значений порогов и уровней . Простейший способ решения этой задачи состоит в разбиении динамического диапазона на одинаковые интервалы. Однако такое решение не является наилучшим. Если значения яркости большинства отсчетов изображения сгруппированы, например, в «темной» области и число уровней ограничено, то целесообразно квантовать неравномерно. В «темной» области следует квантовать чаще, а в «светлой» реже. Это позволит уменьшить ошибку квантования .

В реальных системах в основном используются два типа квантования – линейное гамма-корректированное. В последнем случае аналоговый сигнал перед квантованием подвергается нелинейному преобразованию x’=x 1 /  . Такая функция реализована практически во всех промышленно выпускаемых ПЗС камерах. Стандартное значение  равно 1.4.

Необходимость в гамма-коррекции (даже для чисто аналоговых систем) возникает из-за конечного контраста приборов визуализации, например компьютерных дисплеев. Яркостная кривая чувствительности человеческого глаза имеет приблизительно логарифмический характер, поэтому сжатие динамического диапазона в области ярких тонов оправдано с физиологической точки зрения.

Оптимальный выбор числа уровней дискретизации в значительной степени зависит от характеристик приемного устройства (ПЗС-камеры, например). ПЗС камеры общего назначения редко имеют соотношение сигнал-шум больше чем 46дБ. Соотношение сигнал- шум определяется следующим выражением:
, где
-максимальная амплитуда полезного сигнала,
-среднеквадратичная амплитуда шума. Соответственно, при отношении сигнал шум равном 46дБ полезное число уровней квантования составляет 200, что говорит о целесообразности использования восьмибитного квантователя.

Привлекает внимание

Например, старый добрый формат GIF использует палитру, максимум на 256 цветов. Если вы захотите сохранить серию своих селфи как gif-анимацию (кому бы это надо было), то первое, что вам, а точнее программе, которую вы будете для этого использовать, надо будет сделать – создать палитру. Можно использовать статическую палитру, например web-safe colors , алгоритм квантизации получиться очень простым и быстрым, но результат будет «не очень». Можно создать оптимальную палитру на основе цветов изображения, что даст результат наиболее визуально похожий на оригинал.

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

Далее вас ждёт скучное и непонятное повествование о методе медианного сечения, алгоритму рассеивания ошибок (шума квантизации) по Флойду-Стейнбергу (и не только), особенностях цветового восприятия человеческого глаза, а так же немного говно кода.

Предыстория

Давным-давно, когда Nokia была тёплой и ламповой главенствовала на рынке смартфонов, а владельцы смартфонов гордо звали себя «смартфонщики», в те стародавние времена писал я простенькие программки на python для series60. На одну из них намедни наткнулся копаясь в архивах. GifTool – программка для создания gif-анимации из набора картинок. В ней я реализовал квантизацию методом медианного сечения, алгоритм сжатия LZW, вся структура файла создавалась самостоятельно, для неизменившихся на следующем слайде пикселей использовалась прозрачность, чтобы уменьшить итоговый размер файла. Захотелось мне освежить свою память, посмотреть – как же она работала. Открыл код и … Это чувство, когда ты не можешь разобраться в своём говнокоде десятилетней давности. Про PEP8 я тогда не знал, поэтому читаемость кода была чуть менее чем никакой (тогда мне нравился минимализм, как и многим начинающим программистам). Прослезился, поплевался, отрефакторил в PyCharm, разобрался как реализовал метод медианного сечения, по быстрому накидал «грязный» скрипт. Работает! Палитра создаётся, изображение на выходе получается сносное. И тут меня закусило – смогу ли я добиться лучших результатов, чтобы картинка визуально была как можно ближе к оригиналу.


Итак – метод медианного сечения. Он прост до безобразия. Первым делом надо из всех уникальных цветов изображения составить RGB куб. Далее рассечь его по самой длинной стороне. Например, диапазон красного у нас от 7 до 231 (длина 231-7=224), зелёного от 32 до 170 (длина 170-32=138), синего от 12 до 250 (длина 250-12=238), значит, будем «резать» куб по синей стороне. Получившиеся сегменты так же рассекаем по длинной стороне и т.д. пока не получим 256 сегментов. Для каждого сегмента высчитать средний цвет – так мы и получим палитру.

Пара картинок почти в тему, для наглядности



Что здесь можно улучшить? Первое, что приходит в голову – вычислять средний цвет не тупо сложив все цвета и разделив на их количество [ sum(color) / count(color) ], а с учётом, сколько раз каждый цвет встречается в изображении. То есть каждый цвет умножаем на количество его вхождений в изображении, полученные значения складываем, результат делим на количество вхождений в изображении всех цветов данного сегмента [ sum(color * total) / sum(total) ]. В результате, наиболее часто встречаемые цвета имеют приоритет при вычислении, но и редкие цвета вносят свои корректировки, поэтому палитра получается лучше, визуальное отклонение цветов меньше. Для лучших результатов желательно ещё учитывать гамму, но я оставил это на потом. Второе не так явно – медианное сечение совсем не учитывает особенности восприятия цвета человеческим глазом. Оттенки зелёного мы воспринимаем гораздо лучше оттенков синего. Я решил исправить это недоразумение и «сплющил» куб – длины сторон помножил на коэффициенты из . В результате по зелёной и красной стороне сечений стало больше, по синей меньше. Такого решения я больше нигде не встречал (может плохо искал), но результат на лицо.

Теперь у нас есть оптимальная палитра, не идеальная конечно (я знаю, что её можно ещё улучшить), но достаточно хорошая. Следующий шаг – индексирование цветов изображения. Самый простой вариант – в каком сегменте находится цвет, такой и индекс. Быстро и просто. Но есть одно но, и даже не одно, поэтому к данному шагу мы ещё вернёмся.

Есть ещё один способ улучшить качество получаемого изображения – рассеивание ошибок. Тут тоже всё довольно просто – из индексируемого цвета вычитаем соответствующий цвет палитры, получаем ошибку, рассеиваем её по соседним пикселям в соответствии с определённой формулой (шаблоном), самая известная формула Флойда-Стейнберга, её я и использовал. При рассеивании ошибок размываются резкие переходы между цветами, и визуально кажется, что изображение содержит больше оттенков (цветов). Если интересно – про рассеивание ошибок подробно и интересно можно почитать . Этот алгоритм я так же решил допилить, помножив ошибку на всё те же коэффициенты, как оказалось, это была очень хорошая идея – так как сечений по синему диапазону стало меньше, в нём получалась значительная ошибка, и без корректировки ошибки коэффициентами рассеивание вносило много «шума».

Вот теперь можно снова вернуться к индексированию. Рассеиванием ошибок мы изменяем цвета пикселей и получаем такие, которых нет в нашем RGB-кубе (напомню, он составлен исключительно из цветов изображения). Теперь нельзя просто посмотреть в каком сегменте находится цвет, чтобы назначить индекс. Решение нашлось сразу – поиск ближайшего цвета в палитре . В данную формулу я подставил всё те же коэффициенты. Сравнивая результаты подбора цвета палитры по индексу сегмента в который входит исходный цвет и результаты поиска ближайшего цвета, наглядно увидел, что ближайший цвет часто оказывается в соседнем сегменте. Если исходный цвет находится ближе к центру сегмента – то индекс сегмента соответствует индексу цвета в палитре, но чем ближе исходный цвет к краям сегмента, тем больше вероятность, что ближайший цвет окажется в соседнем сегменте. В общем, единственный правильный путь индексирования – поиск ближайшего цвета в палитре. Но у поиска есть минус – он медленный, очень медленный. Писать числодробилку на python плохая идея.

Ну вот, хотел объяснить в двух словах, а получилась целая куча непонятной писанины. Надеюсь, код я пишу лучше, чем объясняю, поэтому вот ссылочка на github . Код несколько раз переписывался, сначала совершенствовался алгоритм, пока результат меня не устроил, потом оказалось, что он жрёт слишком много оперативы при обработке фотографий (сначала тестировал на небольших картинках), пришлось перенести RGB-куб, медианное сечение и карту пикселей в базу данных (sqlite). Скрипт работает очень медленно, но результат получается лучше, чем квантизация средствами PIL/Pillow и GIMP’ом (в нём эта операция называется индексирование).

Наглядная демонстрация:

Оригинал

Результат квантизации в GIMP, оптимальная палитра на 256 цветов + размывание цвета по Флойду-Стенбергу (нормальное)

Результат квантизации PIL/Pillow image.convert(mode="P", dither=PIL.Image.FLOYDSTEINBERG, palette=PIL.Image.ADAPTIVE, colors=256)

Результат квантизации моим кодом

На что обратить внимание: рассеивание ошибки у GIMP сильно «шумит», PIL/Pillow создает не очень оптимальную палитру и практически не рассеивает ошибки (резкие переходы между цветами).
Если не видите разницу - посмотрите другие примеры на github .


P.S.: есть замечательная программа Color Quantizer , которая справляется с данной задачей лучше и быстрее, поэтому практического смысла мой скрипт не имеет, сделан исключительно из «спортивного» интереса.
UPD: обновил проект на github . Добавил алгоритм квантизации Octree (октодерево), популярные формулы рассеивания ошибок, поиск ближайшего цвета по среднему значению красного.

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

Порядок предсказывающего устройства зависит от статистических характеристик изображения. Как правило, если последовательность отсчетов может быть промоделирована авторегрессионным марковским процессом п-го порядка, то разности, полученные с помощью оптимального предсказывающего устройства п-го порядка, будут образовывать последовательность некоррелированных чисел . Изображения, очевидно, не являются марковскими процессами п-го порядка, но опыт работы по сжатию изображений показывает, что корреляционные свойства изображений можно описать марковским процессом третьего порядка, а это приводит к предсказывающим устройствам третьего порядка (п=3) . Аналогично при моделировании изображений было выяснено, что ДИКМ с предсказывающими устройствами более высоких порядков не дает большего выигрыша в качестве изображения (как по субъективным, так и по объективным данным).

Коэффициенты предсказания а i можно определить с помощыо анализа средних квадратических ошибок. Пусть g ( k ) - отсчеты на строке развертки, a

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

min e = E { g(k) - } (4.21)

повсем k, а i

Это известная задача, и если процесс g ( k ) стационарен, то ее решение имеет вид

, (4.22)

r (j - i) = E [ g (k - j) g (k -i) ] (4.23)

обычно называется автокорреляционной функцией процесса g. Коэффициенты a i получаются решением системы уравнений (4.22).

Оптимальные значения коэффициентов предсказания зависят от взаимосвязей точек изображения, описываемых автокорреляционной функцией. Из определения (4.20) видно, что в случае стационарных данных автокорреляционная функция отличается от вышерассмотренной функции на постоянную величину. При нестационарных данных функция r (в уравнении (4.23) зависит от пространственных переменных и оптимальные коэффициенты предсказания должны изменяться в зависимости от пространственных координат. Это характерно для изображений. К счастью, нестационарные статистические характеристики изображений обычно можно достаточно хорошо аппроксимировать стационарными функциями, так что неперестраивающееся линейное устройство предсказания дает вполне хорошие результаты. При сжатии видеоинформации методом ДИКМ ошибки обычно появляются на границах изображаемых предметов, где предположение о стационарности удовлетворяется в наименьшей степени, и на восстановленном изображении воспринимаются визуально как аномально - светлые или темные точки.

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

Минимальное число уровней квантования paвно двум (одноразрядные числа) и соответствует такому квантованию изображений, при котором разность яркостей принимает фиксированное (положительное или отрицательное) значение. Этот способ обычно называют дельта - модуляцией, схему ДИКМ (рис. 4.8) можно упростить заменой квантователя на ограничитель, а предсказывающего устройства n -го порядка на интегратор. При сокращении избыточности изображений методом дельта-модуляции наблюдаются те же недостатки, что и при дельта-модуляции других сигналов, например речевых , а именно затягивание фронтов и искажения дробления. Однако если частота дискретизации изображения выбрана намного больше частоты Найквиста, то сжатие методом дельта - модуляции приводит к малым (субъективно замечаемым) ошибкам. Если частота дискретизации приближается к частоте Найквиста, то на изображении в большей степени будут проявляться затягивания фронтов (на контурах изображений) и искажения дробления (на участках с постоянной яркостью). Как и при сжатии речи , адаптивная дельта-модуляция позволяет уменьшить эти ошибки. Однако в целом при передаче изображений дельта - модуляция оказалась менее эффективной, что при передаче речи.

Квантование с числом уровней, большим двух, позволяет при сокращении избыточности получить изображения более высокого качества. Система сжатия методом ДИКМ с 8-уровневым (З-разрядным) квантованием при оптимальном размещении порогов дает изображения, качество которых такое же, как в системе с ИКМ, имеющей разрядность от 6 до 8. Исключение составляют ошибки вблизи линий резкого изменения яркости.

Сигнал с выхода устройства квантования, конечно, следует кодировать, поскольку распределение вероятностей «квантованных разностей не является равномерным. При удачном выборе кода (например, кода Шеннона - Фано или Хаффмена) удается дополнительно понизить общую скорость создания информации. Прэтт указывает, что при использовании кода Хаффмена в пределе удается понизить скорость создания информации до 2,5 бит/точка. Это дополнительное понижение скорости требуется сопоставить с увеличением стоимости и сложности запоминающего устройства, синхронизаторов и вспомогательных регистров памяти, необходимых для работы с кодами Хаффмена.

Выше обсуждались вопросы сжатия изображений с помощью ДИКМ при выборе элементов по строке (т.е. для прогноза брались точки, лежащие на текущей строке развертки). В силу двумерного характера изображений возможно (и целесообразно) расширить метод ДИКМ так, чтобы при прогнозе учитывались яркости в точках, лежащих не только на текущей, но и на предшествующих строках развертки. Схемы сжатия методом ДИКМ с таким двумерным предсказанием основаны на тех же принципах, что при одномерном предсказании. Поскольку для изображений характерно наличие двумерных статистических взаимосвязей, можно надеяться, что двумерное предсказание даст лучшие результаты по сжатию изображений, так как декорреляция изображений с помощью операций предсказания и вычитания будет производиться по двум координатам. Действительно, устройства с пространственным предсказанием дают более качественные изображения. Хабиби показал, что с помощью двумерного предсказывающего устройства третьего порядка при 8 - уровневом (3 - разрядном) квантовании получались изображения, которые визуально не удавалось отличить от исходных фотографий, обработанных методом ИКМ с 11- разрядными числами.

Для изображений, состоящих из последовательных кадров, например телевизионных, идеи предсказания и вычитания, связанные с ДИКМ, можно распространить на временную область. В подобных изображениях яркость многих точек от кадра к кадру не изменяется или изменяется медленно. Следовательно, можно построить систему сжатия методом ДИКМ, в которой яркость очередной точки прогнозируется на основе яркостей двумерного набора точек текущего кадра и соответствующих точек предшествующих кадров. На практике порядок временного предсказания не может быть высоким, так как для каждого временного слагаемого необходимо иметь запоминающее устройство, где сохранялся бы весь кадр. Моделирование с предсказывающим устройством третьего порядка, в котором для предсказания использовались точки, расположенные в данном (и предшествующем кадрах слева от рассматриваемой точки и вверх от нее, показало, что можно получить очень хорошие изображения при средней разрядности 1 бит/точка .

4.3.3. Схемы сокращения избыточности изображений с обработкой в области преобразований

Для пояснения основных операций, выполняемых системой сжатия видеоинформации с обработкой в области преобразований, обратимся к ковариационной матрице, определяемой соотношением (4.20). Матрица [C g ] описывает корреляцию отсчетов изображения в плоскости (х, у), являющейся координатной плоскостью изображения. Важным методом многомерного статистического анализа служит исследование массива данных не только в их естественных координатах, но и в системах координат с более удобными свойствами. В частности, весьма полезными оказались системы координат, основанные на собственных значениях и собственных векторах ковариационной матрицы

[ C g ] = [ Ф ] [

] [ Ф ] T = , (4.24)

где [Ф ] - матрица, составленная из ортогональных собственных вектор - столбцов Ф i а [ ] - диагональная матрица собственных значений.

Преобразование координат, определяемое матрицей собственных векторов [Ф ], обладает тем свойством, что оно производит преобразование заданного массива чисел в другой с некоррелированными элементами, причем получающиеся компоненты имеют убывающие дисперсии. Пусть собственные значения матрицы расставлены в убывающем порядке и пронумерованы так, что

Привлекает внимание

Например, старый добрый формат GIF использует палитру, максимум на 256 цветов. Если вы захотите сохранить серию своих селфи как gif-анимацию (кому бы это надо было), то первое, что вам, а точнее программе, которую вы будете для этого использовать, надо будет сделать – создать палитру. Можно использовать статическую палитру, например web-safe colors , алгоритм квантизации получиться очень простым и быстрым, но результат будет «не очень». Можно создать оптимальную палитру на основе цветов изображения, что даст результат наиболее визуально похожий на оригинал.

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

Далее вас ждёт скучное и непонятное повествование о методе медианного сечения, алгоритму рассеивания ошибок (шума квантизации) по Флойду-Стейнбергу (и не только), особенностях цветового восприятия человеческого глаза, а так же немного говно кода.

Предыстория

Давным-давно, когда Nokia была тёплой и ламповой главенствовала на рынке смартфонов, а владельцы смартфонов гордо звали себя «смартфонщики», в те стародавние времена писал я простенькие программки на python для series60. На одну из них намедни наткнулся копаясь в архивах. GifTool – программка для создания gif-анимации из набора картинок. В ней я реализовал квантизацию методом медианного сечения, алгоритм сжатия LZW, вся структура файла создавалась самостоятельно, для неизменившихся на следующем слайде пикселей использовалась прозрачность, чтобы уменьшить итоговый размер файла. Захотелось мне освежить свою память, посмотреть – как же она работала. Открыл код и … Это чувство, когда ты не можешь разобраться в своём говнокоде десятилетней давности. Про PEP8 я тогда не знал, поэтому читаемость кода была чуть менее чем никакой (тогда мне нравился минимализм, как и многим начинающим программистам). Прослезился, поплевался, отрефакторил в PyCharm, разобрался как реализовал метод медианного сечения, по быстрому накидал «грязный» скрипт. Работает! Палитра создаётся, изображение на выходе получается сносное. И тут меня закусило – смогу ли я добиться лучших результатов, чтобы картинка визуально была как можно ближе к оригиналу.


Итак – метод медианного сечения. Он прост до безобразия. Первым делом надо из всех уникальных цветов изображения составить RGB куб. Далее рассечь его по самой длинной стороне. Например, диапазон красного у нас от 7 до 231 (длина 231-7=224), зелёного от 32 до 170 (длина 170-32=138), синего от 12 до 250 (длина 250-12=238), значит, будем «резать» куб по синей стороне. Получившиеся сегменты так же рассекаем по длинной стороне и т.д. пока не получим 256 сегментов. Для каждого сегмента высчитать средний цвет – так мы и получим палитру.

Пара картинок почти в тему, для наглядности



Что здесь можно улучшить? Первое, что приходит в голову – вычислять средний цвет не тупо сложив все цвета и разделив на их количество [ sum(color) / count(color) ], а с учётом, сколько раз каждый цвет встречается в изображении. То есть каждый цвет умножаем на количество его вхождений в изображении, полученные значения складываем, результат делим на количество вхождений в изображении всех цветов данного сегмента [ sum(color * total) / sum(total) ]. В результате, наиболее часто встречаемые цвета имеют приоритет при вычислении, но и редкие цвета вносят свои корректировки, поэтому палитра получается лучше, визуальное отклонение цветов меньше. Для лучших результатов желательно ещё учитывать гамму, но я оставил это на потом. Второе не так явно – медианное сечение совсем не учитывает особенности восприятия цвета человеческим глазом. Оттенки зелёного мы воспринимаем гораздо лучше оттенков синего. Я решил исправить это недоразумение и «сплющил» куб – длины сторон помножил на коэффициенты из этой статьи . В результате по зелёной и красной стороне сечений стало больше, по синей меньше. Такого решения я больше нигде не встречал (может плохо искал), но результат на лицо.

Теперь у нас есть оптимальная палитра, не идеальная конечно (я знаю, что её можно ещё улучшить), но достаточно хорошая. Следующий шаг – индексирование цветов изображения. Самый простой вариант – в каком сегменте находится цвет, такой и индекс. Быстро и просто. Но есть одно но, и даже не одно, поэтому к данному шагу мы ещё вернёмся.

Есть ещё один способ улучшить качество получаемого изображения – рассеивание ошибок. Тут тоже всё довольно просто – из индексируемого цвета вычитаем соответствующий цвет палитры, получаем ошибку, рассеиваем её по соседним пикселям в соответствии с определённой формулой (шаблоном), самая известная формула Флойда-Стейнберга, её я и использовал. При рассеивании ошибок размываются резкие переходы между цветами, и визуально кажется, что изображение содержит больше оттенков (цветов). Если интересно – про рассеивание ошибок подробно и интересно можно почитать . Этот алгоритм я так же решил допилить, помножив ошибку на всё те же коэффициенты, как оказалось, это была очень хорошая идея – так как сечений по синему диапазону стало меньше, в нём получалась значительная ошибка, и без корректировки ошибки коэффициентами рассеивание вносило много «шума».

Вот теперь можно снова вернуться к индексированию. Рассеиванием ошибок мы изменяем цвета пикселей и получаем такие, которых нет в нашем RGB-кубе (напомню, он составлен исключительно из цветов изображения). Теперь нельзя просто посмотреть в каком сегменте находится цвет, чтобы назначить индекс. Решение нашлось сразу – поиск ближайшего цвета в палитре . В данную формулу я подставил всё те же коэффициенты. Сравнивая результаты подбора цвета палитры по индексу сегмента в который входит исходный цвет и результаты поиска ближайшего цвета, наглядно увидел, что ближайший цвет часто оказывается в соседнем сегменте. Если исходный цвет находится ближе к центру сегмента – то индекс сегмента соответствует индексу цвета в палитре, но чем ближе исходный цвет к краям сегмента, тем больше вероятность, что ближайший цвет окажется в соседнем сегменте. В общем, единственный правильный путь индексирования – поиск ближайшего цвета в палитре. Но у поиска есть минус – он медленный, очень медленный. Писать числодробилку на python плохая идея.

Ну вот, хотел объяснить в двух словах, а получилась целая куча непонятной писанины. Надеюсь, код я пишу лучше, чем объясняю, поэтому вот ссылочка на github . Код несколько раз переписывался, сначала совершенствовался алгоритм, пока результат меня не устроил, потом оказалось, что он жрёт слишком много оперативы при обработке фотографий (сначала тестировал на небольших картинках), пришлось перенести RGB-куб, медианное сечение и карту пикселей в базу данных (sqlite). Скрипт работает очень медленно, но результат получается лучше, чем квантизация средствами PIL/Pillow и GIMP’ом (в нём эта операция называется индексирование).

Наглядная демонстрация:

Оригинал

Результат квантизации в GIMP, оптимальная палитра на 256 цветов + размывание цвета по Флойду-Стенбергу (нормальное)

Результат квантизации PIL/Pillow image.convert(mode="P", dither=PIL.Image.FLOYDSTEINBERG, palette=PIL.Image.ADAPTIVE, colors=256)

Результат квантизации моим кодом

На что обратить внимание: рассеивание ошибки у GIMP сильно «шумит», PIL/Pillow создает не очень оптимальную палитру и практически не рассеивает ошибки (резкие переходы между цветами).
Если не видите разницу - посмотрите другие примеры на github .


P.S.: есть замечательная программа Color Quantizer , которая справляется с данной задачей лучше и быстрее, поэтому практического смысла мой скрипт не имеет, сделан исключительно из «спортивного» интереса.
UPD: обновил проект на github . Добавил алгоритм квантизации Octree (октодерево), популярные формулы рассеивания ошибок, поиск ближайшего цвета по среднему значению красного.

Определение глубины цвета

Большинство компьютеров при отображении использует 8, 16 или 24 бит на пиксель. Этим определяется глубина цвета отображаемого изображения.

Независимо от того сколько цветов отображает система, MATLAB может запоминать и обрабатывать изображения с различным числом бит на пиксель: 224 цветов для RGB-изображений в формате uint8, 248 цветов для RGB-изображений в формате uint16 и 2159 цветов для RGB-изображений в формате удвоенной точности. Эти изображения наилучше отображаются в системе с 24-битным представлением цвета, хотя источники формирования изображений не всегда могут обеспечивать такую глубину цвета. В большинстве случаев они обеспечивают 16-битное представление цвета.

  • Описание определения глубины цвета системы
  • Описание выбора глубины цвета

Описание глубины цвета

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

Get(0,"ScreenDepth") ans = 32

MATLAB возвращает число бит на пиксель:

Значение Описание
8 8-битное представление отображается 256 цветами. 8-битные полутоновые изображения являются составной частью 24-битного представления графической информации.
16 16-битное представление обычно использует 5-бит на каждую цветовую компоненту, что равно 32 градациям (т.е. 2 5) на красную, зеленую и синюю составляющие. В результате такое представление поддерживает 32,768 (т.е., 2 15) различных цветов. Некоторые системы используют дополнительный бит для увеличения числа градаций отображаемого цвета. В нашем случае число различных цветов при 16-битном представлении равно 64,536 (т.е. 2 16).
24 24-битная визуализация использует 8 бит на каждую из трех цветовых составляющих, т.е. 256 (2 8) градаций на красную, зеленую и синюю компоненту. В результате получается 16,777,216 (т.е. 2 24) различных цветов.
32 32-битная визуализация использует 24 бита для запоминания цветовой информации а еще 8 бит используется для запоминания насыщенности (прозрачности) данных. Это так называемый альфа канал.

Описание выбора глубины цвета

В зависимости от используемой системы, можно устанавливать различные значения количества бит на пиксель. (Это может быть также связано с разрешением графических объектов. В большинстве случаев 24-битное представление обеспечивает хорошую визуализацию. Если есть необходимость использовать меньшее число бит на пиксель, то можно использовать 16-битное представление. При обработке полутоновых изображений, в большинстве случаев, достаточно 8 бит на пиксель.

Уменьшение числа цветов на изображении

В этом пункте описано метод уменьшения числа цветов в индексных или RGB изображениях. Рассмотрим также метод диффузионного псевдосмешения цветов (dithering). Этот метод использует визуальное увеличение количества цветов на изображении.

Внизу приведены краткие описания функций уменьшения цветов на изображении в приложении Image Processing Toolbox.

В системах с 24-битным отображением цвета RGB изображения могут отображаться 16,777,216 (т.е. 2 24) цветами. В системах с меньшим количеством отображаемых цветов RGB изображения также будут отображаться хорошо, потому что MATLAB автоматически использует аппроксимацию цветов и диффузионное псевдосмешение цветов.

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

  • В системах с 8-битным отображением при визуализации индексных изображений, которые имеют больше, чем 256 цветов, применяется метод диффузионного псевдосмешения цветов (dithering), поскольку не все цвета могут быть представлены системой.
  • В некоторых системах палитра в принципе не может иметь больше, чем 256 позиций.
  • Если индексное изображение содержит больше, чем 256 цветов, MATLAB запоминает данные изображения в массив не в формате uint8, а в формате удвоенной точности. Это приводит к увеличению объема запоминаемых данных, поскольку каждый пиксель занимает 64 бита.
  • Файлы изображений больших размеров желательно записывать в формате, который использует при визуализации 256 цветов. Если при записи (с использованием функции imwrite) в этом же формате изображения имеют больше чем 256 цветов, то в дальнейшем возможны ошибки при визуализации.

Уменьшение числа цветов на индексном изображении

Использование rgb2ind

Функция rgb2ind выполняет преобразование RGB-изображения в индексное изображение, уменьшая количество отображаемых цветов. При обработке исходного изображения функция использует следующие методы аппроксимации цветов:

  • Квантование
    • Равномерное квантование
    • Квантование с наименьшей дисперсией
  • Отображение палитры

Качество результирующего изображения зависит от выбранного метода аппроксимации, диапазона цветов исходного изображения и использования метода диффузионного псевдосмешения цветов (dithering). Отметим, что результат работы методов очень зависит также от конкретного изображения.

Квантование

Квантование приводит к уменьшение количества цветов на изображении. Функция rgb2ind использует квантование как часть алгоритма уменьшения цветов. Функция rgb2ind поддерживает два метода квантования: равномерное квантование и квантование с наименьшей дисперсией.

При рассмотрении этого вопроса применяется понятие куб RGB цветов. Куб RGB цветов представляет собой трехмерный массив всех цветов, которые определены для этого типа данных. Поскольку изображения в MATLAB могут быть представлены в различных форматах (uint8, uint16 или double), то это будет влиять на дискретизацию цветов в кубе RGB.

Равномерное квантование. Для выполнения равномерного квантования используется функция rgb2ind с соответствующими параметрами.

Внизу приведен пример равномерного квантования. Второй аргумент влияет на дискретность квантования.

RGB = imread("peppers.png"); = rgb2ind(RGB, 0.1);

На изображении продемонстрировано равномерное квантование изображения, представленного в формате uint8. Для удобства на изображении показан двухмерный срез цветового куба, где красный цвет равен 0, а диапазон зеленого и голубого равен .

Квантование с наименьшей дисперсией. Для реализации квантования с наименьшей дисперсией используется функция rgb2ind с указанием максимального числа цветов на результирующем изображении. Это число определяет количество ячеек, на которое будет разбит цветовой куб RGB. Рассмотрим пример реализации метода квантования для создания индексного изображения с использованием 185 цветов.

RGB = imread("peppers.png"); = rgb2ind(RGB,185);

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

Уменьшение количества цветов на индексном изображении

Для уменьшения количества цветов на изображении используется также функция imapprox. Функция imapprox использует некоторые методы аппроксимации. По сути, функция imapprox сначала с помощью функции ind2rgb выполняет преобразование изображения в формат RGB, а потом использует функцию rgb2ind для преобразования в индексное изображение с измененным количеством цветов.

Пример.
Рассмотрим пример формирования изображений, которые содержат 128 и 16 цветов с использованием функций rgb2ind и imapprox соответственно.

L=imread("peppers.png"); figure,imshow(L);


Исходное изображение

L=im2double(L); = rgb2ind(L, 128); figure,imshow(x,map);


Изображение со 128 цветами

Imapprox(x,map,16); figure,imshow(Y, newmap);


Изображение с 16 цветами

Качество обработанного изображения зависит от того какой метод аппроксимации используется, от количества цветов на исходном изображении, используется или нет метод диффузионного псевдосмешения цветов (dithering). Отметим, что различные методы дают различный результат для разных изображений.

Сглаживание цветовых переходов

Метод диффузионного псевдосмешения цветов (dithering)

При использовании функций rgb2ind или imapprox для уменьшения количества цветов на изображении, качество результирующего изображения немного ниже. Это связано с уменьшением количества цветов, с помощью которых отображается изображение. Обе функции - rgb2ind и imapprox - применяют метод диффузионного псевдосмешения цветов (dithering). Это приводит к визуальному увеличению количества отображаемых цветов. Метод dithering изменяет цвета пикселей окрестности таким образом, что средний цвет окрестности аппроксимирует исходный RGB-цвет.

Рассмотрим пример работы метода диффузионного псевдосмешения цветов (dithering).

  1. Считывание и визуализация исходного изображения. rgb=imread("onion.png"); imshow(rgb);

  2. Создание индексного изображения с восьмью цветами без применения метода диффузионного псевдосмешения цветов (dithering). =rgb2ind(rgb,8,"nodither"); figure, imshow(X_no_dither,map);

  3. Создание индексного изображения с восьмью цветами с применением метода диффузионного псевдосмешения цветов (dithering). =rgb2ind(rgb,8,"dither"); figure, imshow(X_dither,map);

    Отметим, что обработка изображения методом диффузионного псевдосмешения цветов (dithering) приводит к визуальному увеличению отображаемых цветов. Однако возникает риск возникновения ложных контуров.

    Выполнение преобразования цветовых пространств

    Преобразование цветовых данных между цветовыми пространствами

    Наиболее часто в Image Processing Toolbox для описания цифровых изображений используется цветовая система RGB. В этом случае столбцы палитры представляют собой интенсивности красной, зеленой и синей составляющих. Палитровые изображения могут иметь произвольную глубину цвета, хотя наиболее широкое распространение получили палитровые изображения, глубина цвета которых составляет соответственно 4 и 8 бит на пиксель.

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

    Преобразования между устройство-зависимыми цветовыми пространствами

    Рассмотрим два цветовых пространства RGB и CMYK. Любой цвет в пространстве RGB формируется как сумма разных количеств красной, зеленой и синей составляющих. Когда значения всех составляющих равны нулю, тогда формируется черный цвет. Если все составляющие принимают максимально возможное значение, тогда формируется белый цвет. Однако понятие "белый цвет" является приближенным. Дело в том, что RGB-составляющие обеспечивают только хорошее приближение, а настоящий белый цвет может быть получен только сложением всех его спектральных составляющих, а не только R,G и B. В CMYK-пространстве белый цвет достигается обнулением всех его составляющих, а Cyan (C, Голубой), Magenta (M, Пурпурный) и Yellow (Y, Желтый) используются в нем для создания прочих цветов. Недостатком этого цветового пространства является то, что устройства, которые его используют, не могут отображать яркие и насыщенные цвета.

    Цветовые пространства RGB и CMYK являются устройство-зависимыми, так как цвет в них привязан к тому или иному устройству, для которого заданы эти значения. "Устройством" может быть принтер, сканер, монитор и пр. Действительно, каждый принтер, сканер или монитор одно и то же изображение будут отображать своими цветами, хотя значения RGB на них подаются одинаковые.

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

    В таблице приведен список устройство-зависимых цветовых пространств, которые поддерживаются приложением по обработке изображений системы Matlab.

    Функция Назначение Назначение
    XYZ Система координат спектральных основных цветов, которая была разработана в 1931 г. МКО (Международной комиссией по освещению). xyY, uvL, u"v"L и L*a*b*
    xyY Описание получения нормированных хроматических значений. Составляющая Y, как и в системе XYZ, представляет яркость. XYZ
    uvL Равноконтрастная система координат. L представляет яркость и является аналогом Y в XYZ. XYZ
    u"v"L Развитие прежней системы с целью получения цветового пространства, в котором единичные изменения цветности и яркости воспринимаются одинаково. XYZ
    L*a*b* Попытка учесть зависимость восприятия и яркости. L* представляет собой нелинейное масштабирование L , нормированное относительно некоторых точек. XYZ
    L*ch В этой модели c и h представляют соответственно насыщенность и цветность. В полярных координатах эта система преобразуется в L*a*b* . L*a*b*
    sRGB Цветовое пространство, которое соответствует цветовому охвату среднестатистического ЭЛТ-монитора. XYZ и L*a*b*

    Пример: Представление изображений в различных цветовых пространствах

    Считаем изображение в формате RGB в рабочее пространство MATLAB и преобразуем цветовые данные в цветовое пространство XYZ: