Camera-based dimensional measurement on live video streams. A small, dependency-light Python library built around four composable layers — calibration, detection, geometry, measurement — plus a thin streaming loop on top.
- Calibrate a uniform pixel-to-millimeter scale from a known reference (credit card, ruler, ArUco marker pair) and, optionally, full camera intrinsics from chessboard images.
- Detect objects with classical contour analysis out of the box, or plug in
any callable matching the
Detectorprotocol (YOLO, SAM, MediaPipe…). - Measure distances, polyline lengths, polygon and mask areas, mask perimeters, and full object dimensions (width × height × area × perimeter) in real-world units.
- Stream the whole pipeline against a webcam or video file with a tiny
context-managed
CameraStreamand aPipelineof frame processors.
pip install -e .[dev]Runtime dependencies are just NumPy and OpenCV — no deep-learning frameworks.
Aimed at measuring household objects — remotes, spoons, pans, packaging —
using a coin of known diameter as the calibration reference. A US quarter is
exactly 24.26 mm across; common alternatives are listed in
examples/live_measure.py.
import cv2
import visioncore as vc
# 1. Calibrate against a coin laid flat in the same plane as the object.
# (US quarter diameter = 24.26 mm.)
frame = cv2.imread("coin_and_remote.jpg")
# ...let the user click both edges across the coin; here we hard-code:
scale = vc.calibrate_scale_from_reference(
p1=(412, 388), p2=(580, 392),
known_distance_mm=24.26,
label="us-quarter",
)
# 2. Detect parts in the same scene.
detector = vc.ContourDetector(min_area_px=2000, invert=True)
detections = detector.detect(frame)
# 3. Measure each detection in millimeters.
for det in detections:
dims = vc.measure_object_dimensions(det, scale)
print(dims["width"], dims["height"], dims["area"])The runnable demo at examples/live_measure.py does the same thing live: press
c, click both edges of the coin, press c again, and the live feed
annotates every detected object with {w} x {h} mm.
- Pixel space and world space are strictly separated.
geometry.pyconsumes pixels and returns pixels.measure.pyis the only module that importsScaleCalibrationand emits world units. Keeping that boundary clean makes it easy to swap calibration strategies later (per-pixel scale fields, perspective rectification, full 3D back-projection) without rewriting geometry. - Detectors and pose estimators are
typing.Protocols, not base classes. Anything with adetect(image) -> list[Detection]method is aDetector. No inheritance required — bring your own model. - Tiny dependency surface. NumPy and OpenCV only at runtime. Heavier ML stacks plug in behind the protocols.
- Public API only via
visioncore/__init__.py. Treat module paths as internal implementation detail.
A uniform pixel-to-mm scale is a useful first approximation, but it assumes:
- Lens distortion is negligible. For wide-angle or fisheye lenses, run
calibrate_intrinsics(...)against a chessboard, attach the resultingIntrinsicCalibrationto yourScaleCalibration(or useUndistortProcessorin your pipeline) before measuring. - The object lies in the same plane as the calibration reference. Out-of-plane height effectively magnifies an object — even a few millimeters of standoff can change the apparent size by several percent.
- The camera optical axis is roughly perpendicular to that plane. Perspective skew is not corrected here. For oblique views you need a homography-based rectification step (planned, not in v0.1).
- The reference is measured precisely. Click error of one pixel on a 500 px reference translates directly to a 0.2 % scale error.
In practice, expect ~1–3 % accuracy on flat objects under decent lighting with a static camera. Re-calibrate whenever the camera or scene geometry moves.
visioncore/
├── __about__.py # __version__
├── __init__.py # public re-exports
├── types.py # Point, Box, Mask, Pose, Detection, Frame
├── calibration.py # IntrinsicCalibration, ScaleCalibration, helpers
├── detection.py # Detector protocol, ContourDetector
├── pose.py # PoseEstimator protocol, ManualPose, CallablePoseEstimator
├── geometry.py # pixel-space primitives
├── measure.py # the only place pixels become millimeters
└── stream.py # CameraStream, Pipeline, run()
tests/test_visioncore.py # synthetic-image test suite
examples/live_measure.py # webcam demo
Apache 2.0 — see LICENSE.