• 玩玩图形图像——第一篇:图片灰度化


        去年买了本数字图像处理算法,一直都没有看,前几个星期都一直忙着工作上的活,趁这阶段悠闲点,玩一玩图片处理,这玩意

    还是非常有意思的。

       以前我们在做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");
            }
    

      

  • 相关阅读:
    「JavaSE 重新出发」05.03.02 在运行时使用反射分析对象
    「JavaSE 重新出发」05.03.01 利用反射分析类
    「JavaSE 重新出发」05.03 反射
    「JavaSE 重新出发」05.02 泛型数组列表、包装类
    scp 命令简明介绍
    《鸟哥的Linux私房菜》笔记——04. 简单命令行
    《鸟哥的Linux私房菜》笔记——03. 磁盘分区
    「JavaSE 重新出发」05.01.02 hashCode 方法、toString 方法
    「JavaSE 重新出发」05.01.01 equals 方法
    「JavaSE 重新出发」05.01 继承
  • 原文地址:https://www.cnblogs.com/huangxincheng/p/2845050.html
Copyright © 2020-2023  润新知