6.4. Dtype 資料型別¶
ndarray 的元素型別就是它的 dtype。dtype 一次決定了三件事:每個元素佔用幾個位元組、這些位元組如何被解讀,以及陣列能儲存的數值範圍。選對 dtype 是影響相機 RAM 用量最關鍵的單一決定。
6.4.1. 支援的 dtype¶
相機上的 numpy 支援一小組 dtype:
dtype |
位元組 |
範圍 |
|---|---|---|
|
1 |
0 到 255 |
|
1 |
-128 到 127 |
|
2 |
0 到 65,535 |
|
2 |
-32,768 到 32,767 |
|
4 |
IEEE 754 單精度 |
|
1 |
|
並沒有 int32 或 int64,而且 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.uint8、numpy.int8、numpy.uint16、numpy.int16、numpy.float、numpy.bool —— 因此將 dtype 與模組常數比較,就是指令碼根據陣列所持有的內容進行分支的方式::
if a.dtype == np.uint8:
... # uint8 branch
6.4.4. 向上轉型規則¶
兩個不同 dtype 的陣列可以作為同一運算子的運算元。numpy 會依據一個簡短的表格挑選結果型別:
左 |
右 |
結果 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
任意 |
|
|
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 陣列永遠不會溢位(它們會改為提升至無限大),因此這個轉型技巧只在整數的情況下才需要。