c#中实现图片灰度化技术详解
作者:junjie 发布时间:2023-06-26 22:25:56
去年买了本数字图像处理算法,一直都没有看,前几个星期都一直忙着工作上的活,趁这阶段悠闲点,玩一玩图片处理,这玩意还是非常有意思的。
以前我们在做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");
}


猜你喜欢
- 题目要求为:卡拉兹(Callatz)猜想:对任何一个自然数n,如果它是偶数,那么把它砍掉一半;如果它是奇数,那么把(3n+1)砍掉一半。这样
- 对一个集合中的对象进行排序,根据对象的某个指标的大小进行升序或降序排序。代码如下:进行降序排列 进行降序排列 Co
- 本文实例为大家分享了C#实现飞行棋小游戏的具体代码,供大家参考,具体内容如下逻辑图 以下是掷色子的一个代码,比较有代表性,里面的逻
- 介绍技术之前有用eureka 现在用nacos工作流和gateway接口数据流向数据表新建项目新建cloud-删除src-新建modleEu
- 看到题目后,分析了下, 10的阶乘就已经很大了。计算出来再得到这个末尾的0的个数,完全不现实,即使实现了也是很麻烦的。后来想某个数的阶乘中乘
- 本文实例讲述了C#命令模式。分享给大家供大家参考。具体实现方法如下:using System;using System.Collection
- 注:由于工作需要, 也是第一次接触到打印机的相关内容, 凑巧, 通过找了很多资料和帮助后, 也顺利的解决了打印标签的问题(标签的表面信息[二
- Servlet:在Servlet中拼接html内容JSP:在html中拼接javaJSP+JavaBean:利用javaBean将大量的代码
- kafka消费者kafka-console-consumer接收不到数据发送端接收端问题采用内置的zookeeper,发送端发送数据,接收端
- 有这样一道面试题:如何将SQLite数据库(dictionary.db文件)与apk文件一起发布? 答: 把这个文件
- Spring Aop的原理Spring的AOP就是通过 * 实现的。当为某个Bean或者某些Bean配置切面时,Spring会为其创建代理
- Note:这篇文章是基于Android Studio 3.01版本的,NDK是R16。step1:创建一个包含C++的项目其他默认就可以了。
- 服务端在平台上创建springboot小程序应用创建小程序登录蚂蚁金服开放平台,扫码登录填写信息后,点击支付宝小程序,选择立即接入 >
- 本文实例为大家分享了java实现点击按钮事件弹出子窗口的具体代码,供大家参考,具体内容如下要求:1、在父窗口中添加一个按钮2、点击按钮弹出子
- clone()和Cloneable接口clone顾名思义就是克隆,即,复制一个相等的对象,但是不同的引用地址。我们知道拿到一个对象的地址,只
- 关闭 IDEA 的自动检查更新(截图idea 2020 2.x)idea 右下角会有这样的更新提示2. 关闭 idea 自动检查更新取消勾选
- 一个Java程序的执行要经过编译和执行(解释)这两个步骤,同时Java又是面向对象的编程语言。当子类和父类存在同一个方法,子类重写了父类的方
- 一、ConcurrentBag类ConcurrentBag<T>对外提供的方法没有List<T>那么多,但是同样有E
- select 相当于 for 循环select id from IDArrayLinkedList a = new LinkedList()
- 环境:SpringBoot 2.0.4.RELEASE需求:很多Controller方法,刚进来要先获取当前登录用户的信息,以便做后续的用户