本系列文章由@邻居张师傅 出品,转载请注明出处。
文章链接: https://editor.csdn.net/md?articleId=106491750
邮箱: zhangyh_nb@163.com
当前使用OpenCV版本: 4.0.1
原理
相比于对整幅图像取一个固定的阈值,由于光照不均匀等原因,跟一般的方法是对一幅图像中的每个像素点计算阈值,即可变局部阈值。
算法表示如下:
m
(
k
+
1
)
=
1
n
∑
i
=
k
+
2
−
n
k
+
1
z
i
(
1.1
)
m(k+1)=\frac{1}{n}\sum^{k+1}_{i=k+2-n}z_{i}(1.1)
m(k+1)=n1i=k+2−n∑k+1zi(1.1)
其中,
z
k
+
1
z_{k+1}
zk+1表示在扫描序列中第
k
+
1
k+1
k+1步遇到的点的灰度值,
m
(
k
)
m(k)
m(k)为输入图像的第
k
k
k个点的像素值,
n
n
n表示用于计算平均的点的数量。
根据(1.1)有下式:
m
(
k
)
=
1
n
∑
i
=
k
+
1
−
n
k
z
i
(
1.2
)
m(k)=\frac{1}{n}\sum^{k}_{i=k+1-n}z_{i}(1.2)
m(k)=n1i=k+1−n∑kzi(1.2)
根据(1.1)、(1.2)不难得出下式子:
m
(
k
+
1
)
=
1
n
∑
i
=
k
+
2
−
n
k
+
1
z
i
=
m
(
k
)
+
1
n
(
z
k
+
1
−
z
k
+
1
−
n
)
(
1.3
)
m(k+1)=\frac{1}{n}\sum^{k+1}_{i=k+2-n}z_{i}=m(k)+\frac{1}{n}(z_{k+1}-z_{k+1-n})(1.3)
m(k+1)=n1i=k+2−n∑k+1zi=m(k)+n1(zk+1−zk+1−n)(1.3)
接下来就根据每个点的阈值进行二值化操作。
注:我们仅仅在扫描的点的数量大于n时应用式(1.3),其他情况则将
z
k
+
1
−
n
z_{k+1-n}
zk+1−n视为0,即相当于在图像的边界填充了n-1个0。
实现
以下是实现结果,左边是原图,右边是结果。
下面是代码:
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
static void ThresholdOp (int, void*);
void movingThreshold(const Mat &src,Mat &dst,int n=20,float b=0.5);
int source=0,thresholdType=0;
Mat src,dst;
int main(int argc, char* argv[])
{
namedWindow( "ThresholdOp", WINDOW_NORMAL );
createTrackbar( "Source:", "ThresholdOp", &source, 1, ThresholdOp );
createTrackbar( "Type:", "ThresholdOp", &thresholdType, 1, ThresholdOp );
ThresholdOp(0, 0);
waitKey(0);
return 0;
}
static void ThresholdOp (int, void*){
switch(source){
case 0:
src = imread("1.tif", IMREAD_GRAYSCALE);
namedWindow( "1.tif", WINDOW_NORMAL );
imshow("1.tif", src);
break;
case 1:
src = imread("2.tif", IMREAD_GRAYSCALE);
namedWindow( "2.tif", WINDOW_NORMAL );
imshow("2.tif", src);
break;
}
switch(thresholdType){
case 0:
threshold(src, dst, 0, 255, THRESH_OTSU);
imshow("ThresholdOp", dst);
break;
case 1:
movingThreshold(src,dst);
imshow("ThresholdOp", dst);
break;
}
}
void movingThreshold(const Mat &src,Mat &dst,int n,float b){
cout<<n<<" "<<b;
int arrayNum=src.rows*src.cols;
uchar *temp = new uchar[arrayNum];
for(int y=0;y<src.rows;y++){//二维转换为一维Z字扫描
for(int x=0;x<src.cols;x++){
if(y%2==0)
temp[y*src.cols+x]=src.at<uchar>(y,x);
else
temp[y*src.cols+x]=src.at<uchar>(y,src.cols-1-x);
}
}
dst.create(src.size(),CV_8U);
float m_now=0,m_pre=0,dif=0;
int index=0;
for(int y=0;y<src.rows;y++)
for(int x=0;x<src.cols;x++){
index=y*src.cols+x;
if(index<n)//当前点总数不足时
dif=temp[index];
else
dif=temp[index]-temp[index-n];
dif *= (float)1 / n;
m_now=m_pre+dif;//得出阈值
m_pre=m_now;
if(src.at<uchar>(y,x)>b*m_now)
dst.at<uchar>(y,x)=255;
else
dst.at<uchar>(y,x)=0;
}
delete [] temp;
}
参考
1.冈萨雷斯. 数字图像处理[M]. 北京: 电子工业出版社, 2005.