讓我們從匯入需要的庫開始!
#Import the required Python librariesimport numpy as npimport matplotlib.pyplot as pltimport pandas as pdfrom skimage.io import imshow, imread
我們可以看到,上面的圖片有一個彩色的陰影。儘管從藝術的角度來看這很美,但這可能對於我們的程式造成不小的困擾。
我們需要做的第一個處理是檢查影象的RGB光譜。因此,我們需要將影象分解為RGB分量。
rgb_list = ['Reds','Greens','Blues']fig, ax = plt.subplots(1, 3, figsize=(15,5), sharey = True)for i in range(3): ax[i].imshow(image_overcast[:,:,i], cmap = rgb_list[i]) ax[i].set_title(rgb_list[i], fontsize = 15)
從純視覺的角度,我們可以看到影象有明顯的深紅色。
當然,這只是我們的目測。為了更精確地瞭解影象的實際屬性,我們必須以計算機看到影象的方式對其進行檢測。下面的函式將生成每個顏色通道的相關統計資訊。
def channel_statistics(image): df_color = [] for i in range(0, 3): max_color =np.max(image[:,:,i]) mean_color = np.mean(image[:,:,i]) median_color = np.median(image[:,:,i]) perc_90 = np.percentile(image[:,:,i], 90, axis=(0,1)) perc_95 = np.percentile(image[:,:,i], 95, axis=(0,1)) perc_99 = np.percentile(image[:,:,i], 99, axis=(0,1)) row = (max_color, mean_color, median_color, perc_90, perc_95, perc_99) df_color.append(row) return pd.DataFrame(df_color, index = ['Red', ' Green', 'Blue'], columns = ['Max', 'Mean', 'Median', 'P_90',' P_95', 'P_99'])channel_statistics(image_overcast)
在上表中,我們看到影象上確實有一個紅色的陰影。現在我們如何平衡呢?
儘管這可能沒什麼作用,我們先透過每個通道的平均值、中位數以及最大值來調整影象試試。
fig, ax = plt.subplots(1, 3, figsize=(15,7), sharey = True)f_size = 15ax[0].imshow(image_overcast )ax[0].set_title('Original', fontsize = f_size)ax[1].imshow(img_as_ubyte((image_purple*1.0 / np.mean(image_overcast )).clip(0, 1)))ax[1].set_title('Mean Adjusted', fontsize = f_size)ax[2].imshow(img_as_ubyte((image_purple*1.0 / np.mean(image_overcast)).clip(0, 1))) ax[2].set_title('Median Adjusted', fontsize = f_size);
我們可以看出,問題並沒有得到解決。儘管可以說這張影象明顯要亮得多,但紅色的陰影仍然非常明顯。解決這個問題的一個可能方法是重點關注紅色通道。下面的程式碼將使用其最大值調整紅色通道,而所有其他通道則使用其平均值和中值進行調整。
fig, ax = plt.subplots(1, 3, figsize=(15,7), sharey = True)f_size = 15ax[0].imshow(image_overcast)ax[0].set_title('Original', fontsize = f_size)ax[1].imshow(img_as_ubyte((image_overcast / [np.max(image_overcast[:,:,0]), np.mean(image_overcast[:,:,1]), np.mean(image_overcast[:,:,2]), np.mean(image_overcast[:,:,3])]) .clip(0, 1)))ax[1].set_title('Red : Max, Others : Mean', fontsize = f_size)ax[2].imshow(img_as_ubyte((image_overcast / [np.max(image_overcast[:,:,0]), np.median(image_overcast[:,:,1]), np.median(image_overcast[:,:,2]), np.median(image_overcast[:,:,3])]) .clip(0, 1)))ax[2].set_title('Red : Max, Others : Median', fontsize = f_size);
看來我們的方法是正確的。這張照片明顯地消除了紅色的陰影,整體上更加明亮。然而我們現在看到了更明顯的綠色和藍色的陰影。這意味著我們可能對紅色調得太多,或者對綠色和藍色調得不夠。現在我們需要微調引數。
下面的程式碼將根據每個通道的特定百分比等級處理影象。
def percentile_adjustment(image): fig, ax = plt.subplots(2, 3, figsize=(15,10), sharey = True) f_size = 15 red = 99.75 parameter_matrix = [[red] + [99]*3, [red] + [95]*3, [red] + [90]*3, [red] + [85]*3, [red] + [80]*3] ax[0][0].imshow(image) ax[0][0].set_title('Original', fontsize = f_size) ax[0][1].imshow(img_as_ubyte((image / [np.percentile(image[:,:,i], parameter_matrix[0][i], axis=(0, 1)) \ for i in range(0, 4)]).clip(0,1))) ax[0][1].set_title(f'Red : {red}, Others : {parameter_matrix[0] [1]}', fontsize = f_size) ax[0][2].imshow(img_as_ubyte((image / [np.percentile(image[:,:,i], parameter_matrix[1][i], axis=(0, 1)) \ for i in range(0, 4)]).clip(0,1))) ax[0][2].set_title(f'Red : {red}, Others : {parameter_matrix[1] [1]}', fontsize = f_size); ax[1][0].imshow(img_as_ubyte((image / [np.percentile(image[:,:,i], parameter_matrix[2][i], axis=(0, 1)) \ for i in range(0, 4)]).clip(0,1))) ax[1][0].set_title(f'Red : {red}, Others : {parameter_matrix[2] [1]}', fontsize = f_size); ax[1][1].imshow(img_as_ubyte((image / [np.percentile(image[:,:,i], parameter_matrix[3][i], axis=(0, 1)) \ for i in range(0, 4)]).clip(0,1))) ax[1][1].set_title(f'Red : {red}, Others : {parameter_matrix[3] [1]}', fontsize = f_size); ax[1][2].imshow(img_as_ubyte((image/ [np.percentile(image[:,:,i], parameter_matrix[4][i], axis=(0, 1)) \ for i in range(0, 4)]).clip(0,1))) ax[1][2].set_title(f'Red : {red}, Others : {parameter_matrix[4] [1]}', fontsize = f_size); percentile_adjustment(image_overcast)
影象增強的結果是相當明顯的。不僅是紅色的陰影大大減少,而且其他通道的過度曝光也保持在最低限度。
從視覺上看,最好的結果是當其他通道在90-95%之間調整時。
然而,影象增強也是非常棘手的。對某些影象特別有效的方法對其他影象可能沒有那麼有效。讓我們看下面這個例子。
image_overcast_beach = imread('tinted_sky.png')imshow(image_overcast_beach);
這副影象明顯的與第一幅影象有相似的問題。讓我們試著用同樣的方法,看看結果是否一樣好。
percentile_adjustment(image_overcast_beach)
我們看到紅色陰影大大減少了。然而可以看到我們的方法在圖片的上半部分和下半部分的有效性之間有明顯的差異。在超過95%時,影象的天空部分曝光過度。但是水只有在低於95%才沒有曝光過度。我們該如何解決這個問題?
記住,計算機實際上是用矩陣來讀取影象的。所以我們可以很容易地將其分割,並對每個區域應用不同的引數。在這種情況下,讓我們把影象分為天空和水。
f_size = 15parameter_matrix = [[99.9] + [97.75]*3, [99.75] + [92]*3]fig, ax = plt.subplots(2, 2, figsize=(15,6), sharey = True)ax[0][0].imshow(image_overcast_beach[0:320])ax[0][0].set_title('Sky Original', fontsize = f_size)ax[1][0].imshow(image_overcast_beach[320:])ax[1][0].set_title('Water Original', fontsize = f_size)ax[0][1].imshow(img_as_ubyte(((image_overcast_beach[0:320]/ [np.percentile(image_overcast_beach[:,:,i], parameter_matrix[0][i], axis=(0, 1)) for i in range(4)]).clip(0,1))))ax[0][1].set_title('Sky Adjusted', fontsize = f_size)ax[1][1].imshow(img_as_ubyte(((image_overcast_beach[320:]/ [np.percentile(image_overcast_beach[:,:,i], parameter_matrix[1][i], axis=(0, 1)) for i in range(4)]).clip(0,1))))ax[1][1].set_title('Water Adjusted', fontsize = f_size);fig.tight_layout()
最後,我們可以使用NumPy的連線函式將這兩個部分組合起來。
imshow(np.concatenate((beach_correction_top, beach_correction_bottom), axis=0));
正如我們所見,影象增強是非常棘手的。對一個影象有效的方法可能對另一個影象效果不好。但只要我們記住影象不過是三維矩陣,總能找到一種方法來適當地操縱它以滿足我們的需要。因此多做練習增加經驗,對我們的影象增強將會非常有幫助。讓我們開始動手實踐吧!
· END ·