6.18. 画像と ndarray

Image クラスは、カメラネイティブなピクセル処理のための高速な表面です。このクラスのすべてのメソッドは、カメラのネイティブピクセル形式のフレームバッファ上で直接動作します。numpy は、それ以外のすべてを扱う汎用的な数値処理の表面です。この2つを橋渡しするメソッドが2つあります。

この2つを組み合わせることで、アプリケーションはフレームをキャプチャし、それを numpy に渡してカスタム変換を行い、その結果を画像に戻して表示・保存したり、画像ライブラリの残りの処理に再び供給したりできます。

6.18.1. 画像から ndarray へ

to_ndarray() は新しい ndarray を割り当て、画像のピクセルデータを(以下の dtype マッピングに従って)そこにコピーします。これは決して画像のフレームバッファへのビューではありません。numpy 配列は常に自身のバイト列を所有します。シグネチャは to_ndarray(dtype, *, buffer=None) で、出力の形状は画像形式に依存します。

  • GRAYSCALE -- 2次元配列、形状 (height, width)

  • RGB565 -- 3次元配列、形状 (height, width, 3)、プレーンは R/G/B の順。

dtype 引数は、各8ビットピクセル値 v がどのようにマッピングされるかを制御します。

dtype

要素

8ビットピクセル値 v のマッピング

'B'

uint8

v(生の値)

'b'

int8

v - 128(ゼロを中心に再センタリング)

'f'

float32

float(v)(0.0 ... 255.0)

例 -- グレースケールフレームを 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 から画像へ

逆方向では、ndarrayimage.Image の第1引数として渡します。コンストラクタは新しい画像バッファを割り当て、配列の値を 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 の各軸、オーディオサンプル)と1つの計算の中で組み合わせる必要がある場合に適切な答えです。

これは、Image クラスが既にカバーしている高スループットなピクセル処理には適切ではありません。組み込みメソッドはカメラのネイティブピクセル形式のフレームバッファ上で直接動作し、同等の numpy 式よりもはるかに高速です。画像ライブラリがまだ提供していない演算には、この橋渡しを活用してください。