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.