OpenMV Cam: Machine Vision on STM32H7
A quantitative look at OpenMV Cam's STM32H7 vision pipeline: DCMI DMA, frame-rate budgets, CNN inference limits, and design tradeoffs.
Contents & prerequisites
OpenMV Cam packages an STM32H7 microcontroller, an image sensor, and a MicroPython runtime into a single board that runs classic machine-vision algorithms — blob detection, AprilTags, color tracking, template matching, and increasingly small CNNs — entirely on-device, without an OS, a GPU, or a network link. For engineers evaluating whether a vision task needs a full embedded-Linux SoC (Raspberry Pi + camera) or can be solved on a bare-metal MCU, OpenMV is a useful reference design: it shows exactly what an H7-class core can and cannot do in real time, and where the memory and bus bottlenecks actually sit.
Hardware Architecture
The current-generation boards (H7 / H7 Plus) are built around:
- STM32H7 MCU — Cortex-M7 at up to 480 MHz, single-precision FPU, double-precision on some variants, and a DCMI (Digital Camera Interface) peripheral for parallel sensor input.
- Image sensor — typically an OV7725 (VGA, rolling shutter, cheap) or OV5640 (5 MP, autofocus, higher power) connected over the parallel DVP interface, not MIPI CSI-2 — the H7 doesn't have a CSI-2 D-PHY, so raw parallel timing is used instead.
- SRAM/DRAM split — the H7 has ~1 MB of tightly-coupled/on-chip SRAM; the "Plus" variant adds external SDRAM (typically 32 MB) via the FMC bus, because a single 5 MP RGB565 frame (~10 MB) or even a QVGA RGB565 frame (~150 KB) plus multiple working buffers for a CNN quickly exceeds on-chip SRAM.
- microSD — for storing captured datasets, logging, or loading models at boot.
The key architectural fact for everything downstream: the DCMI peripheral streams pixel data into memory via DMA with no CPU involvement per pixel, but the CPU must still process every frame buffer through the vision pipeline (color conversion, filtering, feature extraction) at the clock rate of a general-purpose Cortex-M7 — there is no dedicated ISP or NPU on standard H7 parts. All the "vision" is software running on the M7 core plus its FPU, optionally with the CMSIS-DSP/CMSIS-NN kernels for the inner loops.
The DCMI → Frame Buffer → Pipeline Flow
Sensor (OV7725/OV5640)
│ parallel data (8-bit), PCLK, HSYNC, VSYNC
▼
DCMI peripheral (hardware frame sync + FIFO)
│ DMA burst transfer
▼
Frame buffer in SRAM/SDRAM (RGB565, RGB888, Grayscale, JPEG raw)
│
▼
OpenMV vision pipeline (image.* functions, MicroPython layer)
│
▼
Result: bounding boxes, blob list, tag pose, class scores
Frame acquisition is essentially free of CPU cycles; frame processing is the entire cost. This is the same division of labor found in any embedded vision system, and understanding it is what lets you predict frame rate before writing code.
Throughput: Where the Cycles Actually Go
For a QVGA (320×240) grayscale frame at 480 MHz with a single-precision FPU, a rough per-pixel budget looks like this:
Pixels per frame: 320 × 240 = 76,800
Target frame rate: 30 fps
Cycle budget per frame: 480,000,000 / 30 = 16,000,000 cycles
Cycles available/pixel: 16,000,000 / 76,800 ≈ 208 cycles/pixel
That sounds generous, but a typical pipeline stage — e.g., a 3×3 convolution for edge detection — costs roughly 9 multiply-accumulates plus loop/index overhead per pixel, and a full blob-detection pass involves multiple such stages (threshold, erode/dilate, connected-components labeling). Each added stage eats directly into that 208-cycle budget. This is why:
- Simple color/blob tracking comfortably hits 30–60 fps at QVGA.
- AprilTag detection (edge extraction + quad fitting + decoding) typically runs 10–25 fps at QVGA depending on tag density, because the quad-fitting and decoding stages are not embarrassingly parallel per-pixel operations.
- A small CNN (e.g., a person/face detector at 96×96 or 128×128 input) using CMSIS-NN INT8 kernels typically lands in the 5–15 fps range on H7, because convolutional layers dominate with far more MACs per pixel than classical filters.
Verification sanity check: a 128×128×3 input through a small MobileNet-like network might require on the order of 10–20 million MACs. At 480 MHz with the M7's single-cycle MAC-capable FPU (roughly 1 MAC/cycle achievable with well-optimized INT8 CMSIS-NN kernels, sometimes better with SIMD DSP instructions), 15 million MACs ≈ 15 million cycles ≈ 31 ms → ~32 fps for the inference alone, before accounting for image capture/resize/normalize overhead. This matches the observed 5–15 fps once preprocessing, memory copies, and non-conv layers (pooling, activation, batchnorm folding) are included — those add real but often underestimated overhead per frame.
Software Stack
- MicroPython runtime — application code (
image.find_blobs(),img.find_apriltags(), etc.) is Python, but every underlying vision primitive is a compiled C routine; the Python layer only orchestrates calls and handles control flow, so interpreter overhead is negligible for per-pixel work and only matters in tight per-frame Python loops. - OpenMV's
imagemodule — wraps threshold, morphology, geometric, and color-space operations, plus find_blobs, find_lines, find_circles, find_apriltags, find_qrcodes, and template matching, all implemented in optimized C. - TensorFlow Lite Micro integration — the
nn/tfmodules load a.tfliteINT8 model (converted via post-training quantization) and run inference through TFLite Micro, optionally accelerated by CMSIS-NN kernels for the Cortex-M7's DSP extensions. - No RTOS — OpenMV runs a single bare-metal loop with MicroPython's cooperative scheduler; this simplifies timing analysis (no preemption jitter) but means one long-running vision call blocks everything else, including the REPL and any interrupt-driven sensor reads.
Practical Design Implications
- Resolution vs. frame rate is the primary lever. Dropping from VGA to QVGA cuts pixel count 4×, which for pixel-bound stages (thresholding, morphology) yields close to a 4× frame-rate improvement; for fixed-cost stages (quad decoding, NN inference on a fixed input size) the gain is smaller since the CNN input is resized independently of sensor resolution.
- Grayscale over RGB565 halves memory bandwidth and often halves per-pixel processing cost for algorithms that don't need color (AprilTags, most blob detection variants), at the cost of losing color-based discrimination.
- External SDRAM adds latency, not just capacity. On the H7 Plus, frame buffers in SDRAM are accessed through the FMC bus at lower effective bandwidth than internal SRAM; pipelines that thrash between SDRAM buffers (e.g., double-buffering with software filtering) can become memory-bound rather than compute-bound. Profiling should distinguish the two before "just increasing clock speed" is assumed to help.
- No hardware NPU means INT8 quantization is not optional for CNN work — running FP32 inference on the M7 is roughly 3–4× slower than INT8 CMSIS-NN kernels for the same network, making the difference between 15 fps and effectively unusable single-digit fps.
- Thermal and power budget matters for continuous vision. Sustained 480 MHz operation with DCMI DMA and active FPU use draws meaningfully more current than idle/sleep states; battery-powered OpenMV deployments (drones, handheld scanners) typically throttle clock speed or duty-cycle the sensor to manage both heat and battery life.
- DVP vs. MIPI CSI-2 is a real constraint, not a detail. Because the H7 lacks a CSI-2 receiver, sensor choice is limited to parallel-interface sensors, which caps practical resolution and frame rate compared to CSI-2-capable platforms (e.g., STM32N6 with its Neural-ART accelerator, or embedded-Linux SoCs) — a relevant consideration when a design's requirements creep past what OpenMV-class hardware can sustain.
When OpenMV Is (and Isn't) the Right Choice
| Requirement | OpenMV / STM32H7 fit |
|---|---|
| Classical CV (blobs, tags, QR, color) at 15–60 fps, low power | Strong fit |
| Small INT8 CNN (person/face detect) at 5–15 fps | Workable, needs quantization discipline |
| Real-time multi-object detection (YOLO-class) at video rate | Poor fit — needs an NPU (Ethos-U55, MAX78000, STM32N6) |
| Rapid prototyping in Python with hardware-in-the-loop iteration | Strong fit — MicroPython REPL over USB speeds iteration significantly |
| High-resolution (>5MP) or MIPI CSI-2 sensor requirement | Not supported natively |
Key Takeaways
- OpenMV Cam pairs an STM32H7 (Cortex-M7, up to 480 MHz) with a DVP-interface image sensor and a MicroPython vision runtime — frame capture is DMA-driven and free, but all processing is software on the M7 core.
- Throughput is bounded by a simple cycles-per-pixel budget (~208 cycles/pixel at QVGA/30 fps/480 MHz); classical algorithms (blob, AprilTag) fit comfortably, CNN inference consumes it fast.
- INT8 quantization via TFLite Micro + CMSIS-NN is the difference between usable (5–15 fps) and impractical (FP32) CNN inference on this hardware — there is no NPU to fall back on.
- Resolution, color depth, and SRAM-vs-SDRAM buffer placement are the main levers for tuning frame rate; profiling should distinguish compute-bound from memory-bound stages before optimizing clock speed.
- Lack of MIPI CSI-2 caps sensor choice and resolution; workloads needing real-time multi-object detection or high-resolution CSI-2 sensors should target NPU-equipped parts (STM32N6, Ethos-U55/U65) instead.
Learning
Sign in to track your progress.
Evidence
Public projects engineers linked to OpenMV Cam: Machine Vision on STM32H7.
No engineer has linked a project to this topic yet. Built something that proves it? Add the project and tag it with embedded-systems-openmv-cam-machine-vision-on-stm32h7 — it then shows here and on your public profile.
