1.17【OpenCV圖像處理】Sobel運算元

Sobel()求導(求梯度)、Scharr()、邊緣

1.17.1 卷積應用-圖像邊緣提取

邊緣 – 是像素值發生躍遷的地方(變化率最大處,導數最大處),是圖像的顯著特徵之一,在圖像特徵提取、對象檢測、模式識別等方面都有重要的作用。

捕捉/提取邊緣 – 對圖像求它的一階導數:delta = f(x) – f(x-1), delta越大,說明像素在X方向變化越大,邊緣信號越強。

檢測邊緣 - 可以通過定位梯度值大於鄰域的像素的方法找到(或者推廣到大於一個閥值)。

求導(梯度值)方法 - 用Sobel運算元進行卷積操作就行。

1.17.2 Sobel運算元和Scharr

(1)Sobel運算元:是離散微分運算元(discrete differentiation operator),用來計算圖像灰度的近似梯度,梯度越大越有可能是邊緣。

Soble運算元的功能集合了高斯平滑和微分求導,又被稱為一階微分運算元,求導運算元,在水平和垂直兩個方向上求導,得到的是圖像在X方法與Y方向梯度圖像。

缺點:比較敏感,容易受影響,要通過高斯模糊(平滑)來降噪。

運算元是通過權重不同來擴大差異。

梯度計算:(在兩個方向求導,假設被作用圖像為 I)

水平變化: 將 I 與一個奇數大小的內核 進行卷積。比如,當內核大小為3時, 的計算結果為:

垂直變化: 將:math:I 與一個奇數大小的內核 進行卷積。比如,當內核大小為3時, 的計算結果為:

在圖像的每一點,結合以上兩個結果求出近似梯度:

有時也用下面更簡單公式代替,計算速度快:(最終圖像梯度)。

(2)Scharr:當內核大小為3時, 以上Sobel內核可能產生比較明顯的誤差(畢竟,Sobel運算元只是求取了導數的近似值)。 為解決這一問題,OpenCV提供了 Scharr 函數,但該函數僅作用於大小為3的內核。該函數的運算與Sobel函數一樣快,但結果卻更加精確,不怕干擾,其內核為:

(3)Sobel/Scharr提取邊緣(求導)步驟:

1)高斯模糊平滑降噪:

GaussianBlur( src, dst, Size(3,3), 0, 0, BORDER_DEFAULT );

2)轉灰度:

cvtColor( src, gray, COLOR_RGB2GRAY );

3)求X和Y方向的梯度(求導):

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

Sobel(gray_src, ygrad, CV_16S, 0, 1, 3);

Scharr(gray_src, xgrad, CV_16S, 1, 0);

Scharr(gray_src, ygrad, CV_16S, 0, 1);

4)像素取絕對值:

convertScaleAbs(A, B); //計算圖像A的像素絕對值,輸出到圖像B

5)相加X和Y,得到綜合梯度,稱為振幅圖像:

addWeighted( A, 0.5,B, 0.5, 0, AB); //混合權重相加,效果較差

或者循環獲取像素,每個點直接相加,效果更好。

1.17.3 API說明

(1)void Sobel(InputArray src, OutputArray dst, int ddepth, int xorder, int yorder, int ksize=3, double scale=1, double delta=0, int borderType=BORDER_DEFAULT );

Sobel (

InputArray Src // 輸入圖像

OutputArray dst// 輸出圖像,大小與輸入圖像一致

int ddepth // 輸出圖像深度,輸入8U有正負一起可能值很大,最好是CV_16S/CV_32F

Int dx. // X方向,幾階導數(x是1,y是0時取X方向)

int dy // Y方向,幾階導數.

int ksize, //SOBEL運算元kernel大小,必須是1、3、5、7、

double scale = 1, //比例放大或縮小几倍

double delta = 0, //額外加的常量值

int borderType = BORDER_DEFAULT //邊界默認

)

Sobel(gray_src, xgrad, CV_16S, 1, 0, 3); //Sobel運算元求X方向的梯度(CV_16S改寫成-1與輸入一樣8U類型的話會漏掉很多信息,效果精準度會變差)nSobel(gray_src, ygrad, CV_16S, 0, 1, 3); //Sobel運算元求Y方向的梯度n

