0
点赞
收藏
分享

微信扫一扫

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

weipeng2k 2022-05-30 阅读 59

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

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

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

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

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

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

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

 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);

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

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

                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实现:

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

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实现:

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

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         }



 

(3)BitmapData实现:

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

 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,采用浮点计算:

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

  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,对小图像采用浮点计算,对大图像采用查表计算:

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

  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 


 

 

测试代码如下:

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

  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编写,界面如下:

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

可输入执行次数。目前,我机器有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%的性能损失问题不大。


举报

相关推荐

0 条评论