6.18. 影像與 ndarray¶
Image 類別是處理相機原生像素的高速介面:它的每個方法都直接在影格緩衝區上以相機原生像素格式運作。numpy 則是處理其他所有工作的通用數值介面。有兩個方法在兩者之間搭起橋樑:
image.Image.to_ndarray()-- 將影像的像素複製到ndarray中。image.Image建構式 -- 從ndarray建立一張全新的影像。
兩者搭配使用,可讓應用程式擷取一張影格、交給 numpy 進行自訂轉換,再將結果放回影像中以顯示、儲存,或回饋給影像函式庫的其餘部分。
6.18.1. 影像轉 ndarray¶
to_ndarray() 會配置一個新的 ndarray,並將影像的像素資料複製進去(依照下方的 dtype 對應方式)。它絕不會是影像影格緩衝區的檢視(view)-- numpy 陣列永遠擁有自己的位元組。其函式簽章為 to_ndarray(dtype, *, buffer=None),輸出的形狀取決於影像格式:
GRAYSCALE -- 二維陣列,形狀為
(height, width)。RGB565 -- 三維陣列,形狀為
(height, width, 3),平面依 R/G/B 順序排列。
dtype 引數控制每個 8 位元像素值 v 的對應方式:
|
元素 |
8 位元像素值 |
|---|---|---|
|
|
|
|
|
|
|
|
|
範例 -- 將灰階影格檢視為 uint8 矩陣:
import csi
from ulab import numpy as np
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.QVGA)
img = csi0.snapshot()
a = img.to_ndarray('B') # shape (240, 320), dtype=uint8
print(a.shape, a.dtype)
print("mean brightness:", np.mean(a))
buffer= 關鍵字可讓應用程式重複使用已配置的 bytearray,這樣相機就不必每張影格都配置一個新的:
buf = bytearray(320 * 240)
while True:
img = csi0.snapshot()
a = img.to_ndarray('B', buffer=buf)
# ... process a ...
6.18.2. ndarray 轉影像¶
反向操作時,將 ndarray 作為第一個引數傳給 image.Image。建構式會配置一個新的影像緩衝區,並將陣列的值複製進去,截斷並四捨五入至 0..255:
image.Image(arr, *, buffer=None, copy_to_fb=False)
建構式會從陣列的形狀推斷幾何尺寸與像素格式:
形狀
(h, w)--GRAYSCALE影像。形狀
(h, w, 3)--RGB565影像。
ndarray 的 dtype 必須為 float;目前建構式僅支援此情況。值會被四捨五入並截斷至 0..255 範圍。
buffer= 可讓應用程式為產生的影像提供已配置的 bytearray。copy_to_fb=True 會將結果寫入相機的影格緩衝區,當結果需要顯示在 IDE 預覽中時,這是正確的選擇。
6.18.3. 往返轉換¶
import csi
import image
from ulab import numpy as np
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.QVGA)
img = csi0.snapshot()
a = img.to_ndarray('f') # work in float space
a = 255.0 * (a / 255.0) ** 0.5 # gamma correction
out = image.Image(a, copy_to_fb=True) # back to an image
6.18.4. 何時該使用橋接¶
當應用程式需要內建 image 方法未提供的通用數值運算時 -- 自訂濾鏡、自訂混合、不尋常的非線性運算 -- 或者當像素資料必須與非影像資料(IMU 軸向、音訊取樣)在單一計算中結合時,這個橋接方式就是正確的答案。
對於 Image 類別已涵蓋的高吞吐量像素處理,它並不是正確的選擇。內建方法直接在影格緩衝區上以相機原生像素格式運作,比起對等的 numpy 運算式要快得多。請只在影像函式庫尚未提供的運算上使用此橋接方式。