嗨,你好! 下面我们一起来学习呢
Bitmap 类及图像处理 .net 里面的
GDI+ 它提供了对那个图像处理的很
完整的支持,包括了就是各种图片的读取,然后显示,以及各种处理等等 那么主要是通过
Image 这个类来实现的 Image 类呢是一个抽象类,我们平时用得更多的呢是一个
Bitmap 类 Bitmap 类呢实际上就叫位图,也就是我们平时说的三个图像
还有一类呢就是矢量图,包括那种 线条图啊,类似于
Word 里面的剪贴画 那么那里也是图,但这个 Bitmap
呢处理是图片 相当于我们的照片,最常见的是
BMP Jpeg、 GIF、 PNG 这种图片
Bitmap 的使用呢可以呢,一个是
直接有个文件名,这种可以,那直接可以创建一个 Bitmap
对象 这是一种,另外一个呢,我们也可以通过已有的
Bitmap 呢来创建一个 当然还有呢,直接在内存里创建一个
宽和高,就这样一个 Bitmap 也可以。
或者呢 我们可以用 FromFile
这种方式 平时呢,这个图像的显示呢比较简单,我们可以用
DrawImage,Graphics 对象的 DrawImage
就可以画出图像来 当然在画的过程当中呢,我们还有一系列的参数
下面呢我们看一个例子,它是呢用那个 DrawImage
这个函数呢 把一幅图呢生成一个缩略图,请看这里
我们从内存里面创建一个 Bitmap,只 给定了一个宽和高,然后呢从那个
Bitmap 呢 用 FromImage,FromImage 呢,Graphics
对象以 FromImage 呢 创建一个 Graphics 对象,在这个 Graphics
对象我们用 DrawImage 那只不过在画之前呢,我们做了一些设置,比如说
InterpolationMode 设置成一个 HighQuality,就是高质量的这样的一个方式,我们画出来呢
更好一些,当然这个过程呢它实际上要做一些像素的运算,会慢一点
然后我们把这个矩形呢先画成白色 再
DrawImage,那在画的时候呢,可以用一个新的宽度和高度 从而画出一个它的缩略图,这个
Bitmap 呢最后再保存就可以了,保存 save,所以它也很简单。
除了显示图片以外呢,我们还可以做一些更复杂的一些图形的处理 比如说交互式绘图,有一个呢开源的项目呢是一个
paint.net 做出一个既可以绘图的,又可以做图像处理的 这么一个类似于一个简洁版的
Photoshop 所以它的功能呢还是比较全面的,你可以把这个呢下载下来,然后呢去研究一下
那么更一般的那个图像处理呢,我们可以使用对这个图片里的像素进行处理
那么得到那个像素呢一个基本的办法就是用 bitmap.GetPixel,得到一个(x,y)
这个坐标下面的一个像素点 当然得到呢是一个像素点的颜色,但用这种方式呢它会比较慢,就是直接用这种方式比较慢
那我们要解决呢图像处理呢,一般说来,我们都用这个内存里面的指针 来操作。
那 C# 呢是支持指针的,只不过呢我们平时的这些对象呢很少用指针
那么什么时候才用呢?就是类似于这种图像处理直接在内存里面
操作这个内存里的那些像素点,这里面它有一个基本方法 就是
LockBits,就是锁定这些像素的,是相当于这些二进制 位,锁住以后,用
LockBits,得到了一个对象呢叫 bitmapData
这种对象,那这种对象呢它有个基本的属性呢,就是 Scan0,就相当于
第 0 条扫描线的那个位置,可以简单理解为就是第 1
行,或者说呢第一个像素点的那个 在内存里的位置,ToPointer,我们就可以把它呢
转成一个指针,然后强制地转成一个 Byte*
也就是我们就认为呢,这是内存里面这个像素 怎么存的,就可以呢得到
任何一个点,用指针的方式,得到一个,那么 这个指针呢就是
G,G 位置呢就是 pBase,第 0 个像素点 然后加上呢高度乘上
stride,这个 stride 呢是 bitmap 的,bitmapData
的一个 属性,它表示一行,图像里面的一行在内存里面
占的这个字节数,那么一般呢如果我们图像是 24 个红绿,24 位的
也就是红绿蓝三个字节的话,大体上一行呢相当于图像的宽度乘以 3 因为一个点占 3 个字节嘛。
但是呢由于这个 习惯呢是一行里面占的字节数总是 4
的倍数 如果宽度乘以 3,不是 4 的倍数,它会补齐,补几个字节 直到补齐为 4
的倍数为止,所以这个 stride 呢 就表示一行像素点它所占的那个字节数,然后呢再加上
每一个点所占的像素的数,那一般的是
红绿蓝三个字节了,所以,这样的话我们用指针的方式一下就能找到这个像素点
要注意的是呢就是,因为我们使用的指针它是一个非安全的环境
所以编译的时候呢,要选择"允许不安全的代码" 如果是用命令行呢,要加个斜杠
unsafe,那如果是用 Visual Studio 呢
在项目属性里面找到编译 有一个"允许不安全的代码",要打上勾
下面我们看一下这个代码,请看这里 就是如果我们用普通的 bitmap,用普通的
bitmap 呢 循环地得到 GetPixel,得到每一个点
然后每一个点我们遍历一遍,然后它花的时间 我们这里用了一个
Stopwatch 就是相当于跑表,跑表,也就是通过它,我们一开始
start 计时 然后呢把所有像素点遍历完了以后再
stop,我们可以呢 显示出跑表所用的时间
Ticks,或者呢 我们把普通的 bitmap 呢包装成一个
unsafe bitmap 用指针的方式再得到像素点,我们可以比较一下
它用的时间,那么用普通的呢用的时间呢是这个
用我们,用指针的这种方式呢大概是 5 倍左右
GetPixel,那这里呢我们先用 PixelAt
得到这样一个 PixelData 的一个指针,这个 PixelData 呢是一个什么结构呢?
PixelData 呢就是红绿蓝,因为在我们一般的内存呢
红绿蓝,它的这个红是高字节,它是排在后头的,所以 这个
red 是在后头,红绿蓝,也就是 高字节呢是排在末尾的。
然后我们看看这个 GetPixel 这个 GetPixel 呢我们是用一个
PixelAt 这样一个指针在取 然后得到的是这样一个结构体。
那么这个 Pixel At 呢就是我们刚才说的 pBase
指针加上 y,乘上一个 stride 加上 x 乘上 sizeof
(PixelData),也就是 3 了,所以用 这种方式呢要更快一些。
那么图像处理呢我们几乎呢都是用这个更快的 这个指针的处理方式。
在图像处理里面呢一个重要的就是对那个像素进行运算
从而我们得到各种滤镜,类似于滤镜或者各种处理,都是对这个像素 进行运算。
对像素的运算呢有的简单、 有的复杂,比如说我们要把一个图片
变成一个灰度图,那实际上就是把每一个点的颜色呢 变成一个灰的颜色。
那如果我们要做增强或者其他一些处理 那我们就要做各种不同的运算。
这里呢给大家呢一个简单的一个图像处理的一个例子
还有一个复杂版的,那么这个复杂版的这个例子呢是从网上下的,大家可以参考
我们先看看简单的这个,那复杂的是跟这个呢只不过加了各种 这个相当于 Photoshop 的滤镜一样。
程序的运行效果是这样的,有一个菜单,然后我们 打开,打开一个文件,比如说这个文件
然后呢进行处理,这个处理呢我们看
一个是反转,就是把颜色就变了,当然我们可以呢再次反转,又回来了
然后我们可以呢变灰,就是一个彩色图呢变成灰度图了 或者呢我们可以再加亮
加亮的参数呢我们设一个 40
那原先的那个亮度呢就变高了,所以这种呢就是基于这个处理。
我们看看那个代码 看这个代码呢,Invert 就是图像反转
那么图像反转呢当然这里呢都要用这个指针来操作了 在指针环境下,我们都要用
unsafe 来标明这是一个非安全的环境 那这里用的呢基本上就是我们说的指针的方式。
核心的 代码呢就是让它的,让它的这个颜色每一个字节
都变成 255 减去这个点的颜色。
当变灰,我们再看一下 Gray 这个函数,变灰呢实际上呢就得到每一个点的红绿蓝
就是红绿蓝,还是红排到后头的,它是高字节
然后我们再把它变成灰度呢,实际上这三个 红绿蓝的分量呢都变成用原先的红绿蓝的一个公式,这个公式呢
是这样来的,就是原先的红乘以 0.299,原先的绿呢乘以
0.587 然后蓝呢是乘以 0.114,。
这个公式呢当然是一个工业上的一个经验公式,就是说 我们人眼睛经过反复的这个实验呢,大体上看见的
这个灰度,变的灰度呢大体上是红 乘以 0.3 左右,绿呢乘以
0.58 左右 蓝呢乘以 0.14,这个变的灰度呢就基本上跟我们人眼的感觉呢比较相似
所以呢这个呢是按照一个像素点来的 刚才那个呢是按每一个字节,这个呢是按每一个像素点
下面呢我们再看一个加亮,那这个加亮也是 基于一个像素点,那或者是我们基于一个字节
我们可以呢在原先那个像素点的每一个字节的基础上 加上一个常数,然后我们稍微做修正,如果大于呢
255 呢,我们就等于 255 最后呢再把加完的那个值呢放到原先那个位置,所以这样我们得到了一幅图呢
在内存里得到这个图呢它就是一个加亮的。
这几个 算法呢相对比较简单,都是对单点进行运算
那我们复杂的呢,比如说变成这个马赛克效果 那实际上就是要把周围的比如
9 个点,可以变成一个点,取它的颜色的平均值 就作为一个点,所以这样。
那再复杂呢,比如说我们要做增强 那我们可以加上相邻像素点的差,这样就使得我们
原先有加了一个差异,后来使原先的那个差值呢就变得更大了 这种呢就是可以把它某个方向的增强,等等。
那么更复杂的操作呢就有更复杂的运算 所以呢,这就是我们 Photoshop
里面用到的那种更复杂的算子 我们从网上呢下载了一个 C# Filters
这样一个例子呢 它是对像素呢做了更多的一些过滤的处理
我们可以呢演示一下,源代码呢大家自己 可以看了。
这里呢也是文件 node 一个 图,看这里,它就有各种滤镜,包括反转啊,变灰啊
加亮啊,比如说我们这个值加亮,亮度就 增大了。
然后还有各种的这个滤镜,包括颜色的改变啊等等 还有呢各种的,比如说高斯模糊
然后呢还有其他的一些对比度增强等等 然后还有一些边缘检测,边缘检测
比如说水平边缘检测,是使得水平这个方向呢 变得更清楚。
那么它实际上做法呢就是我们用
上下两个点这个差值,然后每一个点呢再加上一个差值 这样使得它更清楚。
还有其它的一些,比如说一些变形等等,比如说这里有反转啊 然后还有呢变化,把它变成一个球形
那么不同的这些滤镜它具有不同的算法 大家也可以呢在这上面呢再做一些自己的算法