昨天想基于一张图片做个手机锁屏来着,原图如下:

主要是嫌白底太丑了,一开始是想画图工具直接油漆桶伺候,然而一浇上去就发现问题了,变成了这样:

看来得手工处理一下把底色统一了,原图分辨率挺高的,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使用笔记的更多相关文章

  1. C#数字图像处理算法学习笔记(三)--图像几何变换

    C#数字图像处理算法学习笔记(三)--图像几何变换 几何图像处理包括 图像的平移变换,镜像变换,旋转变换,伸缩变换,在这里仅以水平镜像为例,通过代码来理解其基本操作方式: 翻转前:

  2. 项目笔记---CSharp图片处理

    原文:项目笔记---CSharp图片处理 项目笔记---CSharp图片处理 最近由于项目上需要对图片进行二值化处理,就学习了相关的图片处理上的知识,从开始的二值化的意义到动态阀值检测二值化等等,并用 ...

  3. C#数字图像处理算法学习笔记(一)--C#图像处理的3中方法

    C#数字图像处理算法学习笔记(一)--C#图像处理的3中方法 Bitmap类:此类封装了GDI+中的一个位图,次位图有图形图像及其属性的像素数据组成.因此此类是用于处理像素数据定义的图形的对象.该类的 ...

  4. git-简单流程(学习笔记)

    这是阅读廖雪峰的官方网站的笔记,用于自己以后回看 1.进入项目文件夹 初始化一个Git仓库,使用git init命令. 添加文件到Git仓库,分两步: 第一步,使用命令git add <file ...

  5. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  6. SQL Server技术内幕笔记合集

    SQL Server技术内幕笔记合集 发这一篇文章主要是方便大家找到我的笔记入口,方便大家o(∩_∩)o Microsoft SQL Server 6.5 技术内幕 笔记http://www.cnbl ...

  7. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  8. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  9. NET Core-学习笔记(三)

    这里将要和大家分享的是学习总结第三篇:首先感慨一下这周跟随netcore官网学习是遇到的一些问题: a.官网的英文版教程使用的部分nuget包和我当时安装的最新包版本不一致,所以没法按照教材上给出的列 ...

随机推荐

  1. “全栈2019”Java多线程第五章:线程睡眠sleep()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. 洛谷P5280 [ZJOI2019]线段树(线段树)

    题面 传送门 题解 考场上就这么一道会做的其它连暴力都没打--活该爆炸-- 首先我们得看出问题的本质:有\(m\)个操作,总共\(2^m\)种情况分别对应每个操作是否执行,求这\(2^m\)棵线段树上 ...

  3. 2018国庆YALI集训游记

    想了想,像之前那样简略地叙述题意和做法,根本没讲清楚,没有任何意义,还不如写写自己的感受. 感觉YALI真的是一所挺不错的学校吧.总是能有一机房的julao轮番吊打你,总是能有集训队的奆佬来给你出dl ...

  4. Groovy学习记录-------Groovy安装/配置

    1.Groovy SDK下载 Groovy SDK官网下载地址: http://www.groovy-lang.org/download.html  每个版本有五个选项可供下载,依次为: binary ...

  5. [Swift实际操作]七、常见概念-(12)使用DispatchGroup(调度组)管理线程数组

    本文将为你演示调度组的使用,使用调度组可以将多个线程中的人物进行组合管理,可以设置当多个相同层次的任务完成之后,再执行另一项任务. 首先导入需要使用的界面工具框架 import UIKit 在控制台输 ...

  6. Set的总结

    Set最重要的操作是查找,为查找而设计.存入HashSet的元素必须定义hashCode(); Set不保存重复的元素,元素必须唯一.通过equals()方法一确保对象的唯一性. Set中最常被用于归 ...

  7. 写给移动开发者的 React Native 指南

    本文原创版权归 简书 wingjay 所有,如有转载,请于文章篇头位置显示标注原创作者及出处,以示尊重! 作者:wingjay 出处:http://www.jianshu.com/p/b8894425 ...

  8. leetcode-201-数字范围按位与

    题目描述: 给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点). 示例 1: 输入: [5,7] ...

  9. 关于如何用js完成全选反选框等内容

    在学习js过程中按照视频写了这个页面 可以在点上面全选/全不选时全部选中或者取消 在单击下面的单选框时上面的全选会根据下面的单选框进行相应的调整 功能比较完善 以下是代码 <!DOCTYPE h ...

  10. 工具软件推荐——GifCam

    博文里面的动态gif图片都是使用这款软件录制的,可以选择帧率,清晰度,并且编辑每一帧的图片,非常好用,特此推荐~ 注意: GifCam 是一款免费绿色的软件. 大家尽量在官方下载 下载地址. 最新的版 ...