6.4. Типи даних (dtype)

Тип елементів ndarray — це його dtype. Тип визначає три речі одночасно: скільки байтів займає кожен елемент, як інтерпретуються ці байти та який діапазон значень може зберігати масив. Вибір правильного типу — найважливіше рішення, що впливає на використання RAM на камері.

6.4.1. Підтримувані типи

numpy на камері підтримує невеликий набір типів:

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

Немає типів int32 або int64, і збірка OpenMV ulab не підтримує необов’язковий тип complex.

Вибирайте тип, що відповідає апаратному забезпеченню, яке виробило дані. Зразок 8-бітного ADC потребує uint8; зразок 12-бітного ADC вміщується у uint16; піксель яскравості з камери у відтінках сірого вміщується у uint8 — що заощаджує в чотири рази більше RAM порівняно з типом float за замовчуванням.

6.4.2. Тип за замовчуванням

Тип за замовчуванням для кожного конструктора у Створення масивівfloat. Це рідко те, чого потребує застосунок при обробці даних датчиків. Завжди явно передавайте dtype=, коли природна ширина менша:

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

Повторне обгортання цілочисельного масиву без аргументу dtype= копіює і конвертує у float, що коштує і часу, і RAM. Коли продуктивність важлива — вказуйте тип явно.

6.4.3. Тип існуючого масиву

dtype повертає тип масиву як ціле число — код типу, який масив зберігає внутрішньо:

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

Цілочисельні коди типів збігаються з константами, що визначені в модулі numpynumpy.uint8, numpy.int8, numpy.uint16, numpy.int16, numpy.float, numpy.bool — тому порівняння типу з константою модуля — це спосіб, яким скрипт розгалужується залежно від вмісту масиву:

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

6.4.4. Правила підвищення типу

Два масиви з різними типами можуть бути операндами одного оператора. 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-бітного цілочисельного типу.

Коли бінарний оператор має скалярне значення Python з одного боку, воно перетворюється на масив з одного елемента найменшого підходящого типу: 123 стає масивом uint8, -1000 стає int16, Python float стає float.

6.4.5. Переповнення цілих чисел циклічне

Операції над двома масивами одного цілочисельного типу зберігають цей тип, навіть якщо результат переповнюється. Перенесення мовчки відкидається:

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. Якщо проміжне значення потребує більшого діапазону, ніж дозволяє тип, спочатку виконайте приведення типу:

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

Це правило застосовується до кожного цілочисельного оператора — +, -, *, //, %, &, |, ^. Масиви float ніколи не переповнюються (натомість вони стають нескінченністю), тому прийом з приведенням типу потрібен лише для цілочисельного випадку.