Python 影象處理 OpenCV (13): Scharr 運算元和 LOG 運算元邊緣檢測技術
阿新 • • 發佈:2020-07-11
![](https://cdn.geekdigging.com/opencv/opencv_header.png)
前文傳送門:
[「Python 影象處理 OpenCV (1):入門」](https://www.geekdigging.com/2020/05/17/5513454552/)
[「Python 影象處理 OpenCV (2):畫素處理與 Numpy 操作以及 Matplotlib 顯示影象」](https://www.geekdigging.com/2020/05/18/4936041986/)
[「Python 影象處理 OpenCV (3):影象屬性、影象感興趣 ROI 區域及通道處理」](https://www.geekdigging.com/2020/05/19/1227329671/)
[「Python 影象處理 OpenCV (4):影象算數運算以及修改顏色空間」](https://www.geekdigging.com/2020/05/21/1757913240/)
[「Python 影象處理 OpenCV (5):影象的幾何變換」](https://www.geekdigging.com/2020/05/23/4331122737/)
[「Python 影象處理 OpenCV (6):影象的閾值處理」](https://www.geekdigging.com/2020/06/03/6651375581/)
[「Python 影象處理 OpenCV (7):影象平滑(濾波)處理」](https://www.geekdigging.com/2020/06/06/8676263283/)
[「Python 影象處理 OpenCV (8):影象腐蝕與影象膨脹」](https://www.geekdigging.com/2020/06/08/5731186312/)
[「Python 影象處理 OpenCV (9):影象處理形態學開運算、閉運算以及梯度運算」](https://www.geekdigging.com/2020/06/11/5023174082/)
[「Python 影象處理 OpenCV (10):影象處理形態學之頂帽運算與黑帽運算」](https://www.geekdigging.com/2020/06/18/9182078666/)
[「Python 影象處理 OpenCV (11):Canny 運算元邊緣檢測技術」](https://www.geekdigging.com/2020/06/25/4009152544/)
[「Python 影象處理 OpenCV (12): Roberts 運算元、 Prewitt 運算元、 Sobel 運算元和 Laplacian 運算元邊緣檢測技術」](https://www.geekdigging.com/2020/06/26/7999051794/)
## Scharr 運算元
在說 Scharr 運算元之前,必須要提的是前面我們介紹過的 Sobel 運算元, Sobel 運算元雖然可以有效的提取影象邊緣,但是對影象中較弱的邊緣提取效果較差。
這是由於 Sobel 運算元在計算相對較小的核的時候,其近似計算導數的精度比較低,例如一個 `3 * 3` 的 Sobel 運算元,在梯度角度接近水平或垂直方向時,其不精確性就非常明顯。
因此引入 Scharr 運算元。 Scharr 運算元是對 Sobel 運算元差異性的增強,兩者之間的在檢測影象邊緣的原理和使用方式上相同。
而 Scharr 運算元的主要思路是通過將模版中的權重係數放大來增大畫素值間的差異。
Scharr 運算元又稱為 Scharr 濾波器,也是計算 x 或 y 方向上的影象差分,在 OpenCV 中主要是配合 Sobel 運算元的運算而存在的,其濾波器的濾波係數如下:
$$
Gx = \left[
\begin{matrix}
-3 & 0 & 3\\
-10 & 0 & 10\\
-3 & 0 & 3\\
\end{matrix}
\right]
$$
$$
Gy = \left[
\begin{matrix}
-3 & -10 & -3\\
0 & 0 & 0\\
3 & 10 & 3\\
\end{matrix}
\right]
$$
Scharr 運算元在 OpenCV 中的方法原型如下:
```python
def Scharr(src, ddepth, dx, dy, dst=None, scale=None, delta=None, borderType=None):
```
* src: 表示輸入影象
* ddepth: 表示目標影象所需的深度,針對不同的輸入影象,輸出目標影象有不同的深度
* dx: 表示 x 方向上的差分階數,取值 1 或 0
* dy: 表示 y 方向上的差分階數,取值 1 或 0
可以看到,函式 `Scharr()` 和 `Sobel()` 是非常的相似,在使用上也是完全一樣的,下面看一個示例:
```python
import cv2 as cv
import matplotlib.pyplot as plt
img = cv.imread("maliao.jpg")
rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# Scharr 運算元
x = cv.Scharr(gray_img, cv.CV_16S, 1, 0) # X 方向
y = cv.Scharr(gray_img, cv.CV_16S, 0, 1) # Y 方向
absX = cv.convertScaleAbs(x)
absY = cv.convertScaleAbs(y)
Scharr = cv.addWeighted(absX, 0.5, absY, 0.5, 0)
# 顯示圖形
plt.rcParams['font.sans-serif']=['SimHei']
titles = ['原始影象', 'Scharr 運算元']
images = [rgb_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://cdn.geekdigging.com/opencv/13/charr_result.png)
## LOG 運算元
LOG ( Laplacian of Gaussian ) 邊緣檢測運算元是 David Courtnay Marr 和 Ellen Hildreth 在 1980 年共同提出的,也稱為 Marr & Hildreth 運算元,它根據影象的信噪比來求檢測邊緣的最優濾波器。該演算法首先對影象做高斯濾波,然後再求其拉普拉斯( Laplacian )二階導數,根據二階導數的過零點來檢測影象的邊界,即通過檢測濾波結果的零交叉( Zero crossings )來獲得影象或物體的邊緣。
LOG 運算元實際上是把 Gauss 濾波和 Laplacian 濾波結合了起來,先平滑掉噪聲,再進行邊緣檢測。
LOG 運算元與視覺生理中的數學模型相似,因此在影象處理領域中得到了廣泛的應用。
它具有抗干擾能力強,邊界定位精度高,邊緣連續性好,能有效提取對比度弱的邊界等特點。
常見的 LOG 運算元是 `5 * 5` 的模板;
$$
Gy = \left[
\begin{matrix}
-2 & -4 & -4 & -4 & -2\\
-4 & 0 & 8 & 0 & -4\\
-4 & 8 & 24 & 8 & -4\\
-4 & 0 & 8 & 0 & -4\\
-2 & -4 & -4 & -4 & -2\\
\end{matrix}
\right]
$$
LOG 運算元到中心的距離與位置加權係數的關係曲線像墨西哥草帽的剖面,所以 LOG 運算元也叫墨西哥草帽濾波器。
![](https://cdn.geekdigging.com/opencv/13/log_caomao.webp)
示例程式碼如下:
```python
import cv2 as cv
import matplotlib.pyplot as plt
# 讀取影象
img = cv.imread("maliao.jpg")
rgb_img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 先通過高斯濾波降噪
gaussian = cv.GaussianBlur(gray_img, (3, 3), 0)
# 再通過拉普拉斯運算元做邊緣檢測
dst = cv.Laplacian(gaussian, cv.CV_16S, ksize=3)
LOG = cv.convertScaleAbs(dst)
# 用來正常顯示中文標籤
plt.rcParams['font.sans-serif'] = ['SimHei']
# 顯示圖形
titles = ['原始影象', 'LOG 運算元']
images = [rgb_img, LOG]
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://cdn.geekdigging.com/opencv/13/log_result.png)
## 小結
邊緣檢測演算法主要是基於影象強度的一階和二階導數,但導數通常對噪聲很敏感,因此需要採用濾波器來過濾噪聲,並呼叫影象增強或閾值化演算法進行處理,最後再進行邊緣檢測。
## 參考
https://blog.csdn.net/Eastmount/article/details/89056240
https://blog.csdn.net/qq_42722197/article/details/103825409
https://www.jianshu.com/p/2ac7