编写高效的C#图像处理程序——我的实验(续)

本文是《编写高效的C#图像处理程序——我的实验》的后续实验。

昨天,在wuya的提醒下,仔细检查了下测试代码。发现存在2个问题:

(1)实验对EmguCV和OpenCV不公平,因为,其它测试都是进行处理图像这一个过程,而EmguCV和OpenCV在处理图像之间,需要将Bitmap转换为其内部图像格式IplImage这一过程。因此,今天的测试将这个转换过程提前,不计入执行时间。

(2)翻看OpenCV的实现代码,发现,它执行的是以下算法:

代码
 1 static CvStatus CV_STDCALL
 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* rCache = stackalloc int[256];
                
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实现:

代码
1         /// <summary>
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实现:

代码
1         /// <summary>
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实现:

代码
 1         /// <summary>
 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(00, 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,采用浮点计算:

代码
  1 using System;
  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 { getprivate set; }
 12         public Int32 Length { getprivate set; }
 13         public IntPtr Start { getprivate set; }
 14         public Int32 SizeOfType { getprivate 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.2990.5870.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 == nullthrow 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,对小图像采用浮点计算,对大图像采用查表计算:

代码
  1 using System;
  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.2990.5870.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 == nullthrow 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 

 

 

测试代码如下:

代码
  1 using System;
  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(00, 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 
posted @ 2010-03-09 11:47  xiaotie  阅读(7053)  评论(8编辑  收藏  举报