发布我的高性能纯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; 
 
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 { getprivate set; } 
         
public Int32 Length { getprivate set; } 
         
public Int32 SizeOfType { getprivate set; } 
         
public Int32 Width { getprivate set; } 
         
public Int32 Height { getprivate set; } 
         
public IntPtr StartIntPtr { getprivate 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 == nullthrow 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(00, width, height), PixelFormat.Format32bppArgb); 
                     
break
             } 
             Byte
* t = (Byte*)StartIntPtr; 
             BitmapData data 
= newMap.LockBits(new Rectangle(00, 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 == nullthrow 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(00, 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>(thisthis.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#写出性能更高的代码?小弟奉上银鳞胸甲一件!

若标题中有“转载”字样,则本文版权归原作者所有。若无转载字样,本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
posted @ 2010-03-19 13:11 xiaotie 阅读(4363) 评论(31) 编辑 收藏

 回复 引用 查看   
#1楼 2010-03-19 13:12 麒麟      
挑战一下极限。:)
 回复 引用 查看   
#2楼 2010-03-19 13:18 Robin Zhang      
不错,支持一下
 回复 引用 查看   
#3楼 2010-03-19 13:20 toEverybody      
你用了部分非托管的代码,我可以用纯非托管的代码来挑战你吗?我用Delphi或Vc++
 回复 引用 查看   
#4楼[楼主] 2010-03-19 13:23 xiaotie      
@toEverybody
别用Delphi和C++,在文中引用的我上一篇博客已经做了这方面的比较了。需要的是纯C#的比较,这样的话,编程和调试容易得多。这个程序,性能相对纯C要损失20%左右(如果不用特殊的CPU指令)。

 回复 引用 查看   
#5楼 2010-03-19 13:35 诺贝尔      

看来c#的编译器也没啥了不起,居然比c/c++要慢那么多。

 回复 引用 查看   
#6楼 2010-03-19 13:39 第一控制.NET      
最近贼流行挑战极限……
 回复 引用 查看   
#7楼[楼主] 2010-03-19 13:49 xiaotie      
@诺贝尔
不是个鬼啊,安德尔森在打酱油。。。

 回复 引用 查看   
#8楼 2010-03-19 13:59 申飞      
我只要蓝色品质,5G一件。
 回复 引用 查看   
#9楼 2010-03-19 14:11 AutumnWinter      
既然都用unsafe代码了,就直接用C++算了,费这劲干啥?
 回复 引用 查看   
#10楼[楼主] 2010-03-19 14:23 xiaotie      
@AutumnWinter
C++累啊。unsafe就是把C#当类C用,比C++好用。编译速度比C++快多了。前提是数据结构不复杂。复杂的数据结构不如直接上C++。可图像处理,数据结构就是这么简单…… 我是个人开发者,必须保证最大的开发速度,可以为此损失小部分性能。

 回复 引用 查看   
#11楼 2010-03-19 14:23 不得闲      
又见极限挑战!
 回复 引用 查看   
#12楼[楼主] 2010-03-19 14:25 xiaotie      
@不得闲
恩 做了回标题党

 回复 引用 查看   
#13楼 2010-03-19 14:29 不若相忘于江湖      

过来拜拜铁哥.

 回复 引用 查看   
#14楼 2010-03-19 14:36 李鸣(aicken)      
这个Demo纯C#不假,可是和.Net已经没有太大的关系了
 回复 引用 查看   
#15楼[楼主] 2010-03-19 14:41 xiaotie      
@李鸣(aicken)
why?

 回复 引用 查看   
#16楼 2010-03-19 15:04 Kurodo      
老板,给我来件假的
 回复 引用 查看   
#17楼 2010-03-19 15:11 HuangJacky      
大地的母亲在忽悠着你们
 回复 引用 查看   
#18楼 2010-03-19 15:33 伍华聪      
貌似Codeproject上面也有一个用非托管代码处理Bitmap类的文章,楼主与之的关系是。。。?
 回复 引用 查看   
#19楼[楼主] 2010-03-19 15:39 xiaotie      
@伍华聪
无关

 回复 引用 查看   
#20楼 2010-03-19 16:05 新悟空      
目前的解决方案:
C#+专业的影像处理库(C++),在工业实时的影像处理效果很好(开发速度+运行性能均佳)

 回复 引用 查看   
#21楼[楼主] 2010-03-19 16:09 xiaotie      
@新悟空
C#实时性上还是个软肋,不过我想挑战一下。

 回复 引用 查看   
#22楼 2010-03-19 17:17 Bosn Ma      
@诺贝尔
汗死,C#和Java本来就比C++慢,就像汇编本来就比C++快一样。

C#和Java多做了很多东西,自然会慢,这是常识。

 回复 引用 查看   
#23楼[楼主] 2010-03-19 17:20 xiaotie      
@Bosn Ma
使用指针和非托管内存,理论上会编译出和C一样的代码,慢不了20%。一种可能是小安子在偷懒;另一种可能是我的写法还不是最优的,特此发出来请教。

 回复 引用 查看   
#24楼 2010-03-20 12:38 forhells      
引用Bosn Ma:
@诺贝尔
汗死,C#和Java本来就比C++慢,就像汇编本来就比C++快一样。

C#和Java多做了很多东西,自然会慢,这是常识。


也不一定。关键是看编译器实现的如何。实例暂时忘记了。回家翻了告诉你。

 回复 引用 查看   
#25楼 2010-03-21 13:21 blackcat      
你也来玩标题党哈。呵呵。
不过很有技术含量。

 回复 引用 查看   
#26楼 2010-03-24 04:15 侦探在线      
不安全代碼似乎也不能做CPU指令優化。

 回复 引用 查看   
#27楼 2010-04-19 21:13 装配脑袋      
考不考虑用显卡加速一下这个算法?应该能快不少
 回复 引用 查看   
#28楼[楼主] 2010-04-19 22:42 xiaotie      
@装配脑袋
目前集成显卡 :( 换机器了再考虑显卡加速

 回复 引用 查看   
#29楼 2011-01-13 11:21 funnyrain      
学习中,源文件下载后,编译提示无IMetiable定义?!
 回复 引用 查看   
#30楼[楼主] 2011-01-13 12:37 xiaotie      
@funnyrain
已更新。现在应该可以了。

 回复 引用 查看   
#31楼 2011-01-13 16:51 funnyrain      
还是未能编译过: BaseProcessor, IProcessor的定义呢?