之前接触过全局二值化(OTSU算法),还有OPENCV提供的自适应二值化,最近又了解到一种新的局部二值化算法,Sauvola算法。

转载自:http://www.dididongdong.com/archives/4048

值得注意的是,计算r×r邻域内像素值的时候,一种优化的策略是,使用OPENCV提供的积分图,计算整张图像的积分图,那么计算r×r区域内的均值可以在常数时间内实现。

CV_EXPORTS_W void integral( InputArray src, OutputArray sum, int sdepth = - );

我们常见的图像二值化算法大致可分为全局阈值方法与局部阈值方法这两种类型。其中OTSU算法是全局阈值的代表,而Sauvola算法则是局部阈值方法的标杆。Sauvola算法的输入是灰度图像,它以当前像素点为中心,根据当前像素点邻域内的灰度均值与标准方差来动态计算该像素点的阈值。

假定当前像素点的坐标为(x,y),以该点为中心的领域为r*r,g(x,y)表示(x,y)处的灰度值,Sauvola算法的步骤为:


来自参考《基于光照不均匀图像的自适应二值化方法研究》郭佳

引用

在二值化的操作中,用的比较多的就是全局阈值话OTSU(大津法)和局部阈值NiBlack,Niblack方法是一种简单有效的动态阈值分割方法,修改得到最佳参数之后的效果比大津法要好,因为大津法是根据整个图像来确定一个阈值,而Niblack则是在不同的R*R区域会有不同的阈值。

Niblack的基本思想是:对于图像的每一个像素点,在rxr领域空问里,计算该像素点领域方位内其他像素点的均值和方差。然后利用公式(1)进行二值化。

其中,T(x,y)是阈值,k是预先设定的修正值,图像为f(x,y),均值为m(x,y),方差为s(x,y)。

使用Niblack法的优点在于:

对每一个像素点都独立的跟据此像素点的邻域的情况来计算门限,对于和邻域均值m(x,y)相近的像素点判断为背景而反之判断为前景;而具体相近到什么程度由标准差s(X’y)和修正系数k来决定,这保证了这种的方法的灵活性。

使用Niblack法的不足在于:

由于要利用域r×r模板遍历图像,导致边界区域(r-1)/2的像素范围内无法求取阈值;同时当进行图像遍历时,如果域r×r范围内都是背景,经NIBLACK计算后必有一部分被确定为目标,产生伪噪声。

总之,用Niblack方法进行图像分割时,选择的处理模板窗口R*R大小的选择很关键,选择的空间太小,则噪声抑制的效果不理想,目标主体不够突出,选择的空间太大,则目标的细节会被去除而丢失信息。

参考

1.第一次做MathOCR遇到的参考文献:
《图片中印刷体数学公式的自动识别》陈颂光
2.中文的的链接来自,追溯不到原文:

https://livezingy.com/derivations-of-sauvola-formula/
3.英文文献:值得参考的标准
Efficient implementation of local adaptive thresholding techniques using integral images
PDF链接为:https://pdfs.semanticscholar.org/8130/a9499715d22468492c3786c34ba1ba0b4ed3.pdf
4.Matlab代码参考
http://freesourcecode.net/matlabprojects/59687/sauvola-local-image-thresholding-in-matlab#.Wzsk2oq-vcs

5,https://www.cnblogs.com/guopengfei/p/4766526.html

//求区域内均值 integral即为积分图
float fastMean(cv::Mat& integral, int x, int y, int window)
{ int min_y = std::max(, y - window / );
int max_y = std::min(integral.rows - , y + window / );
int min_x = std::max(, x - window / );
int max_x = std::min(integral.cols - , x + window / ); int topright = integral.at<int>(max_y, max_x);
int botleft = integral.at<int>(min_y, min_x);
int topleft = integral.at<int>(max_y, min_x);
int botright = integral.at<int>(min_y, max_x); float res = (float)((topright + botleft - topleft - botright) / (float)((max_y - min_y) *(max_x - min_x))); return res;
} cv::Mat& Sauvola(cv::Mat& inpImg, cv::Mat& resImg, int window, float k)
{
cv::Mat integral;
int nYOffSet = ;
int nXOffSet = ;
cv::integral(inpImg, integral); //计算积分图像
for (int y = ; y < inpImg.rows; y += nYOffSet)
{
for (int x = ; x < inpImg.cols; x += nXOffSet)
{ float fmean = fastMean(integral, x, y, window);float fthreshold = (float)(fmean*(1.0 - k)); int nNextY = y + nYOffSet;
int nNextX = x + nXOffSet;
int nCurY = y;
while (nCurY < nNextY && nCurY < inpImg.rows)
{
int nCurX = x;
while (nCurX < nNextX && nCurX < inpImg.cols)
{
uchar val = inpImg.at<uchar>(nCurY, nCurX) < fthreshold;
resImg.at<uchar>(nCurY, nCurX) = (val == ? : );
nCurX++;
}
nCurY++;
} }
} return resImg;
}
//************************************

// 函数名称: sauvola

// 函数说明: 局部均值二值化

// 参    数:

//           const unsigned char * grayImage        [in]        输入图像数据

//           const unsigned char * biImage          [out]       输出图像数据     

//           const int w                            [in]        输入输出图像数据宽

//           const int h                            [in]        输入输出图像数据高

//           const int k                            [in]        threshold = mean*(1 + k*((std / 128) - 1))

//           const int windowSize                   [in]        处理区域宽高

// 返 回 值: void

//************************************

