6.4. Dtypes¶
El tipo de elemento de un ndarray es su dtype. El dtype decide tres cosas a la vez: cuántos bytes ocupa cada elemento, cómo se interpretan los bytes y qué rango de valores puede almacenar el array. Elegir el dtype correcto es la decisión más importante que afecta al uso de RAM en la cámara.
6.4.1. Los dtypes admitidos¶
numpy en la cámara admite un pequeño conjunto de dtypes:
dtype |
bytes |
rango |
|---|---|---|
|
1 |
0 a 255 |
|
1 |
-128 a 127 |
|
2 |
0 a 65.535 |
|
2 |
-32.768 a 32.767 |
|
4 |
IEEE 754 de precisión simple |
|
1 |
|
No hay int32 ni int64, y la compilación de ulab de OpenMV no habilita el dtype opcional complex.
Elige el tipo que coincida con el hardware que produjo los datos. Una muestra de un ADC de 8 bits requiere uint8; una muestra de un ADC de 12 bits cabe en uint16; un píxel de luminancia de una cámara en escala de grises cabe en uint8, lo que ahorra cuatro veces la RAM que costaría el float predeterminado.
6.4.2. El dtype predeterminado¶
El dtype predeterminado de cada constructor en Creación de arrays es float. Eso rara vez es lo que la aplicación quiere al manejar datos de sensores. Pasa dtype= de forma explícita siempre que el ancho natural sea menor:
sensor = np.array(samples, dtype=np.uint16)
Volver a envolver un array de enteros sin un argumento dtype= copia y además convierte a float, lo que cuesta tanto tiempo como RAM. Cuando el rendimiento importa, indica el dtype.
6.4.3. El dtype de un array existente¶
dtype devuelve el dtype del array como el código de tipo entero que el array lleva internamente:
a = np.array([1, 2, 3], dtype=np.uint8)
print(a.dtype) # 66 (the integer value of ``'B'``)
Los enteros de código de tipo coinciden con las constantes expuestas en el módulo numpy – numpy.uint8, numpy.int8, numpy.uint16, numpy.int16, numpy.float, numpy.bool – por lo que comparar el dtype con la constante del módulo es la forma en que un script se ramifica según lo que contiene un array:
if a.dtype == np.uint8:
... # uint8 branch
6.4.4. Reglas de promoción de tipos¶
Dos arrays de distintos dtypes pueden ser operandos del mismo operador. numpy elige el tipo del resultado según una tabla breve:
izquierda |
derecha |
resultado |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cualquiera |
|
|
La fila uint16 / int16 promociona directamente a float porque numpy en la cámara no tiene un dtype entero de 32 bits.
Cuando un operador binario tiene un escalar de Python en un lado, el escalar se convierte en un array de un solo elemento del dtype adecuado más pequeño: 123 se convierte en un array uint8, -1000 se convierte en int16, y un float de Python se convierte en float.
6.4.5. El desbordamiento de enteros da la vuelta¶
Las operaciones sobre dos arrays del mismo dtype entero conservan ese dtype, incluso cuando el resultado se desborda. El acarreo se descarta de forma silenciosa:
a = np.array([200, 200], dtype=np.uint8)
b = np.array([100, 100], dtype=np.uint8)
print(a + b)
Salida:
array([44, 44], dtype=uint8)
El resultado es 300 mod 256 == 44. Cuando un valor intermedio necesita más rango del que permite el dtype de entrada, convierte primero:
c = np.array(a, dtype=np.uint16) + b
# array([300, 300], dtype=uint16)
Esta regla se aplica a todos los operadores de enteros – +, -, *, //, %, &, |, ^. Los arrays de float nunca se desbordan (en su lugar promocionan a infinito), por lo que el truco de la conversión solo es necesario en el caso de los enteros.