從匯入所需的Python庫開始。
import numpy as npimport matplotlib.pyplot as pltfrom skimage.io import imshow, imreadfrom skimage.color import rgb2yuv, rgb2hsv, rgb2gray, yuv2rgb, hsv2rgbfrom scipy.signal import convolve2d
我們將使用下圖:
dog = imread('fire_dog.png')plt.figure(num=None, figsize=(8, 6), dpi=80)imshow(dog);
現在我們應用到影象的核是高斯模糊核和銳化核。你可以在下面看到如何定義它們的矩陣。
# 銳化sharpen = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]]) # 高斯模糊gaussian = (1 / 16.0) * np.array([[1., 2., 1.], [2., 4., 2.], [1., 2., 1.]]) fig, ax = plt.subplots(1,2, figsize = (17,10))ax[0].imshow(sharpen, cmap='gray')ax[0].set_title(f'Sharpen', fontsize = 18) ax[1].imshow(gaussian, cmap='gray')ax[1].set_title(f'Gaussian Blur', fontsize = 18) [axi.set_axis_off() for axi in ax.ravel()];
但如何將這些核應用到影象中呢?我首先定義了下面的函式來允許我們迭代地處理核。請注意,我們將邊界設定為fill,將fillvalue設定為0,這對於確保輸出將是一個0填充的矩陣、且其大小與原始矩陣相同非常重要。
def multi_convolver(image, kernel, iterations): for i in range(iterations): image = convolve2d(image, kernel, 'same', boundary = 'fill', fillvalue = 0) return image multi_convolver(dog, gaussian, 2)
似乎我們遇到了一個值錯誤。為什麼會這樣?記住,當我們將一個矩陣與另一個矩陣卷積時,矩陣的維數應該相同。這意味著我們不能將2D卷積應用到3D矩陣(因為顏色通道)。為了解決這個問題,我們必須首先將影象轉換成灰度。
dog_grey = rgb2gray(dog)plt.figure(num=None, figsize=(8, 6), dpi=80)imshow(dog_grey);
現在如果我們執行這個函式,應該會得到想要的效果。
convolved_image = multi_convolver(dog_grey, gaussian, 2)plt.figure(num=None, figsize=(8, 6), dpi=80)imshow(convolved_image);
我們現在可以看到,影象已經明顯模糊了。下面的程式碼將告訴我們,如果繼續對影象執行高斯模糊卷積,影象會發生什麼。
def convolution_plotter(image, kernel): iterations = [1,10,20,30] f_size = 20 fig, ax = plt.subplots(1,4, figsize = (15,7)) for n, ax in enumerate(ax.flatten()): ax.set_title(f'Iteration : {iterations[n]}', fontsize = f_size) ax.imshow(multi_convolver(image, kernel, iterations[n]), cmap='gray') ax.set_axis_off() fig.tight_layout() convolution_plotter(dog_grey, gaussian)
太好了!由於核的應用,我們可以清楚地看到影象的持續模糊。
但是如果你需要模糊影象並保留顏色呢?讓我們首先嚐試應用每個顏色通道的卷積。
def convolver_rgb(image, kernel, iterations = 1): convolved_image_r = multi_convolver(image[:,:,0], kernel, iterations) convolved_image_g = multi_convolver(image[:,:,1], kernel, iterations) convolved_image_b = multi_convolver(image[:,:,2], kernel, iterations) reformed_image = np.dstack((np.rint(abs(convolved_image_r)), np.rint(abs(convolved_image_g)), np.rint(abs(convolved_image_b)))) / 255 fig, ax = plt.subplots(1,3, figsize = (17,10)) ax[0].imshow(abs(convolved_image_r), cmap='Reds') ax[0].set_title(f'Red', fontsize = 15) ax[1].imshow(abs(convolved_image_g), cmap='Greens') ax[1].set_title(f'Green', fontsize = 15) ax[2].imshow(abs(convolved_image_b), cmap='Blues') ax[2].set_title(f'Blue', fontsize = 15) [axi.set_axis_off() for axi in ax.ravel()] return np.array(reformed_image).astype(np.uint8) convolved_rgb_gauss = convolver_rgb(dog, gaussian, 2)
這個函式實際上返回給我們經過修改的影象,我們只需要將它插入show函式。
plt.figure(num=None, figsize=(8, 6), dpi=80)imshow(convolved_rgb_gauss);
太好了!看來這個函式執行得很好。看看當我們將影象卷積10次時會發生什麼。
所以這解決了問題,對嗎?實際上不太對。要了解這個函式的問題,我們嘗試銳化影象。
convolved_rgb_sharpen = convolver_rgb(dog, sharpen, 1)
到目前為止看起來不錯,讓我們看看轉換後的影象是什麼樣子。
影象已經被修改,但我們現在看到有些輕微的扭曲。為什麼會這樣?
記住,RGB顏色空間隱式地混合了畫素的亮度和顏色。這意味著,在不改變顏色的情況下,實際上是不可能在影象的亮度上應用卷積的。那麼我們該如何處理這個問題呢?
解決這個問題的一種方法是改變影象的顏色空間。我們可以利用Y'UV顏色空間來代替RGB顏色空間。我們這樣做是因為Y'UV空間中的亮度通道實際上是與顏色分離的(這是Y分量)。
出於本文的目的,我們將編輯函式,首先將影象轉換為Y'UV顏色空間,然後進行所需的卷積。
def convolver_rgb(image, kernel, iterations = 1): img_yuv = rgb2yuv(image) img_yuv[:,:,0] = multi_convolver(img_yuv[:,:,0], kernel, iterations) final_image = yuv2rgb(img_yuv) fig, ax = plt.subplots(1,2, figsize = (17,10)) ax[0].imshow(image) ax[0].set_title(f'Original', fontsize = 20) ax[1].imshow(final_image); ax[1].set_title(f'YUV Adjusted, Iterations = {iterations}', fontsize = 20) [axi.set_axis_off() for axi in ax.ravel()] fig.tight_layout() return final_image final_image = convolver_rgb(dog, sharpen, iterations = 1)
可以看到,我們的函式現在返回的影象明顯更清晰,沒有任何顏色失真。有許多其他的方法來解決這個問題與Y'UV轉換隻是其中之一。記住,HSV顏色空間的V分量表示幾乎相同的東西。然而,Y'UV空間的亮度分量和HSV空間的明度分量的方式略有不同。讓我們看看用一個替代另一個的後果是什麼。
def convolver_comparison(image, kernel, iterations = 1): img_yuv = rgb2yuv(image) img_yuv[:,:,0] = multi_convolver(img_yuv[:,:,0], kernel, iterations) final_image_yuv = yuv2rgb(img_yuv) img_hsv = rgb2hsv(image) img_hsv[:,:,2] = multi_convolver(img_hsv[:,:,2], kernel, iterations) final_image_hsv = hsv2rgb(img_hsv) convolved_image_r = multi_convolver(image[:,:,0], kernel, iterations) convolved_image_g = multi_convolver(image[:,:,1], kernel, iterations) convolved_image_b = multi_convolver(image[:,:,2], kernel, iterations) final_image_rgb = np.dstack((np.rint(abs(convolved_image_r)), np.rint(abs(convolved_image_g)), np.rint(abs(convolved_image_b)))) / 255 fig, ax = plt.subplots(2,2, figsize = (17,17)) ax[0][0].imshow(image) ax[0][0].set_title(f'Original', fontsize = 30) ax[0][1].imshow(final_image_rgb); ax[0][1].set_title(f'RGB Adjusted, Iterations = {iterations}', fontsize = 30) fig.tight_layout() ax[1][0].imshow(final_image_yuv) ax[1][0].set_title(f'YUV Adjusted, Iterations = {iterations}', fontsize = 30) ax[1][1].imshow(final_image_hsv) ax[1][1].set_title(f'HSV Adjusted, Iterations = {iterations}', fontsize = 30) [axi.set_axis_off() for axi in ax.ravel()] fig.tight_layout() convolver_comparison(dog, sharpen, iterations = 1)
我們看到HSV和Y'UV比原始RGB方法有一些輕微的改進。為了更好的說明,我們可以將迭代次數從1增加到2。
在2次迭代中,失真變得更加明顯。但是也很明顯,HSV和Y'UV調整後的影象比原始的RGB調整後的影象更加平滑。在決定對影象應用卷積核的最佳方法時,應該記住這些屬性。
結論
我們學習瞭如何對影象進行模糊和銳化卷積。這些技術對於任何從事影象處理和計算機視覺領域的資料科學家來說都是至關重要的。