OpenCV C++開發 第二節:圖像處理(八、處理邊緣、Sobel算子、Laplance算子、Canny邊緣檢測)

一、處理邊緣

代碼:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
int main(int argc, char** argv) {
	Mat src, dst;
	src = imread("C:\\Users\\Administrator\\Desktop\\test.png");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}

	char INPUT_WIN[] = "input image";
	char OUTPUT_WIN[] = "Border Demo";
	namedWindow(INPUT_WIN, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_WIN, CV_WINDOW_AUTOSIZE);
	imshow(INPUT_WIN, src);
	
	int top = (int)(0.05*src.rows);
	int bottom = (int)(0.05*src.rows);
	int left = (int)(0.05*src.cols);
	int right = (int)(0.05*src.cols);
	RNG rng(12345);
	int borderType = BORDER_DEFAULT;

	int c = 0;
	while (true) {
		c = waitKey(500);
		// ESC
		if ((char)c == 27) {
			break;
		}
		if ((char)c == 'r') {
			borderType = BORDER_REPLICATE;
		} else if((char)c == 'w') {
			borderType = BORDER_WRAP;
		} else if((char)c == 'c') {
			borderType = BORDER_CONSTANT;
		}
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		copyMakeBorder(src, dst, top, bottom, left, right, borderType, color);//這兒
		imshow(OUTPUT_WIN, dst);
	}
	waitKey(0);
	return 0;
}

首先,爲什麼要做邊緣處理。我們在做卷積計算時,圖像卷積的時候邊界像素,不能被卷積操作,原因在於邊界像素沒有完全跟kernel重疊,所以當3x3濾波時候有1個像素的邊緣沒有被處理,5x5濾波的時候有2個像素的邊緣沒有被處理。所以要在圖像卷積前加入固定寬度的邊緣。

以上代碼中主要的幾個知識點解釋下:

1.copyMakeBorder(src, dst, top, bottom, left, right, borderType, color);

borderType是該邊緣的類型

(1)BORDER_DEFAULT

默認的,如下圖,觀察這個女的肩膀,自己理解

(2)BORDER_REPLICATE

填充邊緣像素用已知的邊緣像素值,如下圖。

(3)BORDER_WRAP

用另外一邊的像素來補償填充。

(4)BORDER_CONSTANT

填充邊緣用指定像素值

二、Sobel算子

代碼:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
int main(int argc, char** argv) {
	Mat src, dst;
	src = imread("C:\\Users\\Administrator\\Desktop\\test.png");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}

	char INPUT_TITLE[] = "input image";
	char OUTPUT_TITLE[] = "sobel-demo";
	namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE);
	imshow(INPUT_TITLE, src);

	Mat gray_src;
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	cvtColor(dst, gray_src, CV_BGR2GRAY);
	imshow("gray image", gray_src);

	Mat xgrad, ygrad;
	//Scharr(gray_src, xgrad, CV_16S, 1, 0);
	//Scharr(gray_src, ygrad, CV_16S, 0, 1);

	 Sobel(gray_src, xgrad, CV_16S, 1, 0, 3);
	 Sobel(gray_src, ygrad, CV_16S, 0, 1, 3);
	convertScaleAbs(xgrad, xgrad);
	convertScaleAbs(ygrad, ygrad);
	imshow("xgrad", xgrad);
	imshow("ygrad", ygrad);

	Mat xygrad = Mat(xgrad.size(), xgrad.type());
	printf("type : %d\n", xgrad.type());
	int width = xgrad.cols;
	int height = ygrad.rows;
	for (int row = 0; row < height; row++) {
		for (int col = 0; col < width; col++) {
			int xg = xgrad.at<uchar>(row, col);
			int yg = ygrad.at<uchar>(row, col);
			int xy = xg + yg;
			xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy);
		}
	}
	//addWeighted(xgrad, 0.5, ygrad, 0.5, 0, xygrad);
	imshow(OUTPUT_TITLE, xygrad);

	waitKey(0);
	return 0;
}

Sobel算子其實就是一個邊緣計算的計算方法,如何判斷是邊緣呢,例如當相鄰像素點顏色變化程度突變大,到一個臨界值,說明這個是邊緣,如下圖,這是高中的斜率知識,就是一階導數

以上代碼中主要的幾個知識點解釋下:

1.Sobel(gray_src, xgrad, CV_16S, 1, 0, 3);

Sobel算子的主要方法。gray_src是輸入圖像。xgrad是輸出圖像。CV_16S是圖像深度,有CV_8U、CV_16U、CV_32F、CV_64F。1是x方向幾階導數,就是斜率。0是y方向幾階導數。3是算子kernel,必須是1、3、5、7

2.convertScaleAbs(xgrad, xgrad);

計算圖像的絕對像素值,意思就是負數取正。

三、Laplance算子

代碼:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
int main(int argc, char** argv) {
	Mat src, dst;
	src = imread("C:\\Users\\Administrator\\Desktop\\test.png");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}

	char input_title[] = "input image";
	char output_title[] = "Laplaiance Result";
	namedWindow(input_title, CV_WINDOW_AUTOSIZE);
	imshow(input_title, src);

	Mat gray_src, edge_image;
	GaussianBlur(src, dst, Size(3, 3), 0, 0);
	cvtColor(dst, gray_src, CV_BGR2GRAY);

	Laplacian(gray_src, edge_image, CV_16S, 3);
	convertScaleAbs(edge_image, edge_image);

	threshold(edge_image, edge_image, 0, 255, THRESH_OTSU | THRESH_BINARY);
	namedWindow(output_title, CV_WINDOW_AUTOSIZE);
	imshow(output_title, edge_image);

	waitKey(0);
	return 0;
}

與上面啊類似,也是計算邊緣的算法,這裏是二階導數,如下圖。

以上代碼中主要的幾個知識點解釋下:

1.Laplacian(gray_src, edge_image, CV_16S, 3);

Laplacian算子的主要方法,3是算子kernel,必須是1、3、5、7。

2.convertScaleAbs(edge_image, edge_image);

計算絕對值。

四、Canny邊緣檢測

代碼:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
Mat src, gray_src, dst;
int t1_value = 50;
int max_value = 255;
const char* OUTPUT_TITLE = "Canny Result";
void Canny_Demo(int, void*);
int main(int argc, char** argv) {
	src = imread("C:\\Users\\Administrator\\Desktop\\test.png");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}

	char INPUT_TITLE[] = "input image";
	namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE);
	imshow(INPUT_TITLE, src);

	cvtColor(src, gray_src, CV_BGR2GRAY);
	createTrackbar("Threshold Value:", OUTPUT_TITLE, &t1_value, max_value, Canny_Demo);
	Canny_Demo(0, 0);

	waitKey(0);
	return 0;
}

void Canny_Demo(int, void*) {
	Mat edge_output;
	blur(gray_src, gray_src, Size(3, 3), Point(-1, -1), BORDER_DEFAULT);
	Canny(gray_src, edge_output, t1_value, t1_value * 2, 3, false);//這兒

	//dst.create(src.size(), src.type());
	//src.copyTo(dst, edge_output);
	// (edge_output, edge_output);
	imshow(OUTPUT_TITLE, ~edge_output);
}

輸出結果一般都是二值圖像。背景是黑色。

以上代碼中主要的幾個知識點解釋下:

1.Canny(gray_src, edge_output, t1_value, t1_value * 2, 3, false);

Canny邊緣獲取的主要方法。gray_src是輸入圖像。edge_output是輸出圖像。t1_value是低閥值,是高閥值的二分之一或者三分之一。t1_value*2是高閥值。3是算子的size。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章