作者:xiaotie
转自:http://www.cnblogs.com/xiaotie/archive/2010/03/09/1681476.html
本文是《编写高效的C#图像处理程序——我的实验》的后续实验。
昨天,在wuya的提醒下,仔细检查了下测试代码。发现存在2个问题:
(1)实验对EmguCV和OpenCV不公平,因为,其它测试都是进行处理图像这一个过程,而EmguCV和OpenCV在处理图像之间,需要将Bitmap转换为其内部图像格式IplImage这一过程。因此,今天的测试将这个转换过程提前,不计入执行时间。
(2)翻看OpenCV的实现代码,发现,它执行的是以下算法:

2 icvBGRx2Gray_8u_CnC1R( const uchar * src, int srcstep,
3 uchar * dst, int dststep, CvSize size,
4 int src_cn, int blue_idx )
5 {
6 int i;
7 srcstep -= size.width * src_cn;
8
9 if ( size.width * size.height >= 1024 )
10 {
11 int * tab = ( int * )cvStackAlloc( 256 * 3 * sizeof (tab[ 0 ]) );
12 int r = 0 , g = 0 , b = ( 1 << (csc_shift - 1 ));
13 for ( i = 0 ; i < 256 ; i ++ )
14 {
15 tab[i] = b;
16 tab[i + 256 ] = g;
17 tab[i + 512 ] = r;
18 g += cscGg;
19 if ( ! blue_idx )
20 b += cscGb, r += cscGr;
21 else
22 b += cscGr, r += cscGb;
23 }
24
25 for ( ; size.height -- ; src += srcstep, dst += dststep )
26 {
27 for ( i = 0 ; i < size.width; i ++ , src += src_cn )
28 {
29 int t0 = tab[src[ 0 ]] + tab[src[ 1 ] + 256 ] + tab[src[ 2 ] + 512 ];
30 dst[i] = (uchar)(t0 >> csc_shift);
31 }
32 }
33 }
34 else
35 {
36 for ( ; size.height -- ; src += srcstep, dst += dststep )
37 {
38 for ( i = 0 ; i < size.width; i ++ , src += src_cn )
39 {
40 int t0 = src[blue_idx] * cscGb + src[ 1 ] * cscGg + src[blue_idx ^ 2 ] * cscGr;
41 dst[i] = (uchar)CV_DESCALE(t0, csc_shift);
42 }
43 }
44 }
45 return CV_OK;
46 }
这一算法有什么特点呢?
第一,它不进行浮点计算。它首先将浮点数乘于一个Scale(2的N次幂),转换为整数,计算后再经过 >> 计算,消去这个Scale
第二,对于大图像,它使用的是查表方式而不是乘法方式来计算灰度的。
鉴于此,我改进了C#的实现代码,对小图像,依然采用浮点计算:
*to = (Byte)(p->Red * rCoeff + p->Green * gCoeff + p->Blue * bCoeff);
对大图像,则使用查表计算:

int * gCache = stackalloc int [ 256 ];
int * bCache = stackalloc int [ 256 ];
const int shift = 1 << 10 ;
int rShift = ( int )(rCoeff * shift);
int gShift = ( int )(gCoeff * shift);
int bShift = shift - rShift - gShift;
int r = 0 , g = 0 , b = 0 ;
for ( int i = 0 ; i < 256 ; i ++ )
{
rCache[i] = r;
gCache[i] = g;
bCache[i] = b;
r += rShift;
g += gShift;
b += bShift;
}
while (p != end)
{
* to = (Byte)((rCache[p -> Red] + gCache[p -> Green] + bCache[p -> Red]) >> 10 );
p ++ ;
to ++ ;
}
这样,保证对比的是同一算法。今天的实验,主要比较下面五种图像灰度化方法:
(1)EmguCV实现:

2 /// 使用EmguCv处理图像
3 /// </summary>
4 private static void ProcessImageWithEmgucv(Image < Bgr, Byte > imageSource)
5 {
6 // 灰度
7 Image < Gray, Byte > imageGrayscale = imageSource.Convert < Gray, Byte > ();
8 }
(2)OpenCV/PInvoke实现:

2 /// 使用Open Cv P/Invoke处理图像
3 /// </summary>
4 unsafe private static void ProcessImageWithOpencv(IntPtr ptrSource, Size size)
5 {
6 IntPtr ptrGrayscale = CvInvoke.cvCreateImage(size, IPL_DEPTH.IPL_DEPTH_8U, 1 );
7 CvInvoke.cvCvtColor(ptrSource, ptrGrayscale, COLOR_CONVERSION.CV_BGR2GRAY);
8 }
9
(3)BitmapData实现:

