将C#图像库的基础部分开源了(https://github.com/xiaotie/GebImage)。这个库比较简单,且离成熟还有一段距离,但它是一种新的开发模式的探索:以指针和非托管内存为主的C#程序开发。
我许多项目都是在这个库基础上的开发,实战证明了它的有效。从今天起,将断断续续(太忙了)的写一系列文章来介绍这种开发方法,介绍基于此的图像编程。本文便是第一篇。
以指针和非托管内存为主的C#程序开发,无论对.Net程序员来说,还是对传统的C/C++程序员来说,均属异类。然而这种方法在很多场景下是非常有效的,尤其是图像编程,所谓谈笑间,樯橹灰飞烟灭,不外如是。
既有C/C++的高性能,又能直接管理内存不给GC带来压力,同时又拥有.net开发的大部分优势,可以快速迭代,何乐而不为呢?
一、简洁优美的代码
本来初稿这节写了好几百字,将C#指针开发与C/C++开发,Java开发、D语言开发等进行对比,阐述理念。不过现在觉得,阐述一个新事物,没有比用例子更直接的了。
例子:打开一张图像,先将它转化为灰度图像,再进行二值化(变成黑白图像),然后进行染色,将白色的像素变成红色。以上每一个过程都弹出窗体显示出来。
代码截图更有视觉冲击力:
像诗歌一样简洁和优美,这就是孤的代码。具备C/C++的高性能和C#的行云流水,同时又有IDE的强大生产力相助,说这些话已属多余,看见这样的代码,更应该想到的是:妹纸,今天工作全部搞定,现在有空吗,哥来接你。
这才是工作,这才是生活。留下时间,看看书,看看漫画,玩玩乐乐。最近在看《偷星九月天》,就拿沧殿来测试这段程序吧:
改编 程序员A:那帮孙子又新提了几百条需求,老大,你要带我们突围吗?
程序员B:不是突围,是杀光它们!
下面,请跟随我,来一段短程探险吧。
(本文中的代码可在 https://github.com/xiaotie/GebImage/tree/develop 处下载。打包下载地址见 集异璧图像与视觉分析库 )
二、C# 指针基础
在C#中使用指针,需要在项目属性中选中“Allow unsafe code”:
接着,还需要在使用指针的代码的上下文中使用unsafe关键字,表明这是一段unsafe代码。
可以用unsafe { } 将代码围住,如:
unsafe
{
new ImageArgb32(path).ShowDialog("原始图像")
.ToGrayscaleImage().ShowDialog("灰度图像")
.ApplyOtsuThreshold().ShowDialog("二值化图像")
.ToImageArgb32()
.ForEach((Argb32* p) => { if (p->Red == 255) *p = Argb32.RED; })
.ShowDialog("染色");
}
也可在方法或属性上加入unsafe关键字,如:
private unsafe void btnSubmit_Click(object sender, EventArgs e)
也可在class或struct 上加上unsafe 关键字,如:
public partial unsafe class FrmDemo1 : Form 指针配合fixed关键字可以操作托管堆上的值类型,如:
public unsafe class Person
{
public int Age;
public void SetAge(int age)
{
fixed (int* p = &Age)
{
*p = age;
}
}
} 指针可以操作栈上的值类型,如:
int age = 0;
int* p = &age;
*p = 20;
MessageBox.Show(p->ToString());
指针也可以操作非托管堆上的内存,如:
IntPtr handle = System.Runtime.InteropServices.Marshal.AllocHGlobal(4);
Int32* p = (Int32*)handle;
*p = 20;
MessageBox.Show(p->ToString());
System.Runtime.InteropServices.Marshal.FreeHGlobal(handle);
System.Runtime.InteropServices.Marshal.AllocHGlobal 用来从非托管堆上分配内存。System.Runtime.InteropServices.Marshal.FreeHGlobal(handle)用来释放从非托管对上分配的内存。这样我们就可以避开GC,自己管理内存了。
三、几种常用用法
1、使用Dispose模式管理非托管内存
如果使用非托管内存,建议用Dispose模式来管理内存,这样做有以下好处: 可以手动dispose来释放内存;可以使用using 关键字开管理内存;即使不释放,当Dispose对象被GC回收时,也会收回内存。
下面是Dispose模式的简单例子:
View Code 1 public unsafe class UnmanagedMemory : IDisposable
2 {
3 public int Count { get; private set; }
4
5 private byte* Handle;
6 private bool _disposed = false;
7
8 public UnmanagedMemory(int bytes)
9 {
10 Handle = (byte*) System.Runtime.InteropServices.Marshal.AllocHGlobal(bytes);
11 Count = bytes;
12 }
13
14 public void Dispose()
15 {
16 Dispose(true);
17 GC.SuppressFinalize(true);
18 }
19
20 protected virtual void Dispose( bool isDisposing )
21 {
22 if (_disposed) return;
23 if (isDisposing)
24 {
25 if (Handle != null)
26 {
27 System.Runtime.InteropServices.Marshal.FreeHGlobal((IntPtr)Handle);
28 }
29 }
30 _disposed = true;
31 }
32
33 ~UnmanagedMemory()
34 {
35 Dispose( false );
36 }
37 } 使用:
using (UnmanagedMemory memory = new UnmanagedMemory(10))
{
int* p = (int*)memory.Handle;
*p = 20;
MessageBox.Show(p->ToString());
}
2、使用 stackalloc 在栈中分配内存
C# 提供了stackalloc 关键字可以直接在栈中分配内存,一般情况下,使用栈内存会比使用堆内存速度快,且栈内存不用担心内存泄漏。下面是例子:
int* p = stackalloc int[10];
for (int i = 0; i Red = 200; }); 用ForEach测试,对100万像素的图像设置Red通道值为200,循环100次,我的测试结果是 400 ms,约是直接循环的 4-5 倍。可见这是个性能不高的操作(其实也够高了,100万象素,循环100遍,耗时400ms),可以在对性能要求不是特别高时使用。
八、与C/C++的比较
我测试了很多场景,C# 下指针性能约是 C/C++ 的 70-80%,性能差距,可以忽略。
相对于C/C++来说,C#无法直接操作硬件是其遗憾,这种情况,可以使用C/C++写段小程序来弥补,不过,我还没遇到这种场景。很多情况都可以P/Invoke解决。
做图像的话,很多时候需要使用显卡加速,如使用CUDA或OpenCL,幸运的是,C#也可以直接写CUDA或OpenCL代码,但是功能可能会受到所用的库的限制。也可以用传统方式写CUDA或OpenCL代码,再P/Invoke调用。如果用传统的C/C++开发的话,也需要做同样的工作。
和C比较:
这套方案比C的抽象程度高,我们有模板,有lambda表达式,还有一大票的语法糖。在类库上,比C的类库完善的多。我们还有反射,有命名空间等等一大票的东西。
和C++比较:
这套方案的抽象程度比C++要低一些。毕竟,值类型无法继承,模板机制比C++ 差一点。但是在生产力上比C++要高很多。抛开C++那一大票陷阱不说,以秒计算的编译速度就够让C++程序员流口水的。当我们在咖啡馆里约会喝咖啡时,C++程序员还正端着一杯咖啡坐在电脑前等待程序编译结束。
九、接下来的工作
接下来的工作主要有两个:
内联工具:C# 的内联还不够强大。需要一个内联工具,对想要内联的方法使用特性标记一下,在编译结束后,在IL代码层面内联。
翻译工具:移动开发是个痛。如何将C#的代码翻译成C/C++的代码,在缺乏.Net的运行时下运行?
这两个工作都不紧要。C#内联效果不好的地方(这种情况很少),可以手动内联。至于移动开发嘛,在哥的一云三端大计中,C# 的定位是云图像开发(C#+CUDA),三端中,桌面运用是用C#和Flash开发,Web和移动应用使用Flash开发,没有C#的事情。
C/C++ 呢?更没有它们的位置啦!不对,还是有的。用它们来开发Flash应用的核心算法!够另类吧!
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |