6.4. Dtypes

Der Elementtyp eines ndarray ist sein dtype. Der dtype legt drei Dinge auf einmal fest: wie viele Bytes jedes Element belegt, wie die Bytes interpretiert werden und welchen Wertebereich das Array speichern kann. Die Wahl des richtigen dtype ist die einzelne wichtigste Entscheidung, die den RAM-Verbrauch auf der Kamera beeinflusst.

6.4.1. Die unterstützten dtypes

numpy auf der Kamera unterstützt eine kleine Menge von dtypes:

dtype

Bytes

Bereich

uint8

1

0 bis 255

int8

1

-128 bis 127

uint16

2

0 bis 65.535

int16

2

-32.768 bis 32.767

float

4

IEEE 754 einfache Genauigkeit

bool

1

True / False

Es gibt kein int32 oder int64, und der ulab-Build von OpenMV aktiviert den optionalen complex-dtype nicht.

Wähle den Typ, der zur Hardware passt, die die Daten erzeugt hat. Ein 8-Bit-ADC-Sample möchte uint8; ein 12-Bit-ADC-Sample passt in uint16; ein Luminanzpixel von einer Graustufenkamera passt in uint8 – und spart das Vierfache des RAM, das der voreingestellte float kosten würde.

6.4.2. Der voreingestellte dtype

Der voreingestellte dtype jedes Konstruktors auf Arrays erstellen ist float. Das ist selten das, was die Anwendung beim Umgang mit Sensordaten möchte. Übergib dtype= explizit, wann immer die natürliche Breite kleiner ist:

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

Das erneute Verpacken eines Integer-Arrays ohne ein dtype=-Argument kopiert und konvertiert nach float, was sowohl Zeit als auch RAM kostet. Wenn die Performance wichtig ist, benenne den dtype.

6.4.3. Der dtype eines vorhandenen Arrays

dtype liest den dtype des Arrays als den Integer-Typcode zurück, den das Array intern trägt:

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

Die Typcode-Integer entsprechen den Konstanten, die auf dem numpy-Modul bereitgestellt werden – numpy.uint8, numpy.int8, numpy.uint16, numpy.int16, numpy.float, numpy.bool – daher ist der Vergleich des dtype mit der Modulkonstante die Art, wie ein Skript je nach Inhalt eines Arrays verzweigt:

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

6.4.4. Upcasting-Regeln

Zwei Arrays unterschiedlicher dtypes können Operanden desselben Operators sein. numpy wählt den Ergebnistyp anhand einer kurzen Tabelle:

links

rechts

Ergebnis

uint8

int8

int16

uint8

int16

int16

uint8

uint16

uint16

int8

int16

int16

int8

uint16

uint16

uint16

int16

float

beliebig

float

float

Die Zeile uint16 / int16 wird direkt zu float heraufgestuft, weil numpy auf der Kamera keinen 32-Bit-Integer-dtype hat.

Wenn ein binärer Operator auf einer Seite einen Python-Skalar hat, wird der Skalar in ein einelementiges Array des kleinsten geeigneten dtype konvertiert: 123 wird zu einem uint8-Array, -1000 wird zu int16, ein Python-float wird zu float.

6.4.5. Integer-Überlauf läuft um

Operationen auf zwei Arrays desselben Integer-dtype behalten diesen dtype, selbst wenn das Ergebnis überläuft. Der Übertrag wird stillschweigend verworfen:

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

Ausgabe:

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

Das Ergebnis ist 300 mod 256 == 44. Wenn ein Zwischenwert mehr Bereich benötigt, als der Eingabe-dtype zulässt, caste zuerst:

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

Diese Regel gilt für jeden Integer-Operator – +, -, *, //, %, &, |, ^. Float-Arrays laufen nie über (sie stufen sich stattdessen zu unendlich hoch), daher wird der Cast-Trick nur im Integer-Fall benötigt.