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");
}
猜你喜欢
- Java提示缺少返回值语句怎么办?这里我们给大家提供具体的解决方法。首先,以下面的程序为例,会看到在控制台有:错误:缺少返回语句的提示。找到
- JMMJMM是指Java内存模型,不是Java内存布局,不是所谓的栈、堆、方法区。每个Java线程都有自己的工作内存。操作数据,首先从主内存
- 文件资源操作Spring 定义了一个 org.springframework.core.io.Resource 接口,Resource 接口
- Android 集成FlutterFlutter 作为 Google 开源的新一代跨平台、高性能 UI 框架,旨在帮助开发者高效地构建出跨平
- 冒泡排序在八大排序中,冒泡排序是最为出名的排序算法之一!冒泡排序的代码还是相当简单的,两层循环,外层是冒泡轮数,里层是依次比较,这个算法的时
- 前言大家都知道,equals和hashcode是java.lang.Object类的两个重要的方法,在实际应用中常常需要重写这两个方法,但至
- 集合嵌套查询和集合嵌套结果的区别嵌套查询是多条sql语句分开写并配置,嵌套结果是一条sql语句关联查询并配置,实质效果是一样的。嵌套语句的查
- 本文实例讲述了java实现mp3合并的方法。分享给大家供大家参考。具体实现方法如下:package test;import java.io.
- 当你在开发flutter应用的时候,有时会需要调用native的api,往往遇到flutter并没有相应的package, 这时候flutt
- java项目中常用maven工具来进行工程管理,但经常遇到的一个问题是生成的jar包越来越大,编译一次工程越来越慢。怎么有效地去除冗余依赖,
- 如下所示: /** * 判断某个界面是否在前台 * * @param context
- 一、java多线程基本入门java多线程编程还是比较重要的,在实际业务开发中经常要遇到这个问题。 java多线程,传统创建线程的方式有两种。
- 前提前段时间在做一个对外的网关项目,涉及到加密和解密模块,这里详细分析解决方案和适用的场景。为了模拟真实的交互场景,先定制一下整个交互流程。
- 一、简介   Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布
- 概述1、邮件相关的标准厂商所提供的 JavaMail 服务程序可以有选择地实现某些邮件协议,常见的邮件协议包括:SMTP(Simple Ma
- 1. 运算符是什么?1.1 定义:对常量和变量进行运算操作的符号程序对数据进行运算时要用运算符1.2 常见运算符的概述1.3 表达式1.3.
- 本文实例为大家分享了java贪吃蛇游戏展示的具体代码,供大家参考,具体内容如下1、采用MVC(model、view、control)框架模式
- 剪贴板是Windows操作系统中最常用的功能之一,它用来从一个应用程序向另一个应用程序传递数据,可以是文本,图象,甚至是程序对象。不过剪贴板
- 效果图片重写DataGridView的OnRowPostPaint方法或者直接在DataGridView的RowPostPaint事件里写,
- 代码如下:/** * 动态生成SQ及SQL参数L * @param ve 接收到的消息的CHGLIST &nbs