Если вы не знаете, что такое цветовая температура, начните отсюда.
Работая над инструментом «Цветовая температура» для PhotoDemon, я целый вечер пытался определить простой и понятный алгоритм преобразования между значениями температуры (в Кельвинах) и RGB. Я думал, что такой алгоритм будет просто найти, ведь во многих фоторедакторах есть инструменты для коррекции цветовой температуры, а в каждой современной камере, включая смартфоны, есть регулировка баланса белого на основе условий освещения.
Пример экрана камеры с установкой баланса белого. Источник
Оказалось, найти надёжную формулу преобразования температуры в RGB практически невозможно. Конечно, есть некоторые алгоритмы, но большинство из них работают путём преобразования температуры в цветовое пространство XYZ, к которому вы потом можете добавить преобразование RGB. Такие алгоритмы, похоже, основаны на методе Робертсона, одна реализация которого здесь, а другая здесь.
К сожалению, такой подход не даёт чисто математической формулы — это просто интерполяция по таблице преобразования. Это может быть разумно при определённых обстоятельствах, но если учесть дополнительное преобразование XYZ > RGB, то получается слишком медленно для простой регулировки цветовой температуры в реальном времени.
Поэтому я написал собственный алгоритм, и он работает чертовски хорошо. Вот как у меня это получилось.
Предупреждения относительно этого алгоритма
Предупреждение 1: мой алгоритм обеспечивает высококачественное приближение, но он недостаточно точен для серьёзного научного использования. Он предназначен в основном для манипуляций с фотографиями — так что не пытайтесь использовать его для астрономии или в медицине.
Предупреждение 2: из-за своей относительной простоты этот алгоритм достаточно быстр для работы в реальном времени на изображениях разумного размера (я тестировал его на 12-мегапиксельных снимках), но для достижения наилучших результатов следует применить математические оптимизации, характерные для вашего языка программирования. Я показываю алгоритм без математических оптимизаций, чтобы не усложнять его.
Предупреждение 3: алгоритм предназначен только для использования в диапазоне от 1000 K до 40000 K, что является хорошим диапазоном для фотографии. (На самом деле он намного больше, чем может потребоваться в большинстве ситуаций). Хотя он работает для температур и за пределами этого диапазона, но качество будет снижаться.
Особая благодарность Митчеллу Чарити
Во-первых, должен отдать большой долг и поблагодарить Митчелла Чарити за исходные данные, которые использовал для создания этих алгоритмов: необработанный файл чёрного тела. Чарити предоставляет два набора данных, и мой алгоритм использует 10-градусную функцию сопоставления цветов CIE 1964. Обсуждение 2-градусной функции CIE 1931 с исправлениями Джадда Воса по сравнению с 10-градусным набором выходит за рамки этой статьи, но если вам интересно, можете начать всесторонний анализ с этой страницы.
Алгоритм: пример выдачи
Вот выдача алгоритма в диапазоне от 1000 К до 40000 К:
Выдача моего алгоритма от 1000 К до 40000 К. Белая точка находится на 6500?6600 К, что идеально подходит для обработки фотографий на современном ЖК-мониторе
Вот более подробный снимок алгоритма в интересном для фотографии диапазоне от 1500 К до 15000 К:
Тот же алгоритм, но от 1500 K до 15000 K
Как можно заметить, бандажирование полосами минимально, что является большим улучшением по сравнению с вышеупомянутыми таблицами соответствия. Алгоритм также делает большую работу по сохранению светло-жёлтого оттенка возле белой точки, что важно для имитации дневного света в постобработке фотографий.
Как я пришёл к этому алгоритму
Первым шагом к выведению надёжной формулы было построить график оригинальных значений чёрного тела от Чарити. Вы можете скачать всю электронную таблицу в формате LibreOffice/OpenOffice .ods (430 КБ).
Вот данные после построения графика:
Данные оригинальной температуры (K) в RGB (sRGB), график LibreOffice Calc. Опять же, преобразование основано на 10-градусной CMF-функции CIE 1964. Белая точка, как и требовалось, находится между 6500 K и 6600 K (пик в левой части графика). Источник
Легко заметить, что есть несколько участков, которые упрощают наш алгоритм. В частности:
- Красные значения ниже 6600 K всегда 255
- Синие значения ниже 2000 K всегда 0
- Синие значения выше 6500 K всегда 255
Ещё важно отметить, что для подгонки кривой под данные зелёный цвет лучше всего рассматривать как две отдельные кривые — одна для температур ниже 6600 K, а другая для температур выше этой точки.
С этого момента я разделил данные (без сегментов «всегда 0» и «всегда 255») на отдельные цветовые компоненты. В идеальном мире кривую можно подогнать к каждому набору точек, но, к сожалению, в реальности это не так просто. Поскольку на графике сильное несоответствие между значениями X и Y — все значения x больше 1000 и отображаются в 100 точечных сегментах, в то время как значения y находятся между 255 и 0 — пришлось транспонировать данные x, чтобы получить лучшее соответствие. В целях оптимизации я сначала разделил значение x (температура) на 100 для каждого цвета, а затем вычел сколько нужно, если это значительно помогало в подгонке к графику. Вот результирующие диаграммы для каждой кривой, а также наиболее подходящая кривая и соответствующее значение коэффициента детерминации (R-квадрат):
Прошу прощения за ужасный кернинг шрифтов и хинтинг на диаграммах. У LibreOffice много плюсов, но неспособность сглаживать шрифты на графиках совершенно постыдна. Мне также не нравится извлекать диаграммы из скриншотов, потому что у них нет опции экспорта, но это лучше оставить на потом.
Как видите, все кривые достаточно хорошо выровнены, со значениями коэффициента детерминации выше 0,987. Я мог бы потратить больше времени на подгонку кривых, но для обработки фотографиями этого достаточно. Ни один обыватель не скажет, что кривые неточно соответствуют исходным идеализированным наблюдениям чёрного тела, верно?
Алгоритм
Вот алгоритм во всей красе.
Во-первых, псевдокод:
Начнём с температуры в Кельвинах где-то между 1000 и 40000. (Другие значения могут работать, но я не могу дать никаких обещаний о качестве оценок алгоритма выше 40000 K). Обратите внимание, что переменные температуры и цвета должны быть объявлены как переменные с плавающей точкой.
Set Temperature = Temperature \ 100
Вычисление красного:
If Temperature <= 66 Then
Red = 255
Else
Red = Temperature - 60
Red = 329.698727446 * (Red ^ -0.1332047592)
If Red < 0 Then Red = 0
If Red > 255 Then Red = 255
End If
Вычисление зелёного:
If Temperature <= 66 Then
Green = Temperature
Green = 99.4708025861 * Ln(Green) - 161.1195681661
If Green < 0 Then Green = 0
If Green > 255 Then Green = 255
Else
Green = Temperature - 60
Green = 288.1221695283 * (Green ^ -0.0755148492)
If Green < 0 Then Green = 0
If Green > 255 Then Green = 255
End If
Вычисление синего:
If Temperature >= 66 Then
Blue = 255
Else
If Temperature <= 19 Then
Blue = 0
Else
Blue = Temperature - 10
Blue = 138.5177312231 * Ln(Blue) - 305.0447927307
If Blue < 0 Then Blue = 0
If Blue > 255 Then Blue = 255
End If
End If
Обратите внимание, что в приведённом псевдокоде Ln() означает натуральный логарифм. Также обратите внимание, что можно опустить проверки «если цвет меньше 0», если температуры всегда в рекомендуемом диапазоне. (Однако всё равно нужно оставить проверки «если цвет больше 255»).
Что касается фактического кода, вот точная функция Visual Basic, которую я использую в PhotoDemon. Она ещё не оптимизирована (например, логарифмы стианут намного быстрее с таблицами соответствия), но хотя бы код краткий и читаемый:
'Для данной температуры (в Кельвинах) вычисляем RGB-эквивалент Private Sub getRGBfromTemperature(ByRef r As Long, ByRef g As Long, ByRef b As Long, ByVal tmpKelvin As Long)
Static tmpCalc As Double
'Температура должна быть в диапазоне от 1000 до 40000 градусов
If tmpKelvin < 1000 Then tmpKelvin = 1000
If tmpKelvin > 40000 Then tmpKelvin = 40000
'Все вычисления требуют tmpKelvin \ 100, так что можно обойтись однократным преобразованием
tmpKelvin = tmpKelvin \ 100
'Вычисляем все цвета по очереди
'Сначала красный
If tmpKelvin <= 66 Then
r = 255
Else
'Примечание: значение R-квадрата для этого приближения 0,988
tmpCalc = tmpKelvin - 60
tmpCalc = 329.698727446 * (tmpCalc ^ -0.1332047592)
r = tmpCalc
If r < 0 Then r = 0
If r > 255 Then r = 255
End If
'Затем зелёный
If tmpKelvin <= 66 Then
'Примечание: значение R-квадрата для этого приближения 0,996
tmpCalc = tmpKelvin
tmpCalc = 99.4708025861 * Log(tmpCalc) - 161.1195681661
g = tmpCalc
If g < 0 Then g = 0
If g > 255 Then g = 255
Else
'Примечание: значение R-квадрата для этого приближения 0,987
tmpCalc = tmpKelvin - 60
tmpCalc = 288.1221695283 * (tmpCalc ^ -0.0755148492)
g = tmpCalc
If g < 0 Then g = 0
If g > 255 Then g = 255
End If
'Наконец, синий
If tmpKelvin >= 66 Then
b = 255
ElseIf tmpKelvin <= 19 Then
b = 0
Else
'Примечание: значение R-квадрата для этого приближения 0,998
tmpCalc = tmpKelvin - 10
tmpCalc = 138.5177312231 * Log(tmpCalc) - 305.0447927307
b = tmpCalc
If b < 0 Then b = 0
If b > 255 Then b = 255
End If
End Sub
Функция использовалась для генерации образца выдачи в начале этой статьи, поэтому я могу гарантировать, что она работает.
Примеры изображений
Вот отличный пример того, что могут сделать регулировки цветовой температуры. Изображение ниже — рекламный плакат для сериала HBO «Настоящая кровь» — зрелищно демонстрирует потенциал регулировки цветовой температуры. Слева — исходный кадр; справа — регулировка цветовой температуры с помощью кода выше. Одним щелчком мыши ночную сцену можно переделать для дневного света.
Регулировка цветовой температуры в действии
Реальный инструмент цветовой температуры в моей программе PhotoDemon выглядит следующим образом:
Инструмент цветовой температуры PhotoDemon
Скачайте программу и посмотрите его в действии.
Дополнение, октябрь 2014
Рено Бедар сделал отличное онлайн-демо для этого алгоритма. Спасибо, Рено!
Дополнение, апрель 2015
Спасибо всем, кто предложил улучшения оригинального алгоритма. Я знаю, что у статьи много комментариев, но их стоит прочитать, если вы планируете реализовать собственную версию.
Хочу выделить два конкретных улучшения. Во-первых, Neil B любезно предоставил лучшую версию для исходных функций подгонки кривой, что слегка меняет температурные коэффициенты. Изменения подробно описаны в его превосходной статье.
Затем Фрэнсис Лох добавил некоторые комментарии и примеры изображений, которые очень полезны, если вы хотите применить эти преобразования к фотографиям. Его модификации производят гораздо более детальное изображение, что видно на примерах.
Комментарии (26)
Moskus
22.10.2018 22:06Сюда стоит посылать тех студентов и школьников-двоечников с Тостера, которые постоянно задают там вопросы «нужна ли программисту математика», пытаясь найти оправдание своей лени.
Или тех, кто просит «простую формулу» для вычислений из предметной области, которую они совершенно не понимают.
kAIST
22.10.2018 22:08Спасибо за полезную статью. В фоторедакторах, кстати, это очень редкий инструмент. Даже в фотошопе его нет (вернее есть, но запрятан в очень не очевидное место).
AEP
23.10.2018 00:05Вообще-то существует два распространенных стандарта на цветовую температуру (blackbody и daylight). Было бы неплохо явно указать, и поближе к началу статьи, какой именно стандарт используется. Или реализовать оба.
Упс, стандартов целых три, не знал про TM-30.
www.waveformlighting.com/tech/calculate-cie-1931-xy-coordinates-from-cct
Refridgerator
23.10.2018 07:06Непонятно, как собственно коррекция изображения происходит. Ну, преобразовали температуру в RGB, а что с ним дальше делать — складывать, умножать?
Или надо сначала RGB в температуру преобразовать, сдвинуть, и обратно температуру в RGB. Но в статье преобразование RGB в температуру не рассматривается, или я что-то пропустил?yurisv3
23.10.2018 08:12В статье и НЕ рассматривается алгоритм КОРРЕКЦИИ. Ее предмет — получение RGB БЕЛОЙ точки для заданной цветовой температуры.
Как ее использовать — по ссылкам, там исходники. При этом коррекция по понятным (вроде как, мне так точно) причинам идет не в RGB, а в HSL. В RGB можно, если исходное изображение камерный RAW, тогда RGB белой точки — напрямую коэффициенты для камерных RGB.
huginn
23.10.2018 08:55+1Кхм, RGB — величина относительная, она не имеет физической привязки.
Значения RGB показывают процент (ок. не процент про256) от диапазона измерения датчиков измеряющего устройства, в цветовых координатах конкретного измеряющего устройства.
Абсолютный физический смысл имеют значения в цветовых координатах CIE Lab.
Соответственно могу порекомендовать прочитать про CIE Lab, про спектрофотометры, в частности Gretag Macbeth (Xrite) iOne и про icc цветовые профиля устройств.
В данный момент, МНЕ КАЖЕТСЯ, вы изобретаете велосипед, давным давно изобретенный и лет пятнадцать используемый в полиграфии.yurisv3
23.10.2018 09:13-1А мне кажется — чукча не читатель. От слова «совсем».
«вы изобретаете велосипед» — это к кому вы вот сейчас обращаетесь, на секундочку?katzen
23.10.2018 11:30+1huginn вообще-то абсолютно прав. RGB — профилезависимое пространство, цветов RGB вне описываемой системы координат, то есть профилируемых излучателей, не существует.
yurisv3
23.10.2018 12:09-1Если человек не в состоянии увидеть значок «Перевод» — что вообще обсуждать, он там ничего не понял и не факт, что вообще прочитал (как и вы, кстати)?
Статья не о RGB значениях пикселей, а о коэффициентах для каналов R, G и B.
Которые и есть собственно «Баланс Белого». Но поскольку в нашей жизни пользоваться «2.573242 1.000000 1.267578» не так удобно, как «6285(К) 1.011(Тинт)», человек подобрал аппроксимирующий полином, чтобы в своей программе обойтись одним движком «Температура».
Все. Что непонятно? Что у цветового сдвига относительно «потолочного» 6500, как он делает, неизбежна погрешность — тем больше, чем насыщенней цвет — ну так начинает с этого, «он недостаточно точен для серьёзного научного использования». Как инструмент в фоторедакторе, где коррекция делается «на глаз» с получением приемлемого на вид результата — никаких проблем.katzen
24.10.2018 02:41… что вообще прочитал (как и вы, кстати)?
Я как раз прочитал ровно до тех пор, как стал искать слово «профиль» в статье. Вот только переводить плохо написанное или неполностью понятое не стоит.
… относительно «потолочного» 6500
Что вы сейчас имели в виду? 6500 на потолке или 6500 как максимальное?
Все. Что непонятно?
Вы так меня спрашиваете, что мне на секунду показалось, что я бестолочь, а вы мне как бы объясняете, что тут к чему. Мне же, как человеку, немного знакомому с цветом и с этими самыми цветовыми температурами, профилями и цветовыми пространствами постоянного немного работающему, непонятно, зачем подавать публике статью, которая грешит серьёзной теоретической непроработкой — причём в своей базе.
JewelFox
23.10.2018 10:47-1Абсолютный физический смысл имеют значения в цветовых координатах CIE Lab
Какой абсолютный физический смысл может иметь, грубо говоря, разница измеряемого цвета и заданной вручную точки белого, если цвет сам по себе — субъективное ощущение человека? Тем более что все стандартные наблюдатели получены усреднением экспериментальных данных разных людей.yurisv3
23.10.2018 11:08Объясняю.
Одинаковые приборно измеренные значения цвета мотков пряжи, произведенных на разных заводах (или одним заводом, но разное время), глаз (независимо чей) видит одного цвета, и связанный из них свитер не демонстрирует границ — где закончилось одно и началось другое.
Таких миллион примеров.JewelFox
23.10.2018 12:54Одинаковость цветов предмета под одним источником света не гарантирует одинаковости под другим, поэтому для строгости стоило упомянуть про источник, которым освещают.
Мой комментарий был направлен на то, что katzen ниже уже написал — говорить про физический смысл цвета не корректно.
huginn
24.10.2018 01:23Когда вы разберетесь с тем, что такое свет и цвет, что такое субтрактивная и аддитивная цветовые модели, что такое спектр излучения и спектр поглощения, то вам сразу станет легче.
Когда вы прочитаете про стандартные излучатели (A, B, C, D50, D65 и тд) вам полегчает ещё больше.
Потом уже можно будет порассуждать в чем разница между белым листом освещенным желтым светом и желтым листом освещенным белым светом.
А ещё там есть такая штука как дельта Е…JewelFox
24.10.2018 19:49Все это мне известно, потому что это область моей деятельности. И именно поэтому коробит от приписывания физического смысла цвету.
А менторским тоном писать комментарии не надо, а то возникает ощущение, что у вас большущее такое чувство собственной компетентности в данном вопросе.huginn
25.10.2018 03:05>И именно поэтому коробит от приписывания физического смысла цвету.
Если вас от чего то коробит то это повод сходить к психологу или попить успокаивающих препаратов. Спектр излучения и спектр поглощения однако же никуда не делись и вполне позволяют производить математические и аналитические операции со столь коробящим вас понятием «цвет».
>А менторским тоном писать комментарии не надо, а то возникает ощущение, что у вас большущее такое чувство собственной компетентности в данном вопросе.
Я не нуждаюсь в ваших оценках своей компетентности и своего тона. Если я сделал ошибку — исправьте, покажите где ошибка и объясните почему ошибка. Я же вам первым спасибо и скажу. Ваши же личные обиды и проекции оставьте при себе, я в них не нуждаюсь. Ну и фразы типа «ты неправ потому что мне не нравится» тоже оставьте при себе.
С красками, спектрами, пигментами, растискиваниями, вязкостями, источниками освещения, профилированием устройств и прочим подобным вожусь с 1993 года и на данный момент, мне кажется, немного понимаю о чем говорю.
katzen
23.10.2018 11:34+1Абсолютный физический смысл имеют значения в цветовых координатах CIE Lab.
Я бы уточнил: психофизиологический, а не физический.
Статья на самом деле страдает от того, что автор не ознакомился в должном объёме с теорией цвета.arabesc
23.10.2018 16:29Конечно, есть некоторые алгоритмы, но большинство из них работают путём преобразования температуры в цветовое пространство XYZ, к которому вы потом можете добавить преобразование RGB.
Автор пытался, но не осилил. В общем, понятно, что от ФотоДемона и его магии цвета нужно держаться подальше.
Статья — говно, такой мусор нужно выпиливать из интернета, чтобы никто не тратил время попусту. А тут кто-то рейтнг даже накручивает мракобесию, печаль…
GeMir
BD9
Навскидку: диагностика кожных заболеваний (особенно — рака) по состоянию кожных покровов. Нужны методы определения степени красноты (ну и других цветов) для передачи изображения между врачами. Телемедицина и пр. (нужно получить одинаковое изображение на разных устройствах).
Плюс — машинное обучение (диагностика по снимкам).
Вопрос «при какой степени покраснения горла (которое и так всегда красное) имеется воспаление» пока что решается обучением нейронной сети в мозге врача с помощью другого врача.
nae
А может просто тепловизором глянуть?
vics001
Но ведь цвета зависят (точка белого) от освещения в помещении, неужели они каждый раз калибруют мониторы?