僅自學做筆記用,后續(xù)有錯誤會更改
理論
- 卷積的應用 - 圖像邊緣提?。?/li>
- 邊緣是什么:是像素值發(fā)生躍遷的地方, 是圖像的顯著特征之一, 再圖像特征提取丶對象檢測丶模式識別等方面都有重要作用
- 如何捕捉/提取邊緣:對圖像求它的一階導數(shù),delta = f(x) - f(x-1), delta值越大, 說明像素在x方向變化越大,邊緣信號越強
- 如果你已經(jīng)忘記了數(shù)學求導什么的概念, 也不用擔心, 直接用Sobel算子進行卷積操作就可以了!
- Sobel算子
- 是離散微分算子(discrete differentiation operator), 用來計算圖像灰度的近似梯度
- Sobel算子功能集合了 高斯模糊和微分求導
- 又被稱為一階微分算子,求導算子,在水平和垂直兩個方向求導,得到圖像X方向與Y方向的梯度圖像
- 求取導數(shù)的近似值(上圖的實際應用公式), kernel=3時不是很準確,容易受到干擾, Opencv使用改進版本Scharr函數(shù), 算子如下:
- Sobel算子邊緣提取步驟:
- 先把原圖像進行高斯模糊操作
- 再把圖像轉(zhuǎn)灰度圖
- 再通過Sobel算子求X梯度和Y梯度
- 線性混合X與Y的梯度圖(可以不使用addweighted接口,而是手動去寫, 去掉權(quán)重α與1-α的影響,可以使最后的圖更明顯),得到最終的振幅圖像
相關(guān)API
cv::Sobel(
InputArray src, //輸入圖像
OutputArray dst, //輸出圖像
int depth, //輸出圖像深度,填-1表示跟輸入圖像一致, 由于灰度圖是CV_8U,所以Sobel一般使用CV_16S/CV_32F, 需要比輸入的灰度圖的深度更高, 結(jié)果才會更明顯
int dx, //x方向,幾階導數(shù), sobel取1
int dy, //y方向,幾階導數(shù), sobel取1
int ksize, //算子(kernel)大小,Sobel算子必須是奇數(shù), 常見的是3
double scale = 1, //輸出圖像放大或縮小倍數(shù)
double delta = 0, //偏移量
int borderType = BORDER_DEFAULT
)
cv::Scharr(
InputArray src, //輸入圖像
OutputArray dst, //輸出圖像
int depth, //輸出圖像深度,填-1表示跟輸入圖像一致, 由于灰度圖是CV_8U,所以Sobel一般使用CV_16S/CV_32F, 需要比輸入的灰度圖的深度更高, 結(jié)果才會更明顯
int dx, //x方向,幾階導數(shù), sobel取1
int dy, //y方向,幾階導數(shù), sobel取1
int ksize, //算子(kernel)大小,Sobel算子必須是奇數(shù), 常見的是3
double scale = 1, //輸出圖像放大或縮小倍數(shù)
double delta = 0, //偏移量
int borderType = BORDER_DEFAULT
)
代碼示例文章來源:http://www.zghlxwxcb.cn/news/detail-443359.html
using namespace cv;
int main(int argc, char** argv){
Mat src,dst;
int ksize = 0;
src = imread(...);
if( !src.data ){
return -1;
}
//原圖
char INPUT_WIN[] = "input image";
namedWindow(INPUT_WIN, CV_WINDOW_AUTOSIZE);
imshow(INPUT_WIN, src);
//先高斯模糊, 再轉(zhuǎn)灰度圖
Mat gray_src;
GaussianBlur(src, dst, Size(3,3), 0, 0);
cvtColor(dst, gray_src, CV_BGR2GRAY);
imshow("gray image",gray_src);
/*//cv::Scharr操作, 可以看最后的效果截圖, 它的提取效果非常強烈, 根本不怕干擾
Mat xgrad, ygrad;
Scharr(gray_src, xgrad, CV_16S, 1, 0);
Scharr(gray_src, ygrad, CV_16S, 0, 1);*/
//cv::Sobel操作(輸出深度為CV_16S,且有convertScaleAbs轉(zhuǎn)換)
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); //這里為什么用CV_16S, 因為灰度圖是CV_8U,我們設置輸出圖像的深度比灰度圖更大, 就可以容納更大的特征值, 提取效果也就更明顯, 你也可以填-1等同于輸入圖像的深度,但是反正不能小于輸入圖像的深度。
Sobel(gray_src, ygrad, CV_16S, 0, 1, 3);
convertScaleAbs(xgrad, xgrad); //這個函數(shù)的作用是保證Sobel算子操作過后有些負數(shù)結(jié)果值不被置為0, 也就是不被截取掉
convertScaleAbs(ygrad, ygrad);
imshow("xgrad", xgrad);
imshow("ygrad", ygrad);
/*//cv::Sobel操作(輸出深度填-1,且沒有convertScaleAbs轉(zhuǎn)換)
Mat xgrad, ygrad;
Sobel(gray_src, xgrad, -1, 1, 0, 3);
Sobel(gray_src, ygrad, -1, 0, 1, 3);
imshow("xgrad", xgrad);
imshow("ygrad", ygrad);*/
//最終線性混合圖:手動寫線性混合,去掉權(quán)重影響, 可以看最后的效果截圖,更明顯
Mat xygrad = Mat(xgrad.size(), xgrad.type());
int width = xgrad.cols;
int height = ygrad.rows;
for(int row = 0; row < weight; row++){
for(int col = 0;col < width;col++){
int xg = xgrad.at<uchar>(row, col);
int yg = ygrad.at<uchar>(row, col); //因為灰度圖是CV_8U,所以用uchar
int xy = xg + yg; //直接相加, 沒有權(quán)重影響, 更亮更明顯
xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy); //saturate_cast這個東西的作用是保證最終值不超過0~255
}
}
imshow("Final Result", xygrad);
/*//最終線性混合圖:直接調(diào)用addWeighted接口
Mat xygrad;
addWeighted(xgrad, 0.5, ygrad, 0.5, 0, xygrad);
imshow("Final Result", xygrad);*/
waitKey(0);
return 0;
}
效果截圖:
使用cv::Sobel(兩個方向的輸出深度都填CV_16S且有convertScaleAbs轉(zhuǎn)換) x梯度圖 與 y梯度圖:
使用cv::Sobel(兩個方向的輸出深度都填-1且沒有convertScaleAbs轉(zhuǎn)換) x梯度圖 與 y梯度圖:
使用cv::Sobel(兩個方向的輸出深度都填CV_16S且有convertScaleAbs轉(zhuǎn)換, 直接調(diào)用addWeighted接口) 最終線性混合圖:
使用cv::Sobel(兩個方向的輸出深度都填CV_16S且有convertScaleAbs轉(zhuǎn)換, 手動寫線性混合) 最終線性混合圖:
使用cv::Scharr(兩個方向的輸出深度都填CV_16S且有convertScaleAbs轉(zhuǎn)換, 且手動寫線性混合) 最終線性混合圖:文章來源地址http://www.zghlxwxcb.cn/news/detail-443359.html
到了這里,關(guān)于【OpenCV學習】第16課:圖像邊緣提取 - Sobel算子詳細剖析(圖像梯度)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!