`
touchinsert
  • 浏览: 1376743 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

[转]使用C#进行图像处理的几种方法

 
阅读更多

本文转自:http://conner-wang.spaces.live.com/blog/cns!568D1F7F9D97C059!488.entry本文讨论了C#图像处理中Bitmap类、BitmapData类和unsafe代码的使用以及字节对齐问题。 Bitmap类 命名空间:System.Drawing 封装 GDI+ 位图,此位图由图形图像及其属性的像素数据组成。Bitmap 是用于处理由像素数据定义的图像的对象。 利用C#类进行图像处理,最方便的是使用Bitmap类,使用该类的GetPixel()与SetPixel()来访问图像的每个像素点。下面是MSDN中的示例代码:

public void GetPixel_Example(PaintEventArgs e)
{
// Create a Bitmap object from an image file.
Bitmap myBitmap = new Bitmap("Grapes.jpg");
// Get the color of a pixel within myBitmap.
Color pixelColor = myBitmap.GetPixel(50, 50);
// Fill a rectangle with pixelColor.
SolidBrush pixelBrush = new SolidBrush(pixelColor);
e.Graphics.FillRectangle(pixelBrush, 0, 0, 100, 100);
}
可见,Bitmap类使用一种优雅的方式来操作图像,但是带来的性能的降低却是不可忽略的。比如对一个800*600的彩色图像灰度化,其耗费的时间都要以秒为单位来计算。在实际项目中进行图像处理,这种速度是决对不可忍受的。 BitmapData类 命名空间:System.Drawing.Imaging 指定位图图像的属性。BitmapData 类由 Bitmap 类的 LockBits 和 UnlockBits 方法使用。不可继承。 好在我们还有BitmapData类,通过BitmapData BitmapData LockBits ( )可将 Bitmap 锁定到系统内存中。该类的公共属性有:
  • Width 获取或设置 Bitmap 对象的像素宽度。这也可以看作是一个扫描行中的像素数。
  • Height 获取或设置 Bitmap 对象的像素高度。有时也称作扫描行数。
  • PixelFormat 获取或设置返回此 BitmapData 对象的 Bitmap 对象中像素信息的格式。
  • Scan0 获取或设置位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行。
  • Stride 获取或设置 Bitmap 对象的跨距宽度(也称为扫描宽度)。
下面的MSDN中的示例代码演示了如何使用 PixelFormat、Height、Width 和 Scan0 属性;LockBits 和 UnlockBits 方法;以及 ImageLockMode 枚举。
private void LockUnlockBitsExample(PaintEventArgs e)
{ // Create a new bitmap.
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg"); // Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0; // Declare an array to hold the bytes of the bitmap.
// This code is specific to a bitmap with 24 bits per pixels.
int bytes = bmp.Width * bmp.Height * 3;
byte[] rgbValues = new byte[bytes]; // Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); // Set every red value to 255.
for (int counter = 0; counter < rgbValues.Length; counter+=3)
rgbValues[counter] = 255;
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes); // Unlock the bits.
bmp.UnlockBits(bmpData); // Draw the modified image.
e.Graphics.DrawImage(bmp, 0, 150); }
上面的代码演示了如何用数组的方式来访问一幅图像,而不在使用低效的GetPixel()和SetPixel()。 unsafe代码 而在实际中上面的做法仍然不能满足我们的要求,图像处理是一种运算量比较大的操作,不同于我们写的一般的应用程序。我们需要的是一种性能可以同C++程序相媲美的图像处理程序。C++是怎么提高效率的呢,答曰:指针。幸运的是.Net也允许我们使用指针,只能在非安全代码块中使用指针。何谓非安全代码? 为了保持类型安全,默认情况下,C# 不支持指针运算。不过,通过使用 unsafe 关键字,可以定义可使用指针的不安全上下文。在公共语言运行库 (CLR) 中,不安全代码是指无法验证的代码。C# 中的不安全代码不一定是危险的,只是其安全性无法由 CLR 进行验证的代码。因此,CLR 只对在完全受信任的程序集中的不安全代码执行操作。如果使用不安全代码,由您负责确保您的代码不会引起安全风险或指针错误。不安全代码具有下列属性:
  • 方法、类型和可被定义为不安全的代码块。
  • 在某些情况下,通过移除数组界限检查,不安全代码可提高应用程序的性能。
  • 当调用需要指针的本机函数时,需要使用不安全代码。
  • 使用不安全代码将引起安全风险和稳定性风险。
  • 在 C# 中,为了编译不安全代码,必须用 /unsafe 编译应用程序。
正如《C#语言规范》中所说无论从开发人员还是从用户角度来看,不安全代码事实上都是一种“安全”功能。不安全代码必须用修饰符 unsafe 明确地标记,这样开发人员就不会误用不安全功能,而执行引擎将确保不会在不受信任的环境中执行不安全代码。 以下代码演示如何借助BitmapData类采用指针的方式来遍历一幅图像,这里的unsafe代码块中的代码就是非安全代码。
//创建图像
Bitmap image = new Bitmap( "c:\\images\\image.gif" );
//获取图像的BitmapData对像
BitmapData data = image.LockBits( new Rectangle( 0 , 0 , image.Width , image.Height ) , ImageLockMode.ReadWrite , PixelFormat.Format24bppRgb );
//循环处理
unsafe
{
byte* ptr = ( byte* )( data.Scan0 );
for( int i = 0 ; i < data.Height ; i ++ )
{
for( int j = 0 ; j < data.Width ; j ++ )
{
// write the logic implementation here
ptr += 3;
}
ptr += data.Stride - data.Width * 3;
}
}
毫无疑问,采用这种方式是最快的,所以在实际工程中都是采用指针的方式来访问图像像素的。 字节对齐问题
上例中ptr += data.Stride - data.Width * 3,表示跨过无用的区域,其原因是图像数据在内存中存储时是按4字节对齐的,具体解释如下: 假设有一张图片宽度为6,假设是Format24bppRgb格式的(每像素3字节,在以下的讨论中,除非特别说明,否则Bitmap都被认为是24位RGB)。显然,每一行需要6*3=18个字节存储。对于Bitmap就是如此。但对于BitmapData,虽然data.Width还是等于image.Width,但大概是出于显示性能的考虑,每行的实际的字节数将变成大于等于它的那个离它最近的4的整倍数,此时的实际字节数就是Stride。就此例而言,18不是4的整倍数,而比18大的离18最近的4的倍数是20,所以这个data.Stride = 20。显然,当宽度本身就是4的倍数时,data.Stride = image.Width * 3。 画个图可能更好理解。R、G、B 分别代表3个原色分量字节,BGR就表示一个像素。为了看起来方便我在们每个像素之间插了个空格,实际上是没有的。X表示补足4的倍数而自动插入的字节。为了符合人类的阅读习惯我分行了,其实在计算机内存中应该看成连续的一大段。 |-------Stride-----------|
|-------Width---------| |
Scan0:
BGR BGR BGR BGR BGR BGR XX
BGR BGR BGR BGR BGR BGR XX
BGR BGR BGR BGR BGR BGR XX
.
.
. 首先用data.Scan0找到第0个像素的第0个分量的地址,这个地址指向的是个byte类型,所以当时定义为byte* ptr。行扫描时,在当前指针位置(不妨看成当前像素的第0个颜色分量)连续取出三个值(3个原色分量。注意,0 1 2代表的次序是B G R。在取指针指向的值时,貌似p[n]和p += n再取p[0]是等价的),然后下移3个位置(ptr += 3,看成指到下一个像素的第0个颜色分量)。做过Bitmap.Width次操作后,就到达了Bitmap.Width * 3的位置,应该要跳过图中标记为X的字节了(共有Stride - Width * 3个字节),代码中就是 ptr += dataIn.Stride - dataIn.Width * 3。 通过阅读本文,相信你已经对使用C#进行图像处理可能用到的几种方法有了一个了解。至于采用哪种方式,取决于你的性能要求。其中第一种方式最优雅;第三种方式最快,但不是安全代码;第二种方式取了个折中,保证是安全代码的同时又提高了效率。熟悉C/C++编程的人可能会比较偏向于第三种方式,我个人也比较喜欢第三种方式。 参考: 1. MSDN2005 2. C#语言规范 3. Basic Image Processing support in C# 4. 使用C#的BitmapData 作者:http://conner-wang.spaces.live.com/转载请注明出处!

分享到:
评论

相关推荐

    c#数字图像处理(平滑、修正、锐化、增强等)

    在本文中,我们将深入探讨C#中的数字图像处理技术,主要关注平滑、修正、锐化和增强等关键概念。...而文件"7ba49340eefa45bf84263efb0c55e807"可能包含的就是一个具体的C#图像处理程序实例,可作为学习和研究的起点。

    精通C#数字图像处理算法典型实例05.

    在C#中,数字图像处理是一项关键技能,它涉及到对图像数据的分析、变换和操作,以实现各种视觉效果或进行科学分析。本实例集主要探讨了几个重要的图像处理算法,包括腐蚀、膨胀、开运算、闭运算和击中不击中算法,...

    C#数字图像处理算法典型实例源代码

    C#作为一种强大的编程语言,具有丰富的库和工具支持进行图像处理。本资源"‘C#数字图像处理算法典型实例源代码’"提供了一系列的C#实现的图像处理算法,对于开发者来说是极好的学习资料,可以提升实际开发技能。 ...

    C#数字图像处理算法典型实例](随书光盘)

    C#作为一种现代、面向对象的编程语言,由于其易读性强、库支持丰富,成为了开发图像处理应用的常用工具。本资源是针对《C#数字图像处理算法典型实例》一书的随书光盘代码,旨在帮助读者深入理解和实践图像处理技术。...

    c# c图像处理平台论文

    在图像处理领域,C#提供了System.Drawing命名空间,其中包含了大量用于处理图像的类和方法,如Bitmap、Graphics和ColorMatrix等。这些工具使得开发者能够轻松地进行像素级别的操作,实现诸如图像裁剪、旋转、色彩...

    《C#数字图像处理算法典型实例》(赵春江)源代码

    《C#数字图像处理算法典型实例》是一本深入探讨如何使用C#编程语言进行数字图像处理的书籍。作者赵春江在2009年3月出版此书,旨在为读者提供一系列实用的图像处理算法及其C#实现,帮助开发者理解和掌握图像处理技术...

    C#数字图像处理&lt;七&gt;

    在这个“C#数字图像处理&lt;七&gt;”的主题中,我们将深入探讨图像平滑与去噪的几种常见方法。 图像平滑是图像处理中的一个关键步骤,其主要目的是减少图像中的噪声和干扰,同时尽可能保持图像的主要特征。噪声通常是由...

    图像处理软件源码(C#)

    Emgu CV允许C#开发者使用OpenCV的强大功能,包括图像读取、显示、保存,以及复杂图像处理任务,如特征提取、物体识别、面部检测等。 在这个“图像处理软件源码”中,我们可以期待看到以下几个关键知识点: 1. 图像...

    Visual C#图像处理程序设计实例

    在《Visual C#图像处理程序设计实例》这个主题中,我们深入探讨了使用C#语言进行图像处理的各种技术和方法。C#,作为一种强大的面向对象的编程语言,为开发者提供了丰富的库和工具,使得图像处理变得相对简单且高效...

    C#数字图像处理(2)

    在C#中进行数字图像处理,主要涉及以下几个关键知识点: 1. **基础概念**:首先,我们需要理解图像的基本构成,包括像素、色彩模型(如RGB、CMYK等)、图像格式(如JPEG、PNG、BMP等)。这些基础知识是进行图像处理...

    c#常见错误处理的几种方法

    C# 常见错误处理的几种方法 C# 中的错误处理是编程中不可或缺的一部分,它能够帮助开发者检测和解决程序中的错误。在 C# 中,错误处理是通过 try-catch 块来实现的,try 块中包含可能出现错误的代码,而 catch 块中...

    c#图像处理程序源码

    总之,"c#图像处理程序源码"是一个宝贵的资源,适合初学者和有经验的开发者学习和参考,可以帮助他们在C#环境下构建自己的图像处理工具或应用,进一步提升其编程技能。无论是为了个人兴趣还是专业发展,这个项目都是...

    C# 多种方法对图像进行平滑去噪

    本文将深入探讨几种常见的C#实现的图像平滑去噪技术,包括中值滤波、灰度形态学滤波、小波变换去噪、高斯低通滤波以及统计滤波。 首先,我们来看**中值滤波**。中值滤波是一种非线性的降噪方法,特别适合去除椒盐...

    C#图像处理 C#图片倒影 C#水波纹

    在C#编程环境中,图像处理是一项重要的功能,它允许开发者对图像进行各种操作,包括创建特效、修改像素、转换格式等。在这个特定的场景中,我们关注的是如何使用C#来实现图片的倒影效果以及水波纹效果。这两种效果在...

    C#数字图像处理<九>

    "C#数字图像处理&lt;九&gt;"着重探讨了如何使用C#进行图像分割,这是计算机视觉中的核心任务,目的是将图像划分为具有不同属性的区域,以便进一步分析和理解。以下将详细阐述图像分割的基本概念、常用方法以及与VS2010和C#...

    C# 数字图像处理(锐化,半色调等功能)

    本项目主要关注了几个关键的图像处理技术:锐化、半色调处理以及直方图均衡化。这些技术在图像增强、视觉效果改进以及图像分析等领域有着广泛应用。 1. **锐化**: 锐化是提高图像边缘对比度的过程,使得图像细节...

    C#图形图像处理程序

    本文将深入探讨C#中的图形图像处理技术,包括如何打开和保存图像,实现图像效果如黑白、浮雕和灰度,以及如何进行基本的绘图操作如添加水印、画直线和曲线。 首先,C#提供了System.Drawing命名空间,它是进行图像...

    C#将图像转换为ASCII字符画

    在IT领域,将图像转换为ASCII字符画是一种有趣的技术,它利用不同的ASCII字符来近似地表示图像的颜色和...通过理解和实践这些步骤,开发者可以创建出独特且有趣的字符画作品,同时也加深了对图像处理和字符编码的理解。

    C#实现图像变形(扭曲、水波纹、B样条)

    在图像处理领域,C#是一种常用的编程语言,用于创建各种图像操作和效果。在这个特定的项目中,"C#实现图像变形(扭曲、水波纹、B样条)"涉及了几个关键知识点,包括基本的图像操作、图像变形算法、数学变换以及图形...

    C#实现各种图像模糊、锐化、色彩增强

    在C#中,有几种常见的模糊方法: 1. **高斯滤波**:高斯滤波是一种线性平滑滤波,通过应用高斯函数权重对图像像素进行加权平均,有效地降低了高频噪声。C#中可以使用二维卷积实现,通过定义一个高斯核矩阵并遍历每...

Global site tag (gtag.js) - Google Analytics