C#图像处理程序实现--灰度处理以及Laplace处理 笔记整理1

一直想研究一下图像处理,这一阵在憋在家中正好有时间来学习图像处理,希望能够入图像处理的门,开始一路下来在论坛上各种看各种找,所找的基本上七七八八,但是感觉总是缺点什么,经过导师的一番交流和学习下来,感觉收获颇多,遂整理记录,同时征得导师同意,将源码也发送出来,供大家学习和交流。在这个过程中,使用VS2017 Community 版本,C#语言编写,刚好也乘此机会把这个软件的使用学习一下。

1、图片灰度处理

从开始说吧,找到一幅图片,对其进行灰度处理(新手可以参考,我这里使用的方法相对简单,用了最原始的方法,getpxiel函数。效率低,但是贵在对新手来说,容易理解)

在这里插入代码片
        StringBuilder sbAfGrey = new StringBuilder();
        StringBuilder sbAfGreyAndConvlution = new StringBuilder();
        
            richTextBox1.Text = "图片灰度处理后的值:\n";
            richTextBox2.Text = "图片灰度处理Laplace的值:\n";
            Bitmap bitmap1 = new Bitmap(Properties.Resources.image_1);
            pictureBox1.Image = bitmap1;

            int[,] data = new int[bitmap1.Width, bitmap1.Height];
            Bitmap greyBitmap = new Bitmap(Properties.Resources.image_1);
            for (int i = 0; i < bitmap1.Width; i++)
            {
                int num = 0;
                sbAfGrey.Append("第" + i.ToString() + "行:\n");
                for (int j = 0; j < bitmap1.Height; j++)
                {
                    Color pxielValue = bitmap1.GetPixel(i, j);
                    int grey = (int)(0.3 * pxielValue.R + 0.58 * pxielValue.G + 0.11 * pxielValue.B);
                    data[i, j] = grey;                    
                    sbAfGrey.Append(grey.ToString()+" ");
                    //将获取的二维数组转化为灰度图显示(也就是将新的图片中的对应像素的RGB值全部设置为灰度图像数值)
                    greyBitmap.SetPixel(i, j, Color.FromArgb(data[i, j], data[i, j], data[i, j]));
                    num++;
                }
                sbAfGrey.Append("\n第" + i.ToString() + "行共有:" + num.ToString() + "个数\n");
            }
            richTextBox1.Text = richTextBox1.Text + sbAfGrey + "\n"+"该图片灰度值共有: "+(greyBitmap.Width*greyBitmap.Height).ToString()+"个\n"+"其中:\n"+greyBitmap.Width.ToString()+"列\n" + greyBitmap.Height.ToString() + "行\n";//显示图片灰度处理后的数据            
            pictureBox2.Image = greyBitmap;

灰度处理效果如下图:(刚开始编程是为了验证数据的准确性,在下方添加了一个richtextbox来显示图片灰度处理后的灰度值。
这个过程对新手来说还是挺有意思,你可以把任何你想看到的数据通过文本框显示出来,在验证思路正确性的同时,可以极大地提高新手的自信心。
同时还可以尝试在代码的不同位置添加文本显示内容,然后在观察运行后显示出来的数据是否和自己设想的一致,这样可以进一步帮助理解程序运行的过程)

灰度处理效果图
里面核心的代码就是下面一小段(取出当前点RGB的值,套用灰度转换公式,然后将此灰度值放到之前建立好的保存灰度值的二维数组中,并重新赋值给灰度处理后的图片的对应点的灰度值即可):

在这里插入代码片
Color pxielValue = bitmap1.GetPixel(i, j);
int grey = (int)(0.3 * pxielValue.R + 0.58 * pxielValue.G + 0.11 * pxielValue.B);
data[i, j] = grey;

另外,有个小技巧,刚开始学习的新手,知道的命令不多,可能对于字符串操作就会用string命令,但是当用到频繁读取和写入的时候要学会使用stringbuilder函数,可以大大大的缩短程序响应时间:

在这里插入代码片
StringBuilder sbAfGrey = new StringBuilder();
StringBuilder sbAfGreyAndConvlution = new StringBuilder();

刚开始我这里使用的string命令,导致的结果就是程序不报错,但是也不弹出界面,就在那里一直等待,如下图:
在这里插入图片描述
其实这里是在做运算,但是使用string命令的效率实在是太慢,但是我这里要处理的数据量又非常大,导致计算机一直在计算,甚至可能会因此宕机。(这一点对经验丰富的编程人员说一看便知,但是对于道行尚浅的菜鸟,就会以为是不是哪里出了问题,走弯路。。。。)
其实经过这个简单的过程就可以做到将图像灰度处理了,是不是感觉很简单 ^_^
(我上传的资源中附有详细代码,需要的可以自行下载调试)

2、图片做Laplace处理
说这个之前,我们先来说一个高大上的词“卷积”。对于卷积的理解大家可以参考论坛中很多大神的解释,我这里推荐一个我看到的综合各种讲解比较多的一篇博文:最容易理解的对卷积(convolution)的解释讲的挺好,尤其是最后的那个比较血腥的案例讲解~~~~~~

好了,通过以上基本就大概明白卷积是干嘛的了,从目前我这里后面要说的简化的描述一下卷积:就是找一个模板(我们这里的Laplacian(算子))和我们原始图像进行卷积。(题外话:至于是什么是Laplacian(算子),有什么用之类的blabla~~~~~的自行去论坛学习)

紧接着,问题就出来,我们有了算子,图片也有了,但是怎么在程序中实现呢?刚入门的新手肯定是一脸懵逼的表情~~

//我们这里使用的算子,至于取值的要求,这里重点注意:这个3*3矩阵中的所有数相加为0,这个尤其要注意
int[,] filterMatrix = new int[3, 3] { { -1, -1, -1 }, { -1, 8, -1 }, { -1, -1, -1 } };

先看下面这个说明,帮助理解(当然,个人理解,不喜勿碰)
说明图片
以上色框和基本说明图片中都有,大概解释一下:我们对红框(需要处理的图像区域)中每一个像素的RGB值做卷积处理,从第一个红点开始,这个红点是需要处理图像区域中的第一个像素点,至于看到边上有两个参数offsetX,offsetY,分别是对X、Y的偏置,这个偏置的值取决于你的卷积核(我们这里的就是前面说Laplacian(算子))的大小,或者参考我的理解,把我们的Laplacian(算子)的33矩阵的中心和当前要处理的图像区域的第一个像素(就是左上角的红点)重叠放在一起,当然了,要把这个Laplacian(算子)的33矩阵放在原始图像上(看清楚,是原始图片),不能放到原始图像外面去了。所以前面说的offsetX,offsetY取值是多少就看这个算子的大小了,33的矩阵和55的矩阵在这里肯定是不一眼的嘛。后面会详细说在程序里怎么实现。

好了,矩阵位置也放好了,那就看一下我们要做的核心工作了:算什么?如何算?程序中如何实现?
(1)算什么?图片中每一个像素都有相应的RGB值,而图片在处理前后表现出来的区别,其实在人眼看来就是图片的颜色发生了变化,而颜色的变化其实就是每一个像素的RGB值的变化表现出来,所以,我们要做的就是对每一个像素的RGB做处理,好了,目前我们知道我们要运算处理的对象是每一个像素的RGB值,然后就到了下一步
(2)如何算?首先看一下上图中间的说明,这里做个说明:先看那8个蓝色点和中间的红点,对于原始图片来说,这9个点是原始图片中的依次排列的9个像素点,对于我们的Laplacian(算子)来说,就是这个3*3矩阵中的每一个值了。依照卷积的说明(就是之前让大家看得那个链接博文),我们先对R值做处理,我们要分别将这9个像素点中的R值取出来,然后将每个像素点的R值和对应矩阵的值相乘**(说的再细一点:比如取出左上角第一个蓝点的R值,然后将这个R值与矩阵的第一个数(就是filterMatrix[0,0]= -1)相乘得到第一个新的R值,然后在取出第一行第二个蓝点的R值,和矩阵的第二个数(就是filterMatrix[0,1]= -1)相乘得到第二个新的R值,~~~直到获得右下角最后一个蓝点的新的R值),这样获得了这9个点的所有的R值,将这9个R值求和,对求和获得的R值范围作判断(由于每个像素的颜色值是由RGB这3个值构成,而每个值是由1个byte存储,1byte=8bit,所以每个值的范围在0~255),对于超过0-255范围的求和值,大于255的取255,小于0的取0,到此就获得R值的计算,并将新的R值赋给红点**。红点对于要处理的的图片区域(就是处理完后的图片)是第一个像素点。至此对第一个像素点的R值得卷积处理就做完了。然后依次最B值、G值做处理。然后分别赋值给红点的B值和G值,到此,第一个像素的卷积处理就做完了。(相信看下来,稍微理解一下,也不是太难嘛 ^_^)
(3)程序如何实现。基本有了上面两步的理解,第三步就是用程序把它实现了(其实就是用计算机的语言告诉计算机要怎么做才可以实现前面两步要做的事情)。先看下面的代码

在这里插入代码片
​ int filterOffset = (filterWidth - 1) / 2;
            
int[,] newdata = new int[data.GetLength(0), data.GetLength(1)];
int a = 2;
for (int offsetY = filterOffset; offsetY < bmpToLaplace.Height-filterOffset; offsetY++)
    {
      int num = 0;//每行的数据总数
      sbAfGreyAndConvlution.Append("第" + offsetY.ToString() + "行:\n");
      for (int offsetX = filterOffset; offsetX < bmpToLaplace.Width - filterOffset; offsetX++)
      {
         double red = 0.0;
         double blue = 0.0;
         double green = 0.0;
                  

         for (int filterY = -filterOffset;filterY<=filterOffset; filterY++)
         {
            for (int filterX = -filterOffset; filterX <= filterOffset; filterX++)
             {
                Color newcolor = bmpToLaplace.GetPixel((offsetX + filterX), (offsetY + filterY));
                red  += newcolor.R * filterMatrix[filterY + filterOffset,filterX + filterOffset];
                blue += newcolor.B * filterMatrix[filterY + filterOffset, filterX + filterOffset];
                green +=newcolor.G * filterMatrix[filterY + filterOffset, filterX + filterOffset];
                            
                }
           }

            blue = factor * blue + bias;
            green = factor * green + bias;
            red = factor * red + bias;

                    if (blue > 255)
                    { blue = 255; }
                    else if (blue < 0)
                    { blue = 0; }

                    if (green > 255)
                    { green = 255; }
                    else if (green < 0)
                    { green = 0; }

                    if (red > 255)
                    { red = 255; }
                    else if (red < 0)
                    { red = 0; }

             offsetY] - a * (newdata[j + 1, i] + newdata[j - 1, i] + newdata[j, i + 1] + newdata[j, i - 1]);

              bmpToLaplace.SetPixel(offsetX, offsetY, Color.FromArgb((int)red, (int)green, (int)blue));
         }
   }
pictureBox3.Image = bmpToLaplace;

简单的解释一下利用外面的两个循环定位红点位置,利用里面的两个循环,定位到第一个蓝点的位置,如图表示:
在这里插入图片描述
里面的两个小循环就是对R、G、B值的计算过程了,也就是卷积计算了,这样图片的Lalace的处理过程就完了。

梳理一下:首先在原始图片中定位到要处理的图片区域的第一个像素点,然后再利用两个FOR循环对当前像素点做卷积处理,将卷积运算获得像素值重新赋值给图片并显示。如此就完成了整个处理过程。

怎么样,是不是感觉也不是那么难嘛~~~~
但是我们这个程序由于使用的都是对像素点的get和set操作,效率是最低的,下一步就是要将一些提高效率的做法了。这个在下一篇博文中再来具体讲解。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值