OpenCV邊緣提取算法流程的實現(附DEMO)

在傳統的計算機視覺領域,經常需要使用一些傳統的圖像處理算法完成對圖像的邊緣提取功能,通過對圖像的邊緣進行提取完成對目標對象的分割,目標分割技術又包括語義分割與實例分割,比較高端的魯棒性較強的還是需要卷積神經網絡算法進行相關的訓練,如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 (srcImage.empty())
		{
			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_grad_x.convertTo(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_grad_y.convertTo(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;
		bitwise_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區域小塊連通域填黑。
		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 < outBin5.rows; i++)
		{
			for (int j = 0; j < outBin5.cols; 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;
}

到此這篇關於OpenCV邊緣提取算法流程的實現(附DEMO)的文章就介紹到這瞭,更多相關OpenCV 邊緣提取內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: