2024年2月18日 星期日

Python 學習筆記 : OpenCV (二) 圖檔讀寫與顯示

本篇繼續按圖索驥繼續測試 OpenCV 功能, 本系列之前的文章參考 :


OpenCV 官方教學文件 (Python) 參考 :


OpenCV 提供了豐富的函式來處理影像, 下表為圖像讀寫與顯示會用到的函式 : 


 cv2 圖像處理函式 說明
 imread(file [, mode]) 以指定模式 mode 開啟檔案 file, 傳回一個代表圖片之 ndarray
 imwrite(file, img [quality]) 將圖片物件 img 以品質 quality 寫入 file 檔案 (jpg/jpeg, png 等)
 imshow(name, img) 將圖片物件 img 顯示於名為 name (字串) 的視窗內
 waitKey(t) 圖片視窗於 t 毫秒後關閉 (按 q 提前關閉), t=0 表示等按鍵才關閉
 destroyAllWindows() 關閉全部 cv2 所開啟的視窗
 split(img) 將圖片物件 img 分解為 B, G, R 三色, 傳回 (B, G, R) tuple


一. 讀取並顯示圖片 : 

此處要操作的圖檔是影像處理界赫赫有名的 Lenna 圖, 參考 : 


此圖可從 Wiki 下載使用 :


下載後放在目前的工作目錄下便可用 OpenCV 讀取顯示 :

