Skip to content

Commit cc2dbac

Browse files
committed
Added new sRGB2LAB() function
1 parent 2503553 commit cc2dbac

File tree

3 files changed

+117
-9
lines changed

3 files changed

+117
-9
lines changed

ChangeLog

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
22/02/2025:
2-
- Updated CIELAB decoding to fully handle 8 and 16 bit as well as floating point images
2+
- Updated CIELAB decoding to fully handle 8 and 16 bit as well as floating point images.
3+
- Added new sRGB2LAB() function.
34

45

56
20/02/2025:

src/Transforms.cc

+103-8
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,32 @@ static bool isfinite( float arg )
4141

4242

4343

44+
/* Size threshold for using parallel loops (256x256 pixels)
45+
*/
46+
#define PARALLEL_THRESHOLD 65536
47+
48+
4449
/* D65 temp 6504.
4550
*/
4651
#define D65_X0 95.0470
4752
#define D65_Y0 100.0
4853
#define D65_Z0 108.8827
4954

50-
/* Size threshold for using parallel loops (256x256 pixels)
51-
*/
52-
#define PARALLEL_THRESHOLD 65536
53-
54-
55-
/* XYZ to sRGB conversion matrix
55+
/* D65 XYZ <-> sRGB conversion matrices
5656
*/
5757
static const float XYZ_sRGB[3][3] = { { 3.2406255, -1.537208, -0.4986286},
5858
{-0.9689307, 1.8757561, 0.0415175},
5959
{ 0.0557101, -0.2040211, 1.0569959} };
6060

61+
static const float sRGB_XYZ[3][3] = { { 0.4124564, 0.3575761, 0.1804375},
62+
{ 0.2126729, 0.7151522, 0.0721750},
63+
{ 0.0193339, 0.1191920, 0.9503041} };
64+
65+
6166
using namespace std;
6267

6368

