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 |
|---|---|---|
|
1 |
0 a 255 |
|
1 |
-128 a 127 |
|
2 |
0 a 65.535 |
|
2 |
-32.768 a 32.767 |
|
4 |
IEEE 754 de precisão simples |
|
1 |
|
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 numpy – numpy.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 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
qualquer |
|
|
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.