• 编写高效的C#图像处理程序(3) Rgb=>Lab,图像缺陷检测的例子


    最近项目需要检测图像是否存在偏色、过亮、模糊等缺陷。由于主要用在视频监控上,对性能要求比较高。有几项检测必须要在Lab彩色下进行,而众所周知Rgb => Lab 计算量较大,C#搞得定搞不定?测试表明,用纯C#编写的Rgb => Lab代码在性能上与C编写的Rgb => Lab代码极为接近。

    1. Rgb24和Lab24

    Rgb是电脑上使用较多的彩色空间,Lab是针对人的感知设计的均匀彩色空间,很多情况下进行彩色图像分析,需要在Rgb彩色空间和Lab彩色空间之间进行转化。关于Lab彩色空间的详细介绍和Rgb空间与Lab空间的转换公式见维基百科的对应词条 Lab色彩空间,本文不再叙述。

    使用Rgb24和Lab24两个struct定义Rgb彩色空间的像素和Lab彩色空间的像素。

    Rgb24 与 Lab24
    1 public partial struct Rgb24 
    2 
    3     public static Rgb24 WHITE = new Rgb24 { Red = 255, Green = 255, Blue = 255 }; 
    4     public static Rgb24 BLACK = new Rgb24(); 
    5     public static Rgb24 RED = new Rgb24 { Red = 255 }; 
    6     public static Rgb24 BLUE = new Rgb24 { Blue = 255 }; 
    7     public static Rgb24 GREEN = new Rgb24 { Green = 255 }; 
    8 
    9     [FieldOffset(0)] 
    10     public Byte Blue; 
    11     [FieldOffset(1)] 
    12     public Byte Green; 
    13     [FieldOffset(2)] 
    14     public Byte Red; 
    15 
    16     public Rgb24(int red, int green, int blue) 
    17     { 
    18         Red = (byte)red; 
    19         Green = (byte)green; 
    20         Blue = (byte)blue; 
    21     } 
    22 
    23     public Rgb24(byte red, byte green, byte blue) 
    24     { 
    25         Red = red; 
    26         Green = green; 
    27         Blue = blue; 
    28     } 
    29 }
    30 
    31 public partial struct Lab24 
    32 
    33     public byte L; 
    34     public byte A; 
    35     public byte B; 
    36 
    37     public Lab24(byte l,byte a,byte b) 
    38     { 
    39         L = l; 
    40         A = a; 
    41         B = b; 
    42     } 
    43 
    44     public Lab24(int l,int a,int b) 
    45     { 
    46         L = (byte)l; 
    47         A = (byte)a; 
    48         B = (byte)b; 
    49     } 
    50 }

    Lab空间参照OpenCV,用一个byte来表示Lab空间的每个通道值,以求提高性能。由于标准的Lab空间中a和b通道是可付的,Lab24中的A、B值减去128,就是标准Lab空间的a,b通道值。

    2. Rgb24 <=> Lab24 的实现

    OpenCV中Bgr<=>Lab是用C语言实现的,下面将它转换为C#代码:

    Rgb24 <=> Lab24
      1 public sealed class UnmanagedImageConverter 
      2 
      3     /* 1024*(([0..511]./255)**(1./3)) */ 
      4     static ushort[] icvLabCubeRootTab = new ushort[] { 
      5     0,161,203,232,256,276,293,308,322,335,347,359,369,379,389,398
      6     406,415,423,430,438,445,452,459,465,472,478,484,490,496,501,507
      7     512,517,523,528,533,538,542,547,552,556,561,565,570,574,578,582
      8     586,590,594,598,602,606,610,614,617,621,625,628,632,635,639,642
      9     645,649,652,655,659,662,665,668,671,674,677,680,684,686,689,692
    10     695,698,701,704,707,710,712,715,718,720,723,726,728,731,734,736
    11     739,741,744,747,749,752,754,756,759,761,764,766,769,771,773,776
    12     778,780,782,785,787,789,792,794,796,798,800,803,805,807,809,811
    13     813,815,818,820,822,824,826,828,830,832,834,836,838,840,842,844
    14     846,848,850,852,854,856,857,859,861,863,865,867,869,871,872,874
    15     876,878,880,882,883,885,887,889,891,892,894,896,898,899,901,903
    16     904,906,908,910,911,913,915,916,918,920,921,923,925,926,928,929
    17     931,933,934,936,938,939,941,942,944,945,947,949,950,952,953,955
    18     956,958,959,961,962,964,965,967,968,970,971,973,974,976,977,979
    19     980,982,983,985,986,987,989,990,992,993,995,996,997,999,1000,1002
    20     1003,1004,1006,1007,1009,1010,1011,1013,1014,1015,1017,1018,1019,1021,1022,1024
    21     1025,1026,1028,1029,1030,1031,1033,1034,1035,1037,1038,1039,1041,1042,1043,1044
    22     1046,1047,1048,1050,1051,1052,1053,1055,1056,1057,1058,1060,1061,1062,1063,1065
    23     1066,1067,1068,1070,1071,1072,1073,1074,1076,1077,1078,1079,1081,1082,1083,1084
    24     1085,1086,1088,1089,1090,1091,1092,1094,1095,1096,1097,1098,1099,1101,1102,1103
    25     1104,1105,1106,1107,1109,1110,1111,1112,1113,1114,1115,1117,1118,1119,1120,1121
    26     1122,1123,1124,1125,1127,1128,1129,1130,1131,1132,1133,1134,1135,1136,1138,1139
    27     1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1154,1155,1156
    28     1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172
    29     1173,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188
    30     1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1202,1203,1204
    31     1205,1206,1207,1208,1209,1210,1211,1212,1213,1214,1215,1215,1216,1217,1218,1219
    32     1220,1221,1222,1223,1224,1225,1226,1227,1228,1229,1230,1230,1231,1232,1233,1234
    33     1235,1236,1237,1238,1239,1240,1241,1242,1242,1243,1244,1245,1246,1247,1248,1249
    34     1250,1251,1251,1252,1253,1254,1255,1256,1257,1258,1259,1259,1260,1261,1262,1263
    35     1264,1265,1266,1266,1267,1268,1269,1270,1271,1272,1273,1273,1274,1275,1276,1277
    36     1278,1279,1279,1280,1281,1282,1283,1284,1285,1285,1286,1287,1288,1289,1290,1291 
    37     }; 
    38 
    39     const float labXr_32f = 0.433953f /* = xyzXr_32f / 0.950456 */
    40     const float labXg_32f = 0.376219f /* = xyzXg_32f / 0.950456 */
    41     const float labXb_32f = 0.189828f /* = xyzXb_32f / 0.950456 */
    42 
    43     const float labYr_32f = 0.212671f /* = xyzYr_32f */
    44     const float labYg_32f = 0.715160f /* = xyzYg_32f */
    45     const float labYb_32f = 0.072169f /* = xyzYb_32f */
    46 
    47     const float labZr_32f = 0.017758f /* = xyzZr_32f / 1.088754 */
    48     const float labZg_32f = 0.109477f /* = xyzZg_32f / 1.088754 */
    49     const float labZb_32f = 0.872766f /* = xyzZb_32f / 1.088754 */
    50 
    51     const float labRx_32f = 3.0799327f  /* = xyzRx_32f * 0.950456 */
    52     const float labRy_32f = (-1.53715f) /* = xyzRy_32f */
    53     const float labRz_32f = (-0.542782f)/* = xyzRz_32f * 1.088754 */
    54 
    55     const float labGx_32f = (-0.921235f)/* = xyzGx_32f * 0.950456 */
    56     const float labGy_32f = 1.875991f   /* = xyzGy_32f */
    57     const float labGz_32f = 0.04524426f /* = xyzGz_32f * 1.088754 */
    58 
    59     const float labBx_32f = 0.0528909755f /* = xyzBx_32f * 0.950456 */
    60     const float labBy_32f = (-0.204043f/* = xyzBy_32f */
    61     const float labBz_32f = 1.15115158f   /* = xyzBz_32f * 1.088754 */
    62 
    63     const float labT_32f = 0.008856f
    64 
    65     const int lab_shift = 10
    66 
    67     const float labLScale2_32f = 903.3f
    68 
    69     const int labXr = (int)((labXr_32f) * (1 << (lab_shift)) + 0.5); 
    70     const int labXg = (int)((labXg_32f) * (1 << (lab_shift)) + 0.5); 
    71     const int labXb = (int)((labXb_32f) * (1 << (lab_shift)) + 0.5); 
    72 
    73     const int labYr = (int)((labYr_32f) * (1 << (lab_shift)) + 0.5); 
    74     const int labYg = (int)((labYg_32f) * (1 << (lab_shift)) + 0.5); 
    75     const int labYb = (int)((labYb_32f) * (1 << (lab_shift)) + 0.5); 
    76 
    77     const int labZr = (int)((labZr_32f) * (1 << (lab_shift)) + 0.5); 
    78     const int labZg = (int)((labZg_32f) * (1 << (lab_shift)) + 0.5); 
    79     const int labZb = (int)((labZb_32f) * (1 << (lab_shift)) + 0.5); 
    80 
    81     const float labLScale_32f = 116.0f
    82     const float labLShift_32f = 16.0f
    83 
    84     const int labSmallScale = (int)((31.27 /* labSmallScale_32f*(1<<lab_shift)/255 */ ) * (1 << (lab_shift)) + 0.5); 
    85 
    86     const int labSmallShift = (int)((141.24138 /* labSmallScale_32f*(1<<lab) */ ) * (1 << (lab_shift)) + 0.5); 
    87 
    88     const int labT = (int)((labT_32f * 255) * (1 << (lab_shift)) + 0.5); 
    89 
    90     const int labLScale = (int)((295.8) * (1 << (lab_shift)) + 0.5); 
    91     const int labLShift = (int)((41779.2) * (1 << (lab_shift)) + 0.5); 
    92     const int labLScale2 = (int)((labLScale2_32f * 0.01) * (1 << (lab_shift)) + 0.5); 
    93 
    94     public static unsafe void ToLab24(Rgb24* from, Lab24* to) 
    95     { 
    96         ToLab24(from,to,1); 
    97     } 
    98 
    99     public static unsafe void ToLab24(Rgb24* from, Lab24* to, int length) 
    100     { 
    101         // 使用 OpenCV 中的算法实现
    102 
    103         if (length < 1) return
    104 
    105         Rgb24* end = from + length; 
    106 
    107         int x, y, z; 
    108         int l, a, b; 
    109         bool flag; 
    110 
    111         while (from != end) 
    112         { 
    113             Byte red = from->Red; 
    114             Byte green = from->Green; 
    115             Byte blue = from->Blue; 
    116 
    117             x = blue * labXb + green * labXg + red * labXr; 
    118             y = blue * labYb + green * labYg + red * labYr; 
    119             z = blue * labZb + green * labZg + red * labZr; 
    120 
    121             flag = x > labT; 
    122 
    123             x = (((x) + (1 << ((lab_shift) - 1))) >> (lab_shift)); 
    124 
    125             if (flag) 
    126                 x = icvLabCubeRootTab[x]; 
    127             else 
    128                 x = (((x * labSmallScale + labSmallShift) + (1 << ((lab_shift) - 1))) >> (lab_shift)); 
    129 
    130             flag = z > labT; 
    131             z = (((z) + (1 << ((lab_shift) - 1))) >> (lab_shift)); 
    132 
    133             if (flag == true
    134                 z = icvLabCubeRootTab[z]; 
    135             else 
    136                 z = (((z * labSmallScale + labSmallShift) + (1 << ((lab_shift) - 1))) >> (lab_shift)); 
    137 
    138             flag = y > labT; 
    139             y = (((y) + (1 << ((lab_shift) - 1))) >> (lab_shift)); 
    140 
    141             if (flag == true
    142             { 
    143                 y = icvLabCubeRootTab[y]; 
    144                 l = (((y * labLScale - labLShift) + (1 << ((2 * lab_shift) - 1))) >> (2 * lab_shift)); 
    145             } 
    146             else 
    147             { 
    148                 l = (((y * labLScale2) + (1 << ((lab_shift) - 1))) >> (lab_shift)); 
    149                 y = (((y * labSmallScale + labSmallShift) + (1 << ((lab_shift) - 1))) >> (lab_shift)); 
    150             } 
    151 
    152             a = (((500 * (x - y)) + (1 << ((lab_shift) - 1))) >> (lab_shift)) + 129
    153             b = (((200 * (y - z)) + (1 << ((lab_shift) - 1))) >> (lab_shift)) + 128
    154 
    155             l = l > 255 ? 255 : l < 0 ? 0 : l; 
    156             a = a > 255 ? 255 : a < 0 ? 0 : a; 
    157             b = b > 255 ? 255 : b < 0 ? 0 : b; 
    158 
    159             to->L = (byte)l; 
    160             to->A = (byte)a; 
    161             to->B = (byte)b; 
    162 
    163             from++
    164             to++
    165         } 
    166     } 
    167 
    168     public static unsafe void ToRgb24(Lab24* from, Rgb24* to) 
    169     { 
    170         ToRgb24(from,to,1); 
    171     } 
    172 
    173     public static unsafe void ToRgb24(Lab24* from, Rgb24* to, int length) 
    174     { 
    175         if (length < 1) return
    176 
    177         // 使用 OpenCV 中的算法实现
    178         const float coeff0 = 0.39215686274509809f
    179         const float coeff1 = 0.0f
    180         const float coeff2 = 1.0f
    181         const float coeff3 = (-128.0f); 
    182         const float coeff4 = 1.0f
    183         const float coeff5 = (-128.0f); 
    184 
    185         if (length < 1) return
    186 
    187         Lab24* end = from + length; 
    188         float x, y, z,l,a,b; 
    189         int blue, green, red; 
    190 
    191         while (from != end) 
    192         { 
    193             l = from->L * coeff0 + coeff1; 
    194             a = from->A * coeff2 + coeff3; 
    195             b = from->B * coeff4 + coeff5; 
    196 
    197             l = (l + labLShift_32f) * (1.0f / labLScale_32f); 
    198             x = (l + a * 0.002f); 
    199             z = (l - b * 0.005f); 
    200 
    201             y = l * l * l; 
    202             x = x * x * x; 
    203             z = z * z * z; 
    204 
    205             blue = (int)((x * labBx_32f + y * labBy_32f + z * labBz_32f) * 255 + 0.5); 
    206             green = (int)((x * labGx_32f + y * labGy_32f + z * labGz_32f) * 255 + 0.5); 
    207             red = (int)((x * labRx_32f + y * labRy_32f + z * labRz_32f) * 255 + 0.5); 
    208 
    209             red = red < 0 ? 0 : red > 255 ? 255 : red; 
    210             green = green < 0 ? 0 : green > 255 ? 255 : green; 
    211             blue = blue < 0 ? 0 : blue > 255 ? 255 : blue; 
    212 
    213             to->Red = (byte)red; 
    214             to->Green = (byte)green; 
    215             to->Blue = (byte)blue; 
    216 
    217             from++
    218             to++
    219         } 
    220     } 
    221 }

    由于C代码中使用了宏,在改写成C#代码时需要手动内联,以提高性能。上面的代码已经实现手动内联。

    3. (A)C#实现与(B)C实现的性能对比(C# vs. OpenCV/PInvoke)

    C# 版本(ImageRgb24 代表一幅Rgb24图像,ImageLab24代表一幅Lab24图像,它们之间的变化是调用上文UnmanagedImageConverter中的方法实现的):

    Stopwatch sw = new Stopwatch();
    sw.Start();
    ImageLab24 imgLab = null;
    imgLab = new ImageLab24(img);  // img 是一个 ImageRgb24 对象
    sw.Stop();
    Message = sw.ElapsedMilliseconds.ToString();

    OpenCV版本(使用EmguCV对OpenCV的PInvoke封装)

    private Image<Lab,Byte> TestOpenCV()
    {
        Image<Bgr, Byte> imgBgr = new Image<Bgr, byte>(imgMain.Image as Bitmap);
        Image<Lab,Byte> imgLab = new Image<Lab,byte>(new Size(imgBgr.Width, imgBgr.Height));
        Stopwatch sw = new Stopwatch();
        sw.Start();
        CvInvoke.cvCvtColor(imgBgr.Ptr,imgLab.Ptr, Emgu.CV.CvEnum.COLOR_CONVERSION.CV_BGR2Lab);
        sw.Stop();
        MessageBox.Show(sw.ElapsedMilliseconds.ToString() + "ms");
        return imgLab;
    }

    下面针对三副不同大小的图像进行测试,每张图像测试4次,每次测试将上面两种实现各跑一次,前2次,先跑OpenCV/PInvoke实现,后2次,先跑C#实现,单位皆为ms。

    图像1,大小:485×342

    A: 5    3    5   3
    B: 41   5    6   2

    图像2,大小:1845×611

    A:25  23    23   23  
    B:23  34    20   21  

    图像3,大小:3888×2592

    A:209  210  211  210
    B:185  188  191  185

    从测试结果可以看出,C# 和 OpenCV/PInvoke的性能极为接近。

    4. 进一步改进性能

    偏色、高光检测等不需要多么准确的Rgb=>Lab转换。如果把彩色图像的每个通道用4 bit来表示,则一共有 4096 种颜色,完全可以用查表方式来加速计算。用一个Lab24数组来表示Rgb24到Lab24空间的映射:

    Lab24[] ColorMap

    首先初始化ColorMap:

    ColorMap = new Lab24[4096];
    for (int r = 0; r < 16; r++)
    {
        for (int g = 0; g < 16; g++)
        {
            for (int b = 0; b < 16; b++)
            {
                Rgb24 rgb = new Rgb24(r * 16, g * 16, b * 16);
                Lab24 lab = Lab24.CreateFrom(rgb);
                ColorMap[(r << 8) + (g << 4) + b] = lab;
            }
        }
    }

    然后,查表进行转换:

    private unsafe ImageLab24 ConvertToImageLab24(ImageRgb24 img)
    {
        ImageLab24 lab = new ImageLab24(img.Width, img.Height);
        Lab24* labStart = lab.Start;
        Rgb24* rgbStart = img.Start;
        Rgb24* rgbEnd = img.Start + img.Length;
        while (rgbStart != rgbEnd)
        {
            Rgb24 rgb = *rgbStart;
            *labStart = ColorMap[(((int)(rgb.Red) >> 4) << 8) + (((int)(rgb.Green) >> 4) << 4) + ((int)(rgb.Blue) >> 4) ];
            rgbStart++;
            labStart++;
        }
        return lab;
    }

    下面测试(C)查表计算的性能,结果和(A)C#实现与(B)C实现放在一起做对比。

    图像1,大小:485×342

    A: 5    3    5   3
    B: 41   5    6   2
    C: 3    2    2    2

    图像2,大小:1845×611

    A:25  23    23   23  
    B:23  34    20   21  
    C:  15   15   15   15

    图像3,大小:3888×2592

    A:209  210  211  210
    B:185  188  191  185
    C:  136  134  135  135

    5. 原地进行变换

    还可以进一步提高性能,因为Rgb24和Lab24大小一样,可以在原地进行Rgb24=>Lab24的变换。相应代码如下:

    Rgb24[] ColorMapInSpace
    ...           
    ColorMap = new Lab24[4096];
    ColorMapInSpace = new Rgb24[4096];
    for (int r = 0; r < 16; r++)
    {
        for (int g = 0; g < 16; g++)
        {
            for (int b = 0; b < 16; b++)
            {
                Rgb24 rgb = new Rgb24(r * 16, g * 16, b * 16);
                Lab24 lab = Lab24.CreateFrom(rgb);
                ColorMap[(r << 8) + (g << 4) + b] = lab;
                ColorMapInSpace[(r << 8) + (g << 4) + b] = new Rgb24(lab.L,lab.A,lab.B);
            }
        }
    }

    private unsafe void ConvertToImageLab24InSpace(ImageRgb24 img)
    {
        Rgb24* rgbStart = img.Start;
        Rgb24* rgbEnd = img.Start + img.Length;
        while (rgbStart != rgbEnd)
        {
            Rgb24 rgb = *rgbStart;
            *rgbStart = ColorMapInSpace[(((int)(rgb.Red) >> 4) << 8) + (((int)(rgb.Green) >> 4) << 4) + ((int)(rgb.Blue) >> 4)];
            rgbStart++;
        }
    }

    下面测试D(原地查表变换)的性能,结果和(A)C#实现、(B)C实现、(C)查表计算进行比较:

    图像1,大小:485×342

    A: 5    3    5   3
    B: 41   5    6   2
    C: 3    2    2    2
    D: 2    1    2    1

    图像2,大小:1845×611

    A:25  23    23   23  
    B:23  34    20   21  
    C:  15   15   15   15 
    D:  13   13   13   13

    图像3,大小:3888×2592

    A:209  210  211  210
    B:185  188  191  185
    C:  136  134  135  135
    D:  117  118  122  117

    6. 为什么用C#而不是C/C++

    经常有人问,你为什么用C#而不用C/C++写图像处理程序。原因如下:

    (1)C# 打开unsafe后,写的程序性能非常接近 C 程序的性能(当然,用不了SIMD是个缺陷。mono暂时不考虑。可通过挂接一个轻量级的C库来解决。);

    (2)写C#代码比写C代码爽多了快多了(命名空间、不用管头文件、快速编译、重构、生成API文档 ……);

    (3)庞大的.Net Framework是强有力的后盾。比如,客户想看演示,用Asp.Net写个页面,传个图片给后台,处理了显示出来。还有那些非性能攸关的地方,可以大量使用.Net Framework中的类,大幅度减少开发时间;

    (4)结合强大的WPF,可以快速实现复杂的功能

    (5)大量的时间在算法研究、实现和优化上,用C#可以把那些无关的惹人烦的事情给降到最小,所牺牲的只是一丁点儿性能。如果生产平台没有.net环境,将C#代码转换为C/C++代码也很快。

    ====

    补充测试VC 9.0 版本

    VC 实现与 C# 实现略有区别,C#版本RGB,Lab使用struct来表示,VC下直接用的三个Byte Channel来表示,然后以 redChannel, greenChannel, blueChannel 来代表不同的 Channel Offset。以 nChannel 代表 Channel 数量。VC下有Stride,C#下无Stride。查表实现也和C#版本有区别,直接使用的是静态的表。O2优化。

    E: 非查表实现

    void
    ::ImageQualityDetector::ConvertToLab(Orc::ImageInfo &img)
    {
        static unsigned short icvLabCubeRootTab[] = {
            0,161,203……        };

        const float labXr_32f = 0.433953f /* = xyzXr_32f / 0.950456 */;
        const float labXg_32f = 0.376219f /* = xyzXg_32f / 0.950456 */;
        const float labXb_32f = 0.189828f /* = xyzXb_32f / 0.950456 */;

        const float labYr_32f = 0.212671f /* = xyzYr_32f */;
        const float labYg_32f = 0.715160f /* = xyzYg_32f */;
        const float labYb_32f = 0.072169f /* = xyzYb_32f */;

        const float labZr_32f = 0.017758f /* = xyzZr_32f / 1.088754 */;
        const float labZg_32f = 0.109477f /* = xyzZg_32f / 1.088754 */;
        const float labZb_32f = 0.872766f /* = xyzZb_32f / 1.088754 */;

        const float labRx_32f = 3.0799327f  /* = xyzRx_32f * 0.950456 */;
        const float labRy_32f = (-1.53715f) /* = xyzRy_32f */;
        const float labRz_32f = (-0.542782f)/* = xyzRz_32f * 1.088754 */;

        const float labGx_32f = (-0.921235f)/* = xyzGx_32f * 0.950456 */;
        const float labGy_32f = 1.875991f   /* = xyzGy_32f */ ;
        const float labGz_32f = 0.04524426f /* = xyzGz_32f * 1.088754 */;

        const float labBx_32f = 0.0528909755f /* = xyzBx_32f * 0.950456 */;
        const float labBy_32f = (-0.204043f)  /* = xyzBy_32f */;
        const float labBz_32f = 1.15115158f   /* = xyzBz_32f * 1.088754 */;

        const float labT_32f = 0.008856f;

        const int lab_shift = 10;

        const float labLScale2_32f = 903.3f;

        const int labXr = (int)((labXr_32f) * (1 << (lab_shift)) + 0.5);
        const int labXg = (int)((labXg_32f) * (1 << (lab_shift)) + 0.5);
        const int labXb = (int)((labXb_32f) * (1 << (lab_shift)) + 0.5);

        const int labYr = (int)((labYr_32f) * (1 << (lab_shift)) + 0.5);
        const int labYg = (int)((labYg_32f) * (1 << (lab_shift)) + 0.5);
        const int labYb = (int)((labYb_32f) * (1 << (lab_shift)) + 0.5);

        const int labZr = (int)((labZr_32f) * (1 << (lab_shift)) + 0.5);
        const int labZg = (int)((labZg_32f) * (1 << (lab_shift)) + 0.5);
        const int labZb = (int)((labZb_32f) * (1 << (lab_shift)) + 0.5);

        const float labLScale_32f = 116.0f;
        const float labLShift_32f = 16.0f;

        const int labSmallScale = (int)((31.27 /* labSmallScale_32f*(1<<lab_shift)/255 */ ) * (1 << (lab_shift)) + 0.5);

        const int labSmallShift = (int)((141.24138 /* labSmallScale_32f*(1<<lab) */ ) * (1 << (lab_shift)) + 0.5);

        const int labT = (int)((labT_32f * 255) * (1 << (lab_shift)) + 0.5);

        const int labLScale = (int)((295.8) * (1 << (lab_shift)) + 0.5);
        const int labLShift = (int)((41779.2) * (1 << (lab_shift)) + 0.5);
        const int labLScale2 = (int)((labLScale2_32f * 0.01) * (1 << (lab_shift)) + 0.5);

        int width = img.Width;
        int height = img.Height;
        int nChannel = img.NChannel;
        int redChannel = img.RedChannel;
        int greenChannel = img.GreenChannel;
        int blueChannel = img.BlueChannel;
        int x, y, z;
        int l, a, b;
        bool flag;

        for(int h = 0; h < height; h++)
        {
            byte *line = img.GetLine(h);
            for(int w = 0; w < width; w++)
            {
                int red = line[redChannel];
                int green = line[greenChannel];
                int blue = line[blueChannel];

                x = blue * labXb + green * labXg + red * labXr;
                y = blue * labYb + green * labYg + red * labYr;
                z = blue * labZb + green * labZg + red * labZr;

                flag = x > labT;

                x = (((x) + (1 << ((lab_shift) - 1))) >> (lab_shift));

                if (flag)
                    x = icvLabCubeRootTab[x];
                else
                    x = (((x * labSmallScale + labSmallShift) + (1 << ((lab_shift) - 1))) >> (lab_shift));

                flag = z > labT;
                z = (((z) + (1 << ((lab_shift) - 1))) >> (lab_shift));

                if (flag == true)
                    z = icvLabCubeRootTab[z];
                else
                    z = (((z * labSmallScale + labSmallShift) + (1 << ((lab_shift) - 1))) >> (lab_shift));

                flag = y > labT;
                y = (((y) + (1 << ((lab_shift) - 1))) >> (lab_shift));

                if (flag == true)
                {
                    y = icvLabCubeRootTab[y];
                    l = (((y * labLScale - labLShift) + (1 << ((2 * lab_shift) - 1))) >> (2 * lab_shift));
                }
                else
                {
                    l = (((y * labLScale2) + (1 << ((lab_shift) - 1))) >> (lab_shift));
                    y = (((y * labSmallScale + labSmallShift) + (1 << ((lab_shift) - 1))) >> (lab_shift));
                }

                a = (((500 * (x - y)) + (1 << ((lab_shift) - 1))) >> (lab_shift)) + 129;
                b = (((200 * (y - z)) + (1 << ((lab_shift) - 1))) >> (lab_shift)) + 128;

                l = l > 255 ? 255 : l < 0 ? 0 : l;
                a = a > 255 ? 255 : a < 0 ? 0 : a;
                b = b > 255 ? 255 : b < 0 ? 0 : b;

                int index = 3 * (((red >> 4) << 8) + ((green >> 4) << 4) + (blue >> 4)) ;
                line[0] = (byte)l;
                line[1] = (byte)a;
                line[2] = (byte)b;

                line += nChannel;
            }
        }
    }

    F: 查表实现

    void
    ::ImageQualityDetector::FastConvertToLab(Orc::ImageInfo &img)
    {
        static const byte Rgb2LabSmallTable[] = {
        0,    129,    128 ……
        };

        int width = img.Width;
        int height = img.Height;
        int nChannel = img.NChannel;
        int redChannel = img.RedChannel;
        int greenChannel = img.GreenChannel;
        int blueChannel = img.BlueChannel;
        for(int h = 0; h < height; h++)
        {
            byte *line = img.GetLine(h);
            for(int w = 0; w < width; w++)
            {
                int red = line[redChannel];
                int green = line[greenChannel];
                int blue = line[blueChannel];
                int index = 3 * (((red >> 4) << 8) + ((green >> 4) << 4) + (blue >> 4)) ;
                line[0] = Rgb2LabSmallTable[index];
                line[1] = Rgb2LabSmallTable[index + 1];
                line[2] = Rgb2LabSmallTable[index + 2];
                line += nChannel;
            }
        }
    }

    测试结果:

    图像2,大小:1845×611

    A:25  23    23   23  
    B:23  34    20   21  
    C:  15   15   15   15 
    D:  13   13   13   13
    E:  32   30   37   37
    F:  15    10   13  11

    图像3,大小:3888×2592

    A:209  210  211  210
    B:185  188  191  185
    C:  136  134  135  135
    D:  117  118  122  117
    E:  242  240  243  239
    F:  70    69    67    67

    ====

    补充测试:C# 下查表实现(Byte数组)

    G: C#下直接查找Byte数组,相关代码

    static byte[] Rgb2LabSmallTable = new byte[] {
        0,    129,    128, … }

    private unsafe void ConvertToImageLab24Fast(ImageRgb24 img)
    {
        Rgb24* rgbStart = img.Start;
        Rgb24* rgbEnd = img.Start + img.Length;
        while (rgbStart != rgbEnd)
        {
            Rgb24 rgb = *rgbStart;
            int index = (((int)(rgb.Red) >> 4) << 8) + (((int)(rgb.Green) >> 4) << 4) + ((int)(rgb.Blue) >> 4);
            rgbStart->Red = Rgb2LabSmallTable[index];
            rgbStart->Green = Rgb2LabSmallTable[index+1];
            rgbStart->Blue = Rgb2LabSmallTable[index+2];
            rgbStart++;
        }
    }

    测试结果:

    图像2,大小:1845×611

    A:25  23    23   23  
    B:23  34    20   21  
    C:  15   15   15   15 
    D:  13   13   13   13
    E:  32   30   37   37
    F:  15    10   13  11
    G:  12    11   13  11

    图像3,大小:3888×2592

    A:209  210  211  210
    B:185  188  191  185
    C:  136  134  135  135
    D:  117  118  122  117
    E:  242  240  243  239
    F:  70    69    67    67
    G:  64    64    65    64

    ====

    补充测试:同一种实现下的C#和VC性能对比,附下载

    下面消除两种语言的测试区别,C#版本查表时使用指针而非数组,VC下使用无Stride的Rgb24,相关测试代码见 下载链接

    这又形成了4个测试用例:

    H- C#,非查表;I-C#,查表; J-C++,非查表; K-C++,查表

    C# 版为 .Net 4.0, VS2010 ,代码中选择快速一项为测试I,不选择为测试H。

    C++版 - VS2008。选择快速一项为测试K,不选择为测试J。

    测试结果:

    图像2,大小:1845×611

    H: 31  29  36  32
    I:  10  10  10  10
    J:  39  33  33  30
    K:  9    8    8    8

    图像3,大小:3888×2592

    H: 195  194  194  195
    I:  53    52    51    52
    J: 220  218  218  222
    K: 41   42    41   41

    结论:

    C#下图像开发是很给力的!还在犹豫什么呢?

  • 相关阅读:
    黄聪:解决Web部署 svg/woff/woff2字体 404错误
    黄聪:C#中HtmlAgilityPack判断是否包含或不包含指定的属性或值
    黄聪:GeckoFX如何引用jquery文件并执行自定义JS
    黄聪:css3实现图片划过一束光闪过效果(图片光影掠过效果)
    黄聪:C#带cookie模拟登录百度
    黄聪:如何为IIS增加svg和woff等字体格式的MIME
    黄聪:微信支付错误两个问题的解决:curl出错,错误码:60
    黄聪:《跟黄聪学WordPress插件开发》
    黄聪:GeckoWebBrowser多窗口独立cookie
    黄聪:远程连接mysql数据库注意事项记录(远程连接慢skip-name-resolve)
  • 原文地址:https://www.cnblogs.com/xiaotie/p/1934170.html
Copyright © 2020-2023  润新知