国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

這篇具有很好參考價(jià)值的文章主要介紹了OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

1、概述

邊緣檢測(cè)是圖像處理和計(jì)算機(jī)視覺(jué)中的基本問(wèn)題,邊緣檢測(cè)的目的是標(biāo)識(shí)數(shù)字圖像中亮度變化明顯的點(diǎn)。圖像屬性中的顯著變化通常反映了屬性的重要事件和變化。
圖像邊緣檢測(cè)大幅度地減少了數(shù)據(jù)量,并且剔除了可以認(rèn)為不相關(guān)的信息,保留了圖像重要的結(jié)構(gòu)屬性。有許多方法用于邊緣檢測(cè),它們的絕大部分可以劃分為兩類:基于查找和基于零穿越。
基于查找:通過(guò)尋找圖像一階導(dǎo)數(shù)中的最大值和最小值來(lái)檢測(cè)邊界,然后利用計(jì)算結(jié)果估計(jì)邊緣的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值,代表算法是 Sobel 算子、Roberts 算子、 Scharr 算子和差分邊緣檢測(cè)。
基于零穿越︰通過(guò)尋找圖像二階導(dǎo)數(shù)零穿越來(lái)尋找邊界,代表算法是拉普拉斯(Laplacian)算子或者非線性差分表示的過(guò)零點(diǎn)。

邊緣一般是指圖像在某一局部強(qiáng)度劇烈變化的區(qū)域。強(qiáng)度變化一般有兩種情況:
階躍變化:
OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)
像數(shù)值從低到高變化,圖像從暗到亮

山頂變化:
OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

  • 找到有差異的相鄰像素(邊緣檢測(cè))
  • 增加有差異的像素的對(duì)比度(圖像銳化)

邊緣檢測(cè)步驟:

(1)圖像獲取

(2)圖像濾波

邊緣檢測(cè)的算法主要是基于圖像強(qiáng)度的一階和二階導(dǎo)數(shù),但是導(dǎo)數(shù)通常對(duì)噪聲很敏感,因此必須采用濾波器來(lái)改善與噪聲有關(guān)的邊緣檢測(cè)器的性能。

(3)圖像增強(qiáng)

增強(qiáng)邊緣檢測(cè)的基礎(chǔ)是確定圖像各點(diǎn)的領(lǐng)域強(qiáng)度的變化值。增強(qiáng)算法可以將圖像灰度點(diǎn)領(lǐng)域強(qiáng)度值 有顯著變化的點(diǎn)凸顯出來(lái)。

(4)圖像檢測(cè)

(5)圖像定位

2、差分邊緣檢測(cè)

2.1 原理

使用圖像的一階差分代替圖像函數(shù)的導(dǎo)數(shù)。二維離散圖像函數(shù)在x方向上的一階差分定義為OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè),y方向上為OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè),其中前者計(jì)算垂直邊邊緣,后者計(jì)算水平邊緣。

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

2.2 實(shí)現(xiàn)差分邊緣檢測(cè)

// 圖像差分操作
void diffOperation(const cv::Mat srcImage, cv::Mat& edgeXImage, cv::Mat& edgeYImage)
{
	cv::Mat tempImage = srcImage.clone();
	int nRows = tempImage.rows;
	int nCols = tempImage.cols;
	for (int i = 0; i < nRows - 1; i++)
	{
		for (int j = 0; j < nCols - 1; j++)
		{
			// 計(jì)算垂直邊邊緣
			edgeXImage.at<uchar>(i, j) =
				abs(tempImage.at<uchar>(i + 1, j) -
					tempImage.at<uchar>(i, j));
			// 計(jì)算水平邊緣
			edgeYImage.at<uchar>(i, j) =
				abs(tempImage.at<uchar>(i, j + 1) -
					tempImage.at<uchar>(i, j));
		}
	}
}

int main()
{
	cv::Mat srcImage = cv::imread("E:\\Lena.jpg");
	if (!srcImage.data)
		return -1;
	cv::imshow("srcImage", srcImage);
	cv::Mat edgeXImage(srcImage.size(), srcImage.type());
	cv::Mat edgeYImage(srcImage.size(), srcImage.type());
	// 計(jì)算差分圖像
	diffOperation(srcImage, edgeXImage, edgeYImage);
	cv::imshow("edgeXImage1", edgeXImage);
	cv::imshow("edgeYImage2", edgeYImage);
	cv::Mat edgeImage(srcImage.size(), srcImage.type());
	// 水平與垂直邊緣圖像疊加
	addWeighted(edgeXImage, 0.5, edgeYImage,
		0.5, 0.0, edgeImage);
	cv::imshow("edgeImage3", edgeImage);
	cv::waitKey(0);
	return 0;
}

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

3、Roberts算子邊緣檢測(cè)

3.1 原理

Roberts算子與普通梯度算子類似,都是取一階的差分作為梯度,區(qū)別在于取值的位置:

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

正對(duì)角為水平方向,斜對(duì)角為垂直方向:

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

Roberts算法優(yōu)缺點(diǎn)
優(yōu)點(diǎn):Roberts算子能較好的增強(qiáng)正負(fù)45度的圖像邊緣
缺點(diǎn):對(duì)邊緣的定位不太準(zhǔn)確,提取的邊緣線條較粗

3.2 實(shí)現(xiàn)Roberts算子邊緣檢測(cè)

//Roberts算子實(shí)現(xiàn)
Mat roberts(Mat srcImage)
{
	Mat dstImage = srcImage.clone();
	int nRows = dstImage.rows;
	int nCols = dstImage.cols;
	for (int i = 0; i < nRows - 1; i++) {
		for (int j = 0; j < nCols - 1; j++) {
			//根據(jù)公式計(jì)算
			int t1 = (srcImage.at<uchar>(i, j) -
				srcImage.at<uchar>(i + 1, j + 1)) *
				(srcImage.at<uchar>(i, j) -
					srcImage.at<uchar>(i + 1, j + 1));
			int t2 = (srcImage.at<uchar>(i + 1, j) -
				srcImage.at<uchar>(i, j + 1)) *
				(srcImage.at<uchar>(i + 1, j) -
					srcImage.at<uchar>(i, j + 1));
			//計(jì)算g(x,y)
			dstImage.at<uchar>(i, j) = (uchar)sqrt(t1 + t2);
		}
	}
	return dstImage;
}

int main()
{
	Mat srcImage = imread("E:\\la.jpg");
	if (!srcImage.data) {
		cout << "falied to read" << endl;
		system("pause");
		return -1;
	}
	Mat srcGray;
	cvtColor(srcImage, srcGray, CV_BGR2GRAY);
	//高斯濾波
	GaussianBlur(srcGray, srcGray, Size(3, 3),
		0, 0, BORDER_DEFAULT);
	Mat dstImage = roberts(srcGray);
	imshow("srcImage", srcImage);
	imshow("dstImage", dstImage);
	waitKey(0);
	return 0;
}

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

4、Prewitt算子邊緣檢測(cè)

4.1 原理

? Prewitt算子是一種一階微分算子的邊緣檢測(cè),利用像素點(diǎn)上下、左右鄰點(diǎn)的灰度差,在邊緣處達(dá)到極值檢測(cè)邊緣,去掉部分偽邊緣,對(duì)噪聲具有平滑作用 。其原理是在圖像空間利用兩個(gè)方向模板與圖像進(jìn)行鄰域卷積來(lái)完成的,這兩個(gè)方向模板一個(gè)檢測(cè)水平邊緣,一個(gè)檢測(cè)垂直邊緣。
? prewitt算子是加權(quán)平均算子,對(duì)噪聲有抑制作用,但是像素平均相當(dāng)于對(duì)圖像進(jìn)行的同濾波,所以prewitt算子對(duì)邊緣的定位不如robert算子。