2 /// 将指定图像转换成灰度图
3 /// </summary>
4 /// <param name="bitmapSource"> 源图像支持3通道或者4通道图像,支持Format24bppRgb、Format32bppRgb和Format32bppArgb这3种像素格式 </param>
5 /// <returns> 返回灰度图,如果转化失败,返回null。 </returns>
6 private static Bitmap Grayscale(Bitmap bitmapSource)
7 {
8 Bitmap bitmapGrayscale = null ;
9 if (bitmapSource != null && (bitmapSource.PixelFormat == PixelFormat.Format24bppRgb || bitmapSource.PixelFormat == PixelFormat.Format32bppArgb || bitmapSource.PixelFormat == PixelFormat.Format32bppRgb))
10 {
11 int width = bitmapSource.Width;
12 int height = bitmapSource.Height;
13 Rectangle rect = new Rectangle( 0 , 0 , width, height);
14 bitmapGrayscale = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
15 // 设置调色板
16 ColorPalette palette = bitmapGrayscale.Palette;
17 for ( int i = 0 ; i < palette.Entries.Length; i ++ )
18 palette.Entries[i] = Color.FromArgb( 255 , i, i, i);
19 bitmapGrayscale.Palette = palette;
20 BitmapData dataSource = bitmapSource.LockBits(rect, ImageLockMode.ReadOnly, bitmapSource.PixelFormat);
21 BitmapData dataGrayscale = bitmapGrayscale.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
22 byte b, g, r;
23 int strideSource = dataSource.Stride;
24 int strideGrayscale = dataGrayscale.Stride;
25 unsafe
26 {
27 byte * ptrSource = ( byte * )dataSource.Scan0.ToPointer();
28 byte * ptr1;
29 byte * ptrGrayscale = ( byte * )dataGrayscale.Scan0.ToPointer();
30 byte * ptr2;
31 if (bitmapSource.PixelFormat == PixelFormat.Format24bppRgb)
32 {
33 for ( int row = 0 ; row < height; row ++ )
34 {
35 ptr1 = ptrSource + strideSource * row;
36 ptr2 = ptrGrayscale + strideGrayscale * row;
37 for ( int col = 0 ; col < width; col ++ )
38 {
39 b = * ptr1;
40 ptr1 ++ ;
41 g = * ptr1;
42 ptr1 ++ ;
43 r = * ptr1;
44 ptr1 ++ ;
45 * ptr2 = ( byte )( 0.114 * b + 0.587 * g + 0.299 * r);
46 ptr2 ++ ;
47 }
48 }
49 }
50 else // bitmapSource.PixelFormat == PixelFormat.Format32bppArgb || bitmapSource.PixelFormat == PixelFormat.Format32bppRgb
51 {
52 for ( int row = 0 ; row < height; row ++ )
53 {
54 ptr1 = ptrSource + strideGrayscale * row;
55 ptr2 = ptrGrayscale + strideGrayscale * row;
56 for ( int col = 0 ; col < width; col ++ )
57 {
58 b = * ptr1;
59 ptr1 ++ ;
60 g = * ptr1;
61 ptr1 ++ ;
62 r = * ptr1;
63 ptr1 += 2 ;
64 * ptr2 = ( byte )( 0.114 * b + 0.587 * g + 0.299 * r);
65 ptr2 ++ ;
66 }
67 }
68 }
69 }
70 bitmapGrayscale.UnlockBits(dataGrayscale);
71 bitmapSource.UnlockBits(dataSource);
72 }
73 return bitmapGrayscale;
74 }
(4)我自己的实现,Argb32Image,采用浮点计算:

2 using System.Collections.Generic;
3 using System.Runtime.InteropServices;
4 using System.Text;
5
6 namespace Orc.SmartImage
7 {
8 public class UnmanagedMemory < T > : IDisposable
9 where T : struct
10 {
11 public Int32 ByteCount { get ; private set ; }
12 public Int32 Length { get ; private set ; }
13 public IntPtr Start { get ; private set ; }
14 public Int32 SizeOfType { get ; private set ; }
15
16 public UnmanagedMemory(Int32 length)
17 {
18 Length = length;
19 SizeOfType = SizeOfT();
20 ByteCount = SizeOfType * length;
21 Start = Marshal.AllocHGlobal(ByteCount);
22 }
23
24 public void Dispose()
25 {
26 Dispose( true );
27 GC.SuppressFinalize( this );
28 }
29
30 protected virtual void Dispose( bool disposing)
31 {
32 if ( false == disposed)
33 {
34 disposed = true ;
35 Marshal.FreeHGlobal(Start);
36 }
37 }
38
39 private bool disposed;
40
41 ~ UnmanagedMemory()
42 {
43 Dispose( false );
44 }
45
46 private Int32 SizeOfT()
47 {
48 return Marshal.SizeOf( typeof (T));
49 }
50 }
51 }
52
53
54 using System;
55 using System.Collections.Generic;
56 using System.Drawing;
57 using System.Text;
58
59 namespace Orc.SmartImage.UnmanagedObjects
60 {
61 public struct Argb32
62 {
63 public Byte Alpha;
64 public Byte Red;
65 public Byte Green;
66 public Byte Blue;
67 }
68
69 public class Argb32Image : UnmanagedMemory < Argb32 >
70 {
71 private unsafe Argb32 * m_pointer;
72
73 public unsafe Argb32 * Pointer { get { return m_pointer; } }
74
75 public unsafe Argb32Image( int length)
76 : base (length)
77 {
78 m_pointer = (Argb32 * ) this .Start;
79 }
80
81 public unsafe Argb32 this [ int index]
82 {
83 get { return * (m_pointer + index); }
84 set { * (m_pointer + index) = value; }
85 }
86
87 public Grayscale8Image ToGrayscaleImage()
88 {
89 return ToGrayscaleImage( 0.299 , 0.587 , 0.114 );
90 }
91
92 public unsafe Grayscale8Image ToGrayscaleImage( double rCoeff, double gCoeff, double bCoeff)
93 {
94 Grayscale8Image img = new Grayscale8Image( this .Length);
95 Argb32 * p = Pointer;
96 Byte * to = img.Pointer;
97 Argb32 * end = p + Length;
98
99 while (p != end)
100 {
101 * to = (Byte)(p -> Red * rCoeff + p -> Green * gCoeff + p -> Blue * bCoeff);
102 p ++ ;
103 to ++ ;
104 }
105 return img;
106 }
107
108 public unsafe static Argb32Image CreateFromBitmap(Bitmap map)
109 {
110 if (map == null ) throw new ArgumentNullException( " map " );
111
112 Argb32Image img = new Argb32Image(map.Width * map.Height);
113
114 Argb32 * p = img.Pointer;
115
116 for ( int row = 0 ; row < map.Height; row ++ )
117 {
118 for ( int col = 0 ; col < map.Width; col ++ )
119 {
120 Color c = map.GetPixel(col, row);
121 p -> Alpha = c.A;
122 p -> Red = c.R;
123 p -> Green = c.G;
124 p -> Blue = c.B;
125 p ++ ;
126 }
127 }
128
129 return img;
130 }
131 }
132 }
133
(5)我自己的实现,Rgb24Image,对小图像采用浮点计算,对大图像采用查表计算:

2 using System.Collections.Generic;
3 using System.Drawing;
4 using System.Text;
5
6 namespace Orc.SmartImage.UnmanagedObjects
7 {
8 public struct Rgb24
9 {
10 public Byte Red;
11 public Byte Green;
12 public Byte Blue;
13 }
14
15 public class Rgb24Image : UnmanagedMemory < Rgb24 >
16 {
17 private unsafe Rgb24 * m_pointer;
18
19 public unsafe Rgb24 * Pointer { get { return m_pointer; } }
20
21 public unsafe Rgb24Image( int length)
22 : base (length)
23 {
24 m_pointer = (Rgb24 * ) this .Start;
25 }
26
27 public unsafe Rgb24 this [ int index]
28 {
29 get { return * (m_pointer + index); }
30 set { * (m_pointer + index) = value; }
31 }
32
33 public Grayscale8Image ToGrayscaleImage()
34 {
35 return ToGrayscaleImage( 0.299 , 0.587 , 0.114 );
36 }
37
38 public unsafe Grayscale8Image ToGrayscaleImage( double rCoeff, double gCoeff, double bCoeff)
39 {
40 Grayscale8Image img = new Grayscale8Image( this .Length);
41 Rgb24 * p = Pointer;
42 Byte * to = img.Pointer;
43 Rgb24 * end = p + Length;
44
45 if (Length < 1024 )
46 {
47 while (p != end)
48 {
49 * to = (Byte)(p -> Red * rCoeff + p -> Green * gCoeff + p -> Blue * bCoeff);
50 p ++ ;
51 to ++ ;
52 }
53 }
54 else
55 {
56 int * rCache = stackalloc int [ 256 ];
57 int * gCache = stackalloc int [ 256 ];
58 int * bCache = stackalloc int [ 256 ];
59
60 const int shift = 1 << 10 ;
61 int rShift = ( int )(rCoeff * shift);
62 int gShift = ( int )(gCoeff * shift);
63 int bShift = shift - rShift - gShift;
64
65 int r = 0 , g = 0 , b = 0 ;
66 for ( int i = 0 ; i < 256 ; i ++ )
67 {
68 rCache[i] = r;
69 gCache[i] = g;
70 bCache[i] = b;
71 r += rShift;
72 g += gShift;
73 b += bShift;
74 }
75
76 while (p != end)
77 {
78 * to = (Byte)((rCache[p -> Red] + gCache[p -> Green] + bCache[p -> Red]) >> 10 );
79 p ++ ;
80 to ++ ;
81 }
82 }
83 return img;
84 }
85
86 public unsafe static Rgb24Image CreateFromBitmap(Bitmap map)
87 {
88 if (map == null ) throw new ArgumentNullException( " map " );
89
90 Rgb24Image img = new Rgb24Image(map.Width * map.Height);
91
92 Rgb24 * p = img.Pointer;
93
94 for ( int row = 0 ; row < map.Height; row ++ )
95 {
96 for ( int col = 0 ; col < map.Width; col ++ )
97 {
98 Color c = map.GetPixel(col, row);
99 p -> Red = c.R;
100 p -> Green = c.G;
101 p -> Blue = c.B;
102 p ++ ;
103 }
104 }
105
106 return img;
107 }
108 }
109 }
110
测试代码如下:

