[C#]LockBits使用笔记
昨天想基于一张图片做个手机锁屏来着,原图如下:
主要是嫌白底太丑了,一开始是想画图工具直接油漆桶伺候,然而一浇上去就发现问题了,变成了这样:
看来得手工处理一下把底色统一了,原图分辨率挺高的,SetPixel显然会太慢,所以只能LockBits咯。
LockBits的使用方法和参数什么的都可以百度和MSDN,不多说,直接贴一个BitmapWrapper先:
unsafe class BitmapWrapper
{
private readonly Bitmap bmp;
private readonly BitmapData bmpData; private readonly byte* scan0;
private readonly int byteCount; public BitmapWrapper(Bitmap bitmap)
{
bmp = bitmap;
bmpData = bmp.LockBits(
new Rectangle(, , bmp.Width, bmp.Height),
ImageLockMode.ReadWrite,
bmp.PixelFormat); scan0 = (byte*) bmpData.Scan0;
// byteCount = bmpData.Stride / bmpData.Width;
byteCount = bmpData.PixelFormat.ToString().IndexOf("") > ? : ;
}
public Bitmap UnWrapper()
{
bmp.UnlockBits(bmpData);
return bmp;
}
public void SetPixel(Point point, Color color)
{
int offset = (point.X - ) * byteCount + (point.Y - ) * bmpData.Stride;
scan0[offset] = color.B;
scan0[offset + ] = color.G;
scan0[offset + ] = color.R;
if (byteCount == )
scan0[offset + ] = color.A;
}
public Color GetPixel(Point point)
{
int offset = (point.X - ) * byteCount + (point.Y - ) * bmpData.Stride;
Color color = Color.FromArgb(
scan0[offset + ],
scan0[offset + ],
scan0[offset]
);
if (byteCount == )
color = Color.FromArgb(scan0[offset + ], color);
return color;
}
}
注意代码里头有一句注掉了,那里是我出现第一个问题的地方。。。
本来是想计算每一像素占的字节数,那就拿每行的字节数除每一行的像素数咯,于是就错了。。。
MSDN查BitmapData.Stride可以看到备注里面的一句话:
跨距是单行像素(一个扫描行)的宽度,舍入为一个 4 字节的边界。
所以跨距其实应该是等于这样的:Stride = byteCount * Width + ((byteCount * Width) % 4) == 0 ? 0 : (4 - (byteCount * Width) % 4)
于是不知道该怎么反解byteCount,所以用了19行的那个方法,暂时忽略其他情况吧。。。
第二个问题是发生在存取RGB三个byte值的时候。
因为每个像素的RGB三个值是从高位到低位放置的,所以SetPixel里面应该是这样:
scan0[offset] = color.B;
scan0[offset + ] = color.G;
scan0[offset + ] = color.R;
而不是这样:
scan0[offset] = color.R;
scan0[offset + ] = color.G;
scan0[offset + ] = color.B;
第三个问题发生在保存图片的时候。。。本来是这么写的:
bmp.Save("Juven.bmp");
打开图片再用油漆桶,发现还是和原来差不多,底色里面仍然参杂了高度接近纯白的灰色斑点。
因为Save不管你文件扩展名是什么的啊!通通默认Jpeg啊!一压缩就前功尽弃了!所以应该改成这样:
bmp.Save(@"Juven.bmp", ImageFormat.Bmp);
这样就对了,油漆桶后的效果如下(上传前转回jpg了,所以这张图的底色其实还是不纯的):
既然都走到这一步了,就干脆走得远一点,直接代码做成品了:
Bitmap bmp = new Bitmap(src);
BitmapWrapper wrapper = new BitmapWrapper(bmp); byte r, g, b;
for (int y = ; y <= bmp.Height; y++)
{
for (int x = ; x <= bmp.Width; x++)
{
Point point = new Point(x, y);
Color cr = wrapper.GetPixel(point);
if (cr.R + cr.G + cr.B >= )
{
if (x < )
{
r = ;
g = ;
b = ;
}
else if (x > )
{
r = ;
g = ;
b = ;
}
else
r = g = b = ;
wrapper.SetPixel(point, Color.FromArgb(r, g, b));
}
else break;
} for (int x = bmp.Width; x > ; x--)
{
Point point = new Point(x, y);
Color cr = wrapper.GetPixel(point);
if (cr.R + cr.G + cr.B >= )
{
if (x < )
{
r = ;
g = ;
b = ;
}
else if (x > )
{
r = ;
g = ;
b = ;
}
else
r = g = b = ;
wrapper.SetPixel(point, Color.FromArgb(r, g, b));
}
else break;
}
}
57 wrapper.UnWrapper();
bmp.Save(target);
成品图如下:
最后想说的是,巴萨梅球王求轻虐十个以内啊!
[C#]LockBits使用笔记的更多相关文章
- C#数字图像处理算法学习笔记(三)--图像几何变换
C#数字图像处理算法学习笔记(三)--图像几何变换 几何图像处理包括 图像的平移变换,镜像变换,旋转变换,伸缩变换,在这里仅以水平镜像为例,通过代码来理解其基本操作方式: 翻转前:
- 项目笔记---CSharp图片处理
原文:项目笔记---CSharp图片处理 项目笔记---CSharp图片处理 最近由于项目上需要对图片进行二值化处理,就学习了相关的图片处理上的知识,从开始的二值化的意义到动态阀值检测二值化等等,并用 ...
- C#数字图像处理算法学习笔记(一)--C#图像处理的3中方法
C#数字图像处理算法学习笔记(一)--C#图像处理的3中方法 Bitmap类:此类封装了GDI+中的一个位图,次位图有图形图像及其属性的像素数据组成.因此此类是用于处理像素数据定义的图形的对象.该类的 ...
- git-简单流程(学习笔记)
这是阅读廖雪峰的官方网站的笔记,用于自己以后回看 1.进入项目文件夹 初始化一个Git仓库,使用git init命令. 添加文件到Git仓库,分两步: 第一步,使用命令git add <file ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- SQL Server技术内幕笔记合集
SQL Server技术内幕笔记合集 发这一篇文章主要是方便大家找到我的笔记入口,方便大家o(∩_∩)o Microsoft SQL Server 6.5 技术内幕 笔记http://www.cnbl ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- NET Core-学习笔记(三)
这里将要和大家分享的是学习总结第三篇:首先感慨一下这周跟随netcore官网学习是遇到的一些问题: a.官网的英文版教程使用的部分nuget包和我当时安装的最新包版本不一致,所以没法按照教材上给出的列 ...
随机推荐
- LOJ#6047. 「雅礼集训 2017 Day10」决斗(set)
题面 传送门 题解 这么简单一道题我考试的时候居然只打了\(40\)分暴力? 如果我们把每个点的\(a_i\)记为\(deg_i-1\),其中\(deg_i\)表示有\(deg_i\)个数的\(A_i ...
- BZOJ 1834--网络扩容(最大流&费用流)
1834: [ZJOI2010]network 网络扩容 Time Limit: 3 Sec Memory Limit: 64 MBSubmit: 3351 Solved: 1750[Submit ...
- PHP之编写日志文件留后门(免杀)
(我知道你们都喜欢干货,所以也没亏待你们,请到文末吧,成果附件已上传~) 本文原创作者:Laimooc(原名xoanHn) 鄙人宗旨: 本人秉着爱学习爱恶搞爱研究爱进步并且遵纪守法的心态写下这篇文章, ...
- 设置、读取、删除cookie
刚才用虚拟机当服务器,开了两个服务(端口号不同),发现同样的cookie:在别的网站下面没有发现该cookie.说明cookie只是对应相应的网站的(自己得出的结论) ---------------- ...
- flask后端获取前端post/get数据
post:用request.form 而且要加上return !!记着加上return get:用 request.args()就可以了
- python获取aliyun ECS实例
#!/usr/bin/env python #-*- coding:utf-8 -*- # Description : get ecs from aliyun # Author : quke # Da ...
- MongoDB健壮集群——用副本集做分片
1. MongoDB分片+副本集 健壮的集群方案 多个配置服务器 多个mongos服务器 每个片都是副本集 正确设置w 架构图 说明: 1. 此实验环境在一台机器上通过不同port和dbp ...
- 【Quartz】基本原理
1 核心概念 1.1 核心元素 (1)Scheduler 任务调度器,是Quartz框架的核心,负责管理其他组件. (2)Trigger 触发器,用于定义任务调度的时间规则,有SimpleTri ...
- js中数组的操作方法
今天给大家带来一篇有关数组操作方法的文章. 新建数组 方法一:通过new运算符创建一个数组构造函数. var arr = new Array(); 方法二:通过方括号直接创建直接量数组. var ar ...
- Springboot第五篇:结合myBatis进行SQL操作
前提:和之前同样的,本篇会从前端和后台一起讲述关于SQL的select操作(其他操作原理大致类似,不多做解释了). 大致流程:前端通过AJAX将数据发送到后台的路由,后台路由会根据发送的数据进行SQL ...