色彩(颜色)空间原理(实现代码)

色彩(颜色)空间原理(实现代码)

编写代码             

对于代码示例,我将展示生成线性变换矩阵的算法和在sRGB空间和XYZ空间之间进行完全转换的示例。为了实现其他RGB空间,您只需要实现适当的gamma校正曲线(它应该比sRGB曲线更简单),并提供原色和白点的色度值。             

这些代码示例是在以下许可下发布的。

/******************************************************************************
  Copyright (c) 2010 Ryan Juckett
  
  This software is provided 'as-is', without any express or implied
  warranty. In no event will the authors be held liable for any damages
  arising from the use of this software.
  
  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:
  
  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  
  3. This notice may not be removed or altered from any source
     distribution.
******************************************************************************/

 

First we need a few simple math types.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//******************************************************************************
// 2-dimensional vector.
//******************************************************************************
struct tVec2 { float x, y; };
  
//******************************************************************************
// 3-dimensional vector.
//******************************************************************************
struct tVec3 { float x, y, z; };
  
//******************************************************************************
// 3x3 matrix
//******************************************************************************
struct tMat3x3 { float m[3][3]; };

 

Next we need a few math helper functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//******************************************************************************
// Set an indexed matrix column to a given vector.
//******************************************************************************
void Mat_SetCol(tMat3x3 * pMat, int colIdx, const tVec3 & vec)
{
    pMat->m[0][colIdx] = vec.x;
    pMat->m[1][colIdx] = vec.y;
    pMat->m[2][colIdx] = vec.z;
}
  
//******************************************************************************
// Calculate the inverse of a 3x3 matrix. Return false if it is non-invertible.
//******************************************************************************
bool Mat_Invert(tMat3x3 * pOutMat, const tMat3x3 & inMat)
{
    // calculate the minors for the first row
    float minor00 = inMat.m[1][1]*inMat.m[2][2] - inMat.m[1][2]*inMat.m[2][1];
    float minor01 = inMat.m[1][2]*inMat.m[2][0] - inMat.m[1][0]*inMat.m[2][2];
    float minor02 = inMat.m[1][0]*inMat.m[2][1] - inMat.m[1][1]*inMat.m[2][0];
  
    // calculate the determinant
    float determinant =   inMat.m[0][0] * minor00
                        + inMat.m[0][1] * minor01
                        + inMat.m[0][2] * minor02;
  
    // check if the input is a singular matrix (non-invertable)
    // (note that the epsilon here was arbitrarily chosen)
    if( determinant > -0.000001f && determinant < 0.000001f )
        return false;
  
    // the inverse of inMat is (1 / determinant) * adjoint(inMat)
    float invDet = 1.0f / determinant;
    pOutMat->m[0][0] = invDet * minor00;
    pOutMat->m[0][1] = invDet * (inMat.m[2][1]*inMat.m[0][2] - inMat.m[2][2]*inMat.m[0][1]);
    pOutMat->m[0][2] = invDet * (inMat.m[0][1]*inMat.m[1][2] - inMat.m[0][2]*inMat.m[1][1]);
  
    pOutMat->m[1][0] = invDet * minor01;
    pOutMat->m[1][1] = invDet * (inMat.m[2][2]*inMat.m[0][0] - inMat.m[2][0]*inMat.m[0][2]);
    pOutMat->m[1][2] = invDet * (inMat.m[0][2]*inMat.m[1][0] - inMat.m[0][0]*inMat.m[1][2]);
  
    pOutMat->m[2][0] = invDet * minor02;
    pOutMat->m[2][1] = invDet * (inMat.m[2][0]*inMat.m[0][1] - inMat.m[2][1]*inMat.m[0][0]);
    pOutMat->m[2][2] = invDet * (inMat.m[0][0]*inMat.m[1][1] - inMat.m[0][1]*inMat.m[1][0]);
  
    return true;
}
  
//******************************************************************************
// Multiply a column vector on the right of a 3x3 matrix.
//******************************************************************************
void Mat_MulVec( tVec3 * pOutVec, const tMat3x3 & mat, const tVec3 inVec )
{
    pOutVec->x = mat.m[0][0]*inVec.x + mat.m[0][1]*inVec.y + mat.m[0][2]*inVec.z;
    pOutVec->y = mat.m[1][0]*inVec.x + mat.m[1][1]*inVec.y + mat.m[1][2]*inVec.z;
    pOutVec->z = mat.m[2][0]*inVec.x + mat.m[2][1]*inVec.y + mat.m[2][2]*inVec.z;
}

 

These are the gamma correction functions for sRGB color space. These make use of the common run-time library function, powf, from <math.h>.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//******************************************************************************
// Convert an sRGB color channel to a linear sRGB color channel.
//******************************************************************************
float GammaExpand_sRGB(float nonlinear)
{   
    return   ( nonlinear <= 0.04045f )
           ? ( nonlinear / 12.92f )
           : ( powf( (nonlinear+0.055f)/1.055f, 2.4f ) );
}
  
//******************************************************************************
// Convert a linear sRGB color channel to a sRGB color channel.
//******************************************************************************
float GammaCompress_sRGB(float linear)
{   
    return   ( linear <= 0.0031308f )
           ? ( 12.92f * linear )
           : ( 1.055f * powf( linear, 1.0f/2.4f ) - 0.055f );
}
  
//******************************************************************************
// Convert an sRGB color to a linear sRGB color.
//******************************************************************************
void GammaExpand_sRGB(tVec3 * pColor)
{
    pColor->x = GammaExpand_sRGB( pColor->x );
    pColor->y = GammaExpand_sRGB( pColor->y );
    pColor->z = GammaExpand_sRGB( pColor->z );
}
  
