6.4. Dtypes¶
Typem elementów tablicy ndarray jest jej dtype. Dtype rozstrzyga trzy rzeczy naraz: ile bajtów zajmuje każdy element, jak interpretowane są te bajty oraz jaki zakres wartości tablica może przechowywać. Wybór właściwego dtype to pojedyncza, najważniejsza decyzja wpływająca na zużycie pamięci RAM w kamerze.
6.4.1. Obsługiwane dtype¶
numpy na kamerze obsługuje niewielki zestaw dtype:
dtype |
bajty |
zakres |
|---|---|---|
|
1 |
0 do 255 |
|
1 |
-128 do 127 |
|
2 |
0 do 65 535 |
|
2 |
-32 768 do 32 767 |
|
4 |
IEEE 754 pojedyncza precyzja |
|
1 |
|
Nie ma int32 ani int64, a kompilacja ulab od OpenMV nie włącza opcjonalnego dtype complex.
Wybierz typ pasujący do sprzętu, który wyprodukował dane. 8-bitowa próbka z ADC wymaga uint8; 12-bitowa próbka z ADC mieści się w uint16; piksel luminancji z kamery w skali szarości mieści się w uint8 – oszczędzając czterokrotnie więcej pamięci RAM, niż kosztowałby domyślny float.
6.4.2. Domyślny dtype¶
Domyślnym dtype każdego konstruktora na stronie Tworzenie tablic jest float. To rzadko jest tym, czego aplikacja potrzebuje przy obsłudze danych z sensorów. Przekazuj dtype= jawnie zawsze, gdy naturalna szerokość jest mniejsza:
sensor = np.array(samples, dtype=np.uint16)
Ponowne opakowanie tablicy liczb całkowitych bez argumentu dtype= kopiuje oraz konwertuje do float, co kosztuje zarówno czas, jak i pamięć RAM. Gdy wydajność ma znaczenie, podaj nazwę dtype.
6.4.3. Dtype istniejącej tablicy¶
dtype zwraca dtype tablicy jako całkowitoliczbowy kod typu, który tablica niesie wewnętrznie:
a = np.array([1, 2, 3], dtype=np.uint8)
print(a.dtype) # 66 (the integer value of ``'B'``)
Liczby kodów typów odpowiadają stałym udostępnianym w module numpy – numpy.uint8, numpy.int8, numpy.uint16, numpy.int16, numpy.float, numpy.bool – więc porównanie dtype ze stałą modułu to sposób, w jaki skrypt rozgałęzia się w zależności od tego, co przechowuje tablica:
if a.dtype == np.uint8:
... # uint8 branch
6.4.4. Reguły rozszerzania typów¶
Dwie tablice o różnych dtype mogą być operandami tego samego operatora. numpy wybiera typ wyniku zgodnie z krótką tabelą:
lewy |
prawy |
wynik |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dowolny |
|
|
Wiersz uint16 / int16 jest promowany od razu do float, ponieważ numpy na kamerze nie ma 32-bitowego całkowitoliczbowego dtype.
Gdy operator binarny ma po jednej stronie skalar Pythona, skalar jest konwertowany na jednoelementową tablicę o najmniejszym odpowiednim dtype: 123 staje się tablicą uint8, -1000 staje się int16, a float Pythona staje się float.
6.4.5. Przepełnienie liczb całkowitych zawija się¶
Operacje na dwóch tablicach o tym samym całkowitoliczbowym dtype zachowują ten dtype, nawet gdy wynik się przepełni. Przeniesienie jest po cichu odrzucane:
a = np.array([200, 200], dtype=np.uint8)
b = np.array([100, 100], dtype=np.uint8)
print(a + b)
Wynik:
array([44, 44], dtype=uint8)
Wynik to 300 mod 256 == 44. Gdy wartość pośrednia potrzebuje większego zakresu, niż pozwala dtype wejścia, najpierw rzutuj:
c = np.array(a, dtype=np.uint16) + b
# array([300, 300], dtype=uint16)
Ta reguła dotyczy każdego operatora całkowitoliczbowego – +, -, *, //, %, &, |, ^. Tablice float nigdy się nie przepełniają (zamiast tego są promowane do nieskończoności), więc sztuczka z rzutowaniem jest potrzebna tylko w przypadku liczb całkowitych.