[Python圖像處理]十一.圖像銳化與邊緣檢測之Roberts算子、Prewitt算子、Sobel算子和Laplacian算子,Schar算子


Roberts算子

Roberts算子即為交叉微分算法,它是基於交叉差分的梯度算法,通過局部差分計算檢測邊緣線條。常用來處理具有陡峭的第噪聲圖像,當圖像邊緣接近於正45度或負45度時,該算法處理效果更理想,其缺點時對邊緣的定位不太准確,提取的邊緣線條較粗。

在Python中,Roberts算子主要是通過Numpy定義模板,再調用OpenCV的filter2D()函數實現邊緣提取。該函數主要是利用內核實現對圖像的卷積運算,其函數原型如下:

dst = filter2D(src, ddepth, kernel, dts, anchor,delta, borderType)

src:表示輸入圖像

ddepth: 表示目標圖像所需的深度

kernel: 表示卷積核,一個單通道浮點型矩陣

anchor: 表示內核的基准點,其默認值為(-1, -1),位於中心位置

delta:表示在存儲目標圖像前可選的添加到像素的值,默認值為0

borderType:表示邊框模式

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("src.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Roberts算子
kernelx = np.array([[-1, 0], [0, 1]], dtype=int)
kernely = np.array([[0, -1], [1, 0]], dtype=int)
x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
# 轉轉成uint8
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
# 正常顯示中文標簽
plt.rcParams["font.sans-serif"] = ["SimHei"]
# 顯示圖形
titles = ["原始圖像", "Roberts算子"]
images = [img, Roberts]
for i in range(2):
    plt.subplot(1, 2, i+1)
    plt.imshow(images[i], "gray")
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])
plt.show()

效果如下:

 

 

Prewitt算子

Prewitt是一種圖像邊緣檢測的微分算子,其原理是利用特定區域內像素值產生的差分實現邊緣檢測。由於Prewitt算子采用33模型對區域內的像素值進行計算,而Robert算子的模板為22,故Prewitt算子的邊緣檢測結果在水平/垂直方向均比Robert算子更加明顯。Prewitt算子適合用來識別噪聲較多,灰度漸變的圖像。

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("src.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Prewitt算子
kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
# 轉轉成uint8
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
# 正常顯示中文標簽
plt.rcParams["font.sans-serif"] = ["SimHei"]
# 顯示圖形
titles = ["原始圖像", "Prewitt算子"]
images = [img, Roberts]
for i in range(2):
    plt.subplot(1, 2, i+1)
    plt.imshow(images[i], "gray")
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])
plt.show()

效果如下:

 

 

 Sobel算子

Sobel算子是一種用於邊緣檢測的離散的離散微分算子,它結合了高斯平滑和微分求導。該算子用於計算圖像明暗程度近似值,根據圖像邊緣化旁邊明暗程度把該區域內超過某個數的特定點記為邊緣。Sobel算子在Prewitt算子的基礎上增加了權重的概念,認為相鄰點的距離遠近對當前像素點的影響是不同的,距離越近的像素點對應當前像素的影響越大,從而實現圖像銳化並突出邊緣輪廓。

Sobel算子的邊緣定位更准確,常用於噪聲較多,灰度漸變的圖像。

Sobel算法根據像素點上下,左右鄰點灰度加權差,在邊緣處達到極值這一現象檢測邊緣。對噪聲具有平滑作用,提供較為精確的邊緣方向信息。因為Sobel算子結合了高斯平滑和微分求導,因此結果會具有更多的抗噪性,當對精度要求不是很高時,Sobel算子是一種較為常用的邊緣檢測方法。

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

src表示輸入圖像

dst表示輸出的邊緣圖,其大小和通道數與輸入圖像相同

ddepth表示目標圖像所需的深度,針對不同的輸入圖像,輸出目標圖像有不同的深度

dx表示x方向上的差分階數,取值1或0

dy表示y方向上的差分階數,取值1或0

ksize表示Sobel算子的大小,其值必須是正數和奇數

scale表示縮放導數的比例常數,默認情況下沒有伸縮系數

delta表示將結果存入目標圖像之前,添加到結果中的可選增量值

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("src.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Prewitt算子
# kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
# kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
# x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
# y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
x = cv2.Sobel(grayImage, cv2.CV_16S, 1, 0)
y = cv2.Sobel(grayImage, cv2.CV_16S, 0, 1)
# 轉成uint8
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
# 正常顯示中文標簽
plt.rcParams["font.sans-serif"] = ["SimHei"]
# 顯示圖形
titles = ["原始圖像", "Sobel算子"]
images = [img, Roberts]
for i in range(2):
    plt.subplot(1, 2, i+1)
    plt.imshow(images[i], "gray")
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])
plt.show()

效果如下:

Laplacian算子

