编写高效的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彩色空间的像素。

ExpandedBlockStart.gif 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#代码:

ExpandedBlockStart.gif 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#下图像开发是很给力的!还在犹豫什么呢?

转载于:https://www.cnblogs.com/xiaotie/archive/2011/01/13/1934170.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值