//******************************************************************************
// Convert a linear sRGB color to an sRGB color.
//******************************************************************************
void GammaCompress_sRGB(tVec3 * pColor)
{
    pColor->x = GammaCompress_sRGB( pColor->x );
    pColor->y = GammaCompress_sRGB( pColor->y );
    pColor->z = GammaCompress_sRGB( pColor->z );
}

 

This function builds the transformation matrix from a linear RGB space to XYZ space,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//******************************************************************************
// Convert a linear sRGB color to an sRGB color
//******************************************************************************
void CalcColorSpaceConversion_RGB_to_XYZ
(
    tMat3x3 *     pOutput,  // conversion matrix
    const tVec2 & red_xy,   // xy chromaticity coordinates of the red primary
    const tVec2 & green_xy, // xy chromaticity coordinates of the green primary
    const tVec2 & blue_xy,  // xy chromaticity coordinates of the blue primary
    const tVec2 & white_xy  // xy chromaticity coordinates of the white point
)
{
    // generate xyz chromaticity coordinates (x + y + z = 1) from xy coordinates
    tVec3 r = { red_xy.x,   red_xy.y,   1.0f - (red_xy.x + red_xy.y) };
    tVec3 g = { green_xy.x, green_xy.y, 1.0f - (green_xy.x + green_xy.y) };
    tVec3 b = { blue_xy.x,  blue_xy.y,  1.0f - (blue_xy.x + blue_xy.y) };
    tVec3 w = { white_xy.x, white_xy.y, 1.0f - (white_xy.x + white_xy.y) };
  
    // Convert white xyz coordinate to XYZ coordinate by letting that the white
    // point have and XYZ relative luminance of 1.0. Relative luminance is the Y
    // component of and XYZ color.
    //   XYZ = xyz * (Y / y)
    w.x /= white_xy.y;
    w.y /= white_xy.y;
    w.z /= white_xy.y;
  
    // Solve for the transformation matrix 'M' from RGB to XYZ
    // * We know that the columns of M are equal to the unknown XYZ values of r, g and b.
    // * We know that the XYZ values of r, g and b are each a scaled version of the known
    //   corresponding xyz chromaticity values.
    // * We know the XYZ value of white based on its xyz value and the assigned relative
    //   luminance of 1.0.
    // * We know the RGB value of white is (1,1,1).
    //                 
    //   white_XYZ = M * white_RGB
    //
    //       [r.x g.x b.x]
    //   N = [r.y g.y b.y]
    //       [r.z g.z b.z]
    //
    //       [sR 0  0 ]
    //   S = [0  sG 0 ]
    //       [0  0  sB]
    //
    //   M = N * S
    //   white_XYZ = N * S * white_RGB
    //   N^-1 * white_XYZ = S * white_RGB = (sR,sG,sB)
    //
    // We now have an equation for the components of the scale matrix 'S' and
    // can compute 'M' from 'N' and 'S'
  
    Mat_SetCol( pOutput, 0, r );
    Mat_SetCol( pOutput, 1, g );
    Mat_SetCol( pOutput, 2, b );
  
    tMat3x3 invMat;
    Mat_Invert( &invMat, *pOutput );
  
    tVec3 scale;
    Mat_MulVec( &scale, invMat, w );
  
    pOutput->m[0][0] *= scale.x;
    pOutput->m[1][0] *= scale.x;
    pOutput->m[2][0] *= scale.x;
  
    pOutput->m[0][1] *= scale.y;
    pOutput->m[1][1] *= scale.y;
    pOutput->m[2][1] *= scale.y;
  
    pOutput->m[0][2] *= scale.z;
    pOutput->m[1][2] *= scale.z;
    pOutput->m[2][2] *= scale.z;
}

 

Finally, this function just shows an example of converting back and forth between sRGB space and XYZ space.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//******************************************************************************
// Example of using the color space conversion functions
//******************************************************************************
void ExampleColorSpaceConversion()
{
    // define chromaticity coordinates for sRGB space
    tVec2 sRGB_red_xy   = { 0.64f, 0.33f };
    tVec2 sRGB_green_xy = { 0.30f, 0.60f };
    tVec2 sRGB_blue_xy  = { 0.15f, 0.06f };
    tVec2 sRGB_white_xy = { 0.3127f, 0.3290f };
  
    // generate conversion matrix from linear sRGB space to XYZ space
    tMat3x3 convert_sRGB_to_XYZ;
    CalcColorSpaceConversion_RGB_to_XYZ( &convert_sRGB_to_XYZ,
                                         sRGB_red_xy,
                                         sRGB_green_xy,
                                         sRGB_blue_xy,
                                         sRGB_white_xy );
  
    // generate conversion matrix from XYZ space to linear sRGB space
    tMat3x3 convert_XYZ_to_sRGB;
    Mat_Invert( &convert_XYZ_to_sRGB, convert_sRGB_to_XYZ );
  
    // define a color in sRGB space
    tVec3 myColor = { 0.2f, 0.5f, 0.8f };
  
    // convert form sRGB to XYZ
    {
        // convert from gamma-corrected sRGB to linear sRGB
        GammaExpand_sRGB( &myColor );
  
        // convert from linear sRGB to XYZ
        Mat_MulVec( &myColor, convert_sRGB_to_XYZ, myColor );
    }
  
    // convert form XYZ back to sRGB
    {
        // convert from XYZ to linear sRGB
        Mat_MulVec( &myColor, convert_XYZ_to_sRGB, myColor );
  
        // convert from linear sRGB to gamma-corrected sRGB
        GammaCompress_sRGB( &myColor );
    }
}

 

posted @ 2020-07-13 18:31  吴建明wujianming  阅读(38)  评论(0编辑  收藏