Skip to main content

Choosing the right approach

scalacv usually gives you more than one way to do a thing — a high-level and a low-level tier, a copying and a borrowing video path, several detectors for "find the object." That's on purpose, but it can be paralysing when you're new. This page is the decision guide: for each fork, which branch to take, and why.

High-level Image or mid-level Mat?​

Default to Image. Drop to the raw Mat only for a specific reason.

Reach for Image when…Drop to Mat extensions when…
read → transform → detect → annotate → writeyou need an OpenCV op Image doesn't wrap
you want move-semantics safety and typed enumsyou're processing borrowed video frames
you want Either at the boundariesyou're threading one buffer through several stages by hand
— (the common case)you're porting existing org.opencv.* code

They're twins — same OpenCV call underneath — so switching tiers never changes behaviour. See Architecture.

Which detector for "find the thing"?​

The vision module has several detectors, and the right one depends on what you're finding and whether you can ship a model.

TaskUseShips a model?Notes
Faces, fast & simpleHaar cascade (detectHaar)No — XML is bundledclassic, CPU-cheap, more false positives
Faces, accurateYuNet DNN (faces)Small downloadrobust to angle/lighting; also gives landmarks
Face identity ("who is this?")FaceRecognizerDownloadembeddings + a gallery to match against
QR codesQr.detectAndDecodeNodecodes the payload too
AR / fiducial markersArUcoNoprinted square tags; gives 3-D pose
A known image / logo / buttonfeature matching or template matchingNofeatures handle rotation/scale; templates are exact
Arbitrary objects (car, dog, …)your own ONNX net via DNNYour modelbring a YOLO/SSD export
Anything that movedMotionDetectorNoframe-difference or background subtraction

Rule of thumb: no model needed and it's a face → Haar; accuracy matters → a DNN; a printed code → QR/ArUco; a specific known picture → features/templates.

Copy each frame, or borrow one buffer?​

For video, the choice is convenience vs throughput.

Camera.foreach (copy)Video.frames (borrow)
you getan owned Image, closed for youone reused Mat, valid until the next pull
cost/frameone clonezero
pick it whenyou transform/keep/annotate the frameyou only read/reduce it and throughput matters
the catchthe clonedon't retain or collect the frame

Start with Camera.foreach; switch to Video.frames only when profiling says the per-frame clone matters. See Performance.

Reuse an image, or copy it?​

An Image transform consumes its receiver (move semantics). If you need the same source two ways, branch off .copy first — that's the one allocation you ask for by name. If you only need it once, chain straight through and never copy. Don't reach for .copy reflexively: a linear pipeline needs none.

Return an Either, or let it throw?​

You don't choose this — scalacv chose for you, consistently:

  • Boundary operations (read, write, bytes, decode, model loading, calibration) return Either[CvError, A] — expected, data-dependent failures you handle as values.
  • Transforms throw CvError.NativeCall if OpenCV rejects the pixels mid-chain. Wrap a chain in Cv.attempt (or use Image.reading) to fold that into an Either too.
  • Programmer mistakes (bad kernel size, reusing a consumed handle) throw IllegalArgumentException/IllegalStateException — bugs to fix, not values to match.

See The error model.

Which module do I add?​

Only what you use — the split is real:

  • core (scalacv) — images, filters, contours, drawing, video, the camera model. Most apps need only this.
  • vision (scalacv-vision) — detectors, DNN, pose/tracking/motion, OCR, calibration, SLAM.
  • graphs (scalacv-graphs) — the Picture scene graph, charts, GIFs.
  • zio (scalacv-zio) — effect-based resource scoping, only if you use ZIO.

Still unsure?​

Pick the high-level, no-model, copying option first — it's the one that's hardest to get wrong — get it working, then optimise the one dimension that turns out to matter. The other pages go deep on each choice.

Next​