標(biāo)準(zhǔn)的 Prewitt 邊緣檢測(cè)算子由以下兩個(gè)卷積核組成。

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

4.2 手工實(shí)現(xiàn) Prewitt 算子邊緣檢測(cè)

//Prewitt輪廓提取算法
Mat myPrewitt(Mat src,int x,int y) {
	//獲取圖像屬性
	int nRows = src.rows;
	int nCols = src.cols;
	int dx = 0, dy = 0;
	//定義空白圖像,用于存放Roberts算法提取出來(lái)的輪廓圖
	Mat dst(src.size(), src.type());
	//對(duì)閾值化圖像進(jìn)行遍歷,進(jìn)行Sober算法
	for (int i = 1; i < nRows - 1; i++) {
		for (int j = 1; j < nCols - 1; j++) {
			dx = abs((src.at<uchar>(i + 1, j - 1) + src.at<uchar>(i + 1, j ) + src.at<uchar>(i + 1, j + 1))-(src.at<uchar>(i - 1, j - 1) + src.at<uchar>(i - 1, j) + src.at<uchar>(i - 1, j + 1))) ;
			dy = abs((src.at<uchar>(i - 1, j + 1) + src.at<uchar>(i , j + 1 ) + src.at<uchar>(i + 1, j + 1))-(src.at<uchar>(i - 1, j - 1) + src.at<uchar>(i , j - 1) + src.at<uchar>(i + 1, j - 1))) ;
			if (x == 0) {
				dst.at<uchar>(i, j) = dy;
			}
			else {
				dst.at<uchar>(i, j) = dx;
			}
			
		}
	}

	return dst;
}


int main()
{
	//讀取圖像,黑白圖像邊緣檢測(cè)結(jié)果較為明顯
	Mat img = imread("E:\\la.jpg", IMREAD_ANYCOLOR);
	if (img.empty())
	{
		cout << "請(qǐng)確認(rèn)圖像文件名稱是否正確" << endl;
		return -1;
	}
	//顯示圖像
	imshow("原圖", img);
	cvtColor(img, img, COLOR_RGB2GRAY);
	imshow("灰度圖", img);
	GaussianBlur(img, img, Size(3, 3), 0); //高斯濾波器(模糊/平滑/近似)消除噪點(diǎn)
	Mat dstX = myPrewitt(img,1,0);
	imshow("SoberX", dstX);
	Mat dstY = myPrewitt(img, 0, 1);
	imshow("SoberY", dstY);
	convertScaleAbs(dstX, dstX);
	//imshow("SoberX2", dstX);
	convertScaleAbs(dstY, dstY);
	Mat dst;
	addWeighted(dstX, 0.5, dstY, 0.5, 0, dst);
	imshow("Sober", dst);
	waitKey(0);
	return 0;
}

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

convertScaleAbs()用于實(shí)現(xiàn)對(duì)整個(gè)圖像數(shù)組中的每一個(gè)元素,進(jìn)行如下操作:

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

4.3 函數(shù)實(shí)現(xiàn) Prewitt 算子邊緣檢測(cè)

void getPrewitt_oper(Mat& getPrewitt_horizontal, Mat& getPrewitt_vertical, Mat& getPrewitt_Diagonal1, Mat& getPrewitt_Diagonal2) {
	//水平方向
	getPrewitt_horizontal = (Mat_<float>(3, 3) << -1, -1, -1, 0, 0, 0, 1, 1, 1);
	//垂直方向
	getPrewitt_vertical = (Mat_<float>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
	//對(duì)角135°
	getPrewitt_Diagonal1 = (Mat_<float>(3, 3) << 0, 1, 1, -1, 0, 1, -1, -1, 0);
	//對(duì)角45°
	getPrewitt_Diagonal2 = (Mat_<float>(3, 3) << -1, -1, 0, -1, 0, 1, 0, 1, 1);

	//逆時(shí)針?lè)崔D(zhuǎn)180°得到卷積核
	flip(getPrewitt_horizontal, getPrewitt_horizontal, -1);
	flip(getPrewitt_vertical, getPrewitt_vertical, -1);
	flip(getPrewitt_Diagonal1, getPrewitt_Diagonal1, -1);
	flip(getPrewitt_Diagonal2, getPrewitt_Diagonal2, -1);
}

void edge_Prewitt(Mat& src, Mat& dst1, Mat& dst2, Mat& dst3, Mat& dst4, Mat& dst, int ddepth, double delta = 0, int borderType = BORDER_DEFAULT) {
	//獲取Prewitt算子
	Mat getPrewitt_horizontal;
	Mat getPrewitt_vertical;
	Mat getPrewitt_Diagonal1;
	Mat getPrewitt_Diagonal2;
	getPrewitt_oper(getPrewitt_horizontal, getPrewitt_vertical, getPrewitt_Diagonal1, getPrewitt_Diagonal2);

	//卷積得到水平方向邊緣
	filter2D(src, dst1, ddepth, getPrewitt_horizontal, Point(-1, -1), delta, borderType);

	//卷積得到4垂直方向邊緣
	filter2D(src, dst2, ddepth, getPrewitt_vertical, Point(-1, -1), delta, borderType);

	//卷積得到45°方向邊緣
	filter2D(src, dst3, ddepth, getPrewitt_Diagonal1, Point(-1, -1), delta, borderType);

	//卷積得到135°方向邊緣
	filter2D(src, dst4, ddepth, getPrewitt_Diagonal2, Point(-1, -1), delta, borderType);

	//邊緣強(qiáng)度(近似)
	convertScaleAbs(dst1, dst1); //求絕對(duì)值并轉(zhuǎn)為無(wú)符號(hào)8位圖
	convertScaleAbs(dst2, dst2);

	convertScaleAbs(dst3, dst3); //求絕對(duì)值并轉(zhuǎn)為無(wú)符號(hào)8位圖
	convertScaleAbs(dst4, dst4);
	dst = dst1 + dst2;

}

int main()
{
	Mat dst, dst1, dst2, dst3, dst4;
	Mat img = imread("E:\\la.jpg");
	cvtColor(img, img, COLOR_BGR2GRAY);
	//注意:要采用CV_32F,因?yàn)橛行┑胤骄矸e后為負(fù)數(shù),若用8位無(wú)符號(hào),則會(huì)導(dǎo)致這些地方為0
	edge_Prewitt(img, dst1, dst2, dst3, dst4, dst, CV_32F);
	imshow("原圖", img);
	namedWindow("水平邊緣", WINDOW_NORMAL);
	imshow("水平邊緣", dst1);
	namedWindow("垂直邊緣", WINDOW_NORMAL);
	imshow("垂直邊緣", dst2);
	namedWindow("45°邊緣", WINDOW_NORMAL);
	imshow("45°邊緣", dst3);
	namedWindow("135°邊緣", WINDOW_NORMAL);
	imshow("135°邊緣", dst4);
	namedWindow("邊緣強(qiáng)度", WINDOW_NORMAL);
	imshow("邊緣強(qiáng)度", dst);
	waitKey(0);
	return 0;
}

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

5、Sobel算子邊緣檢測(cè)

5.1 原理

Sobel算法(索貝爾算子)是一種用于邊緣檢測(cè)的離散微分算子,它結(jié)合了高斯平滑和微分求導(dǎo)。該算子用于計(jì)算圖像明暗程度近似值,根據(jù)圖像邊緣旁邊明暗程度把該區(qū)域內(nèi)超過(guò)某個(gè)數(shù)的特定點(diǎn)記為邊緣。Sobel算子在Prewitt算子的基礎(chǔ)上增加了權(quán)重的概念,認(rèn)為相鄰點(diǎn)的距離遠(yuǎn)近對(duì)當(dāng)前像素點(diǎn)的影響是不同的,距離越近的像素點(diǎn)對(duì)應(yīng)當(dāng)前像素的影響越大,從而實(shí)現(xiàn)圖像銳化并突出邊緣輪廓。當(dāng)對(duì)精度要求不是很高時(shí),Sobel算子是一種較為常用的邊緣檢測(cè)方法。

Sobel算法提取圖像邊緣的三大步驟:

(1)提取X方向的邊緣,即Gx;

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

以卷積核的中心為中心,將卷積核與圖像上像素值一一對(duì)應(yīng),卷積核上的數(shù)字相當(dāng)于系數(shù)。利用如下公式即可計(jì)算出卷積核中心的x方向梯度。
OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

(2)提取Y方向的邊緣,即Gy;

同理,如果想要計(jì)算y方向的梯度,卷積核應(yīng)該是這樣的,公式也是同理。

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

(3)綜合兩個(gè)方向的邊緣信息得到整幅圖的邊緣。

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

Sobel算法優(yōu)缺點(diǎn)
優(yōu)點(diǎn):Sobel算子的邊緣定位更準(zhǔn)確,會(huì)具有更多的抗噪性,不但產(chǎn)生較好的檢測(cè)效果,而且對(duì)噪聲具有平滑抑制作用;方法簡(jiǎn)單、處理速度快,并且所得的邊緣光滑、連續(xù)
缺點(diǎn):得到的邊緣較粗,且可能出現(xiàn)偽邊緣

5.2 手工實(shí)現(xiàn) Sobel 算子邊緣檢測(cè)

//Sobel輪廓提取算法
Mat mySobel(Mat src) {
	//獲取圖像屬性
	int nRows = src.rows;
	int nCols = src.cols;
	int dx = 0, dy = 0;
	//定義空白圖像,用于存放Roberts算法提取出來(lái)的輪廓圖
	Mat dst(src.size(), src.type());
	//對(duì)閾值化圖像進(jìn)行遍歷,進(jìn)行Sober算法
	for (int i = 1; i < nRows - 1; i++) {
		for (int j = 1; j < nCols - 1; j++) {
			dx = (src.at<uchar>(i - 1, j + 1) - src.at<uchar>(i - 1, j - 1)) + 2 * (src.at<uchar>(i, j + 1) - src.at<uchar>(i, j - 1)) + (src.at<uchar>(i + 1, j + 1) - src.at<uchar>(i + 1, j - 1));
			dy = (src.at<uchar>(i + 1, j - 1) - src.at<uchar>(i - 1, j - 1)) + 2 * (src.at<uchar>(i + 1, j) - src.at<uchar>(i -1 , j)) + (src.at<uchar>(i + 1, j + 1) - src.at<uchar>(i - 1, j + 1));
			dst.at<uchar>(i, j) = sqrt(dx * dx + dy * dy);
		}
	}

	return dst;
}


int main()
{
	//讀取圖像,黑白圖像邊緣檢測(cè)結(jié)果較為明顯
	Mat img = imread("E:\\la.jpg", IMREAD_ANYCOLOR);
	if (img.empty())
	{
		cout << "請(qǐng)確認(rèn)圖像文件名稱是否正確" << endl;
		return -1;
	}
	//顯示圖像
	imshow("原圖", img);
	cvtColor(img, img, COLOR_RGB2GRAY);
	imshow("灰度圖", img);
	Mat dst = mySobel(img);
	imshow("Sober", dst);
	
	waitKey(0);
	return 0;
}

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

Mat img = imread("E:\\la.jpg");
	cvtColor(img, img, COLOR_BGR2GRAY);
	Mat imageX = Mat::zeros(img.size(), CV_16SC1);
	Mat imageY = Mat::zeros(img.size(), CV_16SC1);
	Mat imageXY = Mat::zeros(img.size(), CV_16SC1);
	Mat imageX8UC;
	Mat imageY8UC;
	Mat imageXY8UC;

	GaussianBlur(img, img, Size(3, 3), 0); //高斯濾波器(模糊/平滑/近似)消除噪點(diǎn)
	uchar* P = img.data;
	uchar* PX = imageX.data;
	uchar* PY = imageY.data;
	int step = img.step;
	int stepXY = imageX.step;
	for (int i = 1; i < img.rows - 1; i++)
	{
		for (int j = 1; j < img.cols - 1; j++)
		{
			// 通過(guò)指針遍歷圖像上每一個(gè)像素
			// 求出X,Y方向的導(dǎo)數(shù)(梯度) sobel算子加權(quán)的結(jié)果
			PX[i * imageX.step + j * (stepXY / step)] = abs(P[(i - 1) * step + j + 1] + P[i * step + j + 1] * 2 + P[(i + 1) * step + j + 1] - P[(i - 1) * step + j - 1] - P[i * step + j - 1] * 2 - P[(i + 1) * step + j - 1]);
			PY[i * imageX.step + j * (stepXY / step)] = abs(P[(i + 1) * step + j - 1] + P[(i + 1) * step + j] * 2 + P[(i + 1) * step + j + 1] - P[(i - 1) * step + j - 1] - P[(i - 1) * step + j] * 2 - P[(i - 1) * step + j + 1]);
		}
	}
	addWeighted(imageX, 0.5, imageY, 0.5, 0, imageXY);//融合X、Y方向的梯度	
	convertScaleAbs(imageX, imageX8UC);
	convertScaleAbs(imageY, imageY8UC);
	convertScaleAbs(imageXY, imageXY8UC);   //轉(zhuǎn)換為8bit圖像

	Mat imageSobel;
	Mat x_grad, y_grad;
	Sobel(img, x_grad, CV_16S, 1, 0, 3);
	Sobel(img, y_grad, CV_16S, 0, 1, 3);
	convertScaleAbs(x_grad, x_grad);
	convertScaleAbs(y_grad, y_grad);
	addWeighted(x_grad, 0.5, y_grad, 0.5, 0, imageSobel);

	imshow("Source Image", img);
	imshow("X Direction", imageX8UC);
	imshow("Y Direction", imageY8UC);
	imshow("XY Direction", imageXY8UC);
	imshow("Opencv Soble", imageSobel);
	waitKey(0);

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

addWeighted()函數(shù)是將兩張相同大小,相同類型的圖片融合的函數(shù)。他可以實(shí)現(xiàn)圖片的特效,不多說(shuō)了,直接上圖。
void addWeighted( const CvArr* src1, double alpha,const CvArr* src2, double beta,double gamma, CvArr* dst );
參數(shù)1:src1,第一個(gè)原數(shù)組.
參數(shù)2:alpha,第一個(gè)數(shù)組元素權(quán)重
參數(shù)3:src2第二個(gè)原數(shù)組
參數(shù)4:beta,第二個(gè)數(shù)組元素權(quán)重
參數(shù)5:gamma,圖1與圖2作和后添加的數(shù)值。不要太大,不然圖片一片白。總和等于255以上就是純白色了。
參數(shù)6:dst,輸出圖片
convertScaleAbs()用于實(shí)現(xiàn)對(duì)整個(gè)圖像數(shù)組中的每一個(gè)元素,進(jìn)行如下操作:
convertScaleAbs(
    cv::InputArray src, // 輸入數(shù)組
    cv::OutputArray dst, // 輸出數(shù)組
    double alpha = 1.0, // 乘數(shù)因子
    double beta = 0.0 // 偏移量
 );

5.3 函數(shù)實(shí)現(xiàn) Sobel 算子邊緣檢測(cè)

int main()
{
	//讀取圖像,黑白圖像邊緣檢測(cè)結(jié)果較為明顯
	Mat img = imread("E:\\la.jpg", IMREAD_ANYCOLOR);
	if (img.empty())
	{
		cout << "請(qǐng)確認(rèn)圖像文件名稱是否正確" << endl;
		return -1;
	}
	Mat resultX, resultY, resultXY;
	Sobel(img, resultX, CV_16S, 2, 0, 1);//X方向一階邊緣
	convertScaleAbs(resultX, resultX);
	Sobel(img, resultY, CV_16S, 0, 1, 3);//Y方向一階邊緣
	convertScaleAbs(resultY, resultY);
	resultXY = resultX + resultY;//整幅圖像的一階邊緣
	//顯示圖像
	imshow("原圖", img);
	imshow("resultX", resultX);
	imshow("resultY", resultY);
	imshow("resultXY", resultXY);
	waitKey(0);
	return 0;
}

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

CV_EXPORTS_W void Sobel( 
    InputArray src, 
    OutputArray dst, 
    int ddepth,
    int dx, 
    int dy, 
    int ksize = 3,
    double scale = 1, 
    double delta = 0,
    int borderType = BORDER_DEFAULT 
);
參數(shù)說(shuō)明:
src :輸入圖像;
dst : 輸出與src大小、通道數(shù)相同的圖像;、
ddepth :輸出圖像的深度,必須大于輸入的圖像數(shù)據(jù)類型,參見(jiàn)@ref filter_depth " combined ";在輸入圖像為8位的情況下會(huì)導(dǎo)致導(dǎo)數(shù)被截?cái)唷?dx : x的導(dǎo)數(shù)的階次;
dy : y的導(dǎo)數(shù)的階次;
ksize : 擴(kuò)展Sobel內(nèi)核的大小;它一定是1 3 5或7。
scale : 計(jì)算出的導(dǎo)數(shù)值選擇尺度因子;默認(rèn)情況下是1,不縮放;
delta : 表示在結(jié)果存入目標(biāo)圖(第二個(gè)參數(shù)dst)之前可選的delta值,有默認(rèn)值0。
borderType : 邊界的處理模式,默認(rèn)值:BORDER_DEFAULT。

6、Laplace算子邊緣檢測(cè)(拉普拉斯)

6.1 原理

Laplacian算子具有各方向同性的特點(diǎn),能夠?qū)θ我夥较虻倪吘夁M(jìn)行提取,具有無(wú)方向性的優(yōu)點(diǎn),因此使用Laplacian算子提取邊緣不需要分別檢測(cè)X方向的邊緣和Y方向的邊緣,只需要一次邊緣檢測(cè)即可。利用拉普拉斯算子作邊緣檢測(cè)前最好先對(duì)圖像作一個(gè)高斯濾波,效果會(huì)好不少。

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

Laplace 是導(dǎo)數(shù)算子,會(huì)突出圖像中的急劇灰度變化,抑制灰度緩慢變化區(qū)域,往往會(huì)產(chǎn)生暗色背景下的灰色邊緣和不連續(xù)圖像。將拉普拉斯圖像與原圖疊加,可以得到保留銳化效果的圖像。

6.2 手工實(shí)現(xiàn) Laplace 算子邊緣檢測(cè)

//銳化算法
Mat& imgSharpen(const Mat& img, char* arith)       //arith為3*3模板算子
{
	int rows = img.rows;        //原圖的行
	int cols = img.cols * img.channels();   //原圖的列
	int offsetx = img.channels();       //像素點(diǎn)的偏移量

	static Mat dst = Mat::ones(img.rows - 2, img.cols - 2, img.type());

	for (int i = 1; i < rows - 1; i++)
	{
		const uchar* previous = img.ptr<uchar>(i - 1);
		const uchar* current = img.ptr<uchar>(i);
		const uchar* next = img.ptr<uchar>(i + 1);
		uchar* output = dst.ptr<uchar>(i - 1);
		for (int j = offsetx; j < cols - offsetx; j++)
		{
			output[j - offsetx] =
				saturate_cast<uchar>(previous[j - offsetx] * arith[0] + previous[j] * arith[1] + previous[j + offsetx] * arith[2] +
					current[j - offsetx] * arith[3] + current[j] * arith[4] + current[j + offsetx] * arith[5] +
					next[j - offsetx] * arith[6] + next[j] * arith[7] + next[j - offsetx] * arith[8]);
		}
	}
	return dst;
}


//Laplace輪廓提取算法
Mat myLaplace(Mat src) {
	//獲取圖像屬性
	int nRows = src.rows;
	int nCols = src.cols;
	int dx = 0, dy = 0;
	//定義空白圖像,用于存放Roberts算法提取出來(lái)的輪廓圖
	Mat dst(src.size(), src.type());
	//對(duì)閾值化圖像進(jìn)行遍歷,進(jìn)行Sober算法
	for (int i = 1; i < nRows - 1; i++) {
		for (int j = 1; j < nCols - 1; j++) {
			//防止越界
			dst.at<uchar>(i, j) = saturate_cast<uchar>(src.at<uchar>(i - 1,j) + src.at<uchar>(i + 1, j) + src.at<uchar>(i, j - 1) + src.at<uchar>(i , j - 1) - 4 * src.at<uchar>(i, j));
		}
	}

	return dst;
}


int main()
{
	char arith[9] = { 0, -1, 0, -1, 5, -1, 0, -1, 0 };       //使用拉普拉斯算子
	//讀取圖像,黑白圖像邊緣檢測(cè)結(jié)果較為明顯
	Mat img = imread("E:\\la.jpg", IMREAD_ANYCOLOR);
	if (img.empty())
	{
		cout << "請(qǐng)確認(rèn)圖像文件名稱是否正確" << endl;
		return -1;
	}
	//顯示圖像
	imshow("原圖", img);
	/*img = imgSharpen(img, arith);
	imshow("Sharpen", img);*/
	cvtColor(img, img, COLOR_RGB2GRAY);
	imshow("灰度圖", img);
	//高斯濾波器(模糊/平滑/近似)消除噪點(diǎn)
	GaussianBlur(img, img, Size(3, 3), 0);
	
	img = imgSharpen(img,arith);
	imshow("Sharpen2", img);
	convertScaleAbs(img, img);
	Mat dst = myLaplace(img);
	imshow("Laplace", dst);

	waitKey(0);
	return 0;
}

(1)未使用高斯濾波進(jìn)行降噪

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

(2)使用高斯濾波進(jìn)行降噪,但未銳化

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

(3)使用高斯濾波進(jìn)行降噪,且銳化

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

6.3 函數(shù)實(shí)現(xiàn) Laplace 算子邊緣檢測(cè)

int main() {
    Mat img = imread("E:\\la.jpg", IMREAD_GRAYSCALE);
    if (img.empty()) {
        cerr << "image file read error" << endl;
        return -1;
    }
   // resize(im, im, Size(0, 0), 0.5, 0.5);

    Mat result1, resultGauss, result2;
    // 未使用高斯濾波進(jìn)行邊緣檢測(cè)
    Laplacian(img, result1, -1, 3);
    convertScaleAbs(result1, result1);

    // 先用高斯濾波器進(jìn)行濾波后再進(jìn)行邊緣檢測(cè)
    GaussianBlur(img, resultGauss, Size(3, 3), 0);
    Laplacian(resultGauss, result2, -1, 3);
    convertScaleAbs(result2, result2);
    imshow("原圖", img);
    imshow("result1", result1);
    imshow("result2", result2);

    waitKey(0);
    destroyAllWindows();

    return 0;
}

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

void cv::Laplacian(    InputArray  src,
                       OutputArray  dst,
                       int  ddepth,
                       int  ksize = 1,
                       double  scale = 1,
                       double  delta = 0,
                       int  borderType = BORDER_DEFAULT 
                       )
參數(shù)的意義如下:
src---待提取邊緣的圖像。
dst---輸出圖像,與輸入圖像src具有相同的尺寸和通道數(shù),數(shù)據(jù)類型由第三個(gè)參數(shù)ddepth控制。
ddepth---輸出圖像的數(shù)據(jù)類型(深度)。
ksize---表示Laplacian核的大小。
scale---對(duì)導(dǎo)數(shù)計(jì)算結(jié)果進(jìn)行縮放的縮放因子,默認(rèn)系數(shù)為1,不進(jìn)行縮放。
delta---偏移值,在計(jì)算結(jié)果中加上偏移值。

7、LoG算子邊緣檢測(cè)(高斯拉普拉斯)

7.1 原理

LoG邊緣檢測(cè)算子是David Courtnay Marr和Ellen Hildreth(1980)共同提出的。因此,也稱為邊緣檢測(cè)算法或Marr & Hildreth算子。該算法首先對(duì)圖像做高斯濾波,然后再求其拉普拉斯(Laplacian)二階導(dǎo)數(shù)。即圖像與 Laplacian of the Gaussian function 進(jìn)行濾波運(yùn)算。最后,通過(guò)檢測(cè)濾波結(jié)果的零交叉(Zero crossings)可以獲得圖像或物體的邊緣。因而,也被業(yè)界簡(jiǎn)稱為L(zhǎng)aplacian-of-Gaussian (LoG)算子。
算法描述:LoG算子也就是 Laplace of Gaussian function(高斯拉普拉斯函數(shù))。常用于數(shù)字圖像的邊緣提取和二值化。LoG 算子源于D.Marr計(jì)算視覺(jué)理論中提出的邊緣提取思想, 即首先對(duì)原始圖像進(jìn)行最佳平滑處理, 最大程度地抑制噪聲, 再對(duì)平滑后的圖像求取邊緣。
由于噪聲點(diǎn)(灰度與周圍點(diǎn)相差很大的像素點(diǎn))對(duì)邊緣檢測(cè)有一定的影響,所以效果更好的邊緣檢測(cè)器是LoG算子,也就是Laplacian-Gauss算子。它把的Gauss平滑濾波器和Laplacian銳化濾波器結(jié)合了起來(lái),先平滑掉噪聲,再進(jìn)行邊緣檢測(cè),所以效果會(huì)更好。

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

基于模板的LoG算子:

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

7.2 手工實(shí)現(xiàn) LoG 算子邊緣檢測(cè)


//x,y方向聯(lián)合實(shí)現(xiàn)獲取高斯模板
void generateGaussMask(cv::Mat& Mask, cv::Size wsize, double sigma) {
	Mask.create(wsize, CV_64F);
	int h = wsize.height;
	int w = wsize.width;
	int center_h = (h - 1) / 2;
	int center_w = (w - 1) / 2;
	double sum = 0.0;
	double x, y;
	for (int i = 0; i < h; ++i) {
		y = pow(i - center_h, 2);
		for (int j = 0; j < w; ++j) {
			x = pow(j - center_w, 2);
			//因?yàn)樽詈蠖家獨(dú)w一化的,常數(shù)部分可以不計(jì)算,也減少了運(yùn)算量
			double g = exp(-(x + y) / (2 * sigma * sigma));
			Mask.at<double>(i, j) = g;
			sum += g;
		}
	}
	Mask = Mask / sum;
}


//按二維高斯函數(shù)實(shí)現(xiàn)高斯濾波
///
void GaussianFilter(cv::Mat& src, cv::Mat& dst, cv::Mat window) {
	int hh = (window.rows - 1) / 2;
	int hw = (window.cols - 1) / 2;
	dst = cv::Mat::zeros(src.size(), src.type());
	//邊界填充
	cv::Mat Newsrc;
	cv::copyMakeBorder(src, Newsrc, hh, hh, hw, hw, cv::BORDER_REPLICATE);//邊界復(fù)制

	//高斯濾波
	for (int i = hh; i < src.rows + hh; ++i) {
		for (int j = hw; j < src.cols + hw; ++j) {
			double sum[3] = { 0 };

			for (int r = -hh; r <= hh; ++r) {
				for (int c = -hw; c <= hw; ++c) {
					if (src.channels() == 1) {
						sum[0] = sum[0] + Newsrc.at<uchar>(i + r, j + c) * window.at<double>(r + hh, c + hw);
					}
					else if (src.channels() == 3) {
						cv::Vec3b rgb = Newsrc.at<cv::Vec3b>(i + r, j + c);
						sum[0] = sum[0] + rgb[0] * window.at<double>(r + hh, c + hw);//B
						sum[1] = sum[1] + rgb[1] * window.at<double>(r + hh, c + hw);//G
						sum[2] = sum[2] + rgb[2] * window.at<double>(r + hh, c + hw);//R
					}
				}
			}

			for (int k = 0; k < src.channels(); ++k) {
				if (sum[k] < 0)
					sum[k] = 0;
				else if (sum[k] > 255)
					sum[k] = 255;
			}
			if (src.channels() == 1)
			{
				dst.at<uchar>(i - hh, j - hw) = static_cast<uchar>(sum[0]);
			}
			else if (src.channels() == 3)
			{
				cv::Vec3b rgb = { static_cast<uchar>(sum[0]), static_cast<uchar>(sum[1]), static_cast<uchar>(sum[2]) };
				dst.at<cv::Vec3b>(i - hh, j - hw) = rgb;
			}

		}
	}

}


//DOG高斯差分
///
void DOG1(cv::Mat& src, cv::Mat& dst, cv::Size wsize, double sigma, double k = 1.6) {
	cv::Mat Mask1, Mask2, gaussian_dst1, gaussian_dst2;
	generateGaussMask(Mask1, wsize, k * sigma);//獲取二維高斯濾波模板1
	generateGaussMask(Mask2, wsize, sigma);//獲取二維高斯濾波模板2

	//高斯濾波
	GaussianFilter(src, gaussian_dst1, Mask1);
	GaussianFilter(src, gaussian_dst2, Mask2);

	dst = gaussian_dst1 - gaussian_dst2 - 1;

	cv::threshold(dst, dst, 0, 255, cv::THRESH_BINARY);
}



//DOG高斯差分--使用opencv的GaussianBlur

void DOG2(cv::Mat& src, cv::Mat& dst, cv::Size wsize, double sigma, double k = 1.6) {
	cv::Mat gaussian_dst1, gaussian_dst2;
	//高斯濾波
	cv::GaussianBlur(src, gaussian_dst1, wsize, k * sigma);
	cv::GaussianBlur(src, gaussian_dst2, wsize, sigma);

	dst = gaussian_dst1 - gaussian_dst2;
	cv::threshold(dst, dst, 0, 255, cv::THRESH_BINARY);
}

int main() {
	cv::Mat src = cv::imread("E:\\la.jpg");
	if (src.empty()) {
		return -1;
	}
	if (src.channels() > 1) cv::cvtColor(src, src, CV_RGB2GRAY);
	cv::Mat edge1, edge2;
	DOG1(src, edge1, cv::Size(7, 7), 2);
	DOG2(src, edge2, cv::Size(7, 7), 2);
	cv::namedWindow("src", CV_WINDOW_NORMAL);
	imshow("src", src);
	cv::namedWindow("My_DOG", CV_WINDOW_NORMAL);
	imshow("My_DOG", edge1);

	cv::namedWindow("Opencv_DOG", CV_WINDOW_NORMAL);
	imshow("Opencv_DOG", edge2);
	cv::waitKey(0);
	return 0;

}

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

