算法步骤:
注意点:1.2步骤中采用百分比阈值 ,阈值的百分数应设置较大值(如99.7%),可以排除背景中的干扰点
99.97%时候的二值图(为了可视化,灰度值范围设置为0-255)
90%的二值图
2.最后一步中,通过otsu计算阈值时候,灰度值为0的像素点不计入计算。
核心算法:
void edge_threshold(Mat &src)
{
Mat src1 = src.clone();
Mat dst, thre, dst_x, dst_y;
medianBlur(src1, src1, 3);
Mat kernel_x = (Mat_<int>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
Mat kernel_y = (Mat_<int>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);
filter2D(src1, dst_x, CV_16SC1, kernel_x);
filter2D(src1, dst_y, CV_16SC1, kernel_y);
convertScaleAbs(dst_x, dst_x);
convertScaleAbs(dst_y, dst_y);
dst = dst_x + dst_y;
int hist[256] = { 0 };
uchar * d_data = dst.data;
for (int j = 0; j < dst.rows; j++)
{
for (int i = 0; i < dst.cols; i++)
{
if (d_data[j*dst.step + i] != 0)
{
hist[d_data[j*dst.step + i]]++;
}
}
}
long sum = 0, Amount = 0;
double percent;
int T = 0;
for (int k = 0; k < 256; k++)
{
Amount += hist[k];
}
for (int i = 0; i < 256; i++)
{
sum += hist[i];
if (sum >= Amount*0.9)
{
T = i;
break;
}
}
threshold(dst, thre, T, 1, THRESH_BINARY);
Mat src2;
src2 = thre.mul(src1);//取thre 中的像素值为1的像素点。
Mat src3;
threshold(src1, src3, OtsuThreshold(src2), 255, THRESH_BINARY);
imshow("result", src3);
}
otsu算法:
int OtsuThreshold(Mat src)
{
int threshold;
int height = src.rows;
int width = src.cols;
//histogram
float histogram[256] = { 0 };
uchar * s_data = src.data;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
if (s_data[i*src.step + j] != 0)//排除直方图中的0像素值
histogram[s_data[i*src.step + j]]++;
}
}
int m = 0, n = 0; //m:第一个有像素的灰度值 n:最后一个有像素的灰度值
long sum_value = 0;//非零像素个数
bool singel = true;
for (int i = 1; i < 256; i++)
{
if (histogram[i] != 0 && singel)
{
m = i;
singel = false;
}
if (histogram[i] != 0)
{
sum_value++;
}
}
for (int j = 255; j >= 0; j--)
{
if (histogram[j] != 0)
{
n = j;
break;
}
}
float sum0, sum1, cnt0, cnt1, w0, w1, u0, u1, u, variance;
float maxVariance = 0;
for (int i = m+1; i <= n; i++)
{
sum0 = 0;
sum1 = 0;
cnt0 = 0;
cnt1 = 0;
w0 = 0;
w1 = 0;
if (histogram[i] != 0)
{
for (int j = m; j < i; j++)
{
cnt0 += histogram[j];
sum0 += j * histogram[j];
}
u0 = (double)sum0 / cnt0;
w0 = (double)cnt0 / sum_value;
for (int j = i; j <= n; j++)
{
cnt1 += histogram[j];
sum1 += j * histogram[j];
}
u1 = (double)sum1 / cnt1;
w1 = (double)cnt1 / sum_value;
u = u0 * w0 + u1 * w1; //图像的平均灰度
variance = w0 * w1 * (u0 - u1) * (u0 - u1);
if (variance > maxVariance)
{
maxVariance = variance;
threshold = i;
}
}
}
return threshold;
}