一、處理邊緣
代碼:
#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。