GoCV: OpenCV 4 bindings for Go, from install to face detection
Go package for computer vision using OpenCV 4 and beyond. Includes support for DNN, CUDA, OpenCV Contrib, and OpenVINO.
At a glance
- What is it?
- GoCV wraps OpenCV 4.12.0 for Go programs and adds a Mat profiler for the C memory the garbage collector cannot free. The install is the hard part: you must match OpenCV yourself, and the README sends you to gocv.io for every platform.
- Who is it for?
- Adopt GoCV if you already run OpenCV and want the surrounding service, capture loop or CLI in Go, and if you accept that every Mat is yours to close. Do not adopt it if you cannot install OpenCV 4.12.0 on the target machine and have no path to the CUDA or OpenVINO builds described in the cuda/ and openvino/ READMEs.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 111 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What GoCV solves for Go teams already using OpenCV
OpenCV is a C++ library. Calling it from Go normally means writing cgo glue for every function you need, plus the memory rules that come with it. GoCV is the binding layer that already exists: a Go package published as gocv.io/x/gocv that exposes OpenCV 4 types such as Mat, VideoCapture, Window and CascadeClassifier as Go values.
The target reader is a Go developer who needs computer vision inside a program that is otherwise Go. The repository shows the intended shape of that work in its cmd directory, which holds command line utilities for capturing an image file, streaming mjpeg video, counting objects that cross a line, and running face tracking with a DNN. Those are not library demos in the abstract; they are the kind of small binary a Go team would otherwise write from scratch on top of cgo.
The README states the project's mission as helping Go be a first-class client of the OpenCV ecosystem. That framing matters when you evaluate it. GoCV does not reimplement vision algorithms. Everything numerical happens in OpenCV, and the project's job is to keep the binding current with the upstream release.
How the binding works: cgo, Mat handles and the release branch
The repository layout makes the mechanism plain. Alongside the Go files there are matching C++ files and headers: core.go sits next to core.cpp and core.h, and the same pattern repeats for calib3d, aruco and asyncarray. The Go side declares the API, the C++ side forwards to OpenCV, and cgo links them. The go.mod file sets the module path to gocv.io/x/gocv and requires Go 1.21.
That design has one consequence the README states directly: memory for images is allocated through C code, so the Go garbage collector will not clean all resources associated with a Mat. Any Mat you create must be closed. The examples in the README follow this convention with defer webcam.Close(), defer window.Close(), defer img.Close() and defer classifier.Close().
Because the Go package is a thin layer over a C++ library, version alignment is part of the architecture rather than a packaging detail. The README says the current release requires OpenCV 4.12.0, while the Makefile in the repository sets OPENCV_VERSION to 4.13.0 and GOCV_VERSION to v0.43.0. The default branch is release, and the latest tagged release is v0.43.0 from 2026-01-05. If you build from source, check which of those two numbers your environment actually satisfies before you debug a link error.
Installing GoCV on Linux, macOS, Windows or Docker
The README does not inline install commands. It states that you must first have the matching version of OpenCV installed, that the current release requires OpenCV 4.12.0, and then points to https://gocv.io/getting-started/linux/, /macos/, /windows/ and /docker/ for the platform instructions. Android is described as work in progress using Gomobile, with a gist and issue link rather than a supported path.
What the repository does give you is a Dockerfile that starts from a prebuilt OpenCV image and compiles one of the example commands. This is the shortest way to confirm the toolchain works before you touch your own code.
FROM ghcr.io/hybridgroup/opencv:4.13.0
ENV GOPATH /go
COPY . /go/src/gocv.io/x/gocv/
WORKDIR /go/src/gocv.io/x/gocv
RUN go build -tags example -o /build/gocv_version ./cmd/version/
CMD ["/build/gocv_version"]Building that image compiles cmd/version with the example build tag. If it succeeds, cgo found OpenCV and the headers line up. Note the image tag is 4.13.0 while the README's stated requirement is 4.12.0; treat the tag you choose as something to verify rather than assume.
The first real use is the video loop from the README. It opens device 0, reads frames and shows them in a window.
package main
import (
"gocv.io/x/gocv"
)
func main() {
webcam, _ := gocv.OpenVideoCapture(0)
window := gocv.NewWindow("Hello")
img := gocv.NewMat()
for {
webcam.Read(&img)
window.IMShow(img)
window.WaitKey(1)
}
}Expect a GUI window titled Hello showing the camera feed, and a process that never exits on its own. The README's face detection example extends this with a CascadeClassifier loaded from data/haarcascade_frontalface_default.xml, calls DetectMultiScale on each frame, and draws rectangles with gocv.Rectangle. That example also prints the number of faces found per frame, which is the first thing you will want to silence.
The Mat profiler, and why every Mat is your responsibility
The README is unusually direct about the failure mode: any Mat created must be closed to avoid memory leaks, because the Go garbage collector does not clean the C resources. This is the limitation that will bite a Go developer hardest, since it inverts the usual expectation that allocation is safe and collection is automatic. In a capture loop that allocates a Mat per frame, a missing Close is a leak that grows with runtime.
GoCV ships a tool for exactly this. Building or running with the matprofile build tag enables a profiler that records a stack trace when each Mat is created and removes it when the Mat is closed.
go run -tags matprofile cmd/version/main.goThe README shows the count being read with gocv.MatProfile.Count() and the entries dumped through gocv.MatProfile.WriteTo into a bytes.Buffer. Its worked example is a program whose leak function calls gocv.NewMat() and never closes it; the output goes from an initial count of 0 to a final count of 1, followed by the stack trace pointing at gocv.io/x/gocv.newMat in core.go. That trace is the part that makes the tag worth the rebuild: it names the allocation site rather than just reporting a number.
The trade-off is that the profiler only exists under that build tag. A binary compiled without it gives you no count, so a leak in production is invisible until memory pressure shows up. Treat the tag as a development and test configuration, and make the Close calls part of the code review checklist rather than something you plan to catch later.
CUDA and OpenVINO are separate build stories
The README states that GoCV supports CUDA for hardware acceleration on Nvidia GPUs and Intel OpenVINO, and in both cases it defers to a subdirectory README: ./cuda/README.md and ./openvino/README.md. The repository backs this up with a long list of Dockerfiles at the top level, including Dockerfile.gpu, Dockerfile.opencv-gpu-cuda-10 through -13, and Dockerfile.opencv-openvino. There is also a Dockerfile-test.gpu-cuda-13.
That file list is the honest picture of the cost. CUDA support is not a flag on the standard install; it is a different OpenCV build, and the version of CUDA is baked into the image name. If your deployment target has no Nvidia GPU, or you cannot control which CUDA runtime is present, the CUDA path is closed to you and the CPU build is what you get.
The same applies to OpenVINO: it is a distinct toolkit, documented in its own README, with its own Dockerfile. The main README does not describe a fallback if the toolkit is missing at runtime. If inference acceleration is the reason you are considering GoCV, read those two subdirectory READMEs before you commit, because the main README only tells you they exist.
GoCV compared with pure-Go vision libraries
The real alternative for a Go team is a pure-Go image library rather than another OpenCV binding. The difference is not performance on paper; it is what you can call. A pure-Go library compiles with the standard toolchain, needs no cgo, no system OpenCV, and no matching version, and its allocations are ordinary Go allocations that the garbage collector handles. Distribution is a single static binary with no shared library to ship.
GoCV gives up all of that in exchange for OpenCV's algorithm surface. Calibration, ArUco markers, DNN inference, video capture and the cascade classifiers in the examples are all in OpenCV, and a pure-Go library would require you to implement them. The cost is the toolchain: a matching OpenCV install on every machine that builds or runs the program, cgo enabled, and the Mat lifecycle managed by hand.
The go.mod file shows how narrow the dependency footprint is on the Go side: mjpeg, goe and a wasm image-to-ascii helper, plus two indirect packages. Almost all of the weight is in the C library underneath, not in Go modules. That is the trade in one line. You are not adding a Go dependency; you are adding a system dependency.
Maintenance, licensing and what an upgrade actually costs
The last push to the repository was on 2026-05-28, and the most recent tagged release is v0.43.0 from 2026-01-05, preceded by v0.42.0 in July 2025 and v0.41.0 in March 2025. That is a steady cadence of roughly two releases a year, with the default branch named release. The repository is not archived.
Upgrade cost is dominated by the OpenCV version, not by the Go API. The README pins the current release to OpenCV 4.12.0, the Makefile defaults OPENCV_VERSION to 4.13.0, and the Dockerfile builds from ghcr.io/hybridgroup/opencv:4.13.0. When you move to a new GoCV tag you are also moving the OpenCV version your binary links against, which means rebuilding the C library or pulling a new image, and re-running anything that depends on exact detection or inference output. Budget for that as a system upgrade, not a go get.
On licensing, the README's badge and the repository's LICENSE.txt identify Apache 2.0, while the repository metadata reports the licence as NOASSERTION. Those two disagree, and the discrepancy is worth resolving against LICENSE.txt before you rely on either. Apache 2.0 is permissive and includes a patent grant, but OpenCV itself carries its own licence terms, and the CUDA and OpenVINO paths pull in additional components with their own conditions. This is not legal advice; if your product ships the linked libraries, have someone read LICENSE.txt and the OpenCV licence rather than the badge.
Editorial conclusion
Adopt GoCV if you already run OpenCV and want the surrounding service, capture loop or CLI in Go, and if you accept that every Mat is yours to close. Do not adopt it if you cannot install OpenCV 4.12.0 on the target machine and have no path to the CUDA or OpenVINO builds described in the cuda/ and openvino/ READMEs. Before writing application code, verify two things: that your OpenCV version matches the release you pull, and that the matprofile build tag reports a stable count when your main loop runs.
Frequently asked questions
What version of OpenCV does GoCV require?
The README states that the current release of GoCV requires OpenCV 4.12.0, and that you must have the matching version installed before installing GoCV. The repository's Makefile and Dockerfile reference 4.13.0, so confirm the pairing for the tag you actually build.
How do I install GoCV on Linux, macOS or Windows?
The README does not inline the steps. It points to https://gocv.io/getting-started/linux/, /macos/, /windows/ and /docker/ for platform instructions, and there is a Dockerfile in the repository that builds from a prebuilt OpenCV image.
Why does GoCV leak memory when I create a Mat?
The README explains that image memory is allocated through C code, so the Go garbage collector will not clean all resources associated with a Mat, and any Mat created must be closed. Building with the matprofile tag records each Mat's creation and close so you can find the allocation site.
Community notes