6.4. Dtypes

O tipo de elemento de um ndarray é o seu dtype. O dtype define simultaneamente três coisas: quantos bytes cada elemento ocupa, como os bytes são interpretados, e o intervalo de valores que o array pode armazenar. Escolher o dtype correto é a decisão mais importante no que respeita ao uso de RAM na câmara.

6.4.1. Os dtypes suportados

O numpy na câmara suporta um conjunto reduzido de dtypes:

dtype

bytes

intervalo

uint8

1

0 a 255

int8

1

-128 a 127

uint16

2

0 a 65 535

int16

2

-32 768 a 32 767

float

4

Precisão simples IEEE 754

bool

1

True / False

Não existe int32 nem int64, e a compilação ulab do OpenMV não ativa o dtype opcional complex.

Escolha o tipo que corresponde ao hardware que produziu os dados. Uma amostra de ADC de 8 bits usa uint8; uma amostra de ADC de 12 bits cabe em uint16; um pixel de luminância de uma câmara em escala de cinzentos cabe em uint8 – poupando quatro vezes a RAM que o float predefinido consumiria.

6.4.2. O dtype predefinido

O dtype predefinido de todos os construtores em Criação de arrays é float. Isso raramente corresponde ao que a aplicação necessita ao tratar dados de sensor. Passe dtype= explicitamente sempre que a largura natural for menor:

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

Reembrulhar um array de inteiros sem o argumento dtype= copia e converte para float, o que aumenta o tempo e consome RAM. Quando o desempenho importa, indique o dtype.

6.4.3. O dtype de um array existente

dtype devolve o dtype do array como o código de tipo inteiro que o array guarda internamente:

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

Os inteiros de código de tipo correspondem às constantes expostas no módulo numpynumpy.uint8, numpy.int8, numpy.uint16, numpy.int16, numpy.float, numpy.bool – por isso comparar o dtype com a constante do módulo é a forma de um script ramificar consoante o que um array contém:

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

6.4.4. Regras de promoção de tipo

Dois arrays de dtypes diferentes podem ser operandos do mesmo operador. O numpy escolhe o tipo do resultado de acordo com uma tabela resumida:

esquerda

direita

resultado

uint8

int8

int16

uint8

int16

int16

uint8

uint16

uint16

int8

int16

int16

int8

uint16

uint16

uint16

int16

float

qualquer

float

float

A linha uint16 / int16 promove diretamente para float porque o numpy na câmara não possui dtype de inteiro de 32 bits.

Quando um operador binário tem um escalar Python de um lado, o escalar é convertido num array de elemento único do dtype menor adequado: 123 torna-se um array uint8, -1000 torna-se int16, um float Python torna-se float.

6.4.5. Overflow em inteiros causa wrapping

Operações sobre dois arrays do mesmo dtype inteiro mantêm esse dtype, mesmo quando o resultado faz overflow. O transporte é descartado silenciosamente:

a = np.array([200, 200], dtype=np.uint8)
b = np.array([100, 100], dtype=np.uint8)
print(a + b)

Saída:

array([44, 44], dtype=uint8)

O resultado é 300 mod 256 == 44. Quando um valor intermédio necessita de um intervalo maior do que o dtype de entrada permite, converta primeiro:

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

Esta regra aplica-se a todos os operadores inteiros – +, -, *, //, %, &, |, ^. Os arrays float nunca fazem overflow (promovem para infinito em vez disso), pelo que o truque de conversão é necessário apenas no caso de inteiros.