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

uint8

1

0 do 255

int8

1

-128 do 127

uint16

2

0 do 65 535

int16

2

-32 768 do 32 767

float

4

IEEE 754 pojedyncza precyzja

bool

1

True / False

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 numpynumpy.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

uint8

int8

int16

uint8

int16

int16

uint8

uint16

uint16

int8

int16

int16

int8

uint16

uint16

uint16

int16

float

dowolny

float

float

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.