介紹
我們非常熟悉結構化(表格)數據的預處理步驟。你可以找到缺失的值然後添補它,然後檢測並處理異常值,等等這些步驟。這有助於我們建立更好、更健壯的機器學習模型。但是當我們處理圖像數據時,應該如何進行預處理?
事實證明,在計算機視覺領域(圖像、視頻等等),預處理是一個至關重要的步驟。skimage是scikit-learn家族的一部分,它是一個非常有用的庫,可以幫助我們開始學習。
在本文中,我們會介紹Python中使用skimage對圖像進行一些簡單但功能強大的預處理技術。
目錄
- 什麼是skimage?為什麼要使用它?
- 使用skimage在Python中讀取圖像
- 調整圖像大小
- 上下翻轉圖像
- 旋轉不同角度
- 水平和垂直翻轉
- 圖像裁剪
- 改變圖像亮度
- 使用濾鏡
什麼是skimage?為什麼要使用它?
Python中有多個庫和框架可讓我們處理圖像數據。那麼,為什麼要使用skimage?在深入研究本文之前,讓我在這裡回答。
Scikit-image或skimage是一個用於圖像預處理的開源Python包。
如果你以前使用過sklearn,那麼開始使用skimage將是小菜一碟。即使你完全不熟悉Python,skimage還是非常易於學習和使用的。
我真正喜歡skimage的地方在於它有一個結構良好的文檔,列出了skimage中提供的所有模塊,子模塊和函數。以下連結是skimage包中所有子模塊和函數的列表(https://scikit-image.org/docs/stable/api/api.html)
1.使用skimage讀取圖像
讓我們從基礎開始。第一步是學習如何使用skimage在Python中導入圖像。
圖像由稱為像素的多個小方塊組成。我下面顯示的圖像就是一個很好的例子。你在此處看到的小方塊就是像素:
我們可以看到該圖像沿垂直線有22個像素,沿水平線有16個像素。因此,此圖像的大小將為22 x 16。
當我們使用scikit-image(或其他任何包)讀取或加載圖像時,我們看到該圖像是以數字形式存儲。這些數字稱為像素值,它們代表圖像中每個像素的強度。
使用skimage加載圖像
在scikit-image包中,數據模塊中提供了幾個示例圖像。假設我們想加載一個圖像來執行一些實驗。我們不需要使用外部圖像,只需加載包中提供的圖像之一即可。
這是執行此操作的Python代碼:
from skimage.io import imread, imshow
from skimage import data
image = data.astronaut()
imshow(image)
注意,我在這裡使用了imshow函數來查看圖像。
如果你不想使用包提供的圖像而是想對你的系統里的圖像進行加載的話,我們可以使用skimage中的imread函數。
我們可以讀取兩種格式的圖像,彩色圖像和灰度圖像。我們將看到這兩種方法的實際應用,並理解它們是如何不同的。
使用skimage從系統讀取圖像
imread函數有一個參數"as_gray",用於指定是否必須將圖像轉換為灰度圖像。我們將從讀取灰度格式的圖像開始,所以將參數設置為true:
from skimage.io import imread, imshow
import matplotlib.pyplot as plt
%matplotlib inline
image_gray = imread('images.jpeg', as_gray=True)
imshow(image_gray)
我們可以很容易地使用imshow函數查看圖像。但這真的是圖像的存儲方式嗎?讓我們檢查一下變量image_gray中有什麼:
image_gray = imread('images.jpeg', as_gray=True)
print(image_gray.shape)
print(image_gray)
(258,195)
[[0.73586314 0.77115725 0.7907651 ... 0.11822745 0.11822745 0.11430588]
[0.65743176 0.70056902 0.72017686 ... 0.11822745 0.11430588 0.11430588]
[0.41401176 0.45714902 0.48067843 ... 0.11430588 0.11430588 0.11038431]
...
[0.73491725 0.73491725 0.73491725 ... 0.42055725 0.42055725 0.42055725]
[0.72594314 0.72986471 0.72986471 ... 0.41750667 0.41750667 0.41750667]
[0.72594314 0.72986471 0.72986471 ... 0.41750667 0.41750667 0.41750667]
變量以數字矩陣的形式來存儲圖像。如你所見,矩陣的形狀為259 x 195。矩陣里的這些數字稱為像素值,它們表示圖像中像素的強度。
現在,我們將以原始顏色的格式加載圖像。為此,我們必須將參數'as_gray'設置為False:
from skimage.io import imread, imshow
import matplotlib.pyplot as plt
%matplotlib inline
image_color = imread('images.jpeg', as_gray=False)
print(image_color.shape)
imshow(image_color)
(258,195,3)
好了!我們這裡有同樣的圖片,顏色不同。現在你可能想知道這兩種格式之間的區別以及應該使用哪種格式?讓我們一個一個來解決。
你注意到這個例子中圖像的形狀了嗎?它是(258,195,3),而之前的形狀是(258,195)。這裡的三個維度表示圖像中通道的數量。對於彩色圖像,存儲圖像最流行的格式是RGB(紅綠藍)。
但是我們應該使用哪種格式呢?與灰度圖像相比,彩色圖像具有更多的信息,但是彩色圖像的大小更大。RGB中的像素數是灰度圖像的3倍多。當我們沒有足夠的計算資源時,處理彩色圖像是一個巨大的挑戰。
因此,灰度圖像經常被用來減少計算複雜度。因此,如果數據集的大小很大,則可以選擇灰度圖像而不是彩色圖像。
2.更改圖像格式
在上一節中,我們討論了可以加載圖像的兩種重要格式,RGB和灰度格式。在本節中,我們將學習如何將圖像從一種格式轉換為另一種格式。首先,我們將讀取RGB格式的圖像並將其轉換為灰度格式。我們將在此處使用的函數是rgb2gray
from skimage.color import rgb2gray
img = imread('images.jpeg')
img_new = rgb2gray(img)
plt.subplot(121), imshow(img)
plt.title('RGB Format')
plt.subplot(122), imshow(img_new)
plt.title('Grayscale Format')
plt.show()
其他兩種流行的格式是HSV(色調,飽和度,明度)和HSL(色調,飽和度,亮度),它們是RGB格式的替代表示。讓我簡要解釋這些術語的含義。
- 色調(Hue)是色輪上的度數,其中0代表紅色,120代表綠色,240代表藍色,360再次代表紅色。
- 飽和度(Saturation)表示該顏色的百分比,其中0是白色,而100是全色。
- 明度(Value)表示不同數量的黑色或白色混合。
- 亮度(Lightness)是顯示圖像陰影的另一種方式,其中0為黑色,而1為白色。
下面顯示的圖片將使你的理解更清晰
將圖像更改為這些格式中的任何一種格式都與轉換為灰度的方法相同。我們可以使用函數rgb2hsl和rgb2hsv分別轉換成HSL和HSV格式。這裡我演示了如何將圖像轉換為HSV格式。
from skimage.color import rgb2hsv
img = imread('images.jpeg')
img_new = rgb2hsv(img)
plt.subplot(121), imshow(img)
plt.title('RGB Format')
plt.subplot(122), imshow(img_new)
plt.title('HSV Format')
plt.show()
3.使用skimage調整圖像大小
計算機視覺的最大挑戰之一是,我們需要大量的數據來訓練我們的模型。我們收集的數據通常有不同的來源,這可能會導致圖像大小有不同的差異。從圖像中提取特徵或將其用於數據增強時可能就會出現問題。
理想情況下,當我們構建模型時,圖像的大小應該是相同的。如果我們使用的是預訓練模型,那麼重要的是將輸入數據調整大小並將其規範化為與最初訓練網絡時相同的格式。這就是為什麼調整圖像大小是一個重要的圖像預處理步驟。
在這裡,我們將使用skimage的resize功能。此函數的輸入將是我們要更新的圖像以及新圖像所需的大小:
from skimage.transform import resize
img = imread('images.jpeg')
#縮放圖片
img_resized = resize(img, (300, 300))
#顯示圖片
plt.subplot(121), imshow(img)
plt.title('Original Image')
plt.subplot(122), imshow(img_resized)
plt.title('Resized Image')
plt.show()
4.使用skimage重新縮放(放大/縮小)圖片
重新縮放圖像是另一種常見的計算機視覺技術。這意味著按特定比例縮放圖像。例如,將每個圖像的大小減小一半(縮小),或者將圖像的大小增大2倍(放大)。
你可能會疑問說,我們可以簡單地將resize函數用於此任務,有什麼區別?
如果所有圖像的原始尺寸都相同,例如(300,300),我們可以直接使用resize函數並指定所需的尺寸(150,150)。但是,如果圖像的大小不同(如下圖所示),則無法使用resize函數。這是因為每個圖像的"一半"會有所不同。
你將在計算機視覺之旅中遇到很多類似這種情況的例子。
因此,在這裡,我們可以使用rescale函數並指定縮放比例。該函數基於圖像的原始尺寸,所有圖像將以此比例縮放。
from skimage.transform import rescale
img = imread('images.jpeg')
img_rescaled = rescale(img, scale=(0.5, 0.5))
plt.subplot(121), imshow(img)
plt.title('Original Image')
plt.subplot(122), imshow(img_rescaled)
plt.title('Rescaled Image')
plt.show()
5.使用skimage以不同角度旋轉圖像
到目前為止,我們已經研究過調整圖像的大小和縮放比例。讓我們把重點轉向看看如何改變圖像的方向。但是在深入探討之前,我們應該討論為什麼首先需要更改圖像方向。
考慮以下圖像。第一張圖像略微傾斜(可能是由於相機方向所致)。
要解決此方向問題,我們需要將圖像旋轉一定角度。我們可以使用skimage的rotate函數,並指定旋轉圖像的角度:
from skimage.transform import rotate
image = imread('tilt_image.png')
image_rotated = rotate(image, angle=45)
imshow(image_rotated)
看起來很棒!方向問題已解決。但是如果你仔細看,你會發現照片的四角被剪短了。這是因為,在旋轉過程中,圖像的大小保持不變,導致角附近的區域被裁剪。
在這種情況下,我們不會丟失任何重要信息,但情況可能並非總是如此。這個障礙由rotate函數中的resize參數來解決(默認情況下參數值為False):
from skimage.transform import rotate
image = imread('tilt_image.png')
image_rotated = rotate(image, angle=45, resize=True)
imshow(image_rotated)
我們還可以將旋轉概念用於數據增強。數據增強是一種使用可用數據生成更多樣本以訓練模型的技術。
假設你正在建立圖像分類模型,以識別貓和狗的圖像。看一下下面顯示的示例圖像。左側的兩個圖像都將被歸類為"狗",而右側的兩個圖像將被歸類為"貓":
我們在這裡改變了什麼?我們只是將圖像旋轉了180度並生成了新圖像。也就是你只需在現有數據中的每張圖像上添加一張新圖像,即可將訓練數據的大小增加一倍!
6.水平和垂直翻轉圖像
我們可以水平和垂直翻轉圖像。這樣會沿水平/垂直軸創建鏡像。我們可以將這種技術用於圖像預處理和圖像增強。
儘管在skimage中沒有直接的功能,但是我們可以使用NumPy執行此任務。
NumPy提供flipud和fliplr函數分別用於在水平和垂直軸上翻轉圖像。
函數的內部工作非常簡單。對於水平翻轉,行保持不變,而列的進行翻轉。讓我們用同樣的貓狗的例子,並使用flip函數:
from numpy import fliplr, flipud
dog = imread('Puppy.jpg')
cat = imread('whiskers.jpg')
dog_flip = fliplr(dog)
cat_flip = fliplr(cat)
plt.subplot(141), imshow(dog)
plt.subplot(142), imshow(dog_flip)
plt.subplot(143), imshow(cat)
plt.subplot(144), imshow(cat_flip)
plt.show()
7.裁剪圖像
你之前肯定在手機上使用非常多次裁剪功能。
你也可以使用skimage在Python中裁剪圖像。我們裁剪圖像以去除圖像中不需要的部分或聚焦於圖像的特定部分。
假設我們有下面這張籃球比賽的圖片(左圖)。目前,圖像的形狀是1067 x 1600。現在,我想從圖像的四個邊都移去100個像素。這意味著我們從圖像的上、下、左、右移去100個像素,從而聚焦在中心的物體上:
有兩種方法可以實現此目的:
- 首先,簡單地指定你想要的新圖像形狀。在本例中,它是image[100:967, 100:1500]。這對於單個圖像來說很好。如果我們有多個圖像呢?我們不得不為每個圖像都提到新的圖像形狀(不是很友好)。
- 另一種方法是使用當前圖像形狀計算裁剪後的圖像尺寸,可以使用image.shape命令來確定。所以新圖像的高度是[100:img.shape[0]-100] ,寬度為 [100:img.shape[1]-100].
因此,讓我們使用第二種方法裁剪上面的圖像:
image = imread('warriors.jpg')
# 只選擇圖像的一部分
cropped = image[100:(img.shape[0]-100),100:(img.shape[1]-100)]
plt.subplot(121), imshow(image)
plt.title('Original Image')
plt.subplot(122),imshow(cropped)
plt.title('Cropped Image')
plt.show()
8. 使用skimage更改圖像亮度
儘管相機的功能最近有所進步,但低光成像仍然是一個令人頭痛的問題。skimage會幫我們解決這個問題。
可以使用具有不同亮度的圖像使我們的計算機視覺模型對光照條件的變化具有魯棒性。
這對於在室外照明下工作的系統(例如,交通信號燈的閉路電視攝像機)非常重要。
可以使用skimage中的adjust_gamma函數更改圖像的亮度,該函數使用一種稱為gamma相關的方法。對於任何給定的圖像,像素值首先在0-1之間歸一化,然後乘以指定的伽瑪值。得到的像素值被縮放到0-255範圍。
對於大於1的伽瑪,輸出圖像將比輸入圖像暗。當伽馬小於1時,輸出圖像將比輸入圖像亮。
from skimage import exposure
#調節亮度
image = imread('images.jpeg')
image_bright = exposure.adjust_gamma(image, gamma=0.5,gain=1)
image_dark = exposure.adjust_gamma(image, gamma=1.5,gain=1)
# 顯示圖像
plt.subplot(131), imshow(image)
plt.title('Original Image')
plt.subplot(132),imshow(image_bright)
plt.title('Bright Image')
plt.subplot(133),imshow(image_dark)
plt.title('Dark Image')
plt.show()
9.在skimage中使用濾鏡
我們可以使用濾鏡(Filters)來修改或增強圖像的特徵。如果你曾經在社交媒體平台上玩過圖像,就會對濾鏡非常熟悉。
我們可以將濾鏡用於各種目的,例如使圖像平滑和銳化,去除噪聲,突出顯示圖像中的特徵和邊緣等。
當我們在圖像上應用濾鏡時,每個像素值都會替換為使用周圍像素值生成的新值。最簡單的濾鏡是中值濾鏡,其中像素值被替換為相鄰像素的中值。
from skimage.filters import median
image = imread('images.jpeg', as_gray=True)
image_median = median(image)
# 顯示圖像
plt.subplot(121), imshow(image)
plt.title('Original Image')
plt.subplot(122),imshow(image_median)
plt.title('Smooth Image')
plt.show()
當我們想要突出顯示圖像的邊緣時,我們可以使用另一個流行的濾鏡,sobel濾鏡。
from skimage.filters import sobel_h
image = imread('images.jpeg', as_gray=True)
image_sobelh = sobel_h(image)
# 顯示圖像
plt.subplot(121), imshow(image)
plt.title('Original Image')
plt.subplot(122),imshow(image_sobelh, cmap = True)
plt.title('Horizontal Edge')
plt.show()
結尾
祝賀你在計算機視覺領域邁出了第一步!乍一看,這似乎是一個令人望而生畏的領域,但如果你有一個結構化的思維模式,並且對機器學習算法的工作原理有很好的理解,你很快就會發現處理圖像和視頻數據的細微差別。
我們還可以使用skimage做其他事情,比如從圖像中提取邊緣,或者向圖像中添加噪聲等等。我想讓你以這個作為開始,並在Python中試用它們。這才是學習的方式!