OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)
目錄
一、邊緣檢測基礎理論
1、作用:
2、分類
1、基于搜索
?2、基于零穿越
3、算子比較
二、Sobel算子基礎理論
1、作用
2、原理及推導
3、更詳細推導
4、Sobel函數
?二、實戰
1、對x方向微分
2、對y方向微分
3、線性混合
總代碼
其他應用
參考資料
一、邊緣檢測基礎理論
1、作用:
圖像邊緣檢測大幅度地減少了數據量,并且剔除了不相關的信息,保留了圖像重要的結構屬性。
????????邊緣檢測是圖像處理和計算機視覺中的基本問題,邊緣檢測的目的是標識數字圖像中亮度變化明顯的點。圖像屬性中的顯著變化通常反映了屬性的重要事件和變化。邊緣的表現形式如下圖所示:
(縱軸:灰度值; 橫軸:位置信息)
圖像邊緣檢測大幅度地減少了數據量,并且剔除了不相關的信息,保留了圖像重要的結構屬性。有許多方法用于邊緣檢測,它們的絕大部分可以劃分為兩類∶基于搜索和基于零穿越。
2、分類
基于搜索:利用一階導數最大值檢測邊緣
基于零穿越:利用二階導數為0檢測邊緣
1、基于搜索
基于搜索:通過尋找圖像一階導數中的最大值來檢測邊界,然后利用計算結果估計邊緣的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值,代表算法是Sobel算子和Scharr算子。
原理:邊緣附近的像素值會有明顯突變,即變化最大,也就是一階導數最大。那么找到最大的一階導數也就找到了像素變化最大的點,即邊緣點。
?2、基于零穿越
基于零穿越:通過尋找二階導數零穿越來尋找邊界,代表算法是Laplace算子。
原理:在一階導數的基礎上再求一次導,那么此時零點就是變化最大的點,即邊緣點。
3、算子比較
其中,Canny算子永遠的神!(最優邊緣檢測)
二、Sobel算子基礎理論
1、作用
Sobel算子:用于邊緣檢測的離散微分算子。(一階)(結合了高斯平滑和微分)
1、邊緣檢測: Gx 用于檢測縱向邊緣, Gy 用于檢測橫向邊緣.
2、計算法線: Gx 用于計算法線的橫向偏移, Gy 用于計算法線的縱向偏移.
????????Sobel邊緣檢測算法比較簡單,實際應用中效率比canny邊緣檢測效率要高,但是邊緣不如Canny檢測的準確,但是很多實際應用的場合,sobel邊緣卻是首選,Sobel算子是高斯平滑與微分操作的結合體,所以其抗噪聲能力很強,用途較多。尤其是效率要求較高,而對細紋理不太關心的時候。
(注:需要x方向圖像就對y求微分,需要y方向圖像就對x求微分,兩者顛倒)對x方向求導,得到的是y方向的邊緣; 對y方向求導,得到的是x方向的邊緣。
2、原理及推導
因為圖像是二維的,所以需要在兩個方向求導:
垂直方向的邊緣在水平方向的梯度(偏導數)幅值較大
水平方向的邊緣在垂直方向的梯度(偏導數)幅值較大
Sobel算子剛好能描述這個圖像變化。
過程:
先分別求x和y方向sobel算子,再取絕對值(轉換成uint8),然后線性混合。(如圖)
梯度公式:
對于圖像而言,它是離散的,所以h的最小值只能是1了,那么這意味著,圖像中某個像素位置的梯度(以x方向為例)等于它左右兩個像素點的像素之差除以2。
可以看出:后 - 前?
所以:
?
?綜上求出:
例:假設有一行像素是這樣分布的:
123 155 173
那么,像素值為155的像素位置x方向的梯度為(173 - 123)/2 = 25
Schar算子能夠彌補Sobel內核為3時的誤差:(更佳的3*3濾波器:Scahrr ()函數)
3、更詳細推導
?Prewitt算子:x方向和y方向卷積核分別為:
? ? ? ? ? ? ? ? ? ? ? ? ?
sobel算子:在此基礎上加了一些權值,結合了高斯平滑和微分求導。
Sebel算子x方向和y方向的卷積核分別為:
求出了水平方向和豎直方向的梯度Gx和Gy之后
近似的梯度就可以用下面的方法算出來:?
?
4、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 );函數參數解釋:
InputArray src:輸入的原圖像,Mat類型
OutputArray dst:輸出的邊緣檢測結果圖像,Mat型,大小與原圖像相同。
int ddepth:輸出圖像的深度,針對不同的輸入圖像,輸出目標圖像有不同的深度,具體組合如下:
- 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
- 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
- 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
- 若src.depth() = CV_64F, 取ddepth = -1/CV_64F
注:ddepth =-1時,代表輸出圖像與輸入圖像相同的深度。
int dx:int類型dx,x 方向上的差分階數,1或0
int dy:int類型dy,y 方向上的差分階數,1或0
其中,dx=1,dy=0,表示計算X方向的導數,檢測出的是垂直方向上的邊緣;dx=0,dy=1,表示計算Y方向的導數,檢測出的是水平方向上的邊緣。
int ksize:為進行邊緣檢測時的模板大小為ksize*ksize,取值為1、3、5和7,其中默認值為3。特殊情況:ksize=1時,采用的模板為3*1或1*3。
當ksize=3時,Sobel內核可能產生比較明顯的誤差,此時,可以使用 Scharr 函數,該函數僅作用于大小為3的內核。具有跟sobel一樣的速度,但結果更精確,其內核為:
C++:
Sobel(gray, grad_x, CV_16S, 1, 0, 3);// x方向差分階數 y方向差分階數 核大小?python:
x = cv.Sobel(img, cv.CV_16S, 1, 0) y = cv.Sobel(img, cv.CV_16S, 0, 1) # 深度 x方向階數 y方向階數
?二、實戰
首先欣賞下原圖及灰度圖:
src = imread("Resource/test12.jpg");imshow("原圖", src);cvtColor(src, gray, COLOR_RGB2GRAY); //轉變為灰度圖imshow("灰度圖", src);
1、對x方向微分
對x方向微分,得到的是y方向的邊緣。?
//對x方向微分Sobel(gray, grad_x, CV_16S, 1, 0, 3); // x方向差分階數 y方向差分階數 核大小convertScaleAbs(grad_x, abs_grad_x); //可將任意類型的數據轉化為CV_8UC1imshow("【邊緣圖x】", abs_grad_x); //邊緣與梯度方向垂直,所以輸出的邊緣是和我們所計算的某一方向的梯度是垂直的
2、對y方向微分
對y方向微分,得到的是x方向的邊緣。??
//對y方向微分Sobel(gray, grad_y, CV_16S, 0, 1, 3);// x方向差分階數 y方向差分階數 核大小convertScaleAbs(grad_y, abs_grad_y); //可將任意類型的數據轉化為CV_8UC1imshow("邊緣圖y", abs_grad_y);
3、線性混合
//圖像的線性混合addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst);imshow("線性混合", dst);
總代碼
C++:
//Sobel算子(微分)
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;int main()
{Mat src, dst, gray, grad_x, grad_y, abs_grad_x, abs_grad_y;src = imread("Resource/test12.jpg");imshow("原圖", src);cvtColor(src, gray, COLOR_RGB2GRAY); //轉變為灰度圖imshow("灰度圖", gray);//對x方向微分Sobel(gray, grad_x, CV_16S, 1, 0, 3); // x方向差分階數 y方向差分階數 核大小convertScaleAbs(grad_x, abs_grad_x); //可將任意類型的數據轉化為CV_8UC1imshow("邊緣圖x", abs_grad_x); //邊緣與梯度方向垂直,所以輸出的邊緣是和我們所計算的某一方向的梯度是垂直的//對y方向微分Sobel(gray, grad_y, CV_16S, 0, 1, 3);// x方向差分階數 y方向差分階數 核大小convertScaleAbs(grad_y, abs_grad_y); //可將任意類型的數據轉化為CV_8UC1imshow("邊緣圖y", abs_grad_y);//圖像的線性混合addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst);imshow("線性混合", dst);waitKey(0);
}
python:
# 邊緣檢測(Sobel、Laplace、Canny)
import cv2 as cv# Sobel一階微分算子
def Sobel():# 1、對X和Y方向求微分x = cv.Sobel(img, cv.CV_16S, 1, 0)y = cv.Sobel(img, cv.CV_16S, 0, 1)# 深度 x方向階數 y方向階數# 2、取絕對值absX = cv.convertScaleAbs(x) # 轉回uint8absY = cv.convertScaleAbs(y)# 3、線性混合dst = cv.addWeighted(absX, 0.5, absY, 0.5, 0)# 比例 比例 常數# 4、顯示cv.imshow("absX", absX)cv.imshow("absY", absY)cv.imshow("dst", dst)if __name__ == '__main__':# 讀取圖片img = cv.imread("Resource/test5.jpg")cv.imshow("img", img)Sobel() #Sobel一階微分算子cv.waitKey(0)
其他應用
x方向梯度(使豎直的黑條特征變得明顯):
?
參考資料
https://blog.csdn.net/weixin_44586473/article/details/93229385?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162895010516780271586263%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=162895010516780271586263&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-93229385.ecpm_v1_rank_v29&utm_term=sobel&spm=1018.2226.3001.4187
https://www.baidu.com/link?url=14oBZycvBio7eVQbSDl-XWg_eyg92lbbpOCNxZu5g-rxq39fby3yvmI-_OYGULcmmQ0SuvMyGJU0dBPCZBeEYKHPi_pldWGT23zLbLry6DG&wd=&eqid=efae8130000106120000000561187322
https://www.bilibili.com/video/BV1Fo4y1d7JL?p=34&spm_id_from=pageDriver
總結
以上是生活随笔為你收集整理的OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构(8-3)二叉排序树(查找、插入
- 下一篇: OpenCV(十六)边缘检测2 -- L