<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类相关推荐

  1. 【转】浅析动态代理类实现过程

    代理模式 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 代理模式一般涉及到 ...

  2. 浅析Lock工具类LockSupport

    LockSupport是一个编程工具类,主要是为了阻塞和唤醒线程用的,所有的方法都是静态方法,可以让线程在任意位置阻塞,也可以在任意位置唤醒.使用它我们可以实现很多功能,今天主要就是对这个工具类的讲解 ...

  3. 浅析 Python 的类、继承和多态

    类的定义 假如要定义一个类 Point,表示二维的坐标点: # point.py class Point:def __init__(self, x=0, y=0):self.x, self.y = x ...

  4. 优雅的剥洋葱:浅析 Java Optional 类

    前言 杨宗纬唱过一首歌,叫做<洋葱>,里面有一句是:"如果你愿意一层一层一层的剥开我的心...",歌曲是非常的感人. 其实在咱们程序员日常开发中,也经常会遇见需要一层一 ...

  5. 浅析编程及类的本质、类的抽象

    现阶段编程语言极大丰富,以编程思想大致分为面向过程和面向对象两类,前者以C语言作为代表,编程以过程为中心,符合编程人员的直观逻辑,便于理解:后者有C++.C#.Java等,编程以对象为基本程序结构单元 ...

  6. 浅析医药工业类洁净厂房电气消防设计与应用

    摘要:结合国家相关规范及多年药厂洁净车间的设计经验,就医药工业洁净厂房电气消防设计中的具体做法作了简要论述,指出只有在对规范理解的基础上,以人为本,更新设计观念,才能使电气设计更趋完善. 关键词:医药 ...

  7. 【flink】Flink 1.12.2 源码浅析 : StreamTask 浅析

    1.概述 转载:Flink 1.12.2 源码浅析 : StreamTask 浅析 在Task类的doRun方法中, 首先会构建一个运行环境变量RuntimeEnvironment . 然后会调用lo ...

  8. 【flink】Flink 1.12.2 源码浅析 : yarn-per-job模式解析 yarn 提交过程解析

    1.概述 转载:Flink 1.12.2 源码浅析 : yarn-per-job模式解析 [二] 请大家看原文去. 接上文Flink 1.12.2 源码分析 : yarn-per-job模式浅析 [一 ...

  9. Flink 1.12.2 源码浅析 : yarn-per-job模式解析 [二]

    . 一 .前言 二 .启动解析 2.1. StreamExecutionEnvironment#execute 2.2. StreamExecutionEnvironment#executeAsync ...

最新文章

  1. Linux 命令行的聊天工具 CenterIM
  2. 【EventBus】EventBus 源码解析 ( 事件发送 | postToSubscription 方法 | EventBus 线程模式处理细节 )
  3. node-rsa加密,java解密调试
  4. mysql实时读写_[DataBase] MySql 查看实时日志
  5. r型聚类典型指标_六种GAN评估指标的综合评估实验,迈向定量评估GAN的重要一步...
  6. Linux搭建私有Git服务器以及ssh免密登陆配置
  7. python 导出大量数据到excel_怎么在python中将大量数据导出到Excel文件
  8. mysql递归查询所有上下节点_非递归打印二叉树的所有路径,保存父节点和孩子节点到底有啥差别...
  9. android for vs (三)visual studio android 发布为 apk
  10. oracle 脚本定时,Oracle定时任务备份脚本
  11. 横渡办公室里的银河:一座名为企业智慧屏的桥
  12. NavigationDuplicated: Avoided redundant
  13. ui 名前空間の Aura コンポーネントの廃止
  14. Android Studio3.0对于百度地图SDK的开发(基于方向传感器实现手机朝向显示)
  15. 更多 ViewBinding 的封装思路
  16. leetcode 58. 最后一个单词的长度(Length of Last Word)
  17. 无人驾驶技术入门(五)| 没有视觉传感器,还谈什么无人驾驶?
  18. 一张图分出你是用左脑还是右脑 z
  19. 苹果基带坏了怎么办_iPhone12 上市,苹果这次有哪些改变
  20. has been loaded by xml or sqlprovider

热门文章

  1. 如何下载python编译器,以及python 编译器如何使用 图文详解
  2. Scrapy爬取和讯博客个人博客的信息并写人数据库
  3. 当“中国制造2025”遇见“德国工业4.0”2016装博会上举行中德论坛
  4. 【转】3xian之所在
  5. EAS BOS 单据打开新增界面设置字段默认值
  6. idea Translation翻译插件失效解决办法
  7. 分享:阿里P8架构师深度概述分布式架构
  8. 公司邮箱一般是什么邮箱?专业的电子邮件可以事半功倍
  9. 二进制逻辑运算和基本门电路
  10. 一本通-1309-回文数