Python各類圖像庫的圖片讀寫方式總結


最近在研究深度學習視覺相關的東西,經常需要寫python代碼搭建深度學習模型。比如寫CNN模型相關代碼時,我們需要借助python圖像庫來讀取圖像並進行一系列的圖像處理工作。我最常用的圖像庫當然是opencv,很強大很好用,但是opencv也有一些坑,不注意的話也會搞出大麻煩。近期我也在看一些別人寫的代碼,因為個人習慣不一樣,他們在做深度學習時用於圖片讀取的圖像庫各不相同,從opencv到PIL再到skimage等等各種庫都有,有些庫讀進來的圖片存儲方式也不太一樣,如果不好好總結這些主流圖像讀寫庫特點的話,以后看代碼寫代碼都會遇坑無數。這篇文章就總結了以下主流Python圖像庫的一些基本使用方法和需要注意的地方:

  1. opencv
  2. PIL(pillow)
  3. matplotlib.image
  4. scipy.misc
  5. 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)

'''

總結

  1. 除了opencv讀入的彩色圖片以BGR順序存儲外,其他所有圖像庫讀入彩色圖片都以RGB存儲。
  2. 除了PIL讀入的圖片是img類之外,其他庫讀進來的圖片都是以numpy 矩陣。
  3. 各大圖像庫的性能,老大哥當屬opencv,無論是速度還是圖片操作的全面性,都屬於碾壓的存在,畢竟他是一個巨大的cv專用庫。下面那張圖就是我從知乎盜來的一張關於各個主流圖像庫的一些性能比較圖,從測試結果看來,opencv確實勝出太多了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM