圖像梯度
我們知道一階導數可以用來求極值。把圖片想象成連續函數,因為邊緣部分的像素值與旁邊的像素明顯有區別,所以對圖片局部求極值,就可以得到整幅圖片的邊緣信息。不過圖片是二維的離散函數,導數就變成了差分,這個查分就變成了圖像梯度。
1. 垂直邊緣提取
濾波是應用卷積來實現的,卷積的關鍵就是卷積核。我們來考察下面這個卷積核:
這個核是用來提取圖片中的垂直邊緣的,怎么做到的呢?看下圖:
當前列左右兩側的元素進行差分,由於邊緣的值明顯小於(或大於)周邊像素,所以邊緣的差分結果會明顯不同,這樣就提取出垂直邊緣。同理,把上面的那個矩陣轉置一下,就是提取水平邊緣。這種差分操作就成為圖像的梯度計算:
import cv2 import numpy as np img = cv2.imread('sudoku.jpg', 0) # 自己進行垂直邊緣提取 kernel = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], dtype=np.float32) dst_v = cv2.filter2D(img, -1, kernel) # 自己進行水平邊緣提取 dst_h = cv2.filter2D(img, -1, kernel.T) # 橫向並排對比顯示 cv2.imshow('edge', np.hstack((img, dst_v, dst_h))) cv2.waitKey(0)
2. Sobel算子
上面這種差分方法就叫Sobel算子,它先在垂直方向上計算梯度 Gx = k1 x src,再在水平方向計算梯度Gy = k2 x src,最后求出總梯度:
我們可以把前面的代碼用Sobel算子更簡單的實現:
sobelx = cv2.Sobel(img, -1, 1, 0, ksize=3) # 只計算x方向 sobely = cv2.Sobel(img, -1, 0, 1, ksize=3) # 只計算y方向 # 橫向並排對比顯示 cv2.imshow('edge', np.hstack((img, sobelx, sobely))) cv2.waitKey(0)
還有其他算子,比如只利用領域間的原始差值來檢測邊緣的Prewitt算子
還有比Sobel更好用的Scharr算子
這些算法都是一階邊緣檢測的代表。
3. Laplacian算子
高數中用一階導數求極值,在這些極值的地方,二階導數為0,所以也可以求二階導計算梯度:
一維的一階和二階差分公式分別為:
提取前面的系數,那么一維的Laplacian的濾波核是:
對於二維函數f(x,y),兩個方向的二階差分分別是:
合在一起:
同樣提取前面的系數,那么二維的Laplacian濾波核就是:
這就是 Laplacian 算子的圖像卷積模板,有些資料在此基礎上考慮斜對角情況,將卷積核擴展為:
laplacian = cv2.Laplacian(img, -1) # 橫向並排對比顯示 cv2.imshow('edge', np.hstack((img, laplacian))) cv2.waitKey(0)
Laplacian算子是二階邊緣檢測的典型代表。
參考地址:http://ex2tron.wang/opencv-python-extra-image-gradients/