对比使用C# unsafe代码和OpenCV进行图像处理的效率(上)

来源:互联网 发布:mac 下载不在dock 编辑:程序博客网 时间:2024/05/18 03:09

转 http://blog.csdn.net/wmesci/article/details/7009840


        OpenCV是一套使用C/C++编写的开源计算机视觉库,全称Open Computer Vision,因其高效、全面,在计算机视觉领域应用极广。其在C#下的包装有多种,最常用的是Emgu。


        本人最近在写一套计算机视觉处理软件,用的就是C# + Emgu,因为用到的OpenCV方法就那么几个(大概10多个),为了这些为数不多的方法而带着数MB的Emgu DLL,心里很是不爽,于是乎萌生了将这些方法全部用C# unsafe代码重写的想法,反正OpenCV是开源的,算法可以写成一样的,效率上应该差不到哪去。


下面是我自己写的一个图像类:

[csharp] view plaincopyprint?
  1.  ///<summary>灰度图像处理类,作者:wmesci</summary>  
[csharp] view plaincopyprint?
  1. unsafe class Image :CriticalHandle,  IDisposable  
  2. {  
  3.     [DllImport("kernel32.dll")]  
  4.     static extern IntPtr LocalAlloc(int flags, int size);  
  5.   
  6.     [DllImport("kernel32.dll")]  
  7.     static extern IntPtr LocalFree(IntPtr memBlock);  
  8.   
  9.     [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]  
  10.     static extern unsafe void CopyMemory(byte* dst, byte* src, int count);  
  11.   
  12.     [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]  
  13.     static extern unsafe void CopyMemory(byte* dst, IntPtr src, int count);  
  14.   
  15.     [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]  
  16.     static extern unsafe void CopyMemory(byte* dst, byte[] src, int count);  
  17.   
  18.     const byte Max = 255;  
  19.     const byte Min = 0;  
  20.   
  21.     public Image(int width, int height)   
  22.         : base(IntPtr.Zero)  
  23.     {  
  24.         if (width <= 0 || height <= 0)  
  25.             throw new ArgumentOutOfRangeException();  
  26.   
  27.         Width = width;  
  28.         Height = height;  
  29.         Length = Width * Height;  
  30.         base.SetHandle(LocalAlloc(0x40, width * height));  
  31.   
  32.         Pointer = (byte*)handle.ToPointer();  
  33.     }  
  34.   
  35.     public Image(int width, int height, byte[] dat)   
  36.         : this(width, height)  
  37.     {  
  38.         if (dat != null)  
  39.         {  
  40.             CopyMemory(Pointer, dat, Length);  
  41.         }  
  42.     }  
  43.   
  44.     public Image(int width, int height, byte* dat)  
  45.         : this(width, height)  
  46.     {  
  47.         CopyMemory(Pointer, dat, Length);  
  48.     }  
  49.   
  50.     public Image(int width, int height, IntPtr dat)  
  51.         : this(width, height)  
  52.     {  
  53.         CopyMemory(Pointer, dat, Length);  
  54.     }  
  55.   
  56.     public readonly int Width;  
  57.   
  58.     public readonly int Height;  
  59.   
  60.     public readonly int Length;  
  61.   
  62.     public readonly byte* Pointer;  
  63.   
  64.     public byte this[int x, int y]   
  65.     {  
  66.         get  
  67.         {  
  68.             return *(Pointer + y * Width + x);  
  69.         }  
  70.         set  
  71.         {  
  72.             *(Pointer + y * Width + x) = value;  
  73.         }  
  74.     }  
  75.   
  76.     public Image Clone()  
  77.     {  
  78.         return new Image(Width, Height, Pointer);  
  79.     }  
  80.   
  81.     public void Add(Image img)   
  82.     {  
  83.         Action<int> act = y =>  
  84.         {  
  85.             byte* p1 = Pointer + y * Width, p2 = (byte*)img.Pointer + y * img.Width;  
  86.             for (int x = 0; x < Width; x++, p1++, p2++)  
  87.             {  
  88.                 double d = *p1 + *p2;  
  89.                 if (d < 0)  
  90.                     *p1 = 0;  
  91.                 else if (d > 255)  
  92.                     *p1 = 255;  
  93.                 else  
  94.                     *p1 = (byte)d;  
  95.             }  
  96.         };  
  97.         Parallel.For(0, Height, act);  
  98.     }  
  99.   
  100.     public void Sub(Image img)   
  101.     {  
  102.         Action<int> act = y =>  
  103.         {  
  104.             byte* p1 = Pointer + y * Width, p2 = (byte*)img.Pointer + y * img.Width;  
  105.             for (int x = 0; x < Width; x++, p1++, p2++)  
  106.             {  
  107.                 double d = *p1 - *p2;  
  108.                 if (d < 0)  
  109.                     *p1 = 0;  
  110.                 else if (d > 255)  
  111.                     *p1 = 255;  
  112.                 else  
  113.                     *p1 = (byte)d;  
  114.             }  
  115.         };  
  116.         Parallel.For(0, Height, act);  
  117.     }  
  118.   
  119.     public void Mul(Image img, double scale)  
  120.     {  
  121.         Action<int> act = y =>  
  122.         {  
  123.             byte* p1 = Pointer + y * Width, p2 = (byte*)img.Pointer + y * img.Width;  
  124.             for (int x = 0; x < Width; x++, p1++, p2++)  
  125.             {  
  126.                 double d = scale * *p1 * *p2;  
  127.                 if (d < 0)  
  128.                     *p1 = 0;  
  129.                 else if (d > 255)  
  130.                     *p1 = 255;  
  131.                 else  
  132.                     *p1 = (byte)d;  
  133.             }  
  134.         };  
  135.         Parallel.For(0, Height, act);  
  136.     }  
  137.   
  138.     public void Threshold(byte threshold)   
  139.     {  
  140.         Action<int> act = y =>   
  141.         {  
  142.             byte* p = Pointer + y * Width;  
  143.             for (int x = 0; x < Width; x++, p++)  
  144.             {  
  145.                 *p = *p > threshold ? Max : Min;  
  146.             }  
  147.         };  
  148.         Parallel.For(0, Height, act);  
  149.     }  
  150.   
  151.     public void AddWeighted(Image img, double a, double b)  
  152.     {  
  153.         Action<int> act = y =>  
  154.         {  
  155.             byte* p1 = this.Pointer + y * this.Width, p2 = (byte*)img.Pointer + y * img.Width;  
  156.             for (int x = 0; x < this.Width; x++, p1++, p2++)  
  157.             {  
  158.                 double d = a * *p1 + b * *p2;  
  159.                 if (d < 0)  
  160.                     *p1 = 0;  
  161.                 else if (d > 255)  
  162.                     *p1 = 255;  
  163.                 else  
  164.                     *p1 = (byte)d;  
  165.             }  
  166.         };  
  167.         Parallel.For(0, this.Height, act);  
  168.     }  
  169.   
  170.     public static void Smooth(Image src, Image dst, int n)  
  171.     {  
  172.         int* tmp = (int*)Marshal.AllocHGlobal(src.Width * src.Height * 4).ToPointer();  
  173.         Action<int> act = y =>  
  174.         {  
  175.             byte* p = src.Pointer + y * src.Width;  
  176.             int d = 0;  
  177.             for (int i = -n; i <= n; i++)  
  178.             {  
  179.                 int xx = GetIndex(i, src.Width);  
  180.   
  181.                 d += p[xx];  
  182.             }  
  183.             tmp[y * src.Width] = d;  
  184.         };  
  185.         Parallel.For(0, src.Height, act);  
  186.   
  187.         act = y =>  
  188.         {  
  189.             int i = y * src.Width;  
  190.             byte* p = src.Pointer + y * src.Width;  
  191.             for (int x = 1; x < src.Width; x++)  
  192.             {  
  193.                 int d = tmp[i];  
  194.   
  195.                 int x1 = GetIndex(x - n - 1, src.Width);  
  196.                 int x2 = GetIndex(x + n, src.Width);  
  197.   
  198.                 d += (p[x2] - p[x1]);  
  199.   
  200.                 tmp[++i] = d;  
  201.             }  
  202.         };  
  203.         Parallel.For(0, src.Height, act);  
  204.   
  205.         double f = 1.0 / (2 * n + 1);  
  206.         f *= f;  
  207.   
  208.         act = x =>  
  209.         {  
  210.             int d = 0;  
  211.             byte* p = dst.Pointer + x;  
  212.             for (int j = -n; j <= n; j++)  
  213.             {  
  214.                 int yy = GetIndex(j, src.Height);  
  215.   
  216.                 d += tmp[x + yy * src.Width];  
  217.             }  
  218.             *p = (byte)(d * f);  
  219.             p += src.Width;  
  220.   
  221.             for (int y = 1; y < src.Height; y++, p += src.Width)  
  222.             {  
  223.                 int y1 = GetIndex(y - n - 1, src.Height);  
  224.                 int y2 = GetIndex(y + n, src.Height);  
  225.   
  226.                 d += (tmp[x + y2 * src.Width] - tmp[x + y1 * src.Width]);  
  227.   
  228.                 *p = (byte)(d * f);  
  229.             }  
  230.         };  
  231.   
  232.         Parallel.For(0, src.Width, act);  
  233.         Marshal.FreeHGlobal(new IntPtr(tmp));  
  234.     }  
  235.   
  236.     private static int GetIndex(int i, int max)  
  237.     {  
  238.         if (i < 0) return 0;  
  239.         if (i >= max) return max - 1;  
  240.         return i;  
  241.     }  
  242.   
  243.     public override bool IsInvalid  
  244.     {  
  245.         get { return handle == IntPtr.Zero; }  
  246.     }  
  247.   
  248.     protected override bool ReleaseHandle()  
  249.     {  
  250.         LocalFree(handle);  
  251.         return true;  
  252.     }  
  253. }  

(如有可以优化的地方,烦请指正)


        用WPF写了个简单的测试程序,其中运行时间使用Stopwatch计算,取其ElapsedTicks值。

        先看下运行环境:


        OpenCV使用2.2版本。测试图像大小为600*896,预先进行了灰度化,然后再计算处理时间。


        下面直接上结果:

1、Add:

      Image:imgt.Add(img)

      OpenCV:CvInvoke.cvAdd(img, img, img, IntPtr.Zero)

      各执行50次,取平均数:


Image花费时间3246,OpenCV花费时间1514


2、Sub:

      Image:imgt.Sub(img)

      OpenCV:CvInvoke.cvSub(img, img, img, IntPtr.Zero)

      各执行50次,取平均数:


Image花费时间3378,OpenCV花费时间1370


3、Mul:

      Image:imgt.Mul(img, 1)

      OpenCV:CvInvoke.cvMul(img, img, img, 1)

      各执行50次,取平均数:


Image花费时间3817,OpenCV花费时间7480


4、Threshold:

      Image:imgt.Threshold(120)

      OpenCV:CvInvoke.cvcvThreshold(img, img, 120, 255, Emgu.CV.CvEnum.THRESH.CV_THRESH_BINARY)

      各执行50次,取平均数:


Image花费时间1645,OpenCV花费时间1361


5、Smooth:

      Image:Image.Smooth(img, dst, 3)

      OpenCV:CvInvoke.cvSmooth(img, dst, Emgu.CV.CvEnum.SMOOTH_TYPE.CV_BLUR, 7, 0, 0, 0)

      各执行50次,取平均数:


Image花费时间17589,OpenCV花费时间33574


6、AddWeighted:

      Image:dst.AddWeighted(img, 0.4, 0.4)

      OpenCV:CvInvoke.cvAddWeighted(img, 0.4, img, 0.4, 0, dst)

      各执行50次,取平均数:


Image花费时间3952,OpenCV花费时间9845


总结一下:


从上表可以看出,Image类和OpenCV基本上是胜率对半。至于为什么,且听下回分解~~~


更新在另一台电脑上运行的结果:



Image惨败!!


        源码及测试代码下载地址:http://download.csdn.net/detail/wmesci/3841089

        相关讨论帖:http://topic.csdn.net/u/20111124/23/1F236D07-420E-4E2E-83EE-C9C29E689477.html


0 0