发布我的高性能纯C#图像处理基本类,顺便也挑战一下极限。:)
前些天写了《编写高效的C#图像处理程序——我的实验》《编写高效的C#图像处理程序——我的实验(续)》,后来,在这两篇文章的基础上,整理了代码,发布在 http://code.google.com/p/smartimage/ ;可以使用SVN通过下面地址:http://smartimage.googlecode.com/svn/trunk/ smartimage-read-only 下载源代码。
其中:Orc.SmartImage.Common 项目是C#图像处理的基础类。Orc.SmartImage.CommonTest 是单元测试(需nunit),Orc.SmartImageLab.PerformanceTest是性能测试。关于这些基础类和EmguCV和OpenCV(P/Invoke)的性能比较见我的博客:《编写高效的C#图像处理程序——我的实验(续)》,项目核心是泛型类 UnmanagedImage.cs
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
namespace Orc.SmartImage
{
public abstract class UnmanagedImage<T> : IDisposable, IEnumerable<T>
where T : struct
{
public Int32 ByteCount { get; private set; }
public Int32 Length { get; private set; }
public Int32 SizeOfType { get; private set; }
public Int32 Width { get; private set; }
public Int32 Height { get; private set; }
public IntPtr StartIntPtr { get; private set; }
private IByteConverter<T> m_converter;
private unsafe Byte* m_start;
public unsafe UnmanagedImage(Int32 width, Int32 height)
{
Width = width;
Height = height;
Length = Width * Height;
SizeOfType = SizeOfT();
ByteCount = SizeOfType * Length;
m_converter = this.CreateByteConverter();
StartIntPtr = Marshal.AllocHGlobal(ByteCount);
m_start = (Byte*)StartIntPtr;
}
public UnmanagedImage(Bitmap map):this(map.Width, map.Height)
{
if (map == null) throw new ArgumentNullException("map");
this.CreateFromBitmap(map);
}
/// <summary>
/// 性能约是指针操作的1/4。不适用于性能要求高的地方。
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public unsafe T this[int index]
{
get
{
T t = new T();
m_converter.Copy(m_start + index * SizeOfType, ref t);
return t;
}
set
{
Byte* to = m_start + index * SizeOfType;
m_converter.Copy(ref value, to);
}
}
/// <summary>
/// 性能约是指针操作的1/4。不适用于性能要求高的地方。
/// </summary>
/// <param name="row"></param>
/// <param name="col"></param>
/// <returns></returns>
public unsafe T this[int row, int col]
{
get
{
T t = new T();
m_converter.Copy(m_start + (row * Width + col) * SizeOfType, ref t);
return t;
}
set
{
Byte* to = m_start + (row * Width + col) * SizeOfType;
m_converter.Copy(ref value, to);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (false == disposed)
{
disposed = true;
Marshal.FreeHGlobal(StartIntPtr);
}
}
private bool disposed;
~UnmanagedImage()
{
Dispose(false);
}
private static Int32 SizeOfT()
{
return Marshal.SizeOf(typeof(T));
}
private unsafe void CreateFromBitmap(Bitmap map)
{
int height = map.Height;
int width = map.Width;
PixelFormat format = map.PixelFormat;
if (this.Width != width || this.Height != height)
{
return;
}
Bitmap newMap = map;
Int32 step = SizeOfT();
switch (format)
{
case PixelFormat.Format24bppRgb:
break;
case PixelFormat.Format32bppArgb:
break;
default:
format = PixelFormat.Format32bppArgb;
newMap = map.Clone(new Rectangle(0, 0, width, height), PixelFormat.Format32bppArgb);
break;
}
Byte* t = (Byte*)StartIntPtr;
BitmapData data = newMap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, format);
try
{
if (format == PixelFormat.Format24bppRgb)
{
Byte* line = (Byte*)data.Scan0;
for (int h = 0; h < height; h++)
{
Rgb24* c = (Rgb24*)line;
for (int w = 0; w < width; w++)
{
m_converter.Copy(c, t);
t += step;
c++;
}
line += data.Stride;
}
}
else
{
Byte* line = (Byte*)data.Scan0;
for (int h = 0; h < height; h++)
{
Argb32* c = (Argb32*)line;
for (int w = 0; w < width; w++)
{
m_converter.Copy(c, t);
t += step;
c++;
}
line += data.Stride;
}
}
}
catch (Exception)
{
throw;
}
finally
{
newMap.UnlockBits(data);
}
}
public unsafe Bitmap ToBitmap()
{
Bitmap map = new Bitmap(this.Width, this.Height, PixelFormat.Format32bppArgb);
ToBitmap(map);
return map;
}
public unsafe void ToBitmap(Bitmap map)
{
if (map == null) throw new ArgumentNullException("map");
if (map.Width != this.Width || map.Height != this.Height)
{
throw new ArgumentException("尺寸不匹配.");
}
if (map.PixelFormat != PixelFormat.Format32bppArgb)
{
throw new ArgumentException("只支持 Format32bppArgb 格式。 ");
}
Int32 step = SizeOfT();
Byte* t = (Byte*)StartIntPtr;
BitmapData data = map.LockBits(new Rectangle(0, 0, map.Width, map.Height), ImageLockMode.ReadWrite, map.PixelFormat);
try
{
int width = map.Width;
int height = map.Height;
Byte* line = (Byte*)data.Scan0;
for (int h = 0; h < height; h++)
{
Argb32* c = (Argb32*)line;
for (int w = 0; w < width; w++)
{
m_converter.Copy(t, c);
t += step;
c++;
}
line += data.Stride;
}
}
finally
{
map.UnlockBits(data);
}
}
protected abstract IByteConverter<T> CreateByteConverter();
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return new ImageEnum<T>(this, this.m_converter);
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
internal class ImageEnum<T> : IEnumerator<T>
where T : struct
{
private UnmanagedImage<T> m_img;
private unsafe Byte* m_start;
private Int32 m_step;
private unsafe Byte* m_end;
private unsafe Byte* m_current;
private IByteConverter<T> m_converter;
public unsafe ImageEnum(UnmanagedImage<T> img, IByteConverter<T> converter)
{
m_img = img;
m_start = (Byte*)m_img.StartIntPtr;
m_step = m_img.SizeOfType;
m_end = m_start + m_step * m_img.Length;
m_current = m_start;
m_converter = converter;
}
#region IEnumerator<T> Members
public unsafe T Current
{
get
{
T t = new T();
m_converter.Copy(m_current, ref t);
return t;
}
}
#endregion
#region IDisposable Members
public void Dispose()
{
}
#endregion
#region IEnumerator Members
object System.Collections.IEnumerator.Current
{
get { return Current; }
}
public unsafe bool MoveNext()
{
m_current += this.m_step;
return m_current < m_end;
}
public unsafe void Reset()
{
m_current = m_start;
}
#endregion
}
}
Argb32Image,GrayscaleImage, ImageU8, Rgb24Image是UnmanagedImage<T>的四个实现。对于具体的图像类,可以直接使用指针进行操作,也可以通过索引器和迭代器进行操作。直接通过指针操作的性能大概是后者的4倍。通过迭代器进行操作不用考虑指针越界问题。通过指针和索引器进行操作需自行判断指针越界的问题。
这几个基本类和Bitmap之间的转换很简单高效,如:
Rgb24Image rgb24 = new Rgb24Image(map);
Bitmap to = rgb24.ToBitmap();
使用这几个类进行图像处理,性能逼近C/C++代码。且使用的是非托管内存,又实现了Dispose模式,不会发生内存泄漏。想要及时释放内存,Dispose一下即可。
==================================
在此挑战一下,哪位兄弟能用C#写出性能更高的代码?小弟奉上银鳞胸甲一件!