去噪:用于验证码图片识别的类续(C#代码)

    自从发表了用于验证码图片识别的类(C#代码)后,不断有网友下载这个类后,问如何用于一些特定的验证码。总结一下网友们的提问,很多都是不会从复杂背景中提到干净的字符图片来,这主要就是一个去噪问题,即除去图片上的背景、干扰点、干扰线等信息。这当中要用到很多图像学数学算法,首先声明,本人不是学图像学的,以下方法理论说得不对,敬请多批评指正。

1、如何设前景/背景的分界值

UnCodebase类中有一个GetPicValidByValue( int dgGrayValue) 函数,可以得到前景的有效区域,常有人问我前景/背景的分界值dgGrayValue是如何确定的(常用的是灰度128)。这个值的获取是有数学算法,叫最大类间方差法,即图像的前后景的平方差为最大时的值就是我们关心的分界值,对付如

这样较复杂的背景非常管用,下面是具体的C#代码。

Code
        /// <summary>
        /// 得到灰度图像前景背景的临界值 最大类间方差法,yuanbao,2007.08
        /// </summary>
        /// <returns>前景背景的临界值</returns>
        public int GetDgGrayValue()
        {
            int[] pixelNum = new int[256];           //图象直方图,共256个点
            int n, n1, n2;
            int total;                              //total为总和,累计值
            double m1, m2, sum, csum, fmax, sb;     //sb为类间方差,fmax存储最大方差值
            int k, t, q;
            int threshValue = 1;                      // 阈值
            int step = 1;
            //生成直方图
            for (int i =0; i < bmpobj.Width ; i++)
            {
                for (int j = 0; j < bmpobj.Height; j++)
                {
                    //返回各个点的颜色,以RGB表示
                    pixelNum[bmpobj.GetPixel(i,j).R]++;            //相应的直方图加1
                }
            }
            //直方图平滑化
            for (k = 0; k <= 255; k++)
            {
                total = 0;
                for (t = -2; t <= 2; t++)              //与附近2个灰度做平滑化,t值应取较小的值
                {
                    q = k + t;
                    if (q < 0)                     //越界处理
                        q = 0;
                    if (q > 255)
                        q = 255;
                    total = total + pixelNum[q];    //total为总和,累计值
                }
                pixelNum[k] = (int)((float)total / 5.0 + 0.5);    //平滑化,左边2个+中间1个+右边2个灰度,共5个,所以总和除以5,后面加0.5是用修正值
            }
            //求阈值
            sum = csum = 0.0;
            n = 0;
            //计算总的图象的点数和质量矩,为后面的计算做准备
            for (k = 0; k <= 255; k++)
            {
                sum += (double)k * (double)pixelNum[k];     //x*f(x)质量矩,也就是每个灰度的值乘以其点数(归一化后为概率),sum为其总和
                n += pixelNum[k];                       //n为图象总的点数,归一化后就是累积概率
            }

            fmax = -1.0;                          //类间方差sb不可能为负,所以fmax初始值为-1不影响计算的进行
            n1 = 0;
            for (k = 0; k < 256; k++)                  //对每个灰度(从0到255)计算一次分割后的类间方差sb
            {
                n1 += pixelNum[k];                //n1为在当前阈值遍前景图象的点数
                if (n1 == 0) { continue; }            //没有分出前景后景
                n2 = n - n1;                        //n2为背景图象的点数
                if (n2 == 0) { break; }               //n2为0表示全部都是后景图象,与n1=0情况类似,之后的遍历不可能使前景点数增加,所以此时可以退出循环
                csum += (double)k * pixelNum[k];    //前景的“灰度的值*其点数”的总和
                m1 = csum / n1;                     //m1为前景的平均灰度
                m2 = (sum - csum) / n2;               //m2为背景的平均灰度
                sb = (double)n1 * (double)n2 * (m1 - m2) * (m1 - m2);   //sb为类间方差
                if (sb > fmax)                  //如果算出的类间方差大于前一次算出的类间方差
                {
                    fmax = sb;                    //fmax始终为最大类间方差(otsu)
                    threshValue = k;              //取最大类间方差时对应的灰度的k就是最佳阈值
                }
            }
            return threshValue;
        }

2、如何去除干扰点/干扰线

    2.1 干扰点/干扰线的特征分析

    现在网上的大多数的验证码都是加了干扰的,一般分为干扰点和干扰线,如下图。标用1、2、3的分别为点、线、字符。

   去干扰,一般是逐点分析,这三种情况下,每一点及周边8个点的情况都不一样(分别为1点,3点,8点),这是一种干扰信息的粒度比字符的粒度小的典型情况。现在就可以动手编写去杂代码了。

    2.2 根据周边有效点数去噪函数

Code
        /// <summary>
        ///  去掉杂点(适合杂点/杂线粗为1)
        /// </summary>
        /// <param name="dgGrayValue">背前景灰色界限</param>
        /// <returns></returns>
        public void ClearNoise(int dgGrayValue, int MaxNearPoints)
        {
            Color piexl;
            int nearDots = 0;
            int XSpan, YSpan, tmpX, tmpY;
            //逐点判断
            for (int i = 0; i < bmpobj.Width; i++)
                for (int j = 0; j < bmpobj.Height; j++)
                {
                    piexl = bmpobj.GetPixel(i, j);
                    if (piexl.R < dgGrayValue)
                    {
                        nearDots = 0;
                        //判断周围8个点是否全为空
                        if (i == 0 || i == bmpobj.Width - 1 || j == 0 || j == bmpobj.Height - 1)  //边框全去掉
                        {
                            bmpobj.SetPixel(i, j, Color.FromArgb(255, 255, 255));
                        }
                        else
                        {
                            if (bmpobj.GetPixel(i - 1, j - 1).R < dgGrayValue) nearDots++;
                            if (bmpobj.GetPixel(i, j - 1).R < dgGrayValue) nearDots++;
                            if (bmpobj.GetPixel(i + 1, j - 1).R < dgGrayValue) nearDots++;
                            if (bmpobj.GetPixel(i - 1, j).R < dgGrayValue) nearDots++;
                            if (bmpobj.GetPixel(i + 1, j).R < dgGrayValue) nearDots++;
                            if (bmpobj.GetPixel(i - 1, j + 1).R < dgGrayValue) nearDots++;
                            if (bmpobj.GetPixel(i, j + 1).R < dgGrayValue) nearDots++;
                            if (bmpobj.GetPixel(i + 1, j + 1).R < dgGrayValue) nearDots++;
                        }

                        if (nearDots < MaxNearPoints)
                            bmpobj.SetPixel(i, j, Color.FromArgb(255, 255, 255));   //去掉单点 && 粗细小3邻边点
                    }
                    else  //背景
                        bmpobj.SetPixel(i, j, Color.FromArgb(255, 255, 255));
                }
        }

    2.3 滤波算法去噪函数

     图像预处理中有多种滤波算法,其原理与方法分别为

     1) 中值滤波

    它通过从图像中的某个采样窗口取出奇数个数据进行排序得到的结果。顾名思义,所谓中值就是窗口中奇数个数据按大小顺序排列后处于中心位置的那个数。中值滤波以窗口的中值作为处理结果。

    实现起来很简单

    1:先对窗口排序

    2:用排序后的中值取代要处理的数据即可

    注意事项:

    1:注意图像边缘数据的处理

    2:对于不同的目的选用不同的窗体,一般有3×3,5×5等等

Code
        /// <summary>
        /// 3×3中值滤波除杂,yuanbao,2007.10
        /// </summary>
        /// <param name="dgGrayValue"></param>
        public void ClearNoise(int dgGrayValue)
        {
            int x, y;
            byte[] p = new byte[9]; //最小处理窗口3*3
            byte s;
            //byte[] lpTemp=new BYTE[nByteWidth*nHeight];
            int i, j;

            //--!!!!!!!!!!!!!!下面开始窗口为3×3中值滤波!!!!!!!!!!!!!!!!
            for (y = 1; y < bmpobj.Height - 1; y++) //--第一行和最后一行无法取窗口
            {
                for (x = 1; x < bmpobj.Width - 1; x++)
                {
                    //取9个点的值
                    p[0] = bmpobj.GetPixel(x - 1, y - 1).R;
                    p[1] = bmpobj.GetPixel(x, y - 1).R;
                    p[2] = bmpobj.GetPixel(x + 1, y - 1).R;
                    p[3] = bmpobj.GetPixel(x - 1, y).R;
                    p[4] = bmpobj.GetPixel(x, y).R;
                    p[5] = bmpobj.GetPixel(x + 1, y).R;
                    p[6] = bmpobj.GetPixel(x - 1, y + 1).R;
                    p[7] = bmpobj.GetPixel(x, y + 1).R;
                    p[8] = bmpobj.GetPixel(x + 1, y + 1).R;
                    //计算中值
                    for (j = 0; j < 5; j++)
                    {
                        for (i = j + 1; i < 9; i++)
                        {
                            if (p[j] > p[i])
                            {
                                s = p[j];
                                p[j] = p[i];
                                p[i] = s;
                            }
                        }
                    }
              //      if (bmpobj.GetPixel(x, y).R < dgGrayValue)
                        bmpobj.SetPixel(x, y, Color.FromArgb(p[4], p[4], p[4]));    //给有效值付中值
                }
            }
        }

   经过实际运行证实,中值滤波能有效去除图像中的噪声点,特别是在一片连续变化缓和的区域中(比如人的衣服,皮肤),几乎100%去除灰度突变点(可以认为是噪声点),也因为如此,中值滤波不适合用在一些细节多,如细节点,细节线多的图像中,因为细节点有可能被当成噪声点去除。

    中值滤波的窗口还可以有多种形状,上面程序选择的是矩形(容易计算),其实窗口还可以是菱形,圆形,十字形等等,不同的窗口形状有不同的滤波效果,对有缓慢且有较长轮廓线的物体适合用矩形或者原型窗口,对于有尖顶角物体的图像适合采用十字形窗口。

    中值滤波可以进行线性组合,不同窗口形状的滤波器可以线性组合

    改进中值滤波方法:

    对一些内容复杂的图像,可以使用复合型中值滤波。如, 中值滤波线性组合、高阶中值滤波组合、加权中值滤波以及迭代中值滤波等。

    中值滤波的线性组合是将几种窗口尺寸大小和形状不同的中值滤波器复合使用,只要各窗口都与中心对称,滤波输出可保持几个方向上的边缘跳变,而且跳变幅度可调节。

    高阶中值滤波组合可以使输入图像中任意方向的细线条保持不变。

    为了在一定的条件下尽可能去除噪声,又有效保持图像细节,可以对中值滤波器参数进行修正, 如加权中值滤波, 也就是对输入窗口进行加权。

    也可以是对中值滤波器的使用方法进行变化, 保证滤波的效果, 还可以和其他滤波器联合使用。

    2).均值滤波(模糊算法)

    均值滤波是典型的线性滤波算法,它是指在图像上对待处理的像素给一个模板,该模板包括了其周围的临近像素。将模板中的全体像素的均值来代替原来的像素值的方法。

    3)维纳(Wiener)滤波

    维纳(Wiener)滤波是对退化图像进行恢复处理的另一种常用算法,是一种有约束的恢复处理方法,其采用的维纳滤波器是一种最小均方误差滤波器,其数学形式比较复杂:

    F(u,v)=[(1/H(u,v))*(|H(u,v)|2)/(|H(u,v)|2+s*[Sn(u,v)/Sf(u,v)])]*G(u,v)

