執行邊緣檢測的三個基本步驟:
1、爲降噪對圖像進行平滑處理。(導數對噪聲具有敏感性。圖像的正負分量檢測困難)
2、邊緣點的檢測。(提取邊緣點的潛在候選者)
3、邊緣定位。(從候選者中選出真是邊緣點成員)
基本算子:梯度算子(表徵某點邊緣強度和方向,又稱邊緣檢測子)
較爲簡單的邊緣檢測算子模板:
sobel模板能較好地抑制噪聲。
常用絕對值來近似梯度幅值,保持灰度級的相對變化,代價是導致濾波器不再是各向同性的。
sobel計算過程
Opencv庫函數調用方法:
void Sobel (
InputArray src,
OutputArray dst,
int ddepth,
int dx, x方向上的差分階數
int dy, y方向上的差分階數
int ksize=3, 核的大小,爲奇數
double scale=1, 計算導數時的縮放因子
double delta=0, 將結果存入目標圖像前可選的值
int borderType=BORDER_DEFAULT ) ;
int main()
{
// Read image 讀取圖像
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN); //字體爲綠色
//載入原圖
Mat srcImage = imread("D:\\opencv_picture_test\\形態學操作\\coin_inv.png",0); //讀取灰度圖
//判斷圖像是否加載成功
if (srcImage.empty())
{
cout << "圖像加載失敗!" << endl;
return -1;
}
else
cout << "圖像加載成功!" << endl << endl;
Mat gradx, grady;
Mat abs_gradx, abs_grady;
Mat dstImage;
//求x方向的梯度
Sobel(srcImage,gradx,CV_16S,1,0,3,1,1,BORDER_DEFAULT);// x方向1階差分 y方向0 核大小3
convertScaleAbs(gradx, abs_gradx); //絕對值
//求y方向的梯度
Sobel(srcImage, grady, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);// x方向1階差分 y方向0 核大小3
convertScaleAbs(grady, abs_grady); //絕對值
addWeighted(abs_gradx,0.5, abs_grady,0.5,0,dstImage);
imshow("srcImage", srcImage);
imshow("x方向", abs_gradx);
imshow("y方向", abs_grady);
imshow("整體", dstImage);
waitKey(0);
return 0;
}
經過ImageWatch放大發現,提取出的邊緣比較粗。
減少精密細節的兩種方法:
1、對圖像進行平滑處理(例如用均值濾波,得到主要邊緣)
2、對梯度圖像進行閾值處理(梯度幅值大於等於閾值爲黑白,小於閾值爲黑),不過這一方法容易斷線。
當爲了突出主要邊緣並儘可能維護連續性時,平滑處理和閾值處理兩者都要使用。
roberts、prewitt,sobel算子都是以一個或多個模板進行濾波,而未對圖像特性和噪聲內容採取防護措施。
接下來介紹一種最優的邊緣檢測方法:canny算子
●低錯誤率:標識出儘可能多的實際邊緣,同時儘可能地減少噪聲產生的誤報。
●高定位性:標識出的邊緣要與圖像中的實際邊緣儘可能接近。
●最小響應:圖像中的邊緣只能標識-一次,並且可能存在的圖像噪聲不應標
識爲邊緣。
canny算子計算過程
Opencv庫函數調用方法:
void Canny (InputArray image , OutputArray edges, double threshold1,
double threshold2 , 兩個閾值較小的用於邊緣連接,較大的用於控制強邊緣的初始端,一般比例控制在3:1或者2:1
int apertureSize=3, sobel核大小
bool L2gradient=false )
int main()
{
// Read image 讀取圖像
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN); //字體爲綠色
//載入原圖
Mat srcImage = imread("D:\\opencv_picture_test\\形態學操作\\coin_inv.png",0); //讀取灰度圖
//判斷圖像是否加載成功
if (srcImage.empty())
{
cout << "圖像加載失敗!" << endl;
return -1;
}
else
cout << "圖像加載成功!" << endl << endl;
Mat dstImage;
Canny(srcImage,dstImage,100,33,3,false);
imshow("srcImage", srcImage);
imshow("整體", dstImage);
waitKey(0);
return 0;
}
效果:
經過ImageWatch放大,可以發現,邊緣只有一格
總的看來,canny算子確實具有優越性。
laplace計算過程
3*3孔徑的模板:
opencv庫函數調用:
int main()
{
// Read image 讀取圖像
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN); //字體爲綠色
//載入原圖
Mat srcImage = imread("D:\\opencv_picture_test\\形態學操作\\coin_inv.png", 0); //讀取灰度圖
//判斷圖像是否加載成功
if (srcImage.empty())
{
cout << "圖像加載失敗!" << endl;
return -1;
}
else
cout << "圖像加載成功!" << endl << endl;
Mat dstImage,abs_dst;
GaussianBlur(srcImage,srcImage,Size(3,3),0); //高斯模糊
Laplacian(srcImage,dstImage,CV_16S,3,1,0);
convertScaleAbs(dstImage, abs_dst);
imshow("srcImage", srcImage);
imshow("整體", dstImage);
waitKey(0);
return 0;
}