int main(int argc, char** argv)
{
	Mat src, gray_src, edge, LOGdst;
	src = imread("E:\\la.jpg");
	if (!src.data) {
		printf("could not load image...");
		return -1;
	}
	imshow("src", src);
	cvtColor(src, gray_src, CV_BGR2GRAY);


	Mat gauss_output, gauss_output_2;
	//定義x方向的模糊因子
	float sigma_x = 20.0;    //該參數(shù)決定了鄰接像素的權(quán)重
	float sigma_y = sigma_x;
	//不同的高斯核卷積,實(shí)現(xiàn)了不同尺度特征,可以近似LoG
	GaussianBlur(gray_src, gauss_output, Size(3, 3), sigma_x, sigma_y);
	GaussianBlur(gray_src, gauss_output_2, Size(11, 11), sigma_x, sigma_y);

	imshow("gauss_output", gauss_output);
	//基于LoG方法
	Laplacian(gauss_output, LOGdst, -1, 3, 1.0, 0.0);
	imshow("LoGdst", LOGdst);

	//基于DoG 近似
	Mat DOGdst(src.size(), CV_32S);
	subtract(gauss_output_2, gauss_output, DOGdst);

	convertScaleAbs(DOGdst, DOGdst);
	normalize(DOGdst, DOGdst, 0, 255, NORM_MINMAX, CV_8UC1);

	imshow("DoGdst", DOGdst);
	//基于指針的操作比采用at會(huì)快一個(gè)數(shù)量級(jí)

	//基于自定義模板卷積核的實(shí)現(xiàn),在經(jīng)過(guò)NMS后效果或許會(huì)更好

	Mat LoG_kernel = (Mat_<signed>(5, 5) << 0, 0, -1, 0, 0,
		0, -1, -2, -1, 0,
		-1, -2, 16, -2, -1,
		0, -1, -2, -1, 0,
		0, 0, -1, 0, 0);
	Mat self_define, gauss_output2;
	GaussianBlur(gray_src, gauss_output2, Size(5, 5), 0, 0);
	filter2D(gauss_output2, self_define, CV_32FC1, LoG_kernel);
	convertScaleAbs(self_define, self_define);
	normalize(self_define, self_define, 0, 255, NORM_MINMAX, CV_8UC1);
	imshow("self_define", self_define);


	waitKey(0);
	return 0;
}

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

7.3 函數(shù)實(shí)現(xiàn) LoG 算子邊緣檢測(cè)

//DOG高斯差分--使用opencv的GaussianBlur

void DOG2(cv::Mat& src, cv::Mat& dst, cv::Size wsize, double sigma, double k = 1.6) {
	cv::Mat gaussian_dst1, gaussian_dst2;
	//高斯濾波
	cv::GaussianBlur(src, gaussian_dst1, wsize, k * sigma);
	cv::GaussianBlur(src, gaussian_dst2, wsize, sigma);

	dst = gaussian_dst1 - gaussian_dst2;
	cv::threshold(dst, dst, 0, 255, cv::THRESH_BINARY);
}

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

8、Canny算子邊緣檢測(cè)

8.1 原理

Canny算法也被許多人稱為最佳探測(cè)器,旨在滿足三個(gè)主要標(biāo)準(zhǔn):
(1)低錯(cuò)誤率:這意味著只能很好地檢測(cè)存在的邊緣。
(2)良好的本地化:必須將檢測(cè)到的邊緣像素與實(shí)際邊緣像素之間的距離降至最低。
(3)最小響應(yīng):每條邊距只有一個(gè)檢測(cè)器響應(yīng)。
Canny算子邊緣檢測(cè)有以下步驟:
(1)用高斯濾波器過(guò)濾噪音,平滑圖像
(2)用Sobel等梯度算子計(jì)算梯度幅值和方向
(3)對(duì)梯度幅值進(jìn)行非極大值抑制(細(xì)化邊緣)
(4)用雙閾值算法檢測(cè)和連接邊緣
	如果像素漸變高于閾值上限,則該像素被接受為邊緣
	如果像素漸變值低于下限閾值,則會(huì)拒絕該值。
	如果像素漸變介于兩個(gè)閾值之間,則僅當(dāng)它連接到高于閾值上限的像素時(shí),才會(huì)接受它。

8.2 手工實(shí)現(xiàn)Canny 算子邊緣檢測(cè)

#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

//1 高斯濾波
void Gaussfilter_ly(Mat input_image, Mat& output_image, int Gauss_size, double Sigma)
{
	//保證高斯核大小為大于等于3的奇數(shù)
	if (Gauss_size < 3) Gauss_size = 3;
	else Gauss_size = (int)(Gauss_size / 2) * 2 + 1;

	//生成高斯卷積核
	double** Gausskernel = new double* [Gauss_size];
	for (int i = 0; i < Gauss_size; i++)
	{
		Gausskernel[i] = new double[Gauss_size];
	}
	int center = Gauss_size / 2;
	double sum = 0;

	for (int i = 0; i < Gauss_size; i++)
	{
		for (int j = 0; j < Gauss_size; j++)
		{
			Gausskernel[i][j] = exp(-((i - center) * (i - center) + (j - center) * (j - center)) / (2 * Sigma * Sigma));
			sum += Gausskernel[i][j];
		}
	}
	//高斯卷積核歸一化
	double sum1 = 1 / sum;
	for (int i = 0; i < Gauss_size; i++)
	{
		for (int j = 0; j < Gauss_size; j++)
		{
			Gausskernel[i][j] *= sum1;
		}
	}

	//濾波
	Mat tem_image = input_image.clone();
	int rows = input_image.rows - center;
	int cols = input_image.cols - center;
	for (int i = center; i < rows; i++)
	{
		for (int j = center; j < cols; j++)
		{
			double sum = 0;
			for (int m = -center; m <= center; m++)
			{
				for (int n = -center; n <= center; n++)
				{
					sum += Gausskernel[center + m][center + n] * input_image.at<uchar>(i + m, j + n);
				}
			}
			tem_image.at<uchar>(i, j) = static_cast<uchar>(sum);
		}
	}
	output_image = tem_image;

	//釋放內(nèi)存
	for (int i = 0; i < Gauss_size; i++) delete[] Gausskernel[i];
	delete[] Gausskernel;
}


//2 計(jì)算梯度幅值圖像,方向圖像和邊緣圖像
void Grad_dire_ly(Mat input, Mat& Gradimage, Mat& Direimage)
{
	Mat tempGrad = Mat(input.size(), CV_16U, Scalar(0));
	Mat tempDire = Mat(input.size(), CV_8U, Scalar(0));

	int width = input.cols;
	int height = input.rows;

	for (int i = 1; i < height - 1; i++)
	{
		for (int j = 1; j < width - 1; j++)
		{
			//計(jì)算梯度及梯度幅值
			int gx = input.at<uchar>(i + 1, j - 1) + input.at<uchar>(i + 1, j) + input.at<uchar>(i + 1, j + 1)
				- input.at<uchar>(i - 1, j - 1) - input.at<uchar>(i - 1, j) - input.at<uchar>(i - 1, j + 1);
			int gy = input.at<uchar>(i - 1, j + 1) + input.at<uchar>(i, j + 1) + input.at<uchar>(i + 1, j + 1)
				- input.at<uchar>(i - 1, j - 1) - input.at<uchar>(i, j - 1) - input.at<uchar>(i + 1, j - 1);
			int sum = gx + gy;

			//梯度幅值圖像
			tempGrad.at<ushort>(i, j) = abs(sum);

			//方向圖像,圖像中的坐標(biāo)軸
			double dire = atan2(gy, gx) * 180 / 3.1415926;
			if (dire <= -67.5 || dire >= 67.5) tempDire.at<uchar>(i, j) = 1; //1:水平
			else if (dire > -67.5 && dire < -22.5) tempDire.at<uchar>(i, j) = 2; //2:45
			else if (dire > -22.5 && dire < 22.5) tempDire.at<uchar>(i, j) = 3; //3:垂直
			else tempDire.at<uchar>(i, j) = 4; //4:-45
		}
	}
	Gradimage = tempGrad;
	Direimage = tempDire;
}

//3 非極大值抑制圖像
void Nonmax_suppression_ly(Mat Gradimage, Mat Direimage, Mat& Suppimage)
{
	Mat tempSupp = Mat(Gradimage.size(), Gradimage.type(), Scalar(0));

	int width = Gradimage.cols;
	int height = Gradimage.rows;

	for (int i = 1; i < height - 1; i++)
	{
		for (int j = 1; j < width - 1; j++)
		{
			switch (Direimage.at<uchar>(i, j))
			{
			case 1:
				if (Gradimage.at<ushort>(i, j) >= Gradimage.at<ushort>(i, j - 1) && Gradimage.at<ushort>(i, j) >= Gradimage.at<ushort>(i, j + 1))
					tempSupp.at<ushort>(i, j) = Gradimage.at<ushort>(i, j);
				else
					tempSupp.at<ushort>(i, j) = 0;
				break;
			case 2:
				if (Gradimage.at<ushort>(i, j) >= Gradimage.at<ushort>(i + 1, j - 1) && Gradimage.at<ushort>(i, j) >= Gradimage.at<ushort>(i - 1, j + 1))
					tempSupp.at<ushort>(i, j) = Gradimage.at<ushort>(i, j);
				else
					tempSupp.at<ushort>(i, j) = 0;
				break;
			case 3:
				if (Gradimage.at<ushort>(i, j) >= Gradimage.at<ushort>(i - 1, j) && Gradimage.at<ushort>(i, j) >= Gradimage.at<ushort>(i + 1, j))
					tempSupp.at<ushort>(i, j) = Gradimage.at<ushort>(i, j);
				else
					tempSupp.at<ushort>(i, j) = 0;
				break;
			case 4:
				if (Gradimage.at<ushort>(i, j) >= Gradimage.at<ushort>(i - 1, j - 1) && Gradimage.at<ushort>(i, j) >= Gradimage.at<ushort>(i + 1, j + 1))
					tempSupp.at<ushort>(i, j) = Gradimage.at<ushort>(i, j);
				else
					tempSupp.at<ushort>(i, j) = 0;
				break;
			default:

				break;
			}
		}
	}
	Suppimage = tempSupp;
}

//4 滯后閾值處理(雙閾值)
void doubleThread_ly(Mat Suppimage, Mat& Edgeimage, int th_high, int th_low)
{
	int temp;
	if (th_high < th_low)
	{
		temp = th_high;
		th_high = th_low;
		th_low = temp;
	}

	Mat bw_h = Mat(Suppimage.size(), CV_8UC1, Scalar(0));
	Mat bw_l = Mat(Suppimage.size(), CV_8UC1, Scalar(0));

	int width = Suppimage.cols;
	int height = Suppimage.rows;

	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			if (Suppimage.at<ushort>(i, j) >= th_high)
				bw_h.at<uchar>(i, j) = 255;
			else
				bw_h.at<uchar>(i, j) = 0;
			if (Suppimage.at<ushort>(i, j) >= th_low && Suppimage.at<ushort>(i, j) < th_high)
				bw_l.at<uchar>(i, j) = 255;
			else
				bw_l.at<uchar>(i, j) = 0;
		}
	}

	Mat bw = bw_h.clone();
	for (int i = 1; i < height - 1; i++)
	{
		for (int j = 1; j < width - 1; j++)
		{
			if (bw_h.at<uchar>(i, j) == 255)
			{
				if (bw_l.at<uchar>(i - 1, j - 1) == 255)
					bw.at<uchar>(i - 1, j - 1) = 255;
				if (bw_l.at<uchar>(i - 1, j) == 255)
					bw.at<uchar>(i - 1, j) = 255;
				if (bw_l.at<uchar>(i - 1, j + 1) == 255)
					bw.at<uchar>(i - 1, j + 1) = 255;
				if (bw_l.at<uchar>(i, j - 1) == 255)
					bw.at<uchar>(i, j - 1) = 255;
				if (bw_l.at<uchar>(i, j + 1) == 255)
					bw.at<uchar>(i, j + 1) = 255;
				if (bw_l.at<uchar>(i + 1, j - 1) == 255)
					bw.at<uchar>(i + 1, j - 1) = 255;
				if (bw_l.at<uchar>(i + 1, j) == 255)
					bw.at<uchar>(i + 1, j) = 255;
				if (bw_l.at<uchar>(i + 1, j + 1) == 255)
					bw.at<uchar>(i + 1, j + 1) = 255;
			}
		}
	}

	Edgeimage = bw;
}

//5 canny函數(shù)
void canny_ly(Mat input_image, Mat& output_image, int th_high, int th_low, int Gauss_size, double sigmma)
{
	Mat Gaussimage, Gradimage, Direimage, Suppimage, Edgeimage;
	//1 高斯濾波函數(shù)
	Gaussfilter_ly(input_image, Gaussimage, Gauss_size, sigmma);
	//2 計(jì)算梯度幅值圖像和方向圖像
	Grad_dire_ly(Gaussimage, Gradimage, Direimage);
	//3 非極大值抑制圖像
	Nonmax_suppression_ly(Gradimage, Direimage, Suppimage);
	//4 滯后閾值處理(雙閾值)
	doubleThread_ly(Suppimage, Edgeimage, th_high, th_low);

	output_image = Edgeimage;
}

int main()
{
	Mat src = imread("E:\\la.jpg", 1);//讀取灰度圖像
	if (src.empty())
	{
		cout << "讀取錯(cuò)誤" << endl;
		return -1;
	}
	imshow("原圖", src);

	Mat dst;
	//轉(zhuǎn)灰度圖像
	cvtColor(src, dst, COLOR_BGRA2GRAY);
	imshow("灰度", dst);

	Mat img2;
	canny_ly(dst, img2, 50, 20, 3, 1);
	imshow("Canny", img2);
	waitKey();

	return 0;
}

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

8.3 函數(shù)實(shí)現(xiàn)Canny 算子邊緣檢測(cè)

int main()
{
	Mat src = imread("E:\\la.jpg", 1);//讀取灰度圖像
	if (src.empty())
	{
		cout << "讀取錯(cuò)誤" << endl;
		return -1;
	}
	imshow("原圖", src);

	Mat dst;
	//轉(zhuǎn)灰度圖像
	cvtColor(src, dst, COLOR_BGRA2GRAY);
	imshow("灰度", dst);
	//均值濾波過(guò)濾
	blur(dst, dst, Size(3, 3));
	imshow("高斯濾波", dst);
	//opencv自帶canny檢測(cè)函數(shù)
	Canny(src, dst, 50, 150);
	imshow("Canny", dst);
	waitKey(0);

	return 0;
}

OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)

Canny(
InputArray src, // 8-bit的輸入圖像,也就是單通道圖像
OutputArray edges,// 輸出邊緣圖像, 一般都是二值圖像,背景是黑色
double threshold1,// 低閾值,常取高閾值的1/2或者1/3
double threshold2,// 高閾值
int aptertureSize,// Soble算子的size,通常3x3,取值3
bool L2gradient // 選擇 true表示是L2來(lái)歸一化,否則用L1歸一化,一般我們選擇L1,性能更好
)

部分參考來(lái)源數(shù)字圖像處理(c++ opencv):圖像分割-基本邊緣檢測(cè)–canny邊緣檢測(cè) - 知乎 (zhihu.com)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-453461.html

