wallner算法原理简述:
我们用P(n)来表示第n个点的灰度值. T(n)来表示二值化后的值。用f s (n) 来表示第n个点之前s个点的灰度值的和,就是
用这个s和另一个变量t就可以简单的说明P(n)应该是0还是1了, 这个公式就是
根据经验值来看, 这里的s和t最佳的取值范围是s= image.width/8, 而t=15的时候效果最好.且1为黑(背景),0为白(前景))
但是有个问题,现在定义T(n)的时候,用的是平均值,也就是说之前扫描过的若干点对于当前点的影响或者说权重是一样的。
所以这里改进成离当前点越近的像素对当前点的影响越大,越远则越小。用g(n)代替T(n)。公式如下:
还有一个问题存在, 就是现在的颜色计算依赖于我的扫描顺序,(一般都是水平扫描的). 这样的话, 我的像素值实际上取决于我水平位置上的邻接点的灰度值, 可是竖直方向的像素如何关联起来呢? 这里也有一个说明, 我们可以维护前面依次水平扫描产生的g_prev(n)序列, 在某个g(n)被使用之前, 我们可以让他和前一个g_prev(n)取一个平均值, 这样的话, 这个最终的值就更有说服力了.
另外由于需要给定一个初始的迭代值,这里取g(n) = 127 *s,127是灰度0~255的中间值
代码实现:
#include<opencv2\opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
void wallner(Mat & src, Mat & dst)
{
/*
* pn = 当前点的灰度值
* s = 图片宽度/n (n = 8时效果最好)
* t = 比例阈值
* 公式:g(n) = g(n-1) * (1-1/s) + p(n)
*/
int t = 15;
int s = src.cols >> 3;
const int S = 9;
const int power2S = 1 << S;//加速因子
int factor = power2S * (100 - t) / (100 * s);
/*使用初始值127*s *s是因为 原先算法采用均值也就是fn 是前n个像素之和
这次算法优化为与当前点越相邻对其影响越大的思路*/
int gn = 127 * s;
int q = power2S - power2S / s;
int pn, hn;
int *prev_gn = NULL;//前一行各点像素值
//Mat dst = Mat::zeros(src.size(), CV_8UC1);
prev_gn = new int[src.cols];
for (int i = 0; i < src.cols; i++)
prev_gn[i] = gn;
uchar * scanline = NULL;
for (int i = 0; i < src.rows; i++)
{
scanline = src.ptr<uchar>(i);
for (int j = 0; j < src.cols; j++)//从左向右遍历
{
pn = scanline[j];
gn = ((gn * q) >> S) + pn;
hn = (gn + prev_gn[j]) >> 1;
prev_gn[j] = gn;
pn < (hn * factor) >> S ? dst.at<uchar>(i, j) = 0 : dst.at<uchar>(i, j) = 255;
}
i++;
if (i == src.rows)
break;
scanline = src.ptr<uchar>(i);
for (int j = src.cols - 1; j >= 0; j--)//从右向左遍历
{
pn = scanline[j];
gn = ((gn * q) >> S) + pn;
hn = (gn + prev_gn[j]) >> 1;
prev_gn[j] = gn;
pn < (hn * factor) >> S ? dst.at<uchar>(i, j) = 0 : dst.at<uchar>(i, j) = 255;
}
}
}
void main()
{
Mat src = imread("Checkerboard.jpg", IMREAD_GRAYSCALE);
Mat cmp = src.clone();
Mat dst = Mat::zeros(src.size(), CV_8UC1);
threshold(cmp, cmp, 0, 255, THRESH_OTSU);
wallner(src, dst);
imshow("src", src);
imshow("dst", dst);
imshow("cmp", cmp);
waitKey();
}
实现结果:
①原图选用因光照角度导致的棋盘光影亮暗不均
②通过对比OTSU效果说明该文实现的自适应二值化阈值的好处
原图:
实现效果:
①otsu:
②wallner:
结论:可以看到wallner以实现局部自适应的方式相较于otsu全局自适应更大程度还原了原图。