OpenCV 4.13Scala 3.3 LTSJDK 17+headless
Teach the JVM
to see edges, contours, faces, motion, markers, gestures and depth
A fluent, typed Image pipeline over the complete OpenCV Java bindings. No raw int constants, no GUI toolkit, no apt-get — and every intermediate frees its own native memory before the next call sees it.
mvn"com.worxbend::scalacv:0.1.0"Plus one natives line for your platform — the install page explains why.
- capture
- greyscale
- blur
- canny
- contours
- detect
Image.read("bench.jpg")Three colour channels per pixel, in OpenCV’s blue-green-red order.
the shape of it
One chain, from file to result
Every operation returns a new Image and releases the one it consumed. There is no release() to forget, because there is nothing left holding a handle by the time the next call runs.
This runs headless, with no image file and no display server — which is also how it is tested:
import scalacv.*
OpenCv.load()
val edges: Either[CvError, Array[Byte]] =
Image
.blank(160, 120, Scalar.White)
.drawRect(Rect(30, 30, 90, 60), Scalar.Black)
.gray
.canny(50, 150)
.bytes(".png")
Image.reading scopes a file to a block and releases it on success, on failure, and on exception —
it is the entry point that cannot leak, so start there unless you have a reason not to:
Image.reading("photo.jpg") { img => img.gray.blur(2).canny(80, 160).write("edges.png") }
Detection results cross back as ordinary immutable Scala values, not as live native handles — so there is nothing left for you to free:
Image.reading("shelf.jpg") { img =>
val codes: Seq[QrCode] = img.qrCodes // decoded text and corners, no Mat left open
img.drawText(s"${codes.size} codes found", Point(10, 30))
.write("annotated.png")
}
what is in the box
Batteries, and the wiring diagram
Eight groups of capability, every one of them documented. Follow any chip to its guide.
Core imaging
Read, decode, convert, filter and write. Colour-space conversion, blurs and sharpening, morphology, thresholding, resizing, rotation, warping and cropping — all typed, all releasing their own intermediates.
Shapes & measurement
Find the outlines in a binary image, measure their area and perimeter, fit boxes and ellipses, simplify polygons, and pull straight lines and circles out of an edge map.
Detection & deep learning
Haar cascades and the YuNet DNN face detector, ONNX and Caffe models through OpenCV’s own inference engine, QR codes, ArUco markers, and face recognition by embedding distance.
Video & camera
A high-level Camera and Recorder over VideoCapture and VideoWriter, frame streams you can fold over, and background-subtraction motion detection for a fixed or MJPEG camera.
Human sensing
Body and hand skeletons, head-pose angles from facial landmarks, and a gesture recogniser built on top of them — plus the background blur and virtual backgrounds a call needs.
Robotics & 3D vision
Chessboard calibration and lens undistortion, stereo depth and obstacle maps, sparse and dense optical flow, ORB features, visual odometry, loop closure and an occupancy grid.
2D graphics & charts
A composable Picture scene graph for overlays — dashed strokes, alpha compositing, text boxes, charts and animated GIFs — that renders down onto an Image without leaking a Mat.
Built to ship
A native-memory model you can reason about, a concurrency story that names what is and is not thread-safe, benchmark-backed performance guidance, and a production deployment guide.
the hard part
Honest about native memory
An OpenCV Mat lives in native memory the JVM garbage collector cannot see and will not free. In
the Java API, forgetting release() leaks; releasing twice, or using a released Mat, is a
segmentation fault with no Scala stack trace to show you where.
Managed[A] moves that failure earlier and upward. It releases exactly once, and it throws an
ordinary Scala exception on use-after-release — in the JVM, before anything reaches JNI:
val m = Managed(new org.opencv.core.Mat(4, 4, org.opencv.core.CvType.CV_8UC1))
m.close()
m.close() // no-op: release happens exactly once
m.use(identity) // throws IllegalStateException, not SIGSEGV
The Mat lifecycle guide explains the whole model — who owns what, which operations borrow rather than take, and how the leak test suite proves it with resident-set measurements rather than assertions about intent.
find your route
Four ways in
Pick the row that describes you. Each is three pages long, in order.
New to computer vision
You know some Scala. You have never written an image pipeline.
- Image basicsWhat a pixel, a channel and a colour space really are — five minutes.
- Tutorial: count objectsBuild a working thing, one step at a time.
- GlossaryEvery term on this site, in plain language.
You already know OpenCV
You have written this in Python or Java and want the Scala idiom.
- Coming from OpenCVThe idiom map, and the three deliberate differences.
- Getting startedDependency, natives classifier, first pipeline.
- ArchitectureThe two tiers, and why the raw Mat is never hidden.
Building something now
You have a task and want the shortest correct route to it.
- Choosing an approachWhich detector, which tier, which module.
- CookbookRecipes to copy and adapt.
- Operations referenceEvery operation, its parameters and its units.
Shipping to production
It works on your laptop. Now it has to survive a week in a container.
- Mat lifecycleThe memory model that makes this trustworthy.
- ConcurrencyWhat is thread-safe, what is emphatically not.
- DeployingImages, natives, health checks, degradation.
no walled garden
The raw API is always one call away
The high-level pipeline covers the common cases. When it does not cover yours, mat borrows the
underlying handle and the complete typed org.opencv.* surface is right there — along with a
mid-level layer of extension operations that return Managed[Mat] so you keep the safety without
the abstraction:
Image.reading("photo.jpg") { img =>
img.mat.cvtColor(ColorConversion.BgrToGray) // mid-level extension → Managed[Mat]
.pipe(_.gaussianBlur(Size(5, 5)))
.pipe(_.canny(80, 160))
.use(Images.encode(_, ".png"))
}
Nothing is hidden and nothing is final: see the low-level guide for the escape hatches, and coming from OpenCV if you already know the C++ or Python names.
learn by building
Five tutorials, easiest first
Ordered by what each one needs from you, not by topic. The first two need nothing at all — they draw their own input, because this repository ships no image files.
| Tutorial | What it teaches | What you need |
|---|---|---|
| Count objects in an image | threshold → contours → count | nothing — it draws its own scene |
| Track a coloured object | HSV masking and centroids | nothing — it draws its own scene |
| Process a video frame by frame | capture, transform, record | a clip or a webcam — or make one |
| Detect faces in a photo | Haar cascades, then YuNet | a photograph; no model download |
| Run a neural network | ONNX inference through OpenCV | a model file — get one |
next
Start where it suits you
- Never done this before? Image basics, then the glossary when a word stops you.
- Want it running today? Getting started then the cookbook — around sixty recipes, each one runnable.
- Evaluating it? Architecture, performance, benchmark results, and the FAQ.
- Taking it to production? Streaming and backpressure, observability, and degradation and error budgets.
- Stuck? Troubleshooting covers the errors people actually hit first.
Reference: Operations · Enums and constants · Error model · API docs