使用 ndimage


在先前的 OpenCV 系列文件中,〈Laplacian 轉換(一)〉與〈Laplacian 轉換(二)〉,透過 Laplacian 運算等來取得圖像邊緣,如果現在不依賴 OpenCV,該怎麼進行呢?

雖然 Scipy 不是專用於影像處理,然而有時確實有分析影像的需求,因此 Scipy 提供有 ndimage 模組,可以應付基本的影像分析。

首先來解決讀取影像檔的問題,如〈簡介 Scipy〉談到的,ndimage 已經不再提供讀取影像的函式,官方是建議安裝與使用 imageio

import imageio

img = imageio.imread('caterpillar.jpg')

讀入檔案後,傳回的是 NumPy 陣列,在第三個維度部份,是像素的 RGB 資料,若要轉為灰階,可以使用常見的轉灰階公式 R * 0.299 + G * 0.587 + B * 0.114轉灰階有多種不同的公式):

gray = np.dot(img[...,:3], [0.299, 0.587, 0.114])

若想要去除雜訊,ndimage 可以使用 gaussian_filtermedian_filter 等濾波,這邊使用中值濾波 median_filter

filtered = ndimage.median_filter(gray, size = (3, 3))

如果要進行卷積(convolution)運算,可以使用 convolve 函式並指定核,不過對於 Laplacian 運算直接提供有 laplace 函式:

laplace = ndimage.laplace(filtered)

以下是完整的程式:

import imageio
from scipy import ndimage
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

img = imageio.imread('caterpillar.jpg')

# 轉灰階
gray = np.dot(img[...,:3], [0.299, 0.587, 0.114])

# 中值濾波
filtered = ndimage.median_filter(gray, size = (3, 3))

# Laplacian 運算
laplace = ndimage.laplace(filtered)

# 取絕對值轉 uint8
edge = np.abs(laplace).astype('uint8') 

plt.imshow(edge, cmap = cm.gray)
plt.show()

執行結果如下:

使用 ndimage

除了 laplace 函式可以使用之外,ndimage 還提供有 sobel 函式(當然,OpenCV 也有 Sobel 的實作),可以用來偵測邊緣,Sobel 運算的原理也是基於二階偏導數,在 x 方向與 y 方向各自求偏導數的差(懶得用方程式編輯器畫了,直接純文字寫公式XD):

Gx = (f(x + 1,y - 1) + 2 * f(x + 1, y) + f(x + 1, y + 1)) -
     (f(x - 1,y - 1) + 2 * f(x - 1, y) + f(x - 1, y + 1))

Gy = (f(x - 1, y - 1) + 2 * f(x, y - 1) + f(x + 1,y - 1)) -
     (f(x - 1, y + 1) + 2 * f(x, y + 1) + f(x + 1, y + 1))

G = sqrt(Gx * Gx, Gy * Gy)

GxGy 整理一下,以矩陣表示的話就是:

使用 ndimage

ndimagesobel 函式可以透過 axis 來指定處理哪個軸的方向,預設是 -1,也就是考量兩個方向,指定軸 0 時,就圖像而言是指垂直方向(也就是 y 方向),指定軸 1 時是指水平方向(也就是 x 方向),底下的程式可以顯示三個處理的不同結果:

import imageio
from scipy import ndimage
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

img = imageio.imread('caterpillar.jpg')

# 轉灰階
gray = np.dot(img[...,:3], [0.299, 0.587, 0.114])

# 中值濾波
filtered = ndimage.median_filter(gray, size = (3, 3))

# 兩個方向
s = ndimage.sobel(filtered)
plt.subplot(1, 3, 1)
plt.imshow(np.abs(s).astype('uint8') , cmap = cm.gray)

# 水平方向
sx = ndimage.sobel(filtered, axis = 1)
plt.subplot(1, 3, 2)
plt.imshow(np.abs(sx).astype('uint8') , cmap = cm.gray)

# 垂直方向
sy = ndimage.sobel(filtered, axis = 0)
plt.subplot(1, 3, 3)
plt.imshow(np.abs(sy).astype('uint8') , cmap = cm.gray)

plt.show()

執行結果如下:

使用 ndimage

其他更多有關 ndimage 的使用,可以參考〈Multidimensional image processing (scipy.ndimage)〉。