c#中实现图片灰度化技术详解
去年买了本数字图像处理算法,一直都没有看,前几个星期都一直忙着工作上的活,趁这阶段悠闲点,玩一玩图片处理,这玩意还是非常有意思的。
以前我们在做web上的用户注册时,通常都会做一个验证码,大家都知道用来防止暴力注册的,当然提到验证码大家都知道c#里面有一个bitmap类专门用来处理图片的,好吧,这一篇我们从最简单的“图片灰度化”说起。
一:图片灰度化
我们都知道,位图是由一个一个像素点组成的,像素点可能是红色,橙色,粉色等等,这些颜色我们都知道是用rgb来表示的。
每个颜色分量都是一个字节(0-255),所以一般情况下图的像素点都是24位,当然还有32位,64位,当rgb是0-255之间的不同值时,那么该像素点就呈现“五颜六色”,而当rgb都是相同的值是,则像素点呈现“灰色”,如果大家玩过css的话,肯定都知道给一个字体的color通常都是#999999,#666666,#333333这些不同深度的灰色。
1.计算公式
下面我们该如何设置合理的灰度值呢?当然还是用当前的rgb为模板,然后对rgb乘以一个合理的权重就ok了
gary(i,j)=0.299*r(i,j)+0.587*g(i,j)+0.114*b(i,j);
2.编程
有了公式,实现起来就不成问题了。bitmap类中有一个getpixel/setpixel,它可以获取和设置当前的像素点。
static void main(string[] args) { bitmap bitmap = new bitmap(environment.currentdirectory + "//1.jpg"); for (int i = 0; i < bitmap.width; i++) { for (int j = 0; j < bitmap.height; j++) { //取图片当前的像素点 var color = bitmap.getpixel(i, j); var gray = (int)(color.r * 0.299 + color.g * 0.587 + color.b * 0.114); //重新设置当前的像素点 bitmap.setpixel(i, j, color.fromargb(gray, gray, gray)); } } bitmap.save(environment.currentdirectory + "//2.jpg"); }
3.改进
上面这个方法很简单,get/set就ok了,当然这是我们站在像素点这个角度来考虑问题的,貌似只要o(n2)的时间就可以ko问题,但是get/set远远不是o(1)的,基于性能考虑,我们能不能有更优的方法,此时我们可以站在字节这个角度思考,不过这里我们要注意一个问题就是:比如图片的width=21px,一个像素点占用3个字节,但是21个像素点不一定就占用63个字节,这是因为系统基于性能考虑,在每一行中存放着一个“未用区域”,来确保图片每行的byte数是4的倍数,那么如何去读某一行的字节数呢?
c#里面有一个stride属性就可以用来获取,很简单吧。
static void main(string[] args) { bitmap bitmap = new bitmap(environment.currentdirectory + "//1.jpg"); //定义锁定bitmap的rect的指定范围区域 rectangle rect = new rectangle(0, 0, bitmap.width, bitmap.height); //加锁区域像素 var bitmapdata = bitmap.lockbits(rect, imagelockmode.readwrite, bitmap.pixelformat); //位图的首地址 var ptr = bitmapdata.scan0; //stride:扫描行 int len = bitmapdata.stride * bitmap.height; var bytes = new byte[len]; //锁定区域的像素值copy到byte数组中 marshal.copy(ptr, bytes, 0, len); for (int i = 0; i < bitmap.height; i++) { for (int j = 0; j < bitmap.width * 3; j = j + 3) { var color = bytes[i * bitmapdata.stride + j + 2] * 0.299 + bytes[i * bitmapdata.stride + j + 1] * 0.597 + bytes[i * bitmapdata.stride + j] * 0.114; bytes[i * bitmapdata.stride + j] = bytes[i * bitmapdata.stride + j + 1] = bytes[i * bitmapdata.stride + j + 2] = (byte)color; } } //copy回位图 marshal.copy(bytes, 0, ptr, len); //解锁 bitmap.unlockbits(bitmapdata); bitmap.save(environment.currentdirectory + "//3.jpg"); }