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

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

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

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

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

ExpandedBlockStart.gif 代码
 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);

对大图像,则使用查表计算:

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

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

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

ExpandedBlockStart.gif 代码
 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( 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,采用浮点计算:

ExpandedBlockStart.gif 代码
  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 {  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,对小图像采用浮点计算,对大图像采用查表计算:

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

 

 

测试代码如下:

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

 

在每种测试方法运行之前,执行DoSomething(),清空CPU的高速缓存。

 

测试程序采用WinForm编写,界面如下:

image

可输入执行次数。目前,我机器有800M空闲物理内存。下面进行测试:

图片1,分辨率205×581,32位图像,执行50次,结果——

EmguCV:48
OpenCV P/Invoke:30
BitmapData:103
RgbImage24[use table]:38
ArgbImage32:147

再按一下运行,结果——

EmguCV:49
OpenCV P/Invoke:31
BitmapData:107
RgbImage24[use table]:37
ArgbImage32:74

图片2,分辨率864×486,24位图像,执行50次,结果——

EmguCV:131
OpenCV P/Invoke:108
BitmapData:376
RgbImage24[use table]:132
ArgbImage32:258

第二次,

EmguCV:131
OpenCV P/Invoke:107
BitmapData:382
RgbImage24[use table]:130
ArgbImage32:257

图片3,1280×720,24位图像,执行50次,结果——

EmguCV:262
OpenCV P/Invoke:239
BitmapData:822
RgbImage24[use table]:288
ArgbImage32:570

第二次,

EmguCV:280
OpenCV P/Invoke:244
BitmapData:819
RgbImage24[use table]:289
ArgbImage32:568

下面是那张最大的图片 5000×6000的24位图像,执行5次,结果——

内存耗完了(上面的测试都未释放内存),没得到结果。关闭程序,再重新打开,将次数减少为1次,结果——

EmguCV:226
OpenCV P/Invoke:153
BitmapData:553
RgbImage24[use table]:189
ArgbImage32:609

第二次,

EmguCV:155
OpenCV P/Invoke:152
BitmapData:565
RgbImage24[use table]:184
ArgbImage32:745

==================================

下面进行总结(对昨天的结论进行修正):

(1)算法相同、数据结构相同的情况下,C#(采用指针和未托管内存)程序的性能大概是使用P/Invoke调用C程序的1/1.2~1/1.25倍。这个性能降低是可接受的(至少对我而言),并不是很大的性能降低。考虑到GC,性能会再打点折扣,就算是1/1.3吧。

(2)影响C#程序性能的主要因素是所使用的数据结构和算法。上面的测试,同样的功能,采用不同的数据结构和算法,性能差别是2~3倍。有时候可以产生1000倍以上的差异。

==================================

推论:

如果能够认同25%的性能损失,使用C#/非托管内存的优势主要是:快速开发,快速编译,快速测试,维护性好,同时,在非性能关键部分可以使用庞大的library。

==================================

为什么做这一系列实验?

我是想测试一下C#到底适合不适合做图像处理。目前我有几个选择:

(1)matlab。可以快速开发,然很难部署。

(2)OpenCV。速度很快,开发很慢。并且,OpenCV所提供的算法还是很有限。有时需要设计自己的算法,在OpenCV基础上扩展很慢。

(3)EmguCV。它封装了OpenCV。如果只使用既有功能,可以快速开发。一旦扩展,同样问题很大。

(4)AForge.Net。它提供了大量的适用的方法,但是我总感觉还是不好用,并且性能有问题。这个性能问题应该是所使用的算法问题。

所以,还不如写自己的纯C#图像库。因为一般来说一个应用,就只涉及那几个算法,把那几个算法实现就可以了。在C#下实现几个算法再进行研究和扩展,我感觉比在OpenCV下去研究现有算法,再进行扩展还要省事些。只要肯损失25%的性能。而我目前规划了一系列C#图像处理的文章,正好通过这个实验去寻找C#图像处理的最底层的数据结构。而未来多核机器+GPU的广泛应用,这个25%的性能损失问题不大。

转载于:https://www.cnblogs.com/xiaotie/archive/2010/03/09/1681476.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值