最近在研究深度學習視覺相關的東西,經常需要寫python程式碼搭建深度學習模型。比如寫CNN模型相關程式碼時,我們需要藉助python影象庫來讀取影象並進行一系列的影象處理工作。我最常用的影象庫當然是opencv,很強大很好用,但是opencv也有一些坑,不注意的話也會搞出大麻煩。近期我也在看一些別人寫的程式碼,因為個人習慣不一樣,他們在做深度學習時用於圖片讀取的影象庫各不相同,從opencv到PIL再到skimage等等各種庫都有,有些庫讀進來的圖片儲存方式也不太一樣,如果不好好總結這些主流影象讀寫庫特點的話,以後看程式碼寫程式碼都會遇坑無數。這篇文章就總結了以下主流Python影象庫的一些基本使用方法和需要注意的地方:
- opencv
- PIL(pillow)
- matplotlib.image
- scipy.misc
- skimage
opencv: cv2.imread
opencv作為我最常用的影象處理庫,當然第一個介紹,並且介紹得比較全面。毋庸置疑,opencv是今天介紹得所有影象庫中最全面也最強大的庫,如果我們只想掌握一個影象庫,我覺得opencv庫肯定是最適合不過了。
圖片讀取操作
import cv2
import numpy as np
#讀入圖片:預設彩色圖,cv2.IMREAD_GRAYSCALE灰度圖,cv2.IMREAD_UNCHANGED包含alpha通道
img = cv2.imread('1.jpg')
cv2.imshow('src',img)
print(img.shape) # (h,w,c)
print(img.size) # 畫素總數目
print(img.dtype)
print(img)
cv2.waitKey()
值得注意的是,opencv讀進來的圖片已經是一個numpy矩陣了,彩色圖片維度是(高度,寬度,通道數)。資料型別是uint8。
#gray = cv2.imread('1.jpg',cv2.IMREAD_GRAYSCALE) #灰度圖
#cv2.imshow('gray',gray)
#也可以這麼寫,先讀入彩色圖,再轉灰度圖
src = cv2.imread('1.jpg')
gray = cv2.cvtColor(src,cv2.COLOR_BGR2GRAY)
cv2.imshow('gray',gray)
print(gray.shape)
print(gray.size)
print(gray)
cv2.waitKey()
上面提到了兩種獲取灰度圖的方式,讀進來的灰度圖的矩陣格式是(高度,寬度)。
#注意,計算圖片路徑是錯的,Opencv也不會提醒你,但print img時得到的結果是None
img2 = cv2.imread('2.jpg')
print(img2)
#如何解決“讀到的圖片不存在的問題”? #加入判斷語句,如果為空,做異常處理
img2 = cv2.imread('2.jpg')
if img2 == None:
print('fail to load image!')
圖片矩陣變換
opencv讀入圖片的矩陣格式是:(height,width,channels)。而在深度學習中,因為要對不同通道應用卷積,所以會採取另一種方式:(channels,height,width)。為了應對該要求,我們可以這麼做
#注意到,opencv讀入的圖片的彩色圖是一個channel last的三維矩陣(h,w,c),即(高度,寬度,通道)
#有時候在深度學習中用到的的圖片矩陣形式可能是channel first,那我們可以這樣轉一下
print(img.shape)
img = img.transpose(2,0,1)
print(img.shape)
在深度學習搭建CNN時,往往要做相應的影象資料處理,比如影象要擴充套件維度,比如擴充套件成(batch_size,channels,height,width)。
對於這種要求,我們可以這麼做。
#有時候還要擴充套件維度,比如有時候我們需要預測單張圖片,要在要加一列做圖片的個數,可以這麼做
img = np.expand_dims(img, axis=0)
print(img.shape)
上面提到的是預測階段時預測單張圖片的擴充套件維度的操作,如果是訓練階段,構建batch,即得到這種形式:(batch_size,channels,height,width)。我一般喜歡這麼做
data_list = []
loop:
im = cv2.imread('xxx.png')
data_list.append(im)
data_arr = np.array(data_list)
這樣子就能構造成我們想要的形式了。
圖片歸一化
#因為opencv讀入的圖片矩陣數值是0到255,有時我們需要對其進行歸一化為0~1
img3 = cv2.imread('1.jpg')
img3 = img3.astype("float") / 255.0 #注意需要先轉化資料型別為float
print(img3.dtype)
print(img3)
儲存圖片
#儲存圖片
cv2.imwrite('test1.jpg',img3) #得到的是全黑的圖片,因為我們把它歸一化了
#所以要得到視覺化的圖,需要先*255還原
img3 = img3 * 255
cv2.imwrite('test2.jpg',img3) #這樣就可以看到彩色原圖了
opencv大坑之BGR
opencv對於讀進來的圖片的通道排列是BGR,而不是主流的RGB!謹記!
#opencv讀入的矩陣是BGR,如果想轉為RGB,可以這麼轉
img4 = cv2.imread('1.jpg')
img4 = cv2.cvtColor(img4,cv2.COLOR_BGR2RGB)
訪問畫素
#訪問畫素
print(img4[10,10]) #3channels
print(gray[10,10]) #1channel
img4[10,10] = [255,255,255]
gray[10,10] = 255
print(img4[10,10]) #3channels
print(gray[10,10]) #1channel
ROI操作
#roi操作
roi = img4[200:550,100:450,:]
cv2.imshow('roi',roi)
cv2.waitKey()
通道操作
#分離通道
img5 = cv2.imread('1.jpg')
b,g,r = cv2.split(img5)
#合併通道
img5 = cv2.merge((b,g,r))
#也可以不拆分
img5[:,:,2] = 0 #將紅色通道值全部設0
PIL:PIL.Image.open
圖片讀取
from PIL import Image
import numpy as np
PIL即Python Imaging Library,也即為我們所稱的Pillow,是一個很流行的影象庫,它比opencv更為輕巧,正因如此,它深受大眾的喜愛。
影象讀寫
PIL讀進來的影象是一個物件,而不是我們所熟知的numpy 矩陣。
img = Image.open('1.jpg')
print(img.format)
print(img.size) #注意,省略了通道 (w,h)
print(img.mode) #L為灰度圖,RGB為真彩色,RGBA為加了透明通道
img.show() # 顯示圖片
灰度圖的獲取
gray = Image.open('1.jpg').convert('L')
gray.show()
#讀取不到圖片會丟擲異常IOError,我們可以捕捉它,做異常處理
try:
img2 = Image.open('2.jpg')
except IOError:
print('fail to load image!')
#pillow讀進來的圖片不是矩陣,我們將圖片轉矩陣,channel last
arr = np.array(img3)
print(arr.shape)
print(arr.dtype)
print(arr)
灰度圖的轉化與彩圖轉化一樣
arr_gray = np.array(gray)
print(arr_gray.shape)
print(arr_gray.dtype)
print(arr_gray)
儲存圖片
#矩陣再轉為影象
new_im = Image.fromarray(arr)
new_im.save('3.png')
影象操作
#分離合並通道
r, g, b = img.split()
img = Image.merge("RGB", (b, g, r))
img = img.copy() #複製影象
ROI獲取
img3 = Image.open('1.jpg')
roi = img3.crop((0,0,300,300)) #(左上x,左上y,右下x,右下y)座標
roi.show()
matplotlib:matplotlib.image.imread
matplotlib是一個科學繪圖神器,用的人非常多。
import matplotlib.pyplot as plt
import numpy as np
image = plt.imread('1.jpg')
plt.imshow(image)
plt.show()
#也可以關閉顯示x,y軸上的數字
image = plt.imread('1.jpg')
plt.imshow(image)
plt.axis('off')
plt.show()
#plt.imread讀入的就是一個矩陣,跟opencv一樣,但彩圖讀進的是RGB,與opencv有區別
print(image.shape) # (h,w,c)
print(image.size)
print(image.dtype)
print(image)
im_r = image[:,:,0] #紅色通道
plt.imshow(im_r)
plt.show()
#此時會發現顯示的是熱量圖,不是我們預想的灰度圖,可以新增 cmap 引數解決
plt.imshow(im_r,cmap='Greys_r')
plt.show()
#與opencv結合使用
import cv2
im2 = cv2.imread('1.jpg')
plt.imshow(im2)
plt.axis('off')
plt.show()
#發現影象顏色怪怪的,原因當然是我們前面提到的RGB順序不同的原因啦,轉一下就好
im2 = cv2.cvtColor(im2,cv2.COLOR_BGR2RGB)
plt.imshow(im2)
plt.axis('off')
plt.show()
#所以無論用什麼庫讀進圖片,只要把圖片改為矩陣,那麼matplotlib就可以處理了
#再試一試pillow和matplotlib結合
from PIL import Image
im3 = Image.open('1.jpg')
im3 = np.array(im3)
plt.figure(1)
plt.imshow(im3)
plt.axis('off')
#儲存影象,注意,必須在show之前savefig,否則儲存的圖片一片空白
plt.savefig('timo.jpg')
plt.show()
#最後以一個綜合例子總結matplotlib最基本的圖片顯示技巧吧
im_lol1 = plt.imread('lol.jpg')
im_lol2 = plt.imread('1.jpg')
figure = plt.figure(figsize=(20,10)) # 調整顯示圖片的大小
'''
figsize引數:指定繪圖物件的寬度和高度,單位為英寸;dpi引數指定繪圖物件的解析度,
即每英寸多少個畫素,預設值為80。因此本例中所建立的圖表視窗的寬度為8*80 = 640畫素
'''
plt.axis("off")#不顯示刻度
ax = figure.add_subplot(121) # 圖片以1行2列的形式顯示
plt.axis('off')
ax.imshow(im_lol1) #第一張圖
ax.set_title('lol image 1')#給圖片加titile
ax = figure.add_subplot(122)
plt.axis('off')
ax.imshow(im_lol2)
ax.set_title('lol image 2')#給圖片加titile
plt.savefig('twp.jpg')
plt.show()
scipy.misc:scipy.misc.imread
from scipy import misc
import matplotlib.pyplot as plt
im = misc.imread('1.jpg')
print(im.dtype)
print(im.size)
print(im.shape)
misc.imsave('misc1.png',im)
plt.imshow(im)
plt.show()
print(im)
可以看到,有warining,提示我們imread和imsave在後來的版本將會被棄用,叫我們使用imageio.imread和imageio.imwrite。
我們根據她的提示,使用imageio模組進行圖片讀寫,warning也就沒有了。
import imageio
im2 = imageio.imread('1.jpg')
print(im2.dtype)
print(im2.size)
print(im2.shape)
plt.imshow(im)
plt.show()
print(im2)
imageio.imsave('imageio.png',im2)
skimage:skimage.io.imread
from skimage import io
im = io.imread('1.jpg')
print(im.shape) # numpy矩陣,(h,w,c)
print(im.dtype)
print(im.size)
io.imshow(im)
io.imsave('sk.png',im)
print(im)
影象也是以numpy array形式讀入。
灰度圖的獲取方式:
im2 = io.imread('1.jpg',as_grey=True) #讀入灰度圖
print(im2.dtype)
print(im2.size)
print(im2.shape)
io.imshow(im2)
io.imsave('sk_gray.png',im2)
io.show()
print(im2)
可以看到,灰度影象的矩陣的值被歸一化了,注意注意!
也可以以這種方式獲得灰度圖:
from skimage import color
im3 = io.imread('1.jpg')
im3 = color.rgb2grey(im3)
print(im3.dtype)
print(im3.size)
print(im3.shape)
io.imshow(im3)
io.show()
'''
skimage.color.rgb2grey(rgb)
skimage.color.rgb2hsv(rgb)
skimage.color.rgb2lab(rgb)
skimage.color.gray2rgb(image)
skimage.color.hsv2rgb(hsv)
skimage.color.lab2rgb(lab)
'''
總結
- 除了opencv讀入的彩色圖片以BGR順序儲存外,其他所有影象庫讀入彩色圖片都以RGB儲存。
- 除了PIL讀入的圖片是img類之外,其他庫讀進來的圖片都是以numpy 矩陣。
- 各大影象庫的效能,老大哥當屬opencv,無論是速度還是圖片操作的全面性,都屬於碾壓的存在,畢竟他是一個巨大的cv專用庫。下面那張圖就是我從知乎盜來的一張關於各個主流影象庫的一些效能比較圖,從測試結果看來,opencv確實勝出太多了。