Как нужно обрабатывать изображения, чтобы не расстраивать математику?

image-loader.svg

Мы часто расстраиваем математику, выполняя привычные операции с изображениями — например, когда мы масштабируем их или применяем к ним фильтры. Одним словом — тогда, когда мы производим арифметические операции (+,-,*,/) над значениями цветовых каналов. Обычно это не заметно, но иногда это может доставить неприятности.

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

Цветовые пространства

Большинство изображений, хранящихся на наших компьютерах и в интернете, представлены в цветовом пространстве sRGB («standard RGB»). Устройства захвата изображений (сканеры, фотоаппараты, смартфоны), как правило, сохраняют фотографии в пространстве sRGB, а устройства вывода изображений (мониторы, принтеры) по умолчанию предполагают, что им на вход поступают sRGB значения. Художники, обрабатывающие фотографии, могут сохранять их и в других пространствах — в Adobe RGB, ProPhoto, DCI-P3. 

Как понять, в каком цветовом пространстве представлено ваше изображение?

Изображение (в формате jpg, png, tiff, cr2, dng и т.д.) может содержать метаданные, в которых либо указано конкретное название цветового пространства, либо содержится информация о цветовом профиле, который неявно задает это цветовое пространство. Утилита exiftool позволяет прочитать эти метаданные.

Рис. 1. Пример чтения метаданных из AdobeRGB изображения с помощью exiftoolРис. 1. Пример чтения метаданных из AdobeRGB изображения с помощью exiftool

Если метаданные в файле отсутствуют, то принято считать, что это изображение представлено в цветовом пространстве sRGB.

Что происходит?

Почти все цветовые пространства (sRGB, Adobe RGB, ProPhoto, DCI-P3) нелинейны относительно интенсивности воспринимаемого человеком цвета. А с точки зрения математики арифметические операции (+,-,*,/) определены только в линейных пространствах.

То есть, например, умножая значение красного канала на 0.1, мы подразумеваем, что хотим уменьшить интенсивность красного в 10 раз. Но если красный канал был представлен в нелинейном пространстве, то его интенсивность уменьшится не в 10 раз, а в некое K раз, где K вообще не постоянно и зависит от текущего значения красного канала. Как правило, ошибку обнаружить сложно, потому что K все же близко к 10. Такие ошибки не просто визуально искажают результаты работы алгоритмов, а рушат саму логику алгоритмов. 

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

Что делать?

  1. Понять, в каком пространстве закодированы цветовые каналы вашего изображения (чаще всего это sRGB). 

  2. Сконвертировать цветовые каналы в линейное пространство. Например, sRGB можно перевести в линейное RGB пространство с помощью обратной гамма-коррекции (формулы для перевода; python библиотека). Если вам нужны пространства HSV/HSL (они тоже нелинейные), то вместо них нужно использовать линейные CIELAB/CIELUV.

  3. Обработать изображение вашим алгоритмом.

  4. Перевести изображение в исходное цветовое пространство (чаще всего это sRGB) перед выводом на экран или перед сохранением в файл.

Библиотеки для работы с изображениями и нейросетями (OpenCV, Scikit-image, Pillow, PyTorch, TensorFlow) оперируют с изображениями как с массивами абстрактных RGB чисел, без привязки к конкретному цветовому пространству. То есть эти библиотеки обычно не проводят автоматической линеаризации цветового пространства, и поэтому конвертировать изображение линейное RGB пространство вам нужно самостоятельно. 

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

К моменту публикации этой статьи даже Google Chrome неверно выполняет resize изображения (см. вышеупомянутую статью).

Пример: photometric stereo

Мы в Twin3D решаем задачу 3D-реконструкции человеческих лиц с помощью multi-view photometric stereo. Такие алгоритмы очень чувствительны к линейности цветового пространства, поскольку связывают информацию о цвете с информацией о геометрии. 

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

Рис. 2. Слева — фотография в sRGB, справа — фотография в линейном RGBРис. 2. Слева — фотография в sRGB, справа — фотография в линейном RGB

Мы провели следующий эксперимент: вычислили нормали лица на основе фотографий в sRGB пространстве и на основе фотографий в линейном RGB, а затем сравнили полученные нормали с baseline нормалями этого же лица. Для вычисления baseline нормалей мы использовали альтернативный подход — multi-view stereo 3D-реконструкцию. 

Рис. 3. Слева — нормали, вычисленные на основе sRGB цветов, справа — baseline нормалиРис. 3. Слева — нормали, вычисленные на основе sRGB цветов, справа — baseline нормалиРис. 4. Слева — нормали, вычисленные на основе линейных RGB цветов, справа — baseline нормалиРис. 4. Слева — нормали, вычисленные на основе линейных RGB цветов, справа — baseline нормали

На рисунке 3 видно, что при использовании sRGB цветов векторы нормалей получаются геометрически некорректными: лицо слева выглядит слишком «бледным» по сравнению с лицом справа и имеет дефект в виде темного пятна на кончике носа. Если же посмотреть на карту нормалей, вычисленную на основе линейных RGB цветов (рисунок 4), то она гораздо более похожа на baseline карту нормалей. Мы не сравниваем здесь «шероховатость» нормалей, поскольку она зависит от конкретного типа алгоритма. Таким образом, в sRGB пространстве логика алгоритма photometric stereo нарушилась, и это привело к неверному результату. 

Заключение

Хорошая и математически корректная практика — это выполнять все арифметические операции с RGB значениями только в линейных цветовых пространствах. Это критически важно при решении некоторых задач компьютерного зрения.

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

Будем рады, если вы поделитесь своим опытом в комментариях!

В следующих статьях мы расскажем про другие важные и неочевидные нюансы обработки изображений и создания цифровых 3D-людей!

© Habrahabr.ru