[Python影象處理]十一.影象銳化與邊緣檢測之Roberts運算元、Prewitt運算元、Sobel運算元和Laplacian運算元
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()
效果如下:
轉自:https://blog.csdn.net/Eastmount/article/details/89001702