9.19. Wrap up

You have walked through the parts of numpy and scipy that fit on the camera and that an OpenMV application reaches for to do generic numerical work alongside the rest of its code:

  • Concepts – the gap that Image and list leave open (whole-buffer math that runs as one library call instead of a slow Python loop), and the data structure that fills it: an ndarray, a packed block of numbers with a small descriptor recording dtype, shape, and strides. Constructed from a literal, from a pre-filled shape, from a generated sequence, or by wrapping an existing peripheral buffer with frombuffer(). The dtype set is small – uint8, int8, uint16, int16, float, bool, optionally complex – with upcasting rules that promote to float when the integer ranges run out, and silent wrap on integer overflow.

  • Shape and indexing – the descriptor records shape, strides, itemsize, ndim, and size; ndinfo() prints them. Single indices return scalars; slices and single-axis indexing of higher-rank arrays return views that share data with the source. Boolean masks return copies. Slice assignment writes through an array that already exists with no fresh allocation – the basic tool for allocation-free updates on the cam. Reshape, transpose, and .T rewrite the descriptor and leave the data alone.

  • Math – arithmetic, bit-wise, and comparison operators are element-wise and run as a single library call per expression. Integer arithmetic wraps; comparisons want the array on the left. Universal functions cover the trig / exp / log / rounding surface and accept an out= buffer so results land in memory the application already owns. Broadcasting fills in shape mismatches without copying data. Reductions collapse axes (sum, mean, std, min, max, median, argmin, argmax). Selection covers where, clip, nonzero, sort, argsort, take, compress, flip, roll, diff.

  • Linear algebradot(), cross(), trace() for products; inv(), det(), cholesky(), eig(), norm(), qr() for decompositions; and the dedicated solvers solve_triangular() and cho_solve() when the system has structure.

  • Signal processing – FFT in the standard one-array-in / one-array-out shape, or split real / imag depending on the firmware; sosfilt() for IIR filtering; spectrogram() for allocation-free magnitude spectra; the from_int32_buffer()-style helpers for wider-than-16-bit peripheral buffers.

  • Numerical extrasinterp(), polyfit(), polyval(), trapz(), convolve(); scipy submodules for numerical integration of a callable (scipy.integrate), root finding and minimisation (scipy.optimize), and special functions (scipy.special); plus the random-number generator (Generator).

  • Images – the bridge between Image and ndarray for the rare cases where the built-in image library does not already provide what is needed.

  • Performance – pick a small dtype, keep the long axis last, prefer views to copies, allocate buffers once and write into them with out= or slice assignment, use the in-place arithmetic operators, do not let boolean masks accumulate through a streaming loop.

That is enough to express custom signal processing, small-matrix linear algebra, and per-pixel transforms the built-in image library does not provide, on data arriving from any peripheral the camera can read.

9.19.1. Using this reference later

The tutorial chapters cover the shapes; the ulab — numpy-compatible array library reference covers the argument-level detail. When the question is “what is the exact signature of linspace()”, the reference is the place to look.

9.19.2. Where to go from here

Other tutorial sections build on this one wherever numerical work shows up. Custom pixel transforms via the Image/ndarray bridge, calibration matrices that come out of small linear-algebra solves, FFTs of buffered audio or vibration data – numpy is the toolbox the rest of the camera leans on when an operation does not have a built-in shape.