& nbsp;   当s为1时,上式就是普通的维纳滤波;如果s为变量,则为参数维纳滤波,如果没有噪声干扰,即Sn (u,v)=0时,上式实际就是前面的逆滤波。从其数学形式可以看出:维纳滤波比逆滤波在对噪声的处理方面要强一些。以上只是理论上的数学形式,在进行实际处理时,往往不知道噪声函数Sn(u,v)和Sf(u,v)的分布情况,因此在实际应用时多用下式进行近似处理:

    F(u,v)=[(1/H(u,v))* (|H(u,v)|2)/(|H(u,v)|2+K)]*G(u,v)

    其中K是一个预先设定的常数。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏MyBlog

图像增强的几个方法以及Matlab代码

灰度线性变换, 是一种空域的方法, 直接对每一个像素的灰度值进行操作 假设图像为

951
来自专栏星回的实验室

图像处理智能化的探索[一]:人脸识别裁图

最近在对接公司一些新闻接口的时候,发现接口茫茫多:CMS接口、无线CMS接口、正文接口、列表接口……更令人捉急的是,由于新闻推送场景不同,每条新闻的配图尺寸也就...

522
来自专栏李海辰的专栏

Unity 5.6 光照烘焙系统介绍

Unity 一直致力于解决混合光照的问题,在Unity 5.6 Beta 2版本中加入了不断改进后的功能。本文将为大家分享光照烘焙系统介绍。