void sauvola(const unsigned char * grayImage, const unsigned char * biImage,

    const int w, const int h, const int k, const int windowSize){

    int whalf = windowSize >> ;

    int i, j;

    int IMAGE_WIDTH = w;

    int IMAGE_HEIGHT = h;

    // create the integral image

    unsigned long * integralImg = (unsigned long*)malloc(IMAGE_WIDTH*IMAGE_HEIGHT*sizeof(unsigned long*));

    unsigned long * integralImgSqrt = (unsigned long*)malloc(IMAGE_WIDTH*IMAGE_HEIGHT*sizeof(unsigned long*));

    int sum = ;

    int sqrtsum = ;

    int index;

    //收集数据 integralImg像素和积分图 integralImgSqrt像素平方和积分图

    for (i = ; i < IMAGE_HEIGHT; i++){

        // reset this column sum

        sum = ;

        sqrtsum = ;

        for (j = ; j < IMAGE_WIDTH; j++)

        {

            index = i*IMAGE_WIDTH + j;

            sum += grayImage[index];

            sqrtsum += grayImage[index] * grayImage[index];

            if (i == ){

                integralImg[index] = sum;

                integralImgSqrt[index] = sqrtsum;

            }

            else{

                integralImgSqrt[index] = integralImgSqrt[(i - )*IMAGE_WIDTH + j] + sqrtsum;

                integralImg[index] = integralImg[(i - )*IMAGE_WIDTH + j] + sum;

            }

        }

    }

    //Calculate the mean and standard deviation using the integral image

    int xmin, ymin, xmax, ymax;

    double mean, std, threshold;

    double diagsum, idiagsum, diff, sqdiagsum, sqidiagsum, sqdiff, area;

    for (i = ; i < IMAGE_WIDTH; i++){

        for (j = ; j < IMAGE_HEIGHT; j++){

            xmin = max(, i - whalf);

            ymin = max(, j - whalf);

            xmax = min(IMAGE_WIDTH - , i + whalf);

            ymax = min(IMAGE_HEIGHT - , j + whalf);

            area = (xmax - xmin + ) * (ymax - ymin + );

            if (area <= ){

                biImage[i * IMAGE_WIDTH + j] = ;

                continue;

            }

            if (xmin ==  && ymin == ){

                diff = integralImg[ymax * IMAGE_WIDTH + xmax];

                sqdiff = integralImgSqrt[ymax * IMAGE_WIDTH + xmax];

            }

            else if (xmin >  && ymin == ){

                diff = integralImg[ymax * IMAGE_WIDTH + xmax] - integralImg[ymax * IMAGE_WIDTH + xmin - ];

                sqdiff = integralImgSqrt[ymax * IMAGE_WIDTH + xmax] - integralImgSqrt[ymax * IMAGE_WIDTH + xmin - ];

            }

            else if (xmin ==  && ymin > ){

                diff = integralImg[ymax * IMAGE_WIDTH + xmax] - integralImg[(ymin - ) * IMAGE_WIDTH + xmax];

                sqdiff = integralImgSqrt[ymax * IMAGE_WIDTH + xmax] - integralImgSqrt[(ymin - ) * IMAGE_WIDTH + xmax];;

            }

            else{

                diagsum = integralImg[ymax * IMAGE_WIDTH + xmax] + integralImg[(ymin - ) * IMAGE_WIDTH + xmin - ];

                idiagsum = integralImg[(ymin - ) * IMAGE_WIDTH + xmax] + integralImg[ymax * IMAGE_WIDTH + xmin - ];

                diff = diagsum - idiagsum;

                sqdiagsum = integralImgSqrt[ymax * IMAGE_WIDTH + xmax] + integralImgSqrt[(ymin - ) * IMAGE_WIDTH + xmin - ];

                sqidiagsum = integralImgSqrt[(ymin - ) * IMAGE_WIDTH + xmax] + integralImgSqrt[ymax * IMAGE_WIDTH + xmin - ];

                sqdiff = sqdiagsum - sqidiagsum;

            }

            mean = diff / area;

            std = sqrt((sqdiff - diff*diff / area) / (area - ));

            threshold = mean*( + k*((std / ) - ));

            if (grayImage[j*IMAGE_WIDTH + i] < threshold)

                biImage[j*IMAGE_WIDTH + i] = ;

            else

                biImage[j*IMAGE_WIDTH + i] = ;

        }

    }

    free(integralImg);

    free(integralImgSqrt);

sauvola是一种考虑局部均值亮度的图像二值化方法, 以局部均值为基准在根据标准差做些微调.算法实现上一般用积分图方法

来实现.这个方法能很好的解决全局阈值方法的短板—关照不均图像二值化不好的问题. 

代码要注意下面几点:

1 计算区域像素和,几乎使用积分图技术是必然的选择.

2 标准差的表示方法: std = sqrt((sqdiff - diff*diff / area) / (area - )) 终于感到高等代数没有白学,

3 判定方程 threshold = mean*(1 + k*((std / 128) - 1)). 首先均值是基础, 如果标准差大写,阈值就会大些,标准差小些,阈值就会小些.

这个方法对一些不是光照不均的图片有时候效果不好,现在还在找较好的方法,初步打算先用全局均值做二值化,如何效果不好再用局部均值的方法.

以上转载自:

https://www.cnblogs.com/guopengfei/p/4766526.html

一种局部二值化算法:Sauvola算法的更多相关文章

  1. 【计算机视觉】纹理特征之LBP局部二值化模式

    转自http://blog.csdn.NET/ty101/article/details/8905394 本文的PDF版本,以及涉及到的所有文献和代码可以到下列地址下载: 1.PDF版本以及文献:ht ...

  2. OpenCV_基于局部自适应阈值的图像二值化

    在图像处理应用中二值化操作是一个很常用的处理方式,例如零器件图片的处理.文本图片和验证码图片中字符的提取.车牌识别中的字符分割,以及视频图像中的运动目标检测中的前景分割,等等. 较为常用的图像二值化方 ...

  3. 【转】Emgu CV on C# (五) —— Emgu CV on 局部自适应阈值二值化

    局部自适应阈值二值化 相对全局阈值二值化,自然就有局部自适应阈值二值化,本文利用Emgu CV实现局部自适应阈值二值化算法,并通过调节block大小,实现图像的边缘检测. 一.理论概述(转载自< ...

  4. [转载+原创]Emgu CV on C# (五) —— Emgu CV on 局部自适应阈值二值化

    局部自适应阈值二值化 相对全局阈值二值化,自然就有局部自适应阈值二值化,本文利用Emgu CV实现局部自适应阈值二值化算法,并通过调节block大小,实现图像的边缘检测. 一.理论概述(转载自< ...

  5. [转载+原创]Emgu CV on C# (四) —— Emgu CV on 全局固定阈值二值化

    重点介绍了全局二值化原理及数学实现,并利用emgucv方法编程实现. 一.理论概述(转载,如果懂图像处理,可以略过,仅用作科普,或者写文章凑字数)  1.概述 图像二值化是图像处理中的一项基本技术,也 ...

  6. openCV_java 图像二值化

    较为常用的图像二值化方法有:1)全局固定阈值:2)局部自适应阈值:3)OTSU等. 局部自适应阈值则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值.这样做的好处在于每个像素位置处的二值化 ...

  7. sauvola二值化算法研究

    sauvola二值化算法研究   sauvola是一种考虑局部均值亮度的图像二值化方法, 以局部均值为基准在根据标准差做些微调.算法实现上一般用积分图方法 来实现.这个方法能很好的解决全局阈值方法的短 ...

  8. Wellner 自适应阈值二值化算法

    参考文档: Adaptive Thresholding for the DigitalDesk.pdf       Adaptive Thresholding Using the Integral I ...

  9. 《opencv学习》 之 OTSU算法实现二值化

    主要讲解OTSU算法实现图像二值化:    1.统计灰度级图像中每个像素值的个数. 2.计算第一步个数占整个图像的比例. 3.计算每个阈值[0-255]条件下,背景和前景所包含像素值总个数和总概率(就 ...

随机推荐

  1. windows 快捷键相关命令

    Mstsc  远程链接 Taskmgr 任务管理器 Regedit  打开注册表 Netstat -ano | find  “80” 查找内容 tasklist /fi "pid eq 57 ...

  2. AIX中卷组管理

      1.创建卷组 使用mkvg指令创建卷组. mkvg 指令参数 -B 创建大型卷组,该卷组最大能容纳128个物理卷和512个逻辑卷 -C 创建增加型并发卷组 -f 强制创建卷组 -G 与-B一样,创 ...

  3. Codeforces1238F. The Maximum Subtree(树形dp)

    题目链接:传送门 思路: 题意说用线段的相交作为边,来构造树,所以不存在大于等于3个的线段两两相交,否则会构成环.因而构造出的树中,每个点最多只会与2个度大于1的节点相邻. 不妨把1设为树根,用deg ...

  4. 牛客假日团队赛6 D 迷路的牛 (思维)

    链接:https://ac.nowcoder.com/acm/contest/993/D 来源:牛客网 迷路的牛 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其他语言 ...

  5. python基础练习题6

    01:求1+2!+3!+....+20!的和 s=0 t=1 for n in range(1,21): t*=n s+=t print('1!+2!+3!+.....+20!=%d'%s) 02:利 ...

  6. 【ZOJ3627】Treasure Hunt II

    题目大意:给定一个长度为 N 的序列,现有两个人从 P 点出发,每个单位时间每个人最多可以移动一个单位,两人之间的最大距离不能超过 M,一共有 T 单位的时间,求在合法情况下,两人可以获得的序列点权和 ...

  7. SpringMVC的数据转换&&数据格式化&&数据校验

    1 SpringMVC的数据绑定流程 SpringMVC将ServletRequest对象及目标方法的入参实例传递给WebDataBinderFactory实例,以创建DataBinder实例对象. ...

  8. IO体系总结

    字节输入流: 字节输出流: 字符输入流: 字符输出流:

  9. 使用 python 开发 Web Service

    使用 python 开发 Web Service Python 是一种强大的面向对象脚本语言,用 python 开发应用程序往往十分快捷,非常适用于开发时间要求苛刻的原型产品.使用 python 开发 ...

  10. 【leetcode】1218. Longest Arithmetic Subsequence of Given Difference

    题目如下: Given an integer array arr and an integer difference, return the length of the longest subsequ ...