6.4. Dtypes

O tipo de elemento de um ndarray é seu dtype. O dtype decide três coisas de uma só vez: quantos bytes cada elemento ocupa, como os bytes são interpretados e qual faixa de valores o array pode armazenar. Escolher o dtype certo é a maior decisão individual que afeta o uso de RAM na câmera.

6.4.1. Os dtypes suportados

O numpy na câmera suporta um pequeno conjunto de dtypes:

dtype

bytes

faixa

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

IEEE 754 de precisão simples

bool

1

True / False

Não há int32 ou int64, e a build ulab da OpenMV não habilita o dtype opcional complex.

Escolha o tipo que corresponde ao hardware que produziu os dados. Uma amostra de ADC de 8 bits quer uint8; uma amostra de ADC de 12 bits cabe em uint16; um pixel de luminância de uma câmera em escala de cinza cabe em uint8 – economizando quatro vezes a RAM que o float padrão custaria.

6.4.2. O dtype padrão

O dtype padrão de todo construtor em Criando arrays é float. Isso raramente é o que a aplicação quer ao lidar com dados de sensor. Passe dtype= explicitamente sempre que a largura natural for menor:

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

Re-encapsular um array de inteiros sem um argumento dtype= copia e converte para float, o que custa tanto tempo quanto RAM. Quando o desempenho importa, nomeie o dtype.

6.4.3. O dtype de um array existente

dtype lê de volta o dtype do array como o código de tipo inteiro que o array carrega 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 – então comparar o dtype com a constante do módulo é como um script ramifica conforme o que um array contém:

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

6.4.4. Regras de upcasting

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

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 é promovida diretamente para float porque o numpy na câmera não tem dtype inteiro de 32 bits.

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

6.4.5. Overflow de inteiro faz wrap

Operações em dois arrays do mesmo dtype inteiro mantêm esse dtype, mesmo quando o resultado faz overflow. O transbordo é 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 intermediário precisa de mais faixa do que o dtype de entrada permite, faça o cast primeiro:

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

Essa regra se aplica a todo operador inteiro – +, -, *, //, %, &, |, ^. Arrays de float nunca fazem overflow (eles são promovidos para infinito), então o truque do cast só é necessário no caso de inteiros.