6.4. Dtype 資料型別

ndarray 的元素型別就是它的 dtype。dtype 一次決定了三件事:每個元素佔用幾個位元組、這些位元組如何被解讀,以及陣列能儲存的數值範圍。選對 dtype 是影響相機 RAM 用量最關鍵的單一決定。

6.4.1. 支援的 dtype

相機上的 numpy 支援一小組 dtype:

dtype

位元組

範圍

uint8

1

0 到 255

int8

1

-128 到 127

uint16

2

0 到 65,535

int16

2

-32,768 到 32,767

float

4

IEEE 754 單精度

bool

1

True / False

並沒有 int32int64,而且 OpenMV 的 ulab 建置版本並未啟用選用的 complex dtype。

請選擇與產生該資料的硬體相符的型別。8 位元的 ADC 取樣值適合用 uint8;12 位元的 ADC 取樣值可放進 uint16;來自灰階相機的亮度像素適合用 uint8 —— 比預設的 float 節省四倍的 RAM。

6.4.2. 預設的 dtype

建立陣列 上每一個建構函式的預設 dtype 都是 float。在處理感測器資料時,這很少是應用程式想要的。只要自然寬度較小,就明確傳入 dtype=::

sensor = np.array(samples, dtype=np.uint16)

在沒有 dtype= 引數的情況下重新包裝一個整數陣列,會同時複製 轉換成 float,這既耗費時間又耗費 RAM。當效能很重要時,請指明 dtype。

6.4.3. 現有陣列的 dtype

dtype 會以陣列內部所帶的整數型別代碼讀回該陣列的 dtype::

a = np.array([1, 2, 3], dtype=np.uint8)
print(a.dtype)            # 66 (the integer value of ``'B'``)

這些型別代碼整數與 numpy 模組上公開的常數相符 —— numpy.uint8numpy.int8numpy.uint16numpy.int16numpy.floatnumpy.bool —— 因此將 dtype 與模組常數比較,就是指令碼根據陣列所持有的內容進行分支的方式::

if a.dtype == np.uint8:
    ...  # uint8 branch

6.4.4. 向上轉型規則

兩個不同 dtype 的陣列可以作為同一運算子的運算元。numpy 會依據一個簡短的表格挑選結果型別:

結果

uint8

int8

int16

uint8

int16

int16

uint8

uint16

uint16

int8

int16

int16

int8

uint16

uint16

uint16

int16

float

任意

float

float

uint16 / int16 這一列會直接提升為 float,因為相機上的 numpy 沒有 32 位元的整數 dtype。

當二元運算子的某一側是 Python 純量時,該純量會被轉換成 最小 合適 dtype 的單元素陣列:123 變成 uint8 陣列,-1000 變成 int16,而 Python 的 float 則變成 float

6.4.5. 整數溢位會繞回

對兩個相同整數 dtype 陣列的運算會保留該 dtype,即使結果溢位也是如此。進位會被靜默地捨棄::

a = np.array([200, 200], dtype=np.uint8)
b = np.array([100, 100], dtype=np.uint8)
print(a + b)

輸出::

array([44, 44], dtype=uint8)

結果為 300 mod 256 == 44。當中間值需要的範圍超過輸入 dtype 所允許的範圍時,請先轉型::

c = np.array(a, dtype=np.uint16) + b
# array([300, 300], dtype=uint16)

這條規則適用於每一個整數運算子 —— +-*//%&|^。Float 陣列永遠不會溢位(它們會改為提升至無限大),因此這個轉型技巧只在整數的情況下才需要。