到了這里,關(guān)于OpenCV數(shù)字圖像處理基于C++:邊緣檢測(cè)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 圖像處理技術(shù):數(shù)字圖像分割 ------ 圖像分割、邊界分割(邊緣檢測(cè))、區(qū)域分割

    圖像處理技術(shù):數(shù)字圖像分割 ------ 圖像分割、邊界分割(邊緣檢測(cè))、區(qū)域分割

    是指根據(jù)灰度、彩色、空間紋理、幾何形狀等特征把圖像劃分 成若干個(gè)互不相交的區(qū)域,使得這些特征在同一區(qū)域內(nèi)表現(xiàn)出一致 性或相似性,而在不同區(qū)域間表現(xiàn)出明顯的不同 分割出來(lái)的區(qū)域應(yīng)該同時(shí)滿足: ?(1)分割出來(lái)的圖像區(qū)域的均勻性和連通性。 ??均勻性是指該

    2024年02月04日
    瀏覽(33)
  • python數(shù)字圖像處理基礎(chǔ)(五)——Canny邊緣檢測(cè)、圖像金字塔、圖像分割

    python數(shù)字圖像處理基礎(chǔ)(五)——Canny邊緣檢測(cè)、圖像金字塔、圖像分割

    梯度是什么? 梯度就是變化的最快的那個(gè)方向 edge = cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient ]]]) 第一個(gè)參數(shù)是需要處理的原圖像,該圖像必須為單通道的灰度圖; 第二個(gè)參數(shù)是閾值1; 第三個(gè)參數(shù)是閾值2。 原理步驟 1)使用高斯濾波器,以平滑圖像,濾除噪

    2024年01月18日
    瀏覽(28)
  • Python-OpenCV中的圖像處理-邊緣檢測(cè)

    Python-OpenCV中的圖像處理-邊緣檢測(cè)

    Canny 邊緣檢測(cè)是一種非常流行的邊緣檢測(cè)算法,是 John F.Canny 在 1986 年提出的。它是一個(gè)有很多步構(gòu)成的算法:噪聲去除、計(jì)算圖像梯度、非極大值抑制、滯后閥值等。 Canny(image: Mat, threshold1, threshold2, edges=…, apertureSize=…, L2gradient=…) 在 OpenCV 中只需要一個(gè)函數(shù): cv2.Canny(),

    2024年02月13日
    瀏覽(35)
  • QT+OpenCV實(shí)現(xiàn)一個(gè)標(biāo)注工具(圖像處理、邊緣檢測(cè))

    QT+OpenCV實(shí)現(xiàn)一個(gè)標(biāo)注工具(圖像處理、邊緣檢測(cè))

    作者是一名QT初學(xué)者,為檢驗(yàn)學(xué)習(xí)成果及完成畢業(yè)設(shè)計(jì),在張老師和學(xué)姐的指導(dǎo)下,開(kāi)發(fā)了這個(gè)標(biāo)注工具。CSDN上很多文章對(duì)我的學(xué)習(xí)提供了極大的幫助,分享這篇文章給需要的人一起學(xué)習(xí)進(jìn)步~ 廢話不多說(shuō),先看看效果: Windows10、Qt5.13.2(編譯器用的是MinGW64_bit)、OpenCV4.1 環(huán)

    2024年02月11日
    瀏覽(28)
  • 我在Vscode學(xué)OpenCV 圖像處理三(圖像梯度--邊緣檢測(cè)【圖像梯度、Sobel 算子、 Scharr 算子、 Laplacian 算子、Canny 邊緣檢測(cè)】)

    我在Vscode學(xué)OpenCV 圖像處理三(圖像梯度--邊緣檢測(cè)【圖像梯度、Sobel 算子、 Scharr 算子、 Laplacian 算子、Canny 邊緣檢測(cè)】)

    這里需要區(qū)分開(kāi)邊緣檢測(cè)和輪廓檢測(cè) 邊緣檢測(cè)并非萬(wàn)能,邊緣檢測(cè)雖然能夠檢測(cè)出邊緣,但邊緣是不連續(xù)的,檢測(cè)到的邊緣并不是一個(gè)整體。圖像輪廓是指將邊緣連接起來(lái)形成的一個(gè)整體,用于后續(xù)的計(jì)算。 OpenCV 提供了查找圖像輪廓的函數(shù) cv2.findContours(),該函數(shù)能夠查找圖

    2024年02月04日
    瀏覽(27)
  • (數(shù)字圖像處理MATLAB+Python)第七章圖像銳化-第三節(jié):高斯濾波與邊緣檢測(cè)

    (數(shù)字圖像處理MATLAB+Python)第七章圖像銳化-第三節(jié):高斯濾波與邊緣檢測(cè)

    高斯函數(shù) :是一種常見(jiàn)的連續(xù)函數(shù),通常用符號(hào) G ( x ) G(x) G ( x ) 表示。它可以用下面的公式定義 G ( x ) = 1 σ 2 π e ? x 2 2 σ 2 G(x)=frac{1}{sigma sqrt{ 2pi }}e^{-frac{x^{2}}{2sigma^{2}}} G ( x ) = σ 2 π ? 1 ? e ? 2 σ 2 x 2 ? 其中, x x x 是自變量, σ sigma σ 是一個(gè)正實(shí)數(shù),表示高斯函

    2024年02月06日
    瀏覽(108)
  • 【圖像處理】基于matlab蟻群聚類圖像邊緣檢測(cè)

    目錄 基于matlab蟻群聚類圖像邊緣檢測(cè) 蟻群聚類是一種模擬自然界中螞蟻群體行為的算法,常用于解決優(yōu)化問(wèn)題。該算法可以用于圖像處理中的邊緣檢測(cè)。下面給出一個(gè)基于MATLAB的蟻群聚類圖像邊緣檢測(cè)的示例代碼。 我們首先讀入待處理圖像,并將其轉(zhuǎn)換為灰度圖像。然后,

    2023年04月22日
    瀏覽(22)
  • 數(shù)字圖像處理【11】OpenCV-Canny邊緣提取到FindContours輪廓發(fā)現(xiàn)

    數(shù)字圖像處理【11】OpenCV-Canny邊緣提取到FindContours輪廓發(fā)現(xiàn)

    本章主要介紹圖像處理中一個(gè)比較基礎(chǔ)的操作:Canny邊緣發(fā)現(xiàn)、輪廓發(fā)現(xiàn) 和 繪制輪廓。概念不難,主要是結(jié)合OpenCV 4.5+的API相關(guān)操作,為往下 \\\"基于距離變換的分水嶺圖像分割\\\" 做知識(shí)儲(chǔ)備。 在講述輪廓之前,要花點(diǎn)時(shí)間學(xué)學(xué)邊緣檢測(cè)提取的一個(gè)著名算法——Canny邊緣提取算法

    2024年02月16日
    瀏覽(24)
  • 【課程介紹】OpenCV 基礎(chǔ)入門教程:圖像讀取、顯示、保存,圖像處理和增強(qiáng)(如濾波、邊緣檢測(cè)、圖像變換),特征提取和匹配,目標(biāo)檢測(cè)和跟蹤

    [ 專欄推薦 ] ?? 《視覺(jué)探索: OpenCV 基礎(chǔ)入門教程》 ?? ??【簡(jiǎn)介】: Opencv 入門課程適合初學(xué)者,旨在介紹 Opencv 庫(kù)的基礎(chǔ)知識(shí)和核心功能。課程包括圖像讀取、顯示、保存,圖像處理和增強(qiáng)(如濾波、邊緣檢測(cè)、圖像變換),特征提取和匹配,目標(biāo)檢測(cè)和跟蹤等內(nèi)容。學(xué)

    2024年02月16日
    瀏覽(2470)
  • 【圖像處理】基于matlab邊緣檢測(cè) Sobel、Roberts、Prewitt

    【圖像處理】基于matlab邊緣檢測(cè) Sobel、Roberts、Prewitt

    Sobel 算子算子包含兩組 3x3 的矩陣,如圖所示: 對(duì)于圖像而言,取 3 行 3 列的圖像數(shù)據(jù),將圖像數(shù)據(jù)與對(duì)應(yīng)位置的算子的值相乘再相加,得到 x 方向的 Gx ,和 y 方向的 Gy ,將得到的 Gx 和 Gy ,平方后相加,再取算術(shù)平方根,得到 Gxy ,近似值為 Gx 和 Gy 絕對(duì)值之和,將計(jì)算得

    2024年02月04日
    瀏覽(33)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包