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

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是开源的,算法可以写成一样的,效率上应该差不到哪去。


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

  1.  ///<summary>灰度图像处理类,作者:wmesci</summary>  
    ///<summary>灰度图像处理类,作者:wmesci</summary>
  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. }  
    unsafe class Image :CriticalHandle,  IDisposable
    {
        [DllImport("kernel32.dll")]
        static extern IntPtr LocalAlloc(int flags, int size);

        [DllImport("kernel32.dll")]
        static extern IntPtr LocalFree(IntPtr memBlock);

        [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
        static extern unsafe void CopyMemory(byte* dst, byte* src, int count);

        [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
        static extern unsafe void CopyMemory(byte* dst, IntPtr src, int count);

        [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
        static extern unsafe void CopyMemory(byte* dst, byte[] src, int count);

        const byte Max = 255;
        const byte Min = 0;

        public Image(int width, int height) 
            : base(IntPtr.Zero)
        {
            if (width <= 0 || height <= 0)
                throw new ArgumentOutOfRangeException();

            Width = width;
            Height = height;
            Length = Width * Height;
            base.SetHandle(LocalAlloc(0x40, width * height));

            Pointer = (byte*)handle.ToPointer();
        }

        public Image(int width, int height, byte[] dat) 
            : this(width, height)
        {
            if (dat != null)
            {
                CopyMemory(Pointer, dat, Length);
            }
        }

        public Image(int width, int height, byte* dat)
            : this(width, height)
        {
            CopyMemory(Pointer, dat, Length);
        }

        public Image(int width, int height, IntPtr dat)
            : this(width, height)
        {
            CopyMemory(Pointer, dat, Length);
        }

        public readonly int Width;

        public readonly int Height;

        public readonly int Length;

        public readonly byte* Pointer;

        public byte this[int x, int y] 
        {
            get
            {
                return *(Pointer + y * Width + x);
            }
            set
            {
                *(Pointer + y * Width + x) = value;
            }
        }

        public Image Clone()
        {
            return new Image(Width, Height, Pointer);
        }

        public void Add(Image img) 
        {
            Action<int> act = y =>
            {
                byte* p1 = Pointer + y * Width, p2 = (byte*)img.Pointer + y * img.Width;
                for (int x = 0; x < Width; x++, p1++, p2++)
                {
                    double d = *p1 + *p2;
                    if (d < 0)
                        *p1 = 0;
                    else if (d > 255)
                        *p1 = 255;
                    else
                        *p1 = (byte)d;
                }
            };
            Parallel.For(0, Height, act);
        }

        public void Sub(Image img) 
        {
            Action<int> act = y =>
            {
                byte* p1 = Pointer + y * Width, p2 = (byte*)img.Pointer + y * img.Width;
                for (int x = 0; x < Width; x++, p1++, p2++)
                {
                    double d = *p1 - *p2;
                    if (d < 0)
                        *p1 = 0;
                    else if (d > 255)
                        *p1 = 255;
                    else
                        *p1 = (byte)d;
                }
            };
            Parallel.For(0, Height, act);
        }

        public void Mul(Image img, double scale)
        {
            Action<int> act = y =>
            {
                byte* p1 = Pointer + y * Width, p2 = (byte*)img.Pointer + y * img.Width;
                for (int x = 0; x < Width; x++, p1++, p2++)
                {
                    double d = scale * *p1 * *p2;
                    if (d < 0)
                        *p1 = 0;
                    else if (d > 255)
                        *p1 = 255;
                    else
                        *p1 = (byte)d;
                }
            };
            Parallel.For(0, Height, act);
        }

        public void Threshold(byte threshold) 
        {
            Action<int> act = y => 
            {
                byte* p = Pointer + y * Width;
                for (int x = 0; x < Width; x++, p++)
                {
                    *p = *p > threshold ? Max : Min;
                }
            };
            Parallel.For(0, Height, act);
        }

        public void AddWeighted(Image img, double a, double b)
        {
            Action<int> act = y =>
            {
                byte* p1 = this.Pointer + y * this.Width, p2 = (byte*)img.Pointer + y * img.Width;
                for (int x = 0; x < this.Width; x++, p1++, p2++)
                {
                    double d = a * *p1 + b * *p2;
                    if (d < 0)
                        *p1 = 0;
                    else if (d > 255)
                        *p1 = 255;
                    else
                        *p1 = (byte)d;
                }
            };
            Parallel.For(0, this.Height, act);
        }

        public static void Smooth(Image src, Image dst, int n)
        {
            int* tmp = (int*)Marshal.AllocHGlobal(src.Width * src.Height * 4).ToPointer();
            Action<int> act = y =>
            {
                byte* p = src.Pointer + y * src.Width;
                int d = 0;
                for (int i = -n; i <= n; i++)
                {
                    int xx = GetIndex(i, src.Width);

                    d += p[xx];
                }
                tmp[y * src.Width] = d;
            };
            Parallel.For(0, src.Height, act);

            act = y =>
            {
                int i = y * src.Width;
                byte* p = src.Pointer + y * src.Width;
                for (int x = 1; x < src.Width; x++)
                {
                    int d = tmp[i];

                    int x1 = GetIndex(x - n - 1, src.Width);
                    int x2 = GetIndex(x + n, src.Width);

                    d += (p[x2] - p[x1]);

                    tmp[++i] = d;
                }
            };
            Parallel.For(0, src.Height, act);

            double f = 1.0 / (2 * n + 1);
            f *= f;

            act = x =>
            {
                int d = 0;
                byte* p = dst.Pointer + x;
                for (int j = -n; j <= n; j++)
                {
                    int yy = GetIndex(j, src.Height);

                    d += tmp[x + yy * src.Width];
                }
                *p = (byte)(d * f);
                p += src.Width;

                for (int y = 1; y < src.Height; y++, p += src.Width)
                {
                    int y1 = GetIndex(y - n - 1, src.Height);
                    int y2 = GetIndex(y + n, src.Height);

                    d += (tmp[x + y2 * src.Width] - tmp[x + y1 * src.Width]);

                    *p = (byte)(d * f);
                }
            };

            Parallel.For(0, src.Width, act);
            Marshal.FreeHGlobal(new IntPtr(tmp));
        }

        private static int GetIndex(int i, int max)
        {
            if (i < 0) return 0;
            if (i >= max) return max - 1;
            return i;
        }

        public override bool IsInvalid
        {
            get { return handle == IntPtr.Zero; }
        }

        protected override bool ReleaseHandle()
        {
            LocalFree(handle);
            return true;
        }
    }

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


        用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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值