本人最近在写一套计算机视觉处理软件,用的就是C# + Emgu,因为用到的OpenCV方法就那么几个(大概10多个),为了这些为数不多的方法而带着数MB的Emgu DLL,心里很是不爽,于是乎萌生了将这些方法全部用C# unsafe代码重写的想法,反正OpenCV是开源的,算法可以写成一样的,效率上应该差不到哪去。
下面是我自己写的一个图像类:
///<summary>灰度图像处理类,作者:wmesci</summary>
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次,取平均数:
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