浅析C#Image类
<span style="font-size: 24px; "> </span><span style="font-size:18px;">进来初学C#,为了让记忆更深刻一点,避免日后需要用时反复做同样的工作,决定把现在查到的一些资料,和自己的一些理解记录下来。</span>
使用C#读图并不困难。Image类中提供了FromFile()函数,可以直接把指定了路径与文件名的图片载入到Image类的各种派生类中;FromFile函数在MSDN中的声明如下:
C#自身提供了Picture Box控件,新建工程选择Windows应用程序,命名为Picture_Viewer,首先在添加控件OpenFileDialog,命名为ofdSelectPicture,然后添加Picture Box,命名为picShowPicture,同时添加button按钮,命名为btnSelectPicture。效果如下:
直接双击button按钮,进入代码段编辑如下代码:
<span style="white-space:pre"> </span>if (ofdSelectPicture.ShowDialog()==DialogResult.OK){picShowPicture.Image = Image.FromFile(ofdSelectPicture.FileName);this.Text = string.Concat("Picture View(" + ofdSelectPicture.FileName + ")");}
效果展示:
对于码农来讲,更重要的应该是对于Image类应该如何使用,对像素值的访问和操作。于是查看MSDN发现:
Image类只提供了 GetType ,ToString 等方法,以及Height ,Width等属性,并未提供对数据成员的操作的方法。但是在Image 的派生类Bitmap类中,GetPixel 和 SetPixel等方法,可以方便的对图像的像素值进行操作。以SetPixel在MSDN中的声明为例:
x和y分表表示像素点的行列坐标,color为要设置的像素值的颜色值。实例如下:
<span style="white-space:pre"> </span>public static bool GetRGB(Bitmap Source, out int[,] R, out int[,] G, out int[,] B){try{int iWidth = Source.Width;int iHeight = Source.Height;// 注意这个地方图像的两维方向与数组两维的方向是转置的关系R = new int[iHeight, iWidth];G = new int[iHeight, iWidth];B = new int[iHeight, iWidth];for (int i = 0; i < iHeight; i++){for (int j = 0; j < iWidth; j++){Color colCurr = Source.GetPixel(j, i);R[i, j] = colCurr.R;G[i, j] = colCurr.G;B[i, j] = colCurr.B;}}return true;}catch (Exception){R = null;G = null;B = null;return false;}}
然而,这种对于像素值的操作看似很简单,很方便,却是以牺牲时间代价为前提的。实验发现,对于一副640*480的图像的操作需要话费1至2分钟,更别提在图像上做其他的处理了。
由于C#托管代码中不能使用指针(经验证,即使是在unsafe代码中用指针操作,速度仍然很慢),因此我们就不能像C++里面一样直接利用指针在内存中完成读写操作。好在.NET Framework提供了托管内存与非托管内存之间的读写功能,一个强大的Marshal类,使得我们快速读写图像的像素信息成为可能。
需要提到的就是Marshal.Copy 方法 ,该方法用于将托管数组复制到非托管内存指针,或者从非托管内存指针复制到托管数组中。Marshal.Copy 的重载方法很多,
我值列出上图中橙色部分在MSDN中的声明:
其中,source 为内存指针,从中进行复制,destination为要复制到的数组,startIndex为数组中COPY开始位置的从零开始的索引,length为要复制的数组中的元素的数目。
然后再来看一下Bitmap类中的LockBits方法,该方法用于将Bitmap锁定到系统内存中,MSDN的声明如下:
其中,rect指定要锁定的Bitmap区域,flags指定Bitmap的访问级别,format指定Bitmap的数据格式。flags的取值如下:
由于LockBits的返回值类型为System.Drawing.Imaging.BitmapData,下面对BitmapData做一下介绍:
要用到的即是Scan0和Stride方法,下面列出用Marshal类对Bitmap数据的操作的代码:
<span style="white-space:pre"> </span>try{int iWidth = Source.Width;int iHeight = Source.Height;Rectangle rect = new Rectangle(0, 0, iWidth, iHeight);System.Drawing.Imaging.BitmapData bmpData = Source.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, Source.PixelFormat);IntPtr iPtr = bmpData.Scan0;int iBytes = iWidth * iHeight * 3;byte[] PixelValues = new byte[iBytes];System.Runtime.InteropServices.Marshal.Copy(iPtr, PixelValues, 0, iBytes);Source.UnlockBits(bmpData);// 注意这个地方图像的两维方向与数组两维的方向是转置的关系R = new int[iHeight, iWidth];G = new int[iHeight, iWidth];B = new int[iHeight, iWidth];int iPoint = 0;for (int i = 0; i < iHeight; i++){for (int j = 0; j < iWidth; j++){// 注意,Windows 中三基色的排列顺序是 BGR 而不是 RGB!B[i, j] = Convert.ToInt32(PixelValues[iPoint++]);G[i, j] = Convert.ToInt32(PixelValues[iPoint++]);R[i, j] = Convert.ToInt32(PixelValues[iPoint++]);}}return true;}catch (Exception){R = null;G = null;B = null;return false;}}
这段代码仅仅适合于像素是24位RGB的图像(8位R、8位B和8位G,不包含Alpha通道,Format24bppRgb),如果要其它像素格式的(可以参见System.Drawing.Imaging.PixelFormat的成员),回写则与之相反,把需要写入的像素信息先写到byte数组中,再调用Marshal.Copy的第一个重载方法即可。代码如下:
参考资料:MSDN、http://ustc.blog.hexun.com/9374788_d.html。
/// <summary>/// 由灰度矩阵创建位图/// </summary>/// <param name="Gray">灰度矩阵(取值0~255)</param>/// <returns>矩阵对应的位图</returns>public static Bitmap FromGray(int[,] Gray){int iWidth = Gray.GetLength(1);int iHeight = Gray.GetLength(0);Bitmap Result=new Bitmap(iWidth,iHeight,System.Drawing.Imaging.PixelFormat.Format24bppRgb);Rectangle rect=new Rectangle(0,0,iWidth,iHeight);System.Drawing.Imaging.BitmapData bmpData = Result.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);IntPtr iPtr = bmpData.Scan0;int iStride = bmpData.Stride;int iBytes = iWidth * iHeight * 3;byte[] PixelValues = new byte[iBytes];int iPoint=0;for (int i=0;i<iHeight;i++)for (int j = 0; j < iWidth; j++){ int iGray = Gray[i, j];PixelValues[iPoint] = PixelValues[iPoint + 1] = PixelValues[iPoint + 2] = Convert.ToByte(iGray);iPoint += 3;}System.Runtime.InteropServices.Marshal.Copy(PixelValues, 0, iPtr, iBytes);Result.UnlockBits(bmpData);return Result;}
浅析C#Image类相关推荐
- 【转】浅析动态代理类实现过程
代理模式 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 代理模式一般涉及到 ...
- 浅析Lock工具类LockSupport
LockSupport是一个编程工具类,主要是为了阻塞和唤醒线程用的,所有的方法都是静态方法,可以让线程在任意位置阻塞,也可以在任意位置唤醒.使用它我们可以实现很多功能,今天主要就是对这个工具类的讲解 ...
- 浅析 Python 的类、继承和多态
类的定义 假如要定义一个类 Point,表示二维的坐标点: # point.py class Point:def __init__(self, x=0, y=0):self.x, self.y = x ...
- 优雅的剥洋葱:浅析 Java Optional 类
前言 杨宗纬唱过一首歌,叫做<洋葱>,里面有一句是:"如果你愿意一层一层一层的剥开我的心...",歌曲是非常的感人. 其实在咱们程序员日常开发中,也经常会遇见需要一层一 ...
- 浅析编程及类的本质、类的抽象
现阶段编程语言极大丰富,以编程思想大致分为面向过程和面向对象两类,前者以C语言作为代表,编程以过程为中心,符合编程人员的直观逻辑,便于理解:后者有C++.C#.Java等,编程以对象为基本程序结构单元 ...
- 浅析医药工业类洁净厂房电气消防设计与应用
摘要:结合国家相关规范及多年药厂洁净车间的设计经验,就医药工业洁净厂房电气消防设计中的具体做法作了简要论述,指出只有在对规范理解的基础上,以人为本,更新设计观念,才能使电气设计更趋完善. 关键词:医药 ...
- 【flink】Flink 1.12.2 源码浅析 : StreamTask 浅析
1.概述 转载:Flink 1.12.2 源码浅析 : StreamTask 浅析 在Task类的doRun方法中, 首先会构建一个运行环境变量RuntimeEnvironment . 然后会调用lo ...
- 【flink】Flink 1.12.2 源码浅析 : yarn-per-job模式解析 yarn 提交过程解析
1.概述 转载:Flink 1.12.2 源码浅析 : yarn-per-job模式解析 [二] 请大家看原文去. 接上文Flink 1.12.2 源码分析 : yarn-per-job模式浅析 [一 ...
- Flink 1.12.2 源码浅析 : yarn-per-job模式解析 [二]
. 一 .前言 二 .启动解析 2.1. StreamExecutionEnvironment#execute 2.2. StreamExecutionEnvironment#executeAsync ...
最新文章
- Linux 命令行的聊天工具 CenterIM
- 【EventBus】EventBus 源码解析 ( 事件发送 | postToSubscription 方法 | EventBus 线程模式处理细节 )
- node-rsa加密,java解密调试
- mysql实时读写_[DataBase] MySql 查看实时日志
- r型聚类典型指标_六种GAN评估指标的综合评估实验,迈向定量评估GAN的重要一步...
- Linux搭建私有Git服务器以及ssh免密登陆配置
- python 导出大量数据到excel_怎么在python中将大量数据导出到Excel文件
- mysql递归查询所有上下节点_非递归打印二叉树的所有路径,保存父节点和孩子节点到底有啥差别...
- android for vs (三)visual studio android 发布为 apk
- oracle 脚本定时,Oracle定时任务备份脚本
- 横渡办公室里的银河:一座名为企业智慧屏的桥
- NavigationDuplicated: Avoided redundant
- ui 名前空間の Aura コンポーネントの廃止
- Android Studio3.0对于百度地图SDK的开发(基于方向传感器实现手机朝向显示)
- 更多 ViewBinding 的封装思路
- leetcode 58. 最后一个单词的长度(Length of Last Word)
- 无人驾驶技术入门(五)| 没有视觉传感器,还谈什么无人驾驶?
- 一张图分出你是用左脑还是右脑 z
- 苹果基带坏了怎么办_iPhone12 上市,苹果这次有哪些改变
- has been loaded by xml or sqlprovider