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

来源:https://www.cnblogs.com/valveszhishi/archive/2022/08/01/16540275.html
-Advertisement-
Play Games

前言 接著上周寫的截圖控制項繼續更新 繪製箭頭。 1.WPF實現截屏「仿微信」 2.WPF 實現截屏控制項之移動(二)「仿微信」 3.WPF 截圖控制項之伸縮(三) 「仿微信」 4.WPF 截圖控制項之繪製方框與橢圓(四) 「仿微信」 正文 一、首先接著ScreenCut繼續發電。 1)繪製箭頭因為需要只修 ...


大家好,有沒有朋友最近項目需要檢測圖像是否存在​​偏色​​、過亮、模糊等缺陷。由於主要用在視頻監控上,對性能要求比較高。有幾項檢測必須要在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#下圖像開發是很給力的!還在猶豫什麼呢?


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 面向對象03 10.抽象類 abstract修飾符可以用來修飾方法也可以修飾類,如果修飾方法,那麼該方法就是抽象方法;如果修飾類,那麼該類就是抽象類 抽象類中可以沒有抽象方法,但是有抽象方法的類一定要聲明為抽象類 抽象類,不能使用new關鍵字來創建對象,它是用來讓子類繼承的 抽象方法,只有方法的聲明 ...
  • indexOf和subString取值走向圖;首先簡單介紹這者的作用,具體可以看官方API 1.indexOf的作用: 就是獲取某個字元串的其中具體一個字元的位置 2.subString的作用: 就是大家熟悉的切割,從那裡到那裡比如,從第一個到第五個等;當這個方法是重載的,不只有我說的這個方法;具體 ...
  • 明敏 曉查 發自 凹非寺 量子位 報道 | 公眾號 QbitAI 程式 bug 也能負負得正嗎? 還真可以。 比如程式員們再熟悉不過的排序演算法,通過兩個“bug”居然能歪打正著,實在令人匪夷所思。 請看這位程式員寫的數組升序排序代碼: for i = 1 to n do for j = 1 to n ...
  • 基礎確認:HTML、CSS、JavaScript AJAX可以: 不刷新頁面更新網頁 在頁面載入後從伺服器請求數據 在頁面載入後從伺服器接收數據 在後臺向伺服器發送數據 Ajax 的核心是 XMLHttpRequest 對象,用於和伺服器交換數據。 xmlhttp.open("GET","ajax_ ...
  • 多商戶商城系統,也稱為B2B2C(BBC)平臺電商模式多商家商城系統。可以快速幫助企業搭建類似拼多多/京東/天貓/淘寶的綜合商城。 多商戶商城系統支持商家入駐加盟,同時滿足平臺自營、旗艦店等多種經營方式。平臺可以通過收取商家入駐費,訂單交易服務費,提現手續費,簡訊通道費等多手段方式,實現整體盈利。 ...
  • 3 hashCode的內幕 tips:面試常問/常用/常出錯 hashCode到底是什麼?是不是對象的記憶體地址? 1) 直接用記憶體地址? 目標:通過一個Demo驗證這個hasCode到底是不是記憶體地址 public native int hashCode(); com.hashcode.HashCo ...
  • 函數 封裝功能,提高應用的模塊性和代碼重用率 1. 定義函數 1.1 函數內的第一條語句是字元串時,該字元串就是文檔字元串(docstring) def my_fun(): '''我是文檔字元串 函數內第一句是字元串時 該字元串就是文檔字元串 ''' pass print(my_fun.__doc_ ...
  • 前言 做了幾道關於defer的測試題,嚇了一大跳,感覺自己之前的理解有些問題,所以寫下這篇博客,加深下印象。 正文: 多個defer的執行順序: 先進後出,類似於棧的特性。 下麵我們來測試下: 1.defer 與 panic: func deferAndPanic() { defer func() ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...