69+
6470
// Normalization function
6571
void Transform::normalize( RawTile& in, const vector<float>& max, const vector<float>& min ) {
6672

@@ -158,7 +164,7 @@ void Transform::normalize( RawTile& in, const vector<float>& max, const vector<f
158164
in.data = normdata;
159165
in.bpc = 32;
160166
in.sampleType = SampleType::FLOATINGPOINT;
161-
in.dataLength = (uint32_t) np * (in.bpc/8);
167+
in.dataLength = np << 2;
162168
in.capacity = in.dataLength;
163169

164170
}
@@ -304,6 +310,65 @@ void Transform::LAB2sRGB( const float *in, float *out ){
304310

305311

306312

313+
// Convert a single pixel from normalized sRGB to CIELAB
314+
void Transform::sRGB2LAB( const float *in, float *out ){
315+
316+
float R,G,B;
317+
float X,Y,Z;
318+
float L,a,b;
319+
320+
R = in[0];
321+
G = in[1];
322+
B = in[2];
323+
324+
// Linearize
325+
if( R <= 0.04045 ) R /= 12.92;
326+
else R = powf( ( (R+0.055) / 1.055 ), 2.4 );
327+
328+
if( G <= 0.04045 ) G /= 12.92;
329+
else G = powf( ( (G+0.055) / 1.055 ), 2.4 );
330+
331+
if( B <= 0.04045 ) B /= 12.92;
332+
else B = powf( ( (B+0.055) / 1.055 ), 2.4 );
333+
334+
// Linear RGB needs to be scaled to 100
335+
R += 100.0;
336+
G *= 100.0;
337+
B *= 100.0;
338+
339+
// Convert from linear RGB to XYZ
340+
X = (R * sRGB_XYZ[0][0]) + (G * sRGB_XYZ[0][1]) + (B * sRGB_XYZ[0][2]);
341+
Y = (R * sRGB_XYZ[1][0]) + (G * sRGB_XYZ[1][1]) + (B * sRGB_XYZ[1][2]);
342+
Z = (R * sRGB_XYZ[2][0]) + (G * sRGB_XYZ[2][1]) + (B * sRGB_XYZ[2][2]);
343+
344+
// Scale XYZ by illuminant
345+
X /= D65_X0;
346+
Y /= D65_Y0;
347+
Z /= D65_Z0;
348+
349+
// Convert from XYZ to CIELAB
350+
if( X > 0.008856452 ) X = cbrtf(X);
351+
else X = ( 7.787037037 * X ) + 0.137931034;
352+
353+
if( Y > 0.008856452 ) Y = cbrtf(Y);
354+
else Y = ( 7.787037037 * Y ) + 0.137931034;
355+
356+
if( Z > 0.008856452 ) Z = cbrtf(Z);
357+
else Z = ( 7.787037037 * Z ) + 0.137931034;
358+
359+
360+
L = ( 116.0 * Y ) - 16.0;
361+
a = 500.0 * ( X - Y );
362+
b = 200.0 * ( Y - Z );
363+
364+
// Return our CIELAB values
365+
out[0] = L;
366+
out[1] = a;
367+
out[2] = b;
368+
}
369+
370+
371+
307372
// Convert whole image from CIELAB to sRGB. Input data can be in 8 bit, 16 bit or floating point form
308373
void Transform::LAB2sRGB( RawTile& in ){
309374

@@ -355,7 +420,7 @@ void Transform::LAB2sRGB( RawTile& in ){
355420
}
356421

357422
// Perform color conversion on this pixel
358-
LAB2sRGB( &cielab[0], &rgb[0] );
423+
LAB2sRGB( cielab, rgb );
359424

360425
output[n] = rgb[0];
361426
output[n+1] = rgb[1];
@@ -376,6 +441,36 @@ void Transform::LAB2sRGB( RawTile& in ){
376441

377442

378443

444+
// Convert whole image from sRGB to CIELAB - input should be normalized and in floating point format
445+
void Transform::sRGB2LAB( RawTile& in ){
446+
447+
uint32_t np = (uint32_t) in.width * in.height * in.channels;
448+
449+
// Parallelize code using OpenMP
450+
#if defined(__ICC) || defined(__INTEL_COMPILER)
451+
#pragma ivdep
452+
#elif defined(_OPENMP)
453+
#pragma omp parallel for
454+
#endif
455+
for( uint32_t n=0; n<np; n+=in.channels ){
456+
457+
float rgb[3];
458+
float cielab[3];
459+
460+
rgb[0] = ((float*)in.data)[n];
461+
rgb[1] = ((float*)in.data)[n+1];
462+
rgb[2] = ((float*)in.data)[n+2];
463+
464+
sRGB2LAB( rgb, cielab );
465+
466+
((float*)in.data)[n] = cielab[0];
467+
((float*)in.data)[n+1] = cielab[1];
468+
((float*)in.data)[n+2] = cielab[2];
469+
}
470+
}
471+
472+
473+
379474
// Colormap function
380475
// Based on the routine colormap.cpp in Imagin Raytracer by Olivier Ferrand
381476
// http://www.imagin-raytracer.org

src/Transforms.h

+12
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ struct Transform {
4848
void LAB2sRGB( const float *in, float *out );
4949

5050

51+
/// Private function to convert single pixel of sRGB to CIELAB
52+
/** @param in input buffer
53+
@param out output buffer
54+
*/
55+
void sRGB2LAB( const float *in, float *out );
56+
57+
5158
public:
5259

5360
/// Get description of processing engine
@@ -88,6 +95,11 @@ struct Transform {
8895
void LAB2sRGB( RawTile& in );
8996

9097

98+
/// Convert from sRGB to CIELAB colour space
99+
/** @param in tile data to be converted */
100+
void sRGB2LAB( RawTile& in );
101+
102+
91103
/// Fast efficient scaling from higher fixed point bit depths to 8 bit
92104
/** @param in tile data to be converted */
93105
void scale_to_8bit( RawTile& in );

0 commit comments

Comments
 (0)