FAQ
Quick answers to the questions people ask before (and just after) picking up scalacv. For error messages specifically, see Troubleshooting.
import scalacv.*
OpenCv.load()
Getting set up
What is scalacv, exactly?
A Scala 3 wrapper over the official OpenCV 4.13 Java API (shipped by bytedeco). It gives you a fluent, typed, memory-safe surface — Image and friends — instead of raw org.opencv.*. The native OpenCV code is unchanged underneath; scalacv is the ergonomics layer. See Architecture.
Do I need to install OpenCV, or apt-get anything?
No. The native libraries ship inside per-platform jars and are extracted automatically on the first OpenCv.load(). You don't install OpenCV, and you don't need a GUI toolkit — it runs on a bare headless server. See Getting Started.
Which build tools work?
Any JVM build tool — the project itself uses Mill, but sbt/Gradle/Maven all work; you just add the dependency plus the native classifier for your platform. See Getting Started for the exact coordinates.
How big is the download?
For one platform (say linux-x86_64): the OpenCV jar is ~31 MB, OpenBLAS ~20 MB, and the first load extracts ~196 MB into a cache. Prefer a single platform classifier over opencv-platform (which bundles every OS at ~408 MB). See The native cache to relocate or pre-warm it.
Scala 2? Android? GraalVM native-image?
Scala 3 only (3.3.x LTS). Android and GraalVM native-image are not supported today — the native-image blockers are spelled out in Mat lifecycle.
Is there GPU/CUDA support?
CUDA: no. The bytedeco natives do publish -gpu classifier variants, and they are genuine CUDA builds, but scalacv's OpenCv.load() cannot load them — swapping the classifier gets you a CvError.NativesMissing at startup, or, if you leave the ordinary jar on the classpath too, the CPU natives with no warning at all.
OpenCL: yes, for DNN inference only. The ordinary classifier's DNN library is built with OpenCL, so net.setPreferableTarget(DNN_TARGET_OPENCL) can run a network on a GPU that has an OpenCL driver installed. Ordinary image operations (blur, resize, canny) always run on the CPU, because OpenCV's Java bindings ship no UMat.
Both answers, with the evidence and a way to check whether an accelerator actually engaged, are on one page: GPU acceleration: what is and is not reachable.
Doing common things
How do I actually see an image?
Three options. Save it and open the file:
Image.blank(64, 64, Scalar.Red).write("out.png")
Convert to a java.awt.image.BufferedImage for Swing/ImageIO:
val awt: java.awt.image.BufferedImage = Image.blank(32, 32, Scalar.Green).toBufferedImage
(awt.getWidth, awt.getHeight)
// res2: Tuple2[Int, Int] = (32, 32)
Or, in a Jupyter/Almond notebook, a BufferedImage renders inline automatically — see Notebooks.
How do I convert to/from BufferedImage or raw bytes?
toBufferedImage / Image.fromBufferedImage bridge AWT; bytes / Image.decode bridge an in-memory encoded file (PNG/JPG bytes):
val png: Array[Byte] = Image.blank(16, 16, Scalar.White).bytes(".png").toOption.get
val roundTripped: Image = Image.decode(png).toOption.get
roundTripped.close()
png.length > 0
// res4: Boolean = true
Which formats are supported?
Images: whatever this OpenCV build's codecs cover — PNG, JPEG, WebP, BMP, TIFF, … Video: depends on the platform's videoio backends (FFmpeg, OS frameworks); Codec.Mjpg in an .avi is the always-available fallback. Models: ONNX (via the DNN module), plus Haar/LBP cascade XML (bundled).
Can I call an OpenCV function scalacv doesn't wrap?
Yes — borrow the raw Mat with image.mat and call any org.opencv.* function; adopt a raw Mat back with Image.wrap(Managed(mat)). Full story in Working with the raw OpenCV API and Coming from OpenCV.
Correctness & performance
Is it thread-safe?
Native handles (Mat, detectors, captures) are one-owner-per-thread, exactly as in raw OpenCV — but detector results are immutable plain data, safe to share freely. The full matrix is in Concurrency.
Does it leak memory? The GC should handle it, right?
Off-heap pixel buffers are invisible to the GC, so no — you must release, and scalacv makes that a one-liner (a scope, or close()). This is the whole reason the library exists; the Mat lifecycle page has the (dramatic) numbers.
How do I check I'm not leaking?
Run under an RSS ceiling and a leak fails fast:
java -Dorg.bytedeco.javacpp.maxPhysicalBytes=512M -jar your-app.jar
Note: gate on physical bytes / RSS — Pointer.totalBytes() is blind to OpenCV's Mats. See Performance and Testing.
How do I test vision code without shipping image files?
Draw synthetic scenes, and compare with a tolerance (PSNR/max-abs-diff), not byte-equality. See Testing and the Tutorial.
Is the API stable?
It's 0.x under early-SemVer, so breaking changes are still allowed between minor versions. Pin a version and read the changelog before upgrading.
Next
- Getting Started — install and first pipeline.
- Glossary — any unfamiliar term.
- Troubleshooting — specific error messages.