【OpenCV(C++)】圖像變換:邊緣檢測
邊緣檢測的步驟
- 濾波
邊緣檢測的算法主要是基於圖像強度的一階和二階導數,但導數通常對噪聲很敏感,因此必須採用濾波器來改善與噪聲有關的邊緣檢測器的性能。 - 增強
增強邊緣的基礎是確定圖像各點鄰域的變化值。增強算法可以將圖像灰度點鄰域強度值有顯著變化的點凸顯出來。 - 檢測
經過增強的圖像,往往鄰域中有很多點的梯度值比較大,而在特定的應用中,這些點並不是要找的邊緣點,所以應該採用某種方法來對這些點進行取捨。
Canny算子
canny邊緣檢測算子是一個多級邊緣檢測算法。
最優邊緣檢測的評價標準:低錯誤率、高定位性、最小響應。爲了滿足這些要求,Canny使用了變分法,這是一種尋找滿足特定功能的函數的方法。最優檢測用4個指數函數項的和表示,但是它非常近似於高斯函數的一階導數。
Canny函數利用Canny算子來進行圖像的邊緣檢測操作。
void Canny(
InputArray image,
OutputArray edges,
double threshold1,
double threshold2,
int apertureSize=3,
bool L2gradient=false) ;
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
Mat srcImage = imread("fg.jpg");
imshow("[原圖]Canny邊緣檢測", srcImage);
Mat dstImage, edge, grayImage;
dstImage.create(srcImage.size(), srcImage.type());
cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);
blur(grayImage, edge, Size(3, 3));
Canny(edge, edge, 3, 9, 3);
imshow("[效果圖]Canny邊緣檢測", edge);
waitKey(0);
return 0;
}
運行效果如下:
Sobel算子
Sobel算子是一個主要用於邊緣檢測的離散微分算子。它結合了高斯平滑和微分求導,用來計算圖像灰度函數的近似梯度。在圖像的任何一點使用此算子,都將會產生對應的梯度矢量或是其法矢量。
Sobel函數使用擴展的Sobel算子,來計算一階、二階、三階或混合圖像差分。
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) ;
#include <opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y, dst;
Mat src = imread("fg.jpg");
imshow("【原始圖】sobel邊緣檢測", src);
Sobel(src, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x);
imshow("【效果圖】 X方向Sobel", abs_grad_x);
Sobel(src, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_y, abs_grad_y);
imshow("【效果圖】Y方向Sobel", abs_grad_y);
addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst);
imshow("【效果圖】整體方向Sobel", dst);
waitKey(0);
return 0;
}
運行效果如下:
Laplacian算子
Laplacian算子是n維歐幾里得空間中的一個二階微分算子,定義爲梯度grad的散度div。根據圖像處理的原理可知,二階導數可以用來進行邊緣檢測,因爲圖像是二維的,需要在兩個方向進行求導。使用Laplacian算子將會使求導過程變得簡單。
Laplacian算子的定義:
Laplacian函數可以計算出圖像經過拉普拉斯變換後的結果。
void Laplacian(
InputArray src,
OutputArray dst ,
int ddepth,
int ksize=1,
double scale=1,
double delta=0 ,
intborderType=BORDER_DEFAULT) ;
#include <opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
Mat src, src_gray, dst, abs_dst;
src = imread("fg.jpg");
imshow("【原始圖】圖像Laplace變換", src);
GaussianBlur(src, src, Size(3, 3), 0, 0, BORDER_DEFAULT);
cvtColor(src, src_gray, CV_RGB2GRAY);
Laplacian(src_gray, dst, CV_16S, 3, 1, 0, BORDER_DEFAULT);
convertScaleAbs(dst, abs_dst);
imshow("【效果圖】圖像Laplace變換", abs_dst);
waitKey(0);
return 0;
}
運行效果如下:
scharr濾波器
我們一般直接稱scharr爲濾波器,而不是算子。它在OpenCV中主要是配合Sobel算子的運算而存在的。
使用Scharr濾波器運算符計算x或y方向的圖像差分,其參數變量和Sobel基本上是一樣的,除了沒有ksize核的大小。
void Scharr(
InputArray src,
OutputArray dst,
int ddepth,
int dx,
int dy,
double scale=1 ,
double delta=0 ,
intborderType=BORDER_DEFAULT) ;
#include <opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y, dst;
Mat src = imread("fg.jpg");
imshow("【原始圖】Scharr濾波器", src);
Scharr(src, grad_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x);
imshow("【效果圖】 X方向Scharr", abs_grad_x);
Scharr(src, grad_y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT);
convertScaleAbs(grad_y, abs_grad_y);
imshow("【效果圖】Y方向Scharr", abs_grad_y);
addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst);
imshow("【效果圖】合併梯度後Scharr", dst);
waitKey(0);
return 0;
}
運行效果如下: