程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

八、opencv-python圖像處理高級操作(4)——邊緣檢測

編輯:Python

文章目錄

  • 學習目標
    • 了解Sobel算子,Scharr算子和拉普拉斯算子
    • 掌握canny邊緣檢測的原理及應用
  • 一、邊緣檢測的原理
    • 1、基於搜索
    • 2、基於零穿越
  • 二、Sobel檢測算子
    • 1、原理及方法論述
    • 2、應用
  • 三、Laplacian算子
  • 四、canny邊緣檢測
    • 1、Canny算法的原理
    • 2、應用
  • 總結:
    • 1、邊緣檢測的原理
      • (1)基於搜索
      • (2)基於零穿越
    • 2、Sobel算子【實際應用】
      • (1)基於搜索的方法獲取邊界
      • (2)cv2.Sobel()
      • (3)cv2.convertScaleAbs()
      • (4)cv2.addWeighted()
    • 3、Laplacian算子
      • (1)基於零穿越獲取邊界
      • (2)cv2.Laplacian()
    • 4、Canny算法
      • (1)噪聲去除(高斯濾波)
      • (2)計算圖像梯度(Sobel算子)
      • (3)非極大值抑制:判斷像素是否為邊界點
      • (4)滯後阈值:設置兩個阈值,確定最終邊界
    • 5、各個傳統算子之間的比較

  • 邊緣檢測常常被當做碩士課題或者公司面試題,因此是非常重要的,在本章將會對幾種常見的邊緣檢測算子進行介紹。

學習目標

了解Sobel算子,Scharr算子和拉普拉斯算子

掌握canny邊緣檢測的原理及應用

一、邊緣檢測的原理

邊緣檢測是圖像處理和計算機視覺的基本問題,其目的是表示數字圖像中亮度變化明顯的點。圖像屬性中的顯著變化通常反映了屬性的重要事件和變化。邊緣檢測的表現形式如下圖所示:

圖像邊緣檢測大幅度減少了數據量,並且剔除了可以認為不相關的信息,保留了圖像重要的結構屬性。有許多方法用於邊緣檢測,它們絕大部分可以被劃分為兩類:基於搜索、基於零穿越

1、基於搜索

通過尋找圖像的一階導數中的最大值來檢測邊界,然後利用計算結果估計邊緣的局部方向,通常采用梯度的方向,並利用此方向找到局部梯度模的最大值,代表算法有Sobel算子和Scharr算子。

(1)圖像一階導數的最大值 -->
.
(2)邊緣的局部方向(一般梯度方向) -->
.
(3)局部梯度模的最大值

2、基於零穿越

通過尋找圖像二階導數零穿越來尋找邊界,代表算子是laplacian算子。
零點是指函數與y軸的交點。

二、Sobel檢測算子

Sobel邊緣檢測算法比較簡單,實際應用中效率要比canny邊緣檢測效果高。但是邊緣不如canny檢測的准確,但是與很多實際應用的場合,Sobel算子是首選。
Sobel算子是高斯平滑與微分操作的結合體,所以其抗噪聲能力很強,用途較多。尤其是效率要求較高,而對細節紋理不太關心的時候。

1、原理及方法論述

對於不連續的函數,一階導數可以寫作:

或者

所以有:

假設要處理的圖像為I,在兩個方向求導

  • 水平變化:將圖像I與期數大小的模板進行卷積,結果是Gx,比如,當模板大小為3時,Gx為:
  • 垂直變化:將圖像I與技術大小的模板進行卷積,結果為Gy。比如,當模板大小為3時,Gy為:

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

    統計極大值所在的位置,就是圖像的邊緣。
    **注意:**當內核大小為3時, 以上Sobel內核可能產生比較明顯的誤差, 為解決這一問題,我們使用Scharr函數,但該函數僅作用於大小為3的內核。該函數的運算與Sobel函數一樣快,但結果卻更加精確,其計算方法為:

2、應用

利用OpenCV進行Sobel邊緣檢測的API是:

Sobel_x_or_y =
cv2.Sobel(src, ddepth, dx, dy, dst, ksize, scale, delta, borderType)

參數:

  • src:傳入的圖像
  • ddepth: 圖像的深度
  • dx和dy: 指求導的階數,0表示這個方向上沒有求導,取值為0、1。
  • ksize: 是Sobel算子的大小,即卷積核的大小,必須為奇數1、3、5、7,默認為3。
  • 注意:如果ksize=-1,就演變成為3x3的Scharr算子。
  • scale:縮放導數的比例常數,默認情況為沒有伸縮系數。
  • borderType:圖像邊界的模式,默認值為cv2.BORDER_DEFAULT。

Sobel函數求完導數後會有負值,還會有大於255的值。而原圖像是uint8,即8為無符號數,所以Sobel建立的圖像位數不夠,會有截斷。因此要使用16位有符號的數據類型,即cv2.CV_16s。處理完圖像後,再使用cv2.convertScaleAbs()函數將其轉回原來的uint8類型,否則圖像無法顯示。

Sobel算子是在兩個方向計算的,最後還需要用cv2.addWeighted()函數將其組合起來

Scale_abs = cv2.convertScaleAbs(x) # 格式轉換函數
result = cv2.addWeighted(src1, alpha, src2, beta) # 圖像混合

代碼示例:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# 1 讀取圖像
img = cv.imread('./image/horse.jpg',0)
# 2 計算Sobel卷積結果
x = cv.Sobel(img, cv.CV_16S, 1, 0)
y = cv.Sobel(img, cv.CV_16S, 0, 1)
# 3 將數據進行轉換
Scale_absX = cv.convertScaleAbs(x) # convert 轉換 scale 縮放
Scale_absY = cv.convertScaleAbs(y)
# 4 結果合成
result = cv.addWeighted(Scale_absX, 0.5, Scale_absY, 0.5, 0)
# 5 圖像顯示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原圖')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(result,cmap = plt.cm.gray),plt.title('Sobel濾波後結果')
plt.xticks([]), plt.yticks([])
plt.show()


將上述代碼中計算Sobel算子部分的ksize設為-1,就是利用scharr進行邊緣檢測。

x = cv2.Sobel(img, cv2.CV_16S, 1, 0, ksize=-1)
y = cv2.Sobel(img, cv2.CV_16S, 0, 1, ksize=-1)

可以看出,使用Scharr算子,檢測效果要比Sobel算子稍微好一點。

三、Laplacian算子

Laplacian檢測方法利用二階導數來檢測邊緣。因為圖像是“2維”,因此我們需要在兩個方向求導,如下式所示:

那麼不連續的二階導數是:

那麼使用的卷積核是:

API:

laplacian = cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])

參數:

  • Src:圖像
  • Ddepth:圖像深度,-1表示采用的是原圖像相同深度,目標圖像的深度必須大於等於原圖像的深度;
  • ksize:算子的大小,即卷積核的大小,必須是1,3,5,7

代碼示例:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# 1 讀取圖像
img = cv.imread('./image/horse.jpg',0)
# 2 laplacian轉換
result = cv.Laplacian(img,cv.CV_16S)
Scale_abs = cv.convertScaleAbs(result)
# 3 圖像展示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原圖')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(Scale_abs,cmap = plt.cm.gray),plt.title('Laplacian檢測後結果')
plt.xticks([]), plt.yticks([])
plt.show()

四、canny邊緣檢測

canny邊緣檢測算法是一種非常流行的邊緣檢測算法,是John F.Canny與1986年提出的,被認為是最優秀的邊緣檢測算法。

1、Canny算法的原理

Canny邊緣檢測算法由4步構成,分別介紹如下:

  • 噪聲去除

由於邊緣檢測很容易受到噪聲干擾,因此首先使用高斯濾波器去除噪聲。(高斯濾波在圖像平滑中提到過,可以往前翻閱)

  • 計算圖像梯度

對平滑後的圖像使用Sobel算子計算水平方向和垂直方向的一階導數(Gx和Gy)。根據得到的這兩幅梯度圖(Gx和Gy)找到邊界的梯度和方向,公式如下:

如果某個像素點是邊緣,則其梯度方向總是和邊緣方向垂直。梯度方向被歸為四類:垂直、水平,和兩個對角線方向。

  • 非極大值抑制

在獲得梯度方向和大小後,對整幅圖像進行掃描,去除那些非邊界上的點。對每個像素進行檢查,看這個點的梯度是不是周圍具有相同梯度方向的點中最大的。如下圖所示:

A點位於圖像的邊緣,在其梯度變化方向,選擇像素點B和C,用來檢驗A點的梯度是否為極大值,若為極大值,則進行保留,否則A點被抑制,最終的結果是具有“細邊”的二進制圖像。

  • 滯後阈值

現在要確定真正的邊界。 我們設置兩個阈值: minVal 和 maxVal。 當圖像的灰度梯度高於 maxVal 時被認為是真的邊界, 低於 minVal 的邊界會被拋棄。如果介於兩者之間的話,就要看這個點是否與某個被確定為真正的邊界點相連,如果是就認為它也是邊界點,如果不是就拋棄。如下圖:

如上圖所示,A 高於阈值 maxVal 所以是真正的邊界點,C 雖然低於 maxVal 但高於 minVal 並且與 A 相連,所以也被認為是真正的邊界點。而 B 就會被拋棄,因為低於 maxVal 而且不與真正的邊界點相連。所以選擇合適的 maxVal 和 minVal 對於能否得到好的結果非常重要。

2、應用

在opencv中藥實現canny檢測使用的API:

canny = cv2.Canny(image, threshold1, threshold2)

參數:

  • image:灰度圖,
  • threshold1: minval,較小的阈值將間斷的邊緣連接起來
  • threshold2: maxval,較大的阈值檢測圖像中明顯的邊緣
    代碼示例:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# 1 圖像讀取
img = cv.imread('./image/horse.jpg',0)
# 2 Canny邊緣檢測
lowThreshold = 0
max_lowThreshold = 100
canny = cv.Canny(img, lowThreshold, max_lowThreshold)
# 3 圖像展示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img,cmap=plt.cm.gray),plt.title('原圖')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(canny,cmap = plt.cm.gray),plt.title('Canny檢測後結果')
plt.xticks([]), plt.yticks([])
plt.show()

總結:

1、邊緣檢測的原理

(1)基於搜索

(2)基於零穿越

2、Sobel算子【實際應用】

(1)基於搜索的方法獲取邊界

(2)cv2.Sobel()

(3)cv2.convertScaleAbs()

(4)cv2.addWeighted()

3、Laplacian算子

(1)基於零穿越獲取邊界

(2)cv2.Laplacian()

4、Canny算法

(1)噪聲去除(高斯濾波)

(2)計算圖像梯度(Sobel算子)

(3)非極大值抑制:判斷像素是否為邊界點

(4)滯後阈值:設置兩個阈值,確定最終邊界

5、各個傳統算子之間的比較

  • 變強之路任重道遠,加油加油!!!

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved