对理论感兴趣的同学可以参考 基于局部otsu分割与hough变换的海天线检测 这边文献,我的代码是依据该文献实现的。
IDE:vs2015
依赖项:opencv 3.x
一.对图像做中值滤波
opencv现成的函数
cv::Mat src1 = imread("test1.png");
cv::Mat srcGray;
//转换为二值图像
cvtColor(src1, srcGray, CV_BGR2GRAY);
//中值滤波,过滤噪声
medianBlur(srcGray, srcGray, 3);
二、otsu图像分割
//otsu 最大类间距算法,通过灰度直方图,找出阈值。
//类似k-means 聚类算法
int otsu(cv::Mat image)
{
assert(!image.empty());
int width = image.cols;
int height = image.rows;
int x = 0, y = 0;
int pixelCount[256];
float pixelPro[256];
int i, j, pixelSum = width * height, threshold = 0;
uchar* data = (uchar*)image.data;
//初始化
for (i = 0; i < 256; i++)
{
pixelCount[i] = 0;
pixelPro[i] = 0;
}
//统计灰度级中每个像素在整幅图像中的个数
for (i = y; i < height; i++)
{
for (j = x; j < width; j++)
{
pixelCount[data[i * image.step + j]]++;
}
}
//计算每个像素在整幅图像中的比例
for (i = 0; i < 256; i++)
{
pixelPro[i] = (float)(pixelCount[i]) / (float)(pixelSum);
}
//经典ostu算法,得到前景和背景的分割
//遍历灰度级[0,255],计算出方差最大的灰度值,为最佳阈值
float w0, w1, u0tmp, u1tmp, u0, u1, u, deltaTmp, deltaMax = 0;
for (i = 0; i < 256; i++)
{
w0 = w1 = u0tmp = u1tmp = u0 = u1 = u = deltaTmp = 0;
for (j = 0; j < 256; j++)
{
if (j <= i) //背景部分
{
//以i为阈值分类,第一类总的概率
w0 += pixelPro[j];
u0tmp += j * pixelPro[j];
}
else //前景部分
{
//以i为阈值分类,第二类总的概率
w1 += pixelPro[j];
u1tmp += j * pixelPro[j];
}
}
u0 = u0tmp / w0; //第一类的平均灰度
u1 = u1tmp / w1; //第二类的平均灰度
u = u0tmp + u1tmp; //整幅图像的平均灰度
//计算类间方差
deltaTmp = w0 * (u0 - u)*(u0 - u) + w1 * (u1 - u)*(u1 - u);
//找出最大类间方差以及对应的阈值
if (deltaTmp > deltaMax)
{
deltaMax = deltaTmp;
threshold = i;
}
}
//返回最佳阈值;
return threshold;
}
三、边缘提取
//对二值图像做边缘提取,没有采用canny
//对二值图像的纵向相邻像素做异或处理,提取像素值跳跃边界线
cv::Mat edge(cv::Mat image)
{
cv::Mat dst = cv::Mat::zeros(image.rows, image.cols, CV_8UC1);
uchar* data = (uchar*)image.data;
uchar* data1 = (uchar*)dst.data;
for (int i = 0; i
四、hough变换提取直线
//霍夫变换
/*功能:将输入图像按照给出参数要求提取线段,放在lines中。
lines : 是一个vector, Vec4i是一个包含4个int数据类型的结构体,[x1, y1, x2, y2], 可以表示一个线段。
rho : 就是一个半径的分辨率。 以像素为单位的距离精度。 另一种形容方式是直线搜索时的进步尺寸的单位半径。
theta : 角度分辨率。以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度
threshold : 判断直线点数的阈值。累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。 大于阈值 threshold 的线段才可以被检测通过并返回到结果中。
minLineLength:线段长度阈值。有默认值0,表示最低线段的长度,比这个设定参数短的线段就不能被显现出来。
minLineGap : 线段上最近两点之间的阈值 有默认值0,允许将同一行点与点之间连接起来的最大的距离
*/
static void on_HoughLines(cv::Mat src, cv::Mat dst)
{
Mat midImage = src.clone();
imshow("2", midImage);
waitKey(15);
//调用HoughLinesP函数
vector mylines;
HoughLinesP(midImage, mylines, 1, CV_PI / 180, 50 + 1, 30, 10);//局部otsu和全局otsu分别设置不同的参数
//循环遍历绘制每一条线段
for (size_t i = 0; i < mylines.size(); i++)
{
Vec4i l = mylines[i];
line(dst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 255, 0), 1, 8);
}
}
完整代码在github上 https://github.com/Tudou880306/sea-sky-line-detection