>>> import cv2 
>>> lenna=cv2.imread('Lenna.jpg')    # 讀取圖檔傳回 ndarray 物件, 預設為彩色模式
>>> type(lenna)   
<class 'numpy.ndarray'>   
>>> lenna.shape            # 圖片物件的形狀為 316*316 彩色
(316, 316, 3)
>>> lenna.size               # 像素總數 316*316*3=299568 點
299568
>>> lenna.dtype            # 8 位元整數型態 (值 0~255)
dtype('uint8')
>>> lenna 
array([[[125, 137, 225],
        [130, 137, 224],
        [125, 137, 225],
        ...,

可見圖片物件為 3 軸陣列. 注意, OpenCV 的顏色排列順序不是 RGB, 而是 GBR. 

>>> cv2.imshow('lenna', lenna)     # 開啟名為 'lenna' 視窗來顯示圖片物件 lenna
>>> cv2.waitKey(0)     # 維持視窗顯示直到按下任意鍵

在 Python 互動環境執行上述指令時, 呼叫 imshow() 僅建立顯示視窗並未顯示圖片, 而是呼叫 waitKey() 時才顯示, 結果如下 :




將滑鼠移到圖形視窗上按任意鍵會傳回一個 code, 然後呼叫 destroyAllWindows() 可關閉視窗 :

13
>>> cv2.destroyAllWindows()   

寫成 .py 程式如下 :

# opencv_read_picture_1.py
import cv2 
lenna=cv2.imread('Lenna.jpg')
cv2.imshow('lenna', lenna)  
cv2.waitKey(0)
cv2.destroyAllWindows()

在上面測試中呼叫 imread() 時只傳入一個參數 (圖檔名稱), 並未指定開檔模式常數 mode, 預設是 1 (1=cv2.IMREAD_COLOR, 彩色模式), 事實上 OpenCV 定義了如下 13 種模式 :


 imread() 的 mode 參數 說明
 IMREAD_UNCHANGED (-1) 原始模式 (包含 alpha 值)
 IMREAD_GRAYSCALE (0) 灰階模式
 IMREAD_COLOR (1) BGR 彩色模式 (預設值)
 IMREAD_ANYDEPTH (2) 有深度時傳回 16/32 位元物件, 否則傳回 8 位元物件
 IMREAD_ANYCOLOR (4) 以任何顏色格式讀取圖片
 IMREAD_LOAD_GDAL (8) 以 GDAL 驅動程式載入圖片
 IMREAD_REDUCED_GRAYSCALE_2 (16) 尺寸減少 1/2 的灰階模式
 IMREAD_REDUCED_COLOR_2 (17) 尺寸減少 1/2 的 BGR 彩色模式
 IMREAD_REDUCED_GRAYSCALE_4 (32) 尺寸減少 1/4 的灰階模式
 IMREAD_REDUCED_COLOR_4 (33) 尺寸減少 1/4 的 BGR 彩色模式
 IMREAD_REDUCED_GRAYSCALE_8 (64) 尺寸減少 1/8 的灰階模式
 IMREAD_REDUCED_COLOR_8 (65) 尺寸減少 1/8 的 BGR 彩色模式
 IMREAD_IGNORE_ORIENTATION (128) 忽略 EXIF 中的方向資訊 (不旋轉)


這些模式常數是一個整數值, 可以用下列程式來顯示其值 :

>>> modes=[cv2.IMREAD_UNCHANGED,
       cv2.IMREAD_GRAYSCALE,
       cv2.IMREAD_COLOR,
       cv2.IMREAD_ANYDEPTH,
       cv2.IMREAD_ANYCOLOR,
       cv2.IMREAD_LOAD_GDAL,
       cv2.IMREAD_REDUCED_GRAYSCALE_2,
       cv2.IMREAD_REDUCED_COLOR_2,
       cv2.IMREAD_REDUCED_GRAYSCALE_4,
       cv2.IMREAD_REDUCED_COLOR_4,
       cv2.IMREAD_REDUCED_GRAYSCALE_8,
       cv2.IMREAD_REDUCED_COLOR_8,
       cv2.IMREAD_IGNORE_ORIENTATION]
>>> for mode in modes:    
    print(mode, end=',')  
    
-1,0,1,2,4,8,16,17,32,33,64,65,128,    

例如以灰階模式 (mode=0) 讀取圖片 :

# opencv_read_picture_2.py
import cv2 
lenna=cv2.imread('Lenna.jpg', 0)  # 灰階模式
cv2.imshow('lenna', lenna)  
cv2.waitKey(0)
cv2.destroyAllWindows()

結果如下 :




下面範例使用 mode=16 (cv2.IMREAD_REDUCED_GRAYSCALE_2) 來產生 1/2 灰階圖片物件 : 

# opencv_read_picture_3.py
import cv2 
lenna=cv2.imread('Lenna.jpg', 16)  # 1/2 灰階模式
print(lenna.shape)
cv2.imshow('lenna', lenna)  
cv2.waitKey(0)
cv2.destroyAllWindows()

輸出如下 :

>>> %Run opencv_read_picture_3.py   
(158, 158)  

可見尺寸之長寬確實變成 1/2 (因為是灰階, 故無顏色軸). 

下面範例使用 mode=17 (cv2.IMREAD_REDUCED_COLOR_2) 來產生 1/2 彩色圖片 : 

# opencv_read_picture_4.py
import cv2 
lenna=cv2.imread('Lenna.jpg', cv2.IMREAD_REDUCED_COLOR_2)  # 1/2 彩色 
print(lenna.shape)
cv2.imshow('lenna', lenna)  
cv2.waitKey(0)
cv2.destroyAllWindows()

結果如下 :

>>> %Run opencv_read_picture_4py.py    
(158, 158, 3)

可見尺寸已縮為 1/2 (長寬 316/2=158). 


二. 寫入圖片 : 

呼叫 cv2.imwrite() 可將圖片物件儲存為檔案, 此函式可傳入三個參數 :
  • file : 輸出圖檔名稱 (含路徑), 必要參數
  • img : 圖片物件 (ndarray), 必要參數
  • quality : 圖片品質 (串列), 備選參數
下列範例先讀入 Lenna.jpg, 然後存成 Lenna,png :

# opencv_read_picture_5.py
import cv2 
lenna=cv2.imread('Lenna.jpg')       # 讀取 Lenna.jpg 為 ndarray 陣列 
cv2.imwrite('Lenna.png', lenna)    # 存成 PNG 檔 Lenna.png
cv2.waitKey(0)
cv2.destroyAllWindows()

執行完畢會在工作目錄下產生一個 Lenna.png : 




也可以自己製作 ndarray 陣列來產生圖片物件並寫入圖檔, 下面範例改寫自 "一本精通 : OpenCV與AI影像辨識" :

# opencv_read_picture_6.py
import cv2
import numpy as np

img=np.zeros((400, 400, 3), dtype='uint8')
img += 170
img[100:300, 100:300]=[200, 150, 0]
cv2.imwrite('cv2_test_1.jpg', img)
cv2.imshow('cv2_test_1', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

此例先用 Numpy 的 zeros() 建立一個 400*300*3 的 ndarray 陣列物件, 所有元素都是 0, 然後用 += 運算符將每個元素都加一個整數 (170), 所以圖片目前的背景色是 (B, G, R)=(170, 170, 170), 再用切片運算將索引位置 100:300 的值全部改成  (B, G, R)=(200, 150, 0), 最後將其寫入檔案並顯示, 結果如下 : 




注意, 如上所述, OpenCV 的像素的色彩順序是 B, G, R 而不是 R, G, B. 

沒有留言 :