Loading...
Searching...
No Matches
ImageAnalysis.hpp
1#pragma once
2#include <algorithm>
3#include <array>
4#include <cmath>
5
13{
14enum class Matrix
15{
16 Rec601,
17 Rec709,
18 Rec2020
19};
20
22{
23 double kr, kg, kb;
24};
25
26constexpr LumaCoefficients coefficients(Matrix m) noexcept
27{
28 switch(m)
29 {
30 case Matrix::Rec601:
31 return {0.299, 0.587, 0.114};
32 case Matrix::Rec2020:
33 return {0.2627, 0.6780, 0.0593};
34 default:
35 return {0.2126, 0.7152, 0.0722};
36 }
37}
38
39struct YCbCr
40{
41 double y, cb, cr;
42};
43
45constexpr YCbCr toYCbCr(double r, double g, double b, Matrix m) noexcept
46{
47 const auto k = coefficients(m);
48 const double y = k.kr * r + k.kg * g + k.kb * b;
49 return {y, (b - y) / (2. * (1. - k.kb)), (r - y) / (2. * (1. - k.kr))};
50}
51
54inline std::array<YCbCr, 6> barTargets(Matrix m, double amplitude = 1.) noexcept
55{
56 constexpr double bars[6][3]{{1, 0, 0}, {1, 1, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {1, 0, 1}};
57 std::array<YCbCr, 6> out{};
58 for(int i = 0; i < 6; i++)
59 out[i] = toYCbCr(
60 bars[i][0] * amplitude, bars[i][1] * amplitude, bars[i][2] * amplitude, m);
61 return out;
62}
63
64inline constexpr const char* bar_names[6]{"R", "Yl", "G", "Cy", "B", "Mg"};
65
68inline constexpr double skin_tone_degrees = 123.;
69
70enum class Transfer
71{
72 Sdr,
73 PQ,
74 HLG
75};
76
78inline double pqOfNits(double nits) noexcept
79{
80 constexpr double m1 = 0.1593017578125, m2 = 78.84375;
81 constexpr double c1 = 0.8359375, c2 = 18.8515625, c3 = 18.6875;
82 const double y = std::pow(std::clamp(nits / 10000., 0., 1.), m1);
83 return std::pow((c1 + c2 * y) / (1. + c3 * y), m2);
84}
85
87{
88 double rx, ry, gx, gy, bx, by, wx, wy;
89};
90
91inline constexpr Primaries rec709{0.640, 0.330, 0.300, 0.600, 0.150, 0.060, 0.3127, 0.3290};
92inline constexpr Primaries dciP3{0.680, 0.320, 0.265, 0.690, 0.150, 0.060, 0.3127, 0.3290};
93inline constexpr Primaries rec2020{0.708, 0.292, 0.170, 0.797, 0.131, 0.046, 0.3127, 0.3290};
94
95inline const Primaries& primariesOf(Matrix m) noexcept
96{
97 return m == Matrix::Rec2020 ? rec2020 : rec709;
98}
99
100using Mat3 = std::array<std::array<double, 3>, 3>;
101
103inline Mat3 rgbToXyz(const Primaries& p) noexcept
104{
105 auto xyz = [](double x, double y) { return std::array<double, 3>{x / y, 1., (1. - x - y) / y}; };
106 const auto r = xyz(p.rx, p.ry), g = xyz(p.gx, p.gy), b = xyz(p.bx, p.by), w = xyz(p.wx, p.wy);
107 const Mat3 m{{{r[0], g[0], b[0]}, {r[1], g[1], b[1]}, {r[2], g[2], b[2]}}};
108 // Scale each primary so that they sum to the white point
109 const double det = m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1])
110 - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0])
111 + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
112 Mat3 inv{};
113 inv[0][0] = (m[1][1] * m[2][2] - m[1][2] * m[2][1]) / det;
114 inv[0][1] = (m[0][2] * m[2][1] - m[0][1] * m[2][2]) / det;
115 inv[0][2] = (m[0][1] * m[1][2] - m[0][2] * m[1][1]) / det;
116 inv[1][0] = (m[1][2] * m[2][0] - m[1][0] * m[2][2]) / det;
117 inv[1][1] = (m[0][0] * m[2][2] - m[0][2] * m[2][0]) / det;
118 inv[1][2] = (m[0][2] * m[1][0] - m[0][0] * m[1][2]) / det;
119 inv[2][0] = (m[1][0] * m[2][1] - m[1][1] * m[2][0]) / det;
120 inv[2][1] = (m[0][1] * m[2][0] - m[0][0] * m[2][1]) / det;
121 inv[2][2] = (m[0][0] * m[1][1] - m[0][1] * m[1][0]) / det;
122 std::array<double, 3> s{};
123 for(int i = 0; i < 3; i++)
124 s[i] = inv[i][0] * w[0] + inv[i][1] * w[1] + inv[i][2] * w[2];
125 Mat3 out{};
126 for(int i = 0; i < 3; i++)
127 for(int j = 0; j < 3; j++)
128 out[i][j] = m[i][j] * s[j];
129 return out;
130}
131
133inline constexpr std::array<std::array<double, 2>, 25> spectral_locus{{
134 {0.1741, 0.0050}, {0.1714, 0.0051}, {0.1689, 0.0069}, {0.1644, 0.0109},
135 {0.1566, 0.0177}, {0.1440, 0.0297}, {0.1241, 0.0578}, {0.0913, 0.1327},
136 {0.0454, 0.2950}, {0.0082, 0.5384}, {0.0139, 0.7502}, {0.0743, 0.8338},
137 {0.1547, 0.8059}, {0.2296, 0.7543}, {0.3016, 0.6923}, {0.3731, 0.6245},
138 {0.4441, 0.5547}, {0.5125, 0.4866}, {0.5752, 0.4242}, {0.6270, 0.3725},
139 {0.6658, 0.3340}, {0.6915, 0.3083}, {0.7079, 0.2920}, {0.7190, 0.2809},
140 {0.7347, 0.2653}}};
141
143inline std::array<double, 2> planckian(double t) noexcept
144{
145 t = std::clamp(t, 1667., 25000.);
146 const double t2 = t * t, t3 = t2 * t;
147 const double x = t <= 4000.
148 ? -0.2661239e9 / t3 - 0.2343589e6 / t2 + 0.8776956e3 / t + 0.179910
149 : -3.0258469e9 / t3 + 2.1070379e6 / t2 + 0.2226347e3 / t + 0.240390;
150 const double x2 = x * x, x3 = x2 * x;
151 double y;
152 if(t <= 2222.)
153 y = -1.1063814 * x3 - 1.34811020 * x2 + 2.18555832 * x - 0.20219683;
154 else if(t <= 4000.)
155 y = -0.9549476 * x3 - 1.37418593 * x2 + 2.09137015 * x - 0.16748867;
156 else
157 y = 3.0817580 * x3 - 5.87338670 * x2 + 3.75112997 * x - 0.37001483;
158 return {x, y};
159}
160}
Definition ImageAnalysis.hpp:13
constexpr YCbCr toYCbCr(double r, double g, double b, Matrix m) noexcept
Y' in [0, 1], Cb and Cr in [-0.5, 0.5] for R'G'B' in [0, 1].
Definition ImageAnalysis.hpp:45
std::array< double, 2 > planckian(double t) noexcept
Chromaticity of a black body at T kelvin, 1667 to 25000 K (Kim et al.).
Definition ImageAnalysis.hpp:143
constexpr std::array< std::array< double, 2 >, 25 > spectral_locus
CIE 1931 2-degree spectral locus, 380 to 700 nm.
Definition ImageAnalysis.hpp:133
Mat3 rgbToXyz(const Primaries &p) noexcept
Linear RGB to CIE XYZ for a set of primaries and white point.
Definition ImageAnalysis.hpp:103
std::array< YCbCr, 6 > barTargets(Matrix m, double amplitude=1.) noexcept
Definition ImageAnalysis.hpp:54
double pqOfNits(double nits) noexcept
The PQ signal value of a luminance in cd/m2.
Definition ImageAnalysis.hpp:78
constexpr double skin_tone_degrees
Definition ImageAnalysis.hpp:68
Definition ImageAnalysis.hpp:22
Definition ImageAnalysis.hpp:87
Definition ImageAnalysis.hpp:40