【OpenCV】超详细边缘提取算法流程(附详细代码)
在传统的计算机视觉领域,经常需要使⽤⼀些传统的图像处理算法完成对图像的边缘提取功能,通过对图像的边缘进⾏提取完成对⽬标对象的分割,⽬标分割技术⼜包括语义分割与实例分割,⽐较⾼端的鲁棒性较强的还是需要卷积神经⽹络算法进⾏相关的训练,如fcn全连接⽹络,mask-rcnn实例分割⽹络。本案例旨在采⽤传统的图像处理技术完成对图像的边缘检测任务,并通过膨胀腐蚀操作进⾏连通域的提取,之后通过连通域的填充以及掩膜操作完成⽬标对象的分割。
具体需要达到的⽬标如下:
上图所⽰,可以很好的将河道信息进⾏提取。
1. 具体算法流程
吃苹果的十大好处⾸先对采集的图⽚进⾏灰度化处理(⽅便进⾏数据处理),然后对灰度图像进⾏中值滤波操作去除湖⾯上的细⼩杂质,之后通过x⽅向和y⽅向上的Sobel梯度算⼦分别获取梯度图像,并将梯度图像转换成CV_8UC1类型,并对转换后的x,y⽅向上的梯度图像进⾏OTSU⼆值化操作获取⼆值图像,并对两幅⼆值图像按对应像素位置进⾏与运算,⽬的是为了去除河道上的波纹⼲扰。(可以继续对与运算之后的结果图进⾏中值滤波去除湖⾯上的细⼩杂质)。最后对⼆值图进⾏多次迭代的膨胀腐蚀操作以及⼩区域块的填充操作(⽤到findContours与drawContours接⼝进⾏轮廓查找与填充)获取河道连通区域。
2.流程图
可以通过如下流程图进⾏展⽰。最佳跑步时间
3. 涉及到的代码
int main() {
组词鹃
Mat srcImage, srcImage2, srcImage3;
for (int j = 1; j <= 172; j++)
{
//⽩天总共172张图⽚。1-172;验证:1,162,146;
//(挑选最好的效果作为模板。对⽐108与117进⾏分析)。
//if (j == 108 || j == 117)
//{//对⽐分析之后选择第108张图⽚作为模板图⽚
吉林市龙潭山公园
char ch[4096] = { 0 };
sprintf(ch, "..\\findriveredge\\day\\2 (%d).jpg", j);
srcImage = imread(ch, IMREAD_ANYCOLOR);
srcImage2 = srcImage.clone();
srcImage3 = srcImage.clone();
if (pty())
{
return -1;
}
if (srcImage.channels() == 3)
{
cvtColor(srcImage, srcImage, COLOR_BGR2GRAY);
}
Mat outImage;
medianBlur(srcImage, outImage, g_nKrenel);//⾸先对灰度图进⾏中值滤波操作,去除⼀些杂质。
Mat grad_x, abs_grad_x, grayImage_x;
Sobel(outImage, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x);
abs_vertTo(grayImage_x, CV_8U);
Mat grad_y, abs_grad_y, grayImage_y;
Sobel(outImage, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_y, abs_grad_y);
abs_vertTo(grayImage_y, CV_8U);
Mat XBin, YBin;
int n_thresh_x = myOtsu(grayImage_x);
threshold(grayImage_x, XBin, n_thresh_x, 255, THRESH_BINARY);
int n_thresh_y = myOtsu(grayImage_y);
threshold(grayImage_y, YBin, n_thresh_y, 255, THRESH_BINARY);
Mat Bin;
bitwi_and(XBin, YBin, Bin);
Mat outBin;
medianBlur(Bin, outBin, 3);//去除⼀些杂质点。
Mat outBin2 = outBin.clone();
int n_iterations = 5;
周保中Mat element = getStructuringElement(MORPH_ELLIPSE, Size(15, 15));//膨胀变亮形成连通域。
Mat element2 = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));//腐蚀操作断开⼀些连通域。
dilate(outBin2, outBin2, element, Point(-1, -1), n_iterations);
erode(outBin2, outBin2, element2, Point(-1, -1), 3);//腐蚀操作,断开河流区域内部的连接区域,⽅便后续的填充处理。 //将河流ROI区域⼩块连通域填⿊。学习小组组名
//将河流ROI区域⼩块连通域填⿊。
Mat outBin3 = outBin2.clone();
vector<vector<Point>> contours;
vector<Vec4i> hie;
findContours(outBin3, contours, hie, RETR_LIST, CHAIN_APPROX_SIMPLE);
float f_area = 0.0;
for (int i = 0; i < contours.size(); i++)
{
f_area = contourArea(contours[i]);
if (f_area < 250000)
{
drawContours(outBin3, contours, i, Scalar(0), -1);
}
大炮打蚊子
}
//由于final bin2在腐蚀过程中存在部分背景区域为⿊⾊空洞,需要将其填⽩。
Mat outBin4_tmp = ~outBin3;
Mat outBin4;
contours.clear();
hie.clear();
findContours(outBin4_tmp, contours, hie, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); for (unsigned int i = 0; i < contours.size(); i++)
{
f_area = contourArea(contours[i]);
if (f_area < 250000)
{
drawContours(outBin4_tmp, contours, i, Scalar(0), -1);
}
}
outBin4 = ~outBin4_tmp;
//迭代腐蚀突出河流边界区域。
erode(outBin4, outBin4, element, Point(-1, -1), 10);
//(可以对腐蚀图在进⾏⼀次外轮廓填充)。
Mat outBin5_tmp = ~outBin4.clone();
Mat outBin5;
contours.clear();
hie.clear();
findContours(outBin5_tmp, contours, hie, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
for (unsigned int i = 0; i < contours.size(); i++)
{
f_area = contourArea(contours[i]);
if (f_area < 100000)
{
drawContours(outBin5_tmp, contours, i, Scalar(0), -1);
}
}
outBin5 = ~outBin5_tmp;
//根据outBin4,对原rgb图取感兴趣区域(即河流区域)
for (int i = 0; i < ws; i++)
{
for (int j = 0; j < ls; j++)
{
if (outBin5.at<uchar>(i, j) == 255)
{
无边落木萧萧下
srcImage2.at<Vec3b>(i, j)[0] = 0;
srcImage2.at<Vec3b>(i, j)[1] = 0;
srcImage2.at<Vec3b>(i, j)[2] = 0;
}
}
}
}
namedWindow("finalImage", 0);
imshow("finalImage", srcImage2);//这⾥的srcImage2表⽰最后所需效果图
cout << j << endl;
waitKey(30);
}
return 0;
}