拉普拉斯(Laplacian)算子是n維歐幾里德空間中的一個二階微分算子,常用於圖像增強領域和邊緣提取。它通過灰度差分計算鄰域內的像素,基本流程是:判斷圖像中心像素灰度值與它周圍其他像素的灰度值,如果中心像素的灰度更高,則提升中心像素的灰度;反之降低中心像素的灰度,從而實現圖像銳化操作。在算法實現過程中,Laplacian算子通過對鄰域中心像素的四方向或八方向求梯度,再將梯度相加起來判斷中心像素灰度與鄰域內其他像素灰度的關系,最后通過梯度運算的結果對像素灰度進行調整。
Python和OpenCV將Laplacian算子封裝在Laplacian()函數中,其函數原型如下所示:

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

    src表示輸入圖像
    dst表示輸出的邊緣圖,其大小和通道數與輸入圖像相同
    ddepth表示目標圖像所需的深度
    ksize表示用於計算二階導數的濾波器的孔徑大小,其值必須是正數和奇數,且默認值為1,更多詳細信息查閱getDerivKernels
    scale表示計算拉普拉斯算子值的可選比例因子。默認值為1,更多詳細信息查閱getDerivKernels
    delta表示將結果存入目標圖像之前,添加到結果中的可選增量值,默認值為0
    borderType表示邊框模式,更多詳細信息查閱BorderTypes

注意,Laplacian算子其實主要是利用Sobel算子的運算,通過加上Sobel算子運算出的圖像x方向和y方向上的導數,得到輸入圖像的圖像銳化結果。同時,在進行Laplacian算子處理之后,還需要調用convertScaleAbs()函數計算絕對值,並將圖像轉換為8位圖進行顯示。

當ksize=1時,Laplacian()函數采用3×3的孔徑(四鄰域模板)進行變換處理。下面的代碼是采用ksize=3的Laplacian算子進行圖像銳化處理,其代碼如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("src.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Roberts算子
# kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
# kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
# x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
# y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
# x = cv2.Sobel(grayImage, cv2.CV_16S, 1, 0)
# y = cv2.Sobel(grayImage, cv2.CV_16S, 0, 1)
# # 轉轉成uint8
# absX = cv2.convertScaleAbs(x)
# absY = cv2.convertScaleAbs(y)
dst = cv2.Laplacian(grayImage, cv2.CV_16S, ksize=3)
dst = cv2.convertScaleAbs(dst)

# 正常顯示中文標簽
plt.rcParams["font.sans-serif"] = ["SimHei"]
# 顯示圖形
titles = ["原始圖像", "Sobel算子"]
images = [img, dst]
for i in range(2):
    plt.subplot(1, 2, i+1)
    plt.imshow(images[i], "gray")
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])
plt.show()

效果如下:

 

 代碼總結:

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("src.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 灰度處理
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 高斯波
gaussianBlur = cv2.GaussianBlur(grayImage, (3, 3), 0)
# 閾值處理
ret, binary = cv2.threshold(gaussianBlur, 127, 255, cv2.THRESH_BINARY)
# Roberts算子
kernelx = np.array([[-1, 0], [0, 1]], dtype=int)
kernely = np.array([[0, -1], [1, 0]], dtype=int)
x = cv2.filter2D(binary, cv2.CV_16S, kernelx)
y = cv2.filter2D(binary, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)

# Prewitt算子
kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
x = cv2.filter2D(binary, cv2.CV_16S, kernelx)
y = cv2.filter2D(binary, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Prewitt = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)

# Sobel算子
x = cv2.Sobel(binary, cv2.CV_16S, 1, 0)
y = cv2.Sobel(binary, cv2.CV_16S, 0, 1)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)

# Laplacian算子
dst = cv2.Laplacian(grayImage, cv2.CV_16S, ksize=3)
dst = cv2.convertScaleAbs(dst)

# 正常顯示中文標簽
plt.rcParams["font.sans-serif"] = ["SimHei"]
# 顯示圖形
titles = ['Source Image', 'Binary Image', 'Roberts Image', 'Prewitt Image', 'Sobel Image', 'Laplacian Image']
images = [img, binary, Roberts, Prewitt, Sobel, dst]
for i in range(6):
    plt.subplot(2, 3, i+1)
    plt.imshow(images[i], "gray")
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])
plt.show()

效果如下:

 

 

 Schar算子

由於Sobel算子在計算相對較小的時候,其近似計算導數的精度比較低,比如一個33的Sobel算子,當梯度角度接近水平或者垂直方向,其不精確性就越發明顯。Scharr算子同Sobel算子的速度一樣,但是精確度更高,尤其是計算較小核的情景,所以利用33濾波器實現圖像邊緣提取更推薦使用Scharr算子。

dst = Scharr(src, ddepth, dx, dy, dst, scale, delta, borderType)

src表示輸入圖像

ddepth表示輸出目標圖像所需的深度,針對不同的輸入圖像,輸出目標圖像有不同的深度

dx表示x方向上的差分階數,取值1或者0

dy表示y方向上的差分階數,取值0或者1

scale表示縮放導數的比例常數,默認情況下沒有伸縮系數

delta表示將結果存入目標圖像之前,添加到結果中的可選增量值

borderType表示邊框模式

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("src.PNG")
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Scharr算子
x = cv2.Scharr(grayImage, cv2.CV_32F, 1, 0)
y = cv2.Scharr(grayImage, cv2.CV_32F, 0, 1)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
scharr = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
plt.rcParams["font.sans-serif"] = ["SimHei"]
titles = ["原始圖像", "Scharr算子"]
images = [img, scharr]
for i in range(2):
    plt.subplot(1, 2, i+1)
    plt.imshow(images[i], "gray")
    plt.title(titles[i])
    plt.xticks([])
    plt.yticks([])
plt.show()

效果如下:

 

 

 

 

 

轉自:https://blog.csdn.net/Eastmount/article/details/89001702


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM