Реализация CIEDE2000 на C++
| Количество посещений | 917 |
|---|---|
| Количество просмотренных файлов | 859 + 605 |
На этой странице представлена эталонная реализация формулы отличия цветов CIEDE2000, написанная на языке программирования C++. Если вы хотите обеспечить идеальную совместимость (с точностью до десятого знака после запятой) с некоторыми сторонними реализациями, вам может понадобиться изменить комментарии в исходном коде. Чтобы упростить вам эту задачу, следующая ссылка автоматизирует эту операцию.
Функция ΔE2000 в C++
Рассмотрим более распространенную и академическую (Sharma, 2005) из двух формулировок.
// This function written in C++ is not affiliated with the CIE (International Commission on Illumination),
// and is released into the public domain. It is provided "as is" without any warranty, express or implied.
// Expressly defining pi ensures that the code works on different platforms.
// The classic CIE ΔE2000 implementation, which operates on two L*a*b* colors, and returns their difference.
// "l" ranges from 0 to 100, while "a" and "b" are unbounded and commonly clamped to the range of -128 to 127.
template<typename T>
static T ciede_2000(const T l_1, const T a_1, const T b_1, const T l_2, const T a_2, const T b_2) {
// Working in C++ with the CIEDE2000 color-difference formula.
// k_l, k_c, k_h are parametric factors to be adjusted according to
// different viewing parameters such as textures, backgrounds...
const T k_l = T(1.0);
const T k_c = T(1.0);
const T k_h = T(1.0);
T n = (std::sqrt(a_1 * a_1 + b_1 * b_1) + std::sqrt(a_2 * a_2 + b_2 * b_2)) * T(0.5);
n = n * n * n * n * n * n * n;
// A factor involving chroma raised to the power of 7 designed to make
// the influence of chroma on the total color difference more accurate.
n = T(1.0) + T(0.5) * (T(1.0) - std::sqrt(n / (n + T(6103515625.0))));
// Application of the chroma correction factor.
const T c_1 = std::sqrt(a_1 * a_1 * n * n + b_1 * b_1);
const T c_2 = std::sqrt(a_2 * a_2 * n * n + b_2 * b_2);
// atan2 is preferred over atan because it accurately computes the angle of
// a point (x, y) in all quadrants, handling the signs of both coordinates.
T h_1 = std::atan2(b_1, a_1 * n);
T h_2 = std::atan2(b_2, a_2 * n);
h_1 += (h_1 < T(0.0)) * T(2.0) * T(M_PI);
h_2 += (h_2 < T(0.0)) * T(2.0) * T(M_PI);
n = std::fabs(h_2 - h_1);
// Cross-implementation consistent rounding.
if (T(M_PI) - T(1E-14) < n && n < T(M_PI) + T(1E-14))
n = T(M_PI);
// When the hue angles lie in different quadrants, the straightforward
// average can produce a mean that incorrectly suggests a hue angle in
// the wrong quadrant, the next lines handle this issue.
T h_m = (h_1 + h_2) * T(0.5);
T h_d = (h_2 - h_1) * T(0.5);
h_d += (T(M_PI) < n) * T(M_PI);
// 📜 Sharma’s formulation doesn’t use the next line, but the one after it,
// and these two variants differ by ±0.0003 on the final color differences.
h_m += (T(M_PI) < n) * T(M_PI);
// h_m += (T(M_PI) < n) * ((h_m < T(M_PI)) - (T(M_PI) <= h_m)) * T(M_PI);
const T p = T(36.0) * h_m - T(55.0) * T(M_PI);
n = (c_1 + c_2) * T(0.5);
n = n * n * n * n * n * n * n;
// The hue rotation correction term is designed to account for the
// non-linear behavior of hue differences in the blue region.
const T r_t = T(-2.0) * std::sqrt(n / (n + T(6103515625.0)))
* std::sin(T(M_PI) / T(3.0) * std::exp(p * p / (T(-25.0) * T(M_PI) * T(M_PI))));
n = (l_1 + l_2) * T(0.5);
n = (n - T(50.0)) * (n - T(50.0));
// Lightness.
const T l = (l_2 - l_1) / (k_l * (T(1.0) + T(3.0) / T(200.0) * n / std::sqrt(T(20.0) + n)));
// These coefficients adjust the impact of different harmonic
// components on the hue difference calculation.
const T t = T(1.0) + T(6.0) / T(25.0) * std::sin(T(2.0) * h_m + T(M_PI) / T(2.0))
+ T(8.0) / T(25.0) * std::sin(T(3.0) * h_m + T(8.0) * T(M_PI) / T(15.0))
- T(17.0) / T(100.0) * std::sin(h_m + T(M_PI) / T(3.0))
- T(1.0) / T(5.0) * std::sin(T(4.0) * h_m + T(3.0) * T(M_PI) / T(20.0));
n = c_1 + c_2;
// Hue.
const T h = T(2.0) * std::sqrt(c_1 * c_2) * std::sin(h_d) / (k_h * (T(1.0) + T(3.0) / T(400.0) * n * t));
// Chroma.
const T c = (c_2 - c_1) / (k_c * (T(1.0) + T(9.0) / T(400.0) * n));
// Returning the square root ensures that dE00 accurately reflects the
// geometric distance in color space, which can range from 0 to around 185.
return std::sqrt(l * l + h * h + c * c + c * h * r_t);
}
// GitHub Project : https://github.com/michel-leonard/ciede2000-color-matching
// Online Tests : https://michel-leonard.github.io/ciede2000-color-matching
// L1 = 96.5 a1 = 47.8 b1 = 4.6
// L2 = 96.8 a2 = 53.2 b2 = -4.1
// CIE ΔE00 = 4.6680978034 (Bruce Lindbloom, Netflix’s VMAF, ...)
// CIE ΔE00 = 4.6680847226 (Gaurav Sharma, OpenJDK, ...)
// Deviation between implementations ≈ 1.3e-5
// See the source code comments for easy switching between these two widely used ΔE*00 implementation variants.Параметры k_l, k_c и k_h
Параметры k_l, k_c и k_h в формуле CIEDE2000 представляют собой весовые коэффициенты, применяемые соответственно к компонентам яркости (ΔL*), хромы (ΔC*) и тон (ΔH*). В исходном коде они определены как константы со значением по умолчанию 1, что соответствует стандартным условиям наблюдения, установленным Международной комиссией по освещению (CIE). На практике может потребоваться корректировка этих коэффициентов с учетом конкретных условий: например, k_l = 2 иногда используется для придания большего веса различиям в яркости (обычное явление в текстильной промышленности), а k_c или k_h могут быть уменьшены для повышения толерантности к вариациям насыщенности или оттенка. В целом эти коэффициенты обычно колеблются в диапазоне от 0,5 до 2, причем наиболее распространенным значением является 1.
Точность и надежность исходного кода
Разница между академической формулировкой Шармы и упрощенной формулировкой Линдблума не превышает ±0,0003 по конечному значению ΔE2000. Представленная здесь реализация является 64-разрядной и обеспечивает точность более 10 знаков после запятой; следовательно, выбор одной формулировки вместо другой является лишь техническим вопросом. В верхней части страницы можно выбрать одну из двух формул; в данный момент отображается упрощённая формула.
Как определить, является ли имеющаяся у меня реализация CIEDE2000 академической версией или упрощенной версией?
- Вычислите
ciede_2000(56.6, 43.6, 41.1, 68.4, 9.4, -8.6) - Если результат равен
30.0001, это академический тип (на Шармы или OpenJDK) - Если результат равен
29.9999, это упрощенный тип (на Линдблума или Netflix VMAF)
Как преобразовать цвета RGB в L*a*b*?
Перейдите на страницу AWK, C, Dart, Java, JavaScript, Kotlin, Lua, PHP, Python, Ruby или Rust, где такой конвертер (с использованием осветителя D65) уже реализован в дополнение к функции сравнения цветов.
Диапазоны значений в CIELAB и интерпретация ΔE2000
В цветовом пространстве CIELAB компонент L* обозначает светлоту и обычно изменяется от 0 (черный) до 100 (белый). Компоненты a* и b* описывают цветовые оси: a* идет от зеленого к красному, а b* — от синего к желтому. На практике значения a* и b* почти всегда находятся в диапазоне от -128 до +127, хотя в стандарте официальные ограничения для этих двух компонентов не указаны.
| Цвет 1 | Цвет 2 | Значение ΔE2000 |
|---|---|---|
| 1 | ||
| 2 | ||
| 3 |
| Цвет 1 | Цвет 2 | Значение ΔE2000 |
|---|---|---|
| 5 | ||
| 10 | ||
| 15 |
ΔE2000 (CIEDE2000) измеряет воспринимаемую разницу между двумя цветами: 0 означает идентичные цвета, а более высокие значения (до 185 и более) показывают большую разницу. Например, значение ΔE2000 около 5 означает близкие цвета, а около 15 — явно разные. Когда значение ΔE2000 превышает 40, сравниваемые цвета практически не имеют ничего общего, и мы больше не можем извлечь из них точной информации.
Пример использования в C++
// Compute the Delta E (CIEDE2000) color difference between two L*a*b* colors in C++
// L1 = 75.5 a1 = 22.5 b1 = -2.5
// L2 = 76.5 a2 = 16.5 b2 = 2.25
const auto delta_e_32_bits = ciede_2000<float>(L1, a1, b1, L2, a2, b2);
const auto delta_e_64_bits = ciede_2000<double>(L1, a1, b1, L2, a2, b2);
std::printf("DeltaE 2000 (float): %.8g\n", delta_e_32_bits);
std::printf("DeltaE 2000 (double): %.8g\n", delta_e_64_bits);
// .................................................. This shows a ΔE2000 of 4.8786078559
// As explained in the comments, compliance with Gaurav Sharma would display 4.8785929856Результаты испытаний
Наша тестовая программа, написанная на C99, включает 250 строгих статических тестов. Эти тесты гарантируют, что ваши вычисления будут выполняться без ошибок даже в критических предельных случаях, например, когда функция арктангенс возвращает математически неопределенное значение. Результаты показывают, что эта функция CIEDE2000 в C++ совместима с 41 другим языком программирования, которые мы предлагаем.
CIEDE2000 Verification Summary :
First Verified Line : 45,65.26,-37.27,78.78,-80,-108,102.75551666659558236
Duration : 13.66 s
Successes : 10000000
Errors : 0
Average Delta E : 62.9626
Average Deviation : 0
Maximum Deviation : 0Файлы для загрузки
Не стесняйтесь использовать эти файлы, предоставленные Мишелем, даже в коммерческих целях.
| Файл | Размер | Количество кликов |
|---|---|---|
| ciede-2000.cpp | 4 KB | 151 |
| ciede-2000-constexpr.cpp | 16 KB | 58 |
| ciede-2000-driver.cpp | 6 KB | 138 |
| ciede-2000-identity.cpp | 9 KB | 132 |
| ciede-2000-random.cpp | 7 KB | 140 |
| identity.yml | 4 KB | 81 |
| test-cpp.yml | 3 KB | 80 |
| vs-dvisvgm.yml | 5 KB | 79 |
| reference-dataset.txt | 4 KB | 605 |
| Нажмите на cpp.zip, чтобы скачать все эти файлы в архиве. | ||
Сообщество
Что вы думаете об этом исходном коде или CIEDE2000? Ваше мнение очень важно для нас! В гостевой книге уже 9 сообщений, в том числе 1 на русском языке. Посмотрите и поделитесь своим мнением.