天天看點

高效編寫C#圖像處理程式(3) Rgb=>Lab,圖像缺陷檢測的案例

大家好,有沒有朋友最近項目需要檢測圖像是否存在​​偏色​​、過亮、模糊等缺陷。由于主要用在視訊監控上,對性能要求比較高。有幾項檢測必須要在Lab彩色下進行,而衆所周知Rgb => Lab 計算量較大,C#搞得定搞不定?測試表明,用純C#編寫的Rgb => Lab代碼在性能上與C編寫的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#下圖像開發是很給力的!還在猶豫什麼呢?