6.4. Dtypes¶
The element type of an ndarray is its dtype. The dtype decides three things at once: how many bytes each element occupies, how the bytes are interpreted, and what range of values the array can store. Choosing the right dtype is the single biggest decision that affects RAM use on the camera.
6.4.1. The supported dtypes¶
numpy on the camera supports a small set of dtypes:
dtype | bytes | range |
|---|---|---|
| 1 | 0 to 255 |
| 1 | -128 to 127 |
| 2 | 0 to 65,535 |
| 2 | -32,768 to 32,767 |
| 4 | IEEE 754 single precision |
| 1 |
|
There is no int32 or int64, and OpenMV’s ulab build does not enable the optional complex dtype.
Pick the type that matches the hardware that produced the data. An 8-bit ADC sample wants uint8; a 12-bit ADC sample fits in uint16; a luminance pixel from a grayscale camera fits in uint8 – saving four times the RAM the default float would cost.
6.4.2. The default dtype¶
The default dtype of every constructor on Making arrays is float. That is rarely what the application wants when handling sensor data. Pass dtype= explicitly whenever the natural width is smaller:
sensor = np.array(samples, dtype=np.uint16)
Re-wrapping an integer array without a dtype= argument copies and converts to float, which both costs time and costs RAM. When performance matters, name the dtype.
6.4.3. The dtype of an existing array¶
dtype reads back the array’s dtype as the integer type code the array carries internally:
a = np.array([1, 2, 3], dtype=np.uint8)
print(a.dtype) # 66 (the integer value of ``'B'``)
The type-code integers match the constants exposed on the numpy module – numpy.uint8, numpy.int8, numpy.uint16, numpy.int16, numpy.float, numpy.bool – so comparing the dtype against the module constant is how a script branches on what an array holds:
if a.dtype == np.uint8:
... # uint8 branch
6.4.4. Upcasting rules¶
Two arrays of different dtypes can be operands of the same operator. numpy picks the result type according to a short table:
left | right | result |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
any |
|
|
The uint16 / int16 row promotes straight to float because numpy on the camera has no 32-bit integer dtype.
When a binary operator has a Python scalar on one side, the scalar is converted to a single-element array of the smallest suitable dtype: 123 becomes a uint8 array, -1000 becomes int16, a Python float becomes float.
6.4.5. Integer overflow wraps¶
Operations on two arrays of the same integer dtype keep that dtype, even when the result overflows. The carry is silently dropped:
a = np.array([200, 200], dtype=np.uint8)
b = np.array([100, 100], dtype=np.uint8)
print(a + b)
Output:
array([44, 44], dtype=uint8)
The result is 300 mod 256 == 44. When an intermediate needs more range than the input dtype allows, cast first:
c = np.array(a, dtype=np.uint16) + b
# array([300, 300], dtype=uint16)
This rule applies to every integer operator – +, -, *, //, %, &, |, ^. Float arrays never overflow (they promote to infinity instead), so the cast trick is only needed in the integer case.