2 using System.Collections.Generic;
3 using System.Runtime.InteropServices;
4 using System.Diagnostics;
5 using System.Linq;
6 using System.Text;
7 using System.Drawing;
8 using System.Drawing.Imaging;
9 using Orc.SmartImage;
10 using Emgu.CV;
11 using Emgu.CV.Structure;
12 using Emgu.CV.CvEnum;
13 using Orc.SmartImage.Gpu;
14 using Orc.SmartImage.UnmanagedObjects;
15
16 namespace Orc.SmartImage.PerformanceTest
17 {
18 public class PerformanceTestCase0
19 {
20 public static String Test(Bitmap src, int count)
21 {
22 Argb32Image img32 = Argb32Image.CreateFromBitmap(src);
23 Rgb24Image img24 = Rgb24Image.CreateFromBitmap(src);
24 Image < Bgr, Byte > imageSource = new Image < Bgr, byte > (src);
25 IntPtr ptrSource = Marshal.AllocHGlobal(Marshal.SizeOf( typeof (MIplImage)));
26 Marshal.StructureToPtr(imageSource.MIplImage, ptrSource, true );
27 Size size = imageSource.Size;
28 StringBuilder sb = new StringBuilder();
29 Stopwatch sw = new Stopwatch();
30
31 DoSomething();
32
33 sw.Reset();
34 sw.Start();
35 for ( int i = 0 ; i < count; i ++ )
36 ProcessImageWithEmgucv(imageSource);
37 sw.Stop();
38 sb.AppendLine( " EmguCV: " + sw.ElapsedMilliseconds.ToString());
39
40 DoSomething();
41 sw.Reset();
42 sw.Start();
43 for ( int i = 0 ; i < count; i ++ )
44 ProcessImageWithOpencv(ptrSource, imageSource.Size);
45 sw.Stop();
46 sb.AppendLine( " OpenCV P/Invoke: " + sw.ElapsedMilliseconds.ToString());
47
48 DoSomething();
49 sw.Reset();
50 sw.Start();
51 for ( int i = 0 ; i < count; i ++ )
52 Grayscale(src);
53 sw.Stop();
54 sb.AppendLine( " BitmapData: " + sw.ElapsedMilliseconds.ToString());
55
56 DoSomething();
57 sw.Reset();
58 sw.Start();
59 for ( int i = 0 ; i < count; i ++ )
60 img24.ToGrayscaleImage();
61 sw.Stop();
62 sb.AppendLine( " RgbImage24[use table]: " + sw.ElapsedMilliseconds.ToString());
63
64 DoSomething();
65 sw.Reset();
66 sw.Start();
67 for ( int i = 0 ; i < count; i ++ )
68 img32.ToGrayscaleImage();
69 sw.Stop();
70 sb.AppendLine( " ArgbImage32: " + sw.ElapsedMilliseconds.ToString());
71
72 return sb.ToString();
73 }
74
75 private static int [] DoSomething()
76 {
77 int [] data = new Int32[ 20000000 ];
78 for ( int i = 0 ; i < data.Length; i ++ )
79 {
80 data[i] = i;
81 }
82 return data;
83 }
84
85 /// <summary>
86 /// 使用EmguCv处理图像
87 /// </summary>
88 private static void ProcessImageWithEmgucv(Image < Bgr, Byte > imageSource)
89 {
90 // 灰度
91 Image < Gray, Byte > imageGrayscale = imageSource.Convert < Gray, Byte > ();
92 }
93
94 /// <summary>
95 /// 使用Open Cv P/Invoke处理图像
96 /// </summary>
97 unsafe private static void ProcessImageWithOpencv(IntPtr ptrSource, Size size)
98 {
99 IntPtr ptrGrayscale = CvInvoke.cvCreateImage(size, IPL_DEPTH.IPL_DEPTH_8U, 1 );
100 CvInvoke.cvCvtColor(ptrSource, ptrGrayscale, COLOR_CONVERSION.CV_BGR2GRAY);
101 }
102
103
104
105 /// <summary>
106 /// 将指定图像转换成灰度图
107 /// </summary>
108 /// <param name="bitmapSource"> 源图像支持3通道或者4通道图像,支持Format24bppRgb、Format32bppRgb和Format32bppArgb这3种像素格式 </param>
109 /// <returns> 返回灰度图,如果转化失败,返回null。 </returns>
110 private static Bitmap Grayscale(Bitmap bitmapSource)
111 {
112 Bitmap bitmapGrayscale = null ;
113 if (bitmapSource != null && (bitmapSource.PixelFormat == PixelFormat.Format24bppRgb || bitmapSource.PixelFormat == PixelFormat.Format32bppArgb || bitmapSource.PixelFormat == PixelFormat.Format32bppRgb))
114 {
115 int width = bitmapSource.Width;
116 int height = bitmapSource.Height;
117 Rectangle rect = new Rectangle( 0 , 0 , width, height);
118 bitmapGrayscale = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
119 // 设置调色板
120 ColorPalette palette = bitmapGrayscale.Palette;
121 for ( int i = 0 ; i < palette.Entries.Length; i ++ )
122 palette.Entries[i] = Color.FromArgb( 255 , i, i, i);
123 bitmapGrayscale.Palette = palette;
124 BitmapData dataSource = bitmapSource.LockBits(rect, ImageLockMode.ReadOnly, bitmapSource.PixelFormat);
125 BitmapData dataGrayscale = bitmapGrayscale.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
126 byte b, g, r;
127 int strideSource = dataSource.Stride;
128 int strideGrayscale = dataGrayscale.Stride;
129 unsafe
130 {
131 byte * ptrSource = ( byte * )dataSource.Scan0.ToPointer();
132 byte * ptr1;
133 byte * ptrGrayscale = ( byte * )dataGrayscale.Scan0.ToPointer();
134 byte * ptr2;
135 if (bitmapSource.PixelFormat == PixelFormat.Format24bppRgb)
136 {
137 for ( int row = 0 ; row < height; row ++ )
138 {
139 ptr1 = ptrSource + strideSource * row;
140 ptr2 = ptrGrayscale + strideGrayscale * row;
141 for ( int col = 0 ; col < width; col ++ )
142 {
143 b = * ptr1;
144 ptr1 ++ ;
145 g = * ptr1;
146 ptr1 ++ ;
147 r = * ptr1;
148 ptr1 ++ ;
149 * ptr2 = ( byte )( 0.114 * b + 0.587 * g + 0.299 * r);
150 ptr2 ++ ;
151 }
152 }
153 }
154 else // bitmapSource.PixelFormat == PixelFormat.Format32bppArgb || bitmapSource.PixelFormat == PixelFormat.Format32bppRgb
155 {
156 for ( int row = 0 ; row < height; row ++ )
157 {
158 ptr1 = ptrSource + strideGrayscale * row;
159 ptr2 = ptrGrayscale + strideGrayscale * row;
160 for ( int col = 0 ; col < width; col ++ )
161 {
162 b = * ptr1;
163 ptr1 ++ ;
164 g = * ptr1;
165 ptr1 ++ ;
166 r = * ptr1;
167 ptr1 += 2 ;
168 * ptr2 = ( byte )( 0.114 * b + 0.587 * g + 0.299 * r);
169 ptr2 ++ ;
170 }
171 }
172 }
173 }
174 bitmapGrayscale.UnlockBits(dataGrayscale);
175 bitmapSource.UnlockBits(dataSource);
176 }
177 return bitmapGrayscale;
178 }
179
180 }
181 }
182