1.7K2
来自专栏机器学习算法全栈工程师

人脸Haar特征与快速计算神器:积分图

背景介绍 iPhone十周年纪念之作iPhoneX刚刚发布,其搭载的“刷脸解锁”功能再次将“人脸识别”技术带入大众视野。 借iPhoneX的东风...

39611
来自专栏大学生计算机视觉学习DeepLearning

cv2.cornerHarris()详解 python+OpenCV 中的 Harris 角点检测

5169
来自专栏Python数据科学

数据分析实战—北京二手房房价分析(建模篇)

本篇将继续上一篇数据分析之后进行数据挖掘建模预测,这两部分构成了一个简单的完整项目。结合两篇文章通过数据分析和挖掘的方法可以达到二手房屋价格预测的效果。

872
来自专栏机器学习算法与Python学习

TensorFlow实战:SoftMax手写体MNIST识别(Python完整源码)

之前的文章 TensorFlow的安装与初步了解,从TensorFlow的安装到基本的模块单元进行了初步的讲解。今天这篇文章我们使用TensorFlow针对于手...

6956
来自专栏图形学与OpenGL

模拟试题C

2.用编码裁剪法裁剪二维线段时,判断下列直线段采用哪种处理方法。假设直线段两个端点M、N的编码为1000和1001(按TBRL顺序)( )

1043
来自专栏marsggbo

DeepLearning.ai学习笔记(四)卷积神经网络 -- week3 目标检测

一、目标定位 这一小节视频主要介绍了我们在实现目标定位时标签该如何定义。 ? 上图左下角给出了损失函数的计算公式(这里使用的是平方差) 如图示,加入我们需...

1866
来自专栏xingoo, 一个梦想做发明家的程序员

动态规划

基本思想:将待求解问题分解成若干子问题,先求解子问题,然后从子问题的解中得到原问题的解。 与分治不同的是,经分解得到的子问题往往不是互相独立的。 若用分治法来解...

1775

扫码关注云+社区