OpenCV 與 NumPy


OpenCV 是基於 C/C++,用於處理圖像,opencv-python 是它的 Python 封裝版本,它是基於 NumPy,若能有對 NumPy 的認識,並遵照陣列程式設計典範,就能獲得便利性與效能,無論如何,先來個基本的圖片讀取與顯示:

import cv2

img = cv2.imread('caterpillar.jpg')
cv2.imshow('caterpillar', img)

cv2.waitKey(0)
cv2.destroyAllWindows()

這邊看到匯入了 cv2,2 並不是指 OpenCV 的版本號,而是指底層是 C++ API,過去的 cv 表示基於 C API。

cv2.imread 用於讀取圖片,傳回具有 NumPy 陣列行為的物件,若只是要顯示圖片,可以透過 cv2.imshow,第一個參數用來指定視窗標題文字,預設它會自動根據圖片調整視窗大小,若想自由縮放視窗,可以透過 cv2.namedWindow,這個函式預設是 cv2.WINDOW_AUTOSIZE,表示自動調整視窗大小,若指定 cv2.WINDOW_NORMAL,就可以自由調整視窗大小。

cv2.imshow 並不會阻斷流程執行,為了不因為執行結束就關閉視窗,這邊使用了 cv2.waitKey,表示等待使用者按下按鍵,可以指定等待時間,指定為 0 的話表示不限制等待時間,cv2.waitKey 會傳回按下的按鍵編碼,若指定的時間到又沒按下任何鍵,會傳回 -1。

cv2.destroyAllWindows 會關閉全部視窗,若想指定特定視窗,可以使用 cv2.destroyWindow 指定標題文字。

以下是執行結果:

OpenCV 與 NumPy

cv2.imread 的預設值是 cv2.IMREAD_COLOR,會讀取圖片的 RGB,然而忽略透明度,傳回的是具有三維的 NumPy 陣列行為的物件,圖片使用的是電腦繪圖座標,因此 y 往下為正,x 往右為正,軸 0 用來指定圖片像素的 y 索引,軸 1 用來指定圖片像素的 x 索引,若是讀取了圖片的 RGB,軸 2 用來指定像素的 BGR 欄位索引(不是 RGB!),上圖是個 250x250 的圖片,因此傳回的陣列形狀是 (250, 250, 3),若顯示字串描述,會像是:

[[[255 255 255]
  [255 255 255]
  [255 255 255]
  ...
  [255 255 255]
  [255 255 255]
  [255 255 255]]

 [[255 255 255]
  [255 255 255]
  [255 255 255]
  ...
  [255 255 255]
  [255 255 255]
  [255 255 255]]

  ...
 [[255 255 255]
  [255 255 255]
  [255 255 255]
  ...
  [255 255 255]
  [255 255 255]
  [255 255 255]]]

只要取出部份的陣列內容,就可以達到裁剪圖片的效果,自行使用迴圈雖然可以達到目的,然而善用一下〈NumPy 陣列索引〉,既方便又能兼顧效能:

import cv2

top = 50
left = 50
right = 200
bottom = 200

img = cv2.imread('caterpillar.jpg')
cv2.imshow('caterpillar', img[top:bottom, left:right])

cv2.waitKey(0)
cv2.destroyAllWindows()

顯示的圖片如下:

OpenCV 與 NumPy

既然改變陣列可以控制圖片,那麼若轉置 y 與 x 的像素,就能達到翻轉圖片的效果:

import cv2

img = cv2.imread('caterpillar.jpg')
cv2.imshow('caterpillar', img.transpose((1, 0, 2)))

cv2.waitKey(0)
cv2.destroyAllWindows()

transpose 是 NumPy 陣列的方法(也可以透過 numpy.transpose),必須指定 axes 參數,代表著哪些軸要轉置,上例的圖片顯示為:

OpenCV 與 NumPy

若是灰階圖片,可以簡單地透過 .T 轉置,因為 cv2.imread 會傳回二維陣列,例如:

import cv2

img = cv2.imread('caterpillar.jpg', cv2.IMREAD_GRAYSCALE)
cv2.imshow('caterpillar', img.T)

cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.IMREAD_GRAYSCALE 指定以灰階格式讀取圖片,傳回的陣列形狀會是 (250, 250),每個元素代表著灰階值,例如顯示其字串描述的話會是:

[[255 255 255 ... 255 255 255]
 [255 255 255 ... 255 255 255]
 [255 255 255 ... 255 255 255]
 ...
 [255 255 255 ... 255 255 255]
 [255 255 255 ... 255 255 255]
 [255 255 255 ... 255 255 255]]

因為是二維陣列,透過 .T 就可以得到轉置矩陣,範例的顯示如下:

OpenCV 與 NumPy

以上範例都是翻轉圖片的效果,如果只是要旋轉呢?NumPy 有 rot90 函式,OpenCV 有 rotate 方法,都可以用來旋轉圖片:

import cv2
import numpy as np

img = cv2.imread('caterpillar.jpg')
# 或使用 cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE))
cv2.imshow('caterpillar', np.rot90(img))

cv2.waitKey(0)
cv2.destroyAllWindows()

執行結果如下:

OpenCV 與 NumPy

這邊主要是著重在 OpenCV 與 NumPy 的關係,當然,OpenCV 還有其他處理圖片的方式,這就之後有機會遇到再來談了。