作者:xiaotie
转自:http://www.cnblogs.com/xiaotie/archive/2010/03/19/1689747.html
前些天写了《编写高效的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#写出性能更高的代码?小弟奉上银鳞胸甲一件!