(2)Scharr (

InputArray Src // 輸入圖像

OutputArray dst// 輸出圖像,大小與輸入圖像一致

int depth // 輸出圖像深度.

Int dx. // X方向,幾階導數

int dy // Y方向,幾階導數.

double scale = 1

double delta = 0

int borderType = BORDER_DEFAULT

)

Scharr(gray_src, xgrad, CV_16S, 1, 0); //Scharr運算元求X方向的梯度,邊緣更得到加強,幾乎所有邊緣都顯出來了nScharr(gray_src, ygrad, CV_16S, 0, 1); //Scharr運算元求Y方向的梯度n


完整程序:

/*1.17 Sobel運算元*/n#include <opencv2/opencv.hpp>n#include <iostream> n#include <math.h>nusing namespace cv; //使用cv命名空間nnint main(int argc, char** argv) { //argc 表示命令行輸入參數的個數(以空白符分隔),argv中存儲了所有的命令行參數nMat src, dst, gray_src;n src = imread("E:/OpenCV/testimage/test7.jpg");nif (src.empty()) {n printf("could not load image...n");nreturn -1;n }n namedWindow("input image", CV_WINDOW_AUTOSIZE);n imshow("input image", src);nnMat xgrad, ygrad, xygrad, sharpen;n GaussianBlur(src, dst, Size(3, 3), 0, 0); //高斯模糊平滑n cvtColor(src, gray_src, CV_BGR2GRAY); //轉灰度nSobel(gray_src, xgrad, CV_16S, 1, 0, 3); //Sobel運算元求X方向的梯度(CV_16S改寫成-1與輸入一樣8U類型的話會漏掉很多信息,效果精準度會變差)nSobel(gray_src, ygrad, CV_16S, 0, 1, 3); //Sobel運算元求Y方向的梯度n// Scharr(gray_src, xgrad, CV_16S, 1, 0); //Scharr運算元求X方向的梯度,邊緣更得到加強,幾乎所有邊緣都顯出來了n// Scharr(gray_src, ygrad, CV_16S, 0, 1); //Scharr運算元求Y方向的梯度nconvertScaleAbs(xgrad, xgrad); // 計算圖像像素絕對值n convertScaleAbs(ygrad, ygrad); // 計算圖像像素絕對值n imshow("xgrad image", xgrad);n imshow("ygrad image", ygrad);nn/*法一:混合權重相加,效果較差*/n addWeighted(xgrad, 0.5, ygrad, 0.5, 0, xygrad); //混合權重相加,效果較差n imshow("hunhe_xygrad image", xygrad);nnaddWeighted(xygrad, 0.5, gray_src, 0.5, 0, sharpen); //灰度圖像銳化處理,圖像和sobel圖像疊加,增強圖像的邊緣n imshow("sharpen image", sharpen);nn/*法二:循環獲取像素,每個點直接相加,效果更好*/nMat addxygrad = Mat(xgrad.size(), xgrad.type()); //type是個函數要加(),變數不用加n printf("type: %d", xgrad.type()); // 0 就是CV_8Unint width = xgrad.cols;nint height = ygrad.rows;nfor (int row = 0; row < height; row++) {nfor (int col = 0; col < width; col++) {nint xg = xgrad.at<uchar>(row, col);nint yg = ygrad.at<uchar>(row, col);nint xy = xg + yg; //各直接相加,效果比混合相加好n addxygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy); //保證值在0-255之間n }n }n imshow("add_xygrad image", addxygrad);//最亮的地方如果點點,是因為有被截斷,類型不一致造成,還有可能值超出了0-255之間nn waitKey(0);nreturn 0;n}n

運行結果:

推薦閱讀:

如何用ps實現這張圖片中的致幻效果?
1.30【OpenCV圖像處理】圖像矩
[171111] Python OpenCV 中 SIFT 特徵點檢測和匹配
如何用 PS 把這種線條手摳出來?
Byakuren:一個 C 實現的主題色提取庫

TAG:OpenCV | 图像处理 | 图像分割 |