Правильное преобразование из рек. 709 в sRGB
Как правильно преобразовать цвета, хранящиеся в виде Y'CrCb (используя рек. 709), в sRGB?
Я работаю с HDTV-видео и извлекаю необработанные данные с помощью libavcodec. Несмотря на то, что мне удалось сделать преобразование, я еще не уверен, что делаю это правильно. VLC предоставляет один результат, конвертирование в Gimp с использованием результатов 'compose' в другом и использование кода из Интернета также не согласовано. Поэтому я не нашел надежной ссылки для сравнения.
Мое исследование и текущая лучшая ставка ниже. (Значения находятся в плавающей точке с диапазоном 0,0-1,0). Больше всего я не уверен в гамма-коррекции. Это делает его немного легче, чем я ожидал, но я не могу сказать, что это выглядит неправильно...
Студия-свинг удаление
Y 'колеблется от 16 до 235 для 8-бит. Cr и Cb колеблются от 16 до 240 и центрируются на 128.
y = (y - (16 / 255.0)) * ( 1 + 16.0 / 255.0 + (256-235) / 255.0 );
u = (u - (16 / 255.0)) * ( 1 + 16.0 / 255.0 + (256-240) / 255.0 );
v = (v - (16 / 255.0)) * ( 1 + 16.0 / 255.0 + (256-240) / 255.0 );
//Move chroma
u -= 0.5;
v -= 0.5;
Я не уверен, можно ли с уверенностью предположить, что вы никогда не получите значения вне диапазонов, или вам нужно их фиксировать.
Для большей битовой глубины спецификация говорит, что младшие биты игнорируются. Что это значит? Я также работаю с материалом, закодированным в 10-битном формате, так что это меня интересует.
От Y'CrCb к RGB
Рек. Спецификация 709 рассказывает, как конвертировать RGB в Y'CrCb:
E'y = 0.2126 * E'r + 0.7152 * E'g + 0.0722 * E'b
E'cb = 0.5389 * ( E'b - E'y )
E'cr = 0.6350 * ( E'r - E'y )
Википедия дает то, что кажется немного более точным для определения Cb и Cr:
Pb = 0.5 * (B' - Y') / (1 - Kb)
Pr = 0.5 * (R' - Y') / (1 - Kr)
где Kb и Kr - факторы для E'b и E'r. Значения из спец. кажется округленным из этих уравнений.
RGB можно найти, поменяв уравнения (используя версию Wikipedia):
double r = y + 2*(1.0-kr) * v;
double b = y + 2*(1.0-kb) * u;
double g = ( y - kr * rr - kb*rb ) / kg;
G можно сделать, используя Cr и Cb напрямую:
double g = y - 2*kr*(1-kr)/kg * v - 2*kb*(1-kb)/kg * u;
(Коэффициент для y равен (1-kr-kb)/ кг, что составляет кг / кг при kr + kb + kg = 1)
RGB в sRGB
Я не видел примеров кода, включая этот шаг вообще. Нам нужно преобразовать цветовое пространство, указанное в рек. 709 к указанному в sRGB. AFAIK, единственное различие между ними - передаточная функция (то есть гамма). Координаты XY, указанные в рек. 709 соответствует sRGB, но я не знаю, почему sRGB включает координату 'Z' в то время как rec. 709 нет. Это имеет значение? (Я ничего не знаю о CIE XYZ.)
Рек. 709 определяет, как гамма-кодировать линейный RGB:
V = 1.099 * L^0.45 - 0.099 for 1 >= L >= 0.018
V = 4.500 * L for 0.018 > L >= 0
Нам нужно обратить это вспять, однако линейное обрезание 0,018 не дает одинаковое значение для V в обоих уравнениях. Итак, каковы диапазоны для обратной версии?
L = ( ( V + 0.099 ) / 1.099 ) ^ (1/0.45) for 1 >= V >= ?
L = V / 4.5000 for ? > V >= 0
sRGB имел ту же проблему, но был изменен на 0,0031308, что является более точным. Я помню, кто-то изобрел фракцию, которая точно представляла его для sRGB, но я не могу найти его снова...
В настоящее время я использую следующее:
double cutoff = 1.099 * pow( 0.018, 0.45 ) - 0.099;
v = ( v < cutoff ) ? 1.0/4.5 * v : pow( (v+0.099)/1.099, 1.0/0.45 );
v = ( v <= 0.0031308 ) ? 12.92 * v : 1.055*pow( v, 1.0/2.4 ) - 0.055;
2 ответа
Для правильного преобразования из линейного sRGB в нелинейный sRGB (процесс компандирования) и обратного процесса (обратный компандинг) я использую следующие функции:
public double Companding(double channel)
{
double v = channel;
double V = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.Pow(v, 1 / 2.4d) - 0.055;
return V;
}
public double InverseCompanding(double channel)
{
double V = channel;
double v = V <= 0.04045 ? V / 12.92 : Math.Pow((V + 0.055) / 1.055, 2.4);
return v;
}
Примечание: v линейно, V нелинейно.
Эти функции основаны на уравнении, найденном здесь: http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
Также есть возможность использовать упрощенный sRGB с функцией компандирования v = V ^ gamma, где гамма равна 2.2, как отмечено на сайте.
Координаты XY, указанные в rec. 709 совпадает с sRGB,
Это xy, а не XY, это не то же самое, что XY из XYZ.
Вздох, прежде всего XYZ - это шаг после линеаризации, вам не нужно туда идти, поскольку sRGB уже использует основные цвета BT.709, как вы сказали. RGB линейный, R'G'B'нелинейный. Y'Cb'Cr'тоже нелинейный.
Я также работаю с материалом, закодированным в 10-битном формате, поэтому мне это интересно.
Это означает, что вы можете просто округлить его, чтобы получить правильные значения для 8 бит. Если последние два бита 10-битных значений равны 10 или 11, вы округляете до следующего 8-битного значения, в противном случае - вниз (00, 01 округляются в меньшую сторону). LSB означает младшие значащие биты. Только не забывайте, что 1023 нужно округлять до 255, а не до переполнения.
Нам нужно перевернуть его, однако линейное ограничение 0,018 не дает одинакового значения для V в обоих уравнениях.
Нет, реверсировать ничего не нужно. EOTF для REC.601/REC.709/REC.2020 не является противоположностью OETF, EOTF указан в BT.1886 и имеет идеальную гамму 2,4 для идеального OLED-дисплея и почти sRGB EOTF для неидеального ЖК-дисплея при окружающем освещении 200 люкс. Вот почему Chrome просто использует sRGB EOTF для BT.709, что означает «нет» EOTF вообще, поскольку Windows по умолчанию использует это значение.
Я помню, как кто-то придумал фракцию, которая точно представляла это для sRGB,
Это всего лишь 0,04045 / 12,92 == 0,003130804954, 809/258400.