colourTools.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2019 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 Note
27  Some implementation details from VTK vtkColorTransferFunction.cxx
28  vtkMath.cxx
29 
30 \*---------------------------------------------------------------------------*/
31 
32 #include "colourTools.H"
33 #include "mathematicalConstants.H"
34 
35 using namespace Foam::constant;
36 
37 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
38 
39 namespace Foam
40 {
41 
42 static constexpr scalar oneThird = 1.0 / 3.0;
43 static constexpr scalar oneSixth = 1.0 / 6.0;
44 static constexpr scalar twoThird = 2.0 / 3.0;
45 static constexpr scalar fiveSixth = 5.0 / 6.0;
46 
47 
48 // Compute HSV from RGB
49 static inline void RGB_to_HSV
50 (
51  const scalar r, const scalar g, const scalar b,
52  scalar& h, scalar& s, scalar& v
53 )
54 {
55  scalar cmin = r, cmax = r;
56 
57  if (g > cmax)
58  {
59  cmax = g;
60  }
61  else if (g < cmin)
62  {
63  cmin = g;
64  }
65  if (b > cmax)
66  {
67  cmax = b;
68  }
69  else if (b < cmin)
70  {
71  cmin = b;
72  }
73 
74  v = cmax;
75  s = (v > 0.0) ? ((cmax - cmin) / cmax) : 0.0;
76 
77  if (s > 0.0)
78  {
79  if (r == cmax)
80  {
81  h = oneSixth * (g - b) / (cmax - cmin);
82  }
83  else if (g == cmax)
84  {
85  h = oneThird + oneSixth * (b - r) / (cmax - cmin);
86  }
87  else
88  {
89  h = twoThird + oneSixth * (r - g) / (cmax - cmin);
90  }
91 
92  if (h < 0.0)
93  {
94  h += 1.0;
95  }
96  }
97  else
98  {
99  h = 0.0;
100  }
101 }
102 
103 
104 // Compute HSV to RGB
105 static inline void HSV_to_RGB
106 (
107  const scalar h, const scalar s, const scalar v,
108  scalar& r, scalar& g, scalar& b
109 )
110 {
111  if (h > oneSixth && h <= oneThird)
112  {
113  // green/red
114  g = 1.0;
115  r = (oneThird - h) / oneSixth;
116  b = 0.0;
117  }
118  else if (h > oneThird && h <= 0.5)
119  {
120  // green/blue
121  g = 1.0;
122  b = (h - oneThird) / oneSixth;
123  r = 0.0;
124  }
125  else if (h > 0.5 && h <= twoThird)
126  {
127  // blue/green
128  b = 1.0;
129  g = (twoThird - h) / oneSixth;
130  r = 0.0;
131  }
132  else if (h > twoThird && h <= fiveSixth)
133  {
134  // blue/red
135  b = 1.0;
136  r = (h - twoThird) / oneSixth;
137  g = 0.0;
138  }
139  else if (h > fiveSixth && h <= 1.0)
140  {
141  // red/blue
142  r = 1.0;
143  b = (1.0 - h) / oneSixth;
144  g = 0.0;
145  }
146  else
147  {
148  // red/green
149  r = 1.0;
150  g = h / oneSixth;
151  b = 0.0;
152  }
153 
154  // Add saturation
155  r = (s * r + (1.0 - s));
156  g = (s * g + (1.0 - s));
157  b = (s * b + (1.0 - s));
158 
159  r *= v;
160  g *= v;
161  b *= v;
162 }
163 
164 
165 // Intermediate calculation to XYZ
166 static inline scalar to_XYZ(scalar val)
167 {
168  const scalar p3 = pow3(val);
169  return (p3 > 0.008856 ? p3 : (val - 16.0 / 116.0) / 7.787);
170 }
171 
172 
173 // Intermediate calculation from XYZ
174 static inline scalar from_XYZ(scalar val)
175 {
176  return (val > 0.008856) ? cbrt(val) : (7.787 * val) + (16.0 / 116.0);
177 }
178 
179 // Observer= 2 deg Illuminant= D65
180 static constexpr scalar ref_X = 0.9505;
181 static constexpr scalar ref_Y = 1.000;
182 static constexpr scalar ref_Z = 1.089;
183 
184 static inline void LAB_to_XYZ
185 (
186  const scalar L, const scalar a, const scalar b,
187  scalar& x, scalar& y, scalar& z
188 )
189 {
190  const scalar var_Y = (L + 16.0) / 116.0;
191  const scalar var_X = a / 500 + var_Y;
192  const scalar var_Z = var_Y - b / 200;
193 
194  x = ref_X * to_XYZ(var_X);
195  y = ref_Y * to_XYZ(var_Y);
196  z = ref_Z * to_XYZ(var_Z);
197 }
198 
199 
200 static inline void XYZ_to_LAB
201 (
202  const scalar x, const scalar y, const scalar z,
203  scalar& L, scalar& a, scalar& b
204 )
205 {
206  const scalar var_X = from_XYZ(x / ref_X);
207  const scalar var_Y = from_XYZ(y / ref_Y);
208  const scalar var_Z = from_XYZ(z / ref_Z);
209 
210  L = (116 * var_Y) - 16;
211  a = 500 * (var_X - var_Y);
212  b = 200 * (var_Y - var_Z);
213 }
214 
215 
216 
217 // "Gamma correction" specified by the sRGB color space.
218 
219 static inline scalar gamma_from_xyz(const scalar val)
220 {
221  return
222  (
223  val > 0.0031308
224  ? (1.055 * (pow(val, 1.0/2.4)) - 0.055)
225  : 12.92 * val
226  );
227 }
228 
229 
230 static inline scalar gamma_to_xyz(const scalar val)
231 {
232  return
233  (
234  val > 0.04045
235  ? (pow((val + 0.055) / 1.055, 2.4))
236  : val / 12.92
237  );
238 }
239 
240 
241 
242 static inline void XYZ_to_RGB
243 (
244  const scalar x, const scalar y, const scalar z,
245  scalar& r, scalar& g, scalar& b
246 )
247 {
248  r = gamma_from_xyz(x * 3.2406 + y * -1.5372 + z * -0.4986);
249  g = gamma_from_xyz(x * -0.9689 + y * 1.8758 + z * 0.0415);
250  b = gamma_from_xyz(x * 0.0557 + y * -0.2040 + z * 1.0570);
251 
252  // Clip colour range
253  scalar cmax = r;
254  if (cmax < g) cmax = g;
255  if (cmax < b) cmax = b;
256  if (cmax > 1.0)
257  {
258  r /= cmax;
259  g /= cmax;
260  b /= cmax;
261  }
262 
263  if (r < 0) r = 0;
264  if (g < 0) g = 0;
265  if (b < 0) b = 0;
266 }
267 
268 
269 static inline void RGB_to_XYZ
270 (
271  scalar r, scalar g, scalar b,
272  scalar& x, scalar& y, scalar& z
273 )
274 {
275  r = gamma_to_xyz(r);
276  g = gamma_to_xyz(g);
277  b = gamma_to_xyz(b);
278 
279  x = r * 0.4124 + g * 0.3576 + b * 0.1805;
280  y = r * 0.2126 + g * 0.7152 + b * 0.0722;
281  z = r * 0.0193 + g * 0.1192 + b * 0.9505;
282 }
283 
284 
285 
286 //- Convert to special polar version of CIELAB
287 // (for creating continuous diverging color maps).
288 inline void labToMsh(const vector& lab, vector& msh)
289 {
290  const scalar& L = lab[0];
291  const scalar& a = lab[1];
292  const scalar& b = lab[2];
293 
294  msh[0] = sqrt(L*L + a*a + b*b);
295  msh[1] = (msh[0] > 0.001) ? acos(L / msh[0]) : 0.0;
296  msh[2] = (msh[1] > 0.001) ? atan2(b,a) : 0.0;
297 }
298 
299 
300 //- Convert from special polar version of CIELAB
301 inline void mshToLab(const vector& msh, vector& lab)
302 {
303  lab[0] = msh[0]*cos(msh[1]);
304  lab[1] = msh[0]*sin(msh[1])*cos(msh[2]);
305  lab[2] = msh[0]*sin(msh[1])*sin(msh[2]);
306 }
307 
308 
309 // Return the smallest angle between the two
310 static inline scalar angleDiff(scalar angle1, scalar angle2)
311 {
312  scalar adiff = angle1 - angle1;
313  if (adiff < 0.0) adiff = -adiff;
314 
315  while (adiff >= mathematical::twoPi) adiff -= mathematical::twoPi;
316  if (adiff > mathematical::pi) adiff = (mathematical::twoPi - adiff);
317  return adiff;
318 }
319 
320 
321 // For the case when interpolating from a saturated color to an unsaturated
322 // color, find a hue for the unsaturated color that makes sense.
323 static inline scalar adjustHue(const vector& msh, scalar unsatM)
324 {
325  if (msh[0] >= unsatM - 0.1)
326  {
327  // The best we can do is hold hue constant.
328  return msh[2];
329  }
330 
331  // This equation is designed to make the perceptual change of the
332  // interpolation to be close to constant.
333  const scalar hueSpin =
334  msh[1]*sqrt(unsatM*unsatM - msh[0]*msh[0]) / (msh[0]*sin(msh[1]));
335 
336  // Spin hue away from 0 except in purple hues.
337  if (msh[2] > -0.3*mathematical::pi)
338  {
339  return msh[2] + hueSpin;
340  }
341  else
342  {
343  return msh[2] - hueSpin;
344  }
345 }
346 
347 } // End namespace Foam
348 
349 
350 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
351 
353 {
354  RGB_to_HSV(rgb[0], rgb[1], rgb[2], hsv[0], hsv[1], hsv[2]);
355 }
356 
358 {
359  HSV_to_RGB(hsv[0], hsv[1], hsv[2], rgb[0], rgb[1], rgb[2]);
360 }
361 
362 
364 {
365  RGB_to_XYZ(rgb[0], rgb[1], rgb[2], xyz[0], xyz[1], xyz[2]);
366 }
367 
369 {
370  XYZ_to_RGB(xyz[0], xyz[1], xyz[2], rgb[0], rgb[1], rgb[2]);
371 }
372 
373 
375 {
376  LAB_to_XYZ(lab[0], lab[1], lab[2], xyz[0], xyz[1], xyz[2]);
377 }
378 
379 
381 {
382  XYZ_to_LAB(xyz[0], xyz[1], xyz[2], lab[0], lab[1], lab[2]);
383 }
384 
385 
387 {
388  vector xyz;
389  RGB_to_XYZ(rgb[0], rgb[1], rgb[2], xyz[0], xyz[1], xyz[2]);
390  XYZ_to_LAB(xyz[0], xyz[1], xyz[2], lab[0], lab[1], lab[2]);
391 }
392 
393 
395 {
396  vector xyz;
397  labToXyz(lab, xyz);
398  xyzToRgb(xyz, rgb);
399 }
400 
401 
403 (
404  scalar s,
405  const vector& rgb1,
406  const vector& rgb2,
407  vector& result
408 )
409 {
410  vector lab1, lab2;
411  rgbToLab(rgb1, lab1);
412  rgbToLab(rgb2, lab2);
413 
414  vector msh1, msh2;
415  labToMsh(lab1, msh1);
416  labToMsh(lab2, msh2);
417 
418  // If the endpoints are distinct saturated colors,
419  // then place white in between them.
420  if
421  (
422  msh1[1] > 0.05
423  && msh2[1] > 0.05
424  && angleDiff(msh1[2], msh2[2]) > mathematical::pi/3.0
425  )
426  {
427  // Insert the white midpoint by setting one end to white and
428  // adjusting the scalar value.
429 
430  scalar Mmid = std::max(msh1[0], msh2[0]);
431  Mmid = std::max(scalar(88.0), Mmid);
432  if (s < 0.5)
433  {
434  msh2[0] = Mmid; msh2[1] = 0; msh2[2] = 0;
435  s = 2.0*s;
436  }
437  else
438  {
439  msh1[0] = Mmid; msh1[1] = 0; msh1[2] = 0;
440  s = 2.0*s - 1.0;
441  }
442  }
443 
444  // If one color has no saturation, then its hue value is invalid.
445  // In this case, we want to set it to something logical so the
446  // interpolation of hue makes sense.
447  if ((msh1[1] < 0.05) && (msh2[1] > 0.05))
448  {
449  msh1[2] = adjustHue(msh2, msh1[0]);
450  }
451  else if ((msh2[1] < 0.05) && (msh1[1] > 0.05))
452  {
453  msh2[2] = adjustHue(msh1, msh2[0]);
454  }
455 
456  // Msh tmp
457  vector mshTmp((1-s)*msh1 + s*msh2);
458 
459  // Convert back to RGB
460  vector lab;
461  mshToLab(mshTmp, lab);
462  labToRgb(lab, result);
463 }
464 
465 
467 (
468  scalar s,
469  const vector& rgb1,
470  const vector& rgb2,
471  vector& result
472 )
473 {
474  vector hsv1, hsv2;
475  rgbToHsv(rgb1, hsv1);
476  rgbToHsv(rgb2, hsv2);
477 
478  // Wrap HSV?
479  if (hsv1[0] - hsv2[0] > 0.5 || hsv2[0] - hsv1[0] > 0.5)
480  {
481  if (hsv1[0] > hsv2[0])
482  {
483  hsv1[0] -= 1.0;
484  }
485  else
486  {
487  hsv2[0] -= 1.0;
488  }
489  }
490 
491  vector hsvTmp((1-s)*hsv1 + s*hsv2);
492 
493  if (hsvTmp[0] < 0.0)
494  {
495  hsvTmp[0] += 1.0;
496  }
497 
498  // Convert back to RGB
499  hsvToRgb(hsvTmp, result);
500 }
501 
502 
503 // ************************************************************************* //
Foam::oneThird
static constexpr scalar oneThird
Definition: colourTools.C:42
L
const vector L(dict.get< vector >("L"))
Foam::gamma_from_xyz
static scalar gamma_from_xyz(const scalar val)
Definition: colourTools.C:219
mathematicalConstants.H
s
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
Definition: gmvOutputSpray.H:25
Foam::colourTools::rgbToHsv
void rgbToHsv(const vector &rgb, vector &hsv)
Convert RGB to HSV.
Definition: colourTools.C:352
Foam::constant
Different types of constants.
Definition: atomicConstants.C:38
Foam::sin
dimensionedScalar sin(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:264
Foam::LAB_to_XYZ
static void LAB_to_XYZ(const scalar L, const scalar a, const scalar b, scalar &x, scalar &y, scalar &z)
Definition: colourTools.C:185
Foam::atan2
dimensionedScalar atan2(const dimensionedScalar &x, const dimensionedScalar &y)
Definition: dimensionedScalar.C:312
Foam::labToMsh
void labToMsh(const vector &lab, vector &msh)
Convert to special polar version of CIELAB.
Definition: colourTools.C:288
Foam::colourTools::labToRgb
void labToRgb(const vector &lab, vector &rgb)
Convert LAB to RGB.
Definition: colourTools.C:394
Foam::ref_Z
static constexpr scalar ref_Z
Definition: colourTools.C:182
Foam::oneSixth
static constexpr scalar oneSixth
Definition: colourTools.C:43
Foam::from_XYZ
static scalar from_XYZ(scalar val)
Definition: colourTools.C:174
Foam::colourTools::labToXyz
void labToXyz(const vector &lab, vector &xyz)
Convert LAB to XYZ.
Definition: colourTools.C:374
Foam::colourTools::xyzToLab
void xyzToLab(const vector &xyz, vector &lab)
Convert XYZ to LAB.
Definition: colourTools.C:380
colourTools.H
Foam::XYZ_to_RGB
static void XYZ_to_RGB(const scalar x, const scalar y, const scalar z, scalar &r, scalar &g, scalar &b)
Definition: colourTools.C:243
Foam::constant::physicoChemical::b
const dimensionedScalar b
Wien displacement law constant: default SI units: [m.K].
Definition: createFields.H:27
Foam::XYZ_to_LAB
static void XYZ_to_LAB(const scalar x, const scalar y, const scalar z, scalar &L, scalar &a, scalar &b)
Definition: colourTools.C:201
Foam::fiveSixth
static constexpr scalar fiveSixth
Definition: colourTools.C:45
Foam::pow3
dimensionedScalar pow3(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:89
Foam::constant::universal::h
const dimensionedScalar h
Planck constant.
Definition: setRegionSolidFields.H:33
Foam::to_XYZ
static scalar to_XYZ(scalar val)
Definition: colourTools.C:166
Foam::colourTools::hsvToRgb
void hsvToRgb(const vector &hsv, vector &rgb)
Convert HSV to RGB.
Definition: colourTools.C:357
Foam::ref_Y
static constexpr scalar ref_Y
Definition: colourTools.C:181
Foam::pow
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
Definition: dimensionedScalar.C:75
Foam::max
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
Foam::constant::mathematical::twoPi
constexpr scalar twoPi(2 *M_PI)
Foam::colourTools::rgbToLab
void rgbToLab(const vector &rgb, vector &lab)
Convert RGB to LAB.
Definition: colourTools.C:386
Foam::twoThird
static constexpr scalar twoThird
Definition: colourTools.C:44
g
const uniformDimensionedVectorField & g
Definition: createFluidFields.H:26
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::vector
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:51
Foam::colourTools::xyzToRgb
void xyzToRgb(const vector &xyz, vector &rgb)
Convert XYZ to RGB.
Definition: colourTools.C:368
Foam::adjustHue
static scalar adjustHue(const vector &msh, scalar unsatM)
Definition: colourTools.C:323
Foam::constant::mathematical::pi
constexpr scalar pi(M_PI)
Foam::Vector< scalar >
Foam::sqrt
dimensionedScalar sqrt(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:144
Foam::acos
dimensionedScalar acos(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:268
x
x
Definition: LISASMDCalcMethod2.H:52
Foam::colourTools::interpolateDiverging
void interpolateDiverging(scalar s, const vector &rgb1, const vector &rgb2, vector &result)
Interpolate RGB values with diverging color map.
Definition: colourTools.C:403
Foam::cbrt
dimensionedScalar cbrt(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:155
Foam::RGB_to_XYZ
static void RGB_to_XYZ(scalar r, scalar g, scalar b, scalar &x, scalar &y, scalar &z)
Definition: colourTools.C:270
Foam::HSV_to_RGB
static void HSV_to_RGB(const scalar h, const scalar s, const scalar v, scalar &r, scalar &g, scalar &b)
Definition: colourTools.C:106
Foam::gamma_to_xyz
static scalar gamma_to_xyz(const scalar val)
Definition: colourTools.C:230
Foam::mshToLab
void mshToLab(const vector &msh, vector &lab)
Convert from special polar version of CIELAB.
Definition: colourTools.C:301
Foam::colourTools::interpolateHSV
void interpolateHSV(scalar s, const vector &rgb1, const vector &rgb2, vector &result)
Interpolate RGB values in HSV colourspace.
Definition: colourTools.C:467
Foam::colourTools::rgbToXyz
void rgbToXyz(const vector &rgb, vector &xyz)
Convert RGB to XYZ.
Definition: colourTools.C:363
y
scalar y
Definition: LISASMDCalcMethod1.H:14
Foam::RGB_to_HSV
static void RGB_to_HSV(const scalar r, const scalar g, const scalar b, scalar &h, scalar &s, scalar &v)
Definition: colourTools.C:50
Foam::ref_X
static constexpr scalar ref_X
Definition: colourTools.C:180
Foam::angleDiff
static scalar angleDiff(scalar angle1, scalar angle2)
Definition: colourTools.C:310
Foam::cos
dimensionedScalar cos(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:265