Using OpenCV 3 from #golang

Go is a great language. It's C-like, but without feeling the weight of history while you're using it. You can use json, maps or concurrency instantly, set up and run a web server within seconds or even reuse your C code from within your Go program. Best of all, building and shipping is a piece of cake.

But when you need to use OpenCV, you're stuck with a dilemma. Do you use OpenCV 2.4, which is aging but has C bindings so it's usable from Go? Or do you use OpenCV 3, which is up-to-date with the latest algorithms and optimisations but only has a C++ interface? If you need OpenCV 3, you cannot call it directly from Go because Go doesn't support calls to C++ code. And unfortunately, the work on making go-opencv work with OpenCV 3 seems to halted.

I've done some testing with OpenCV 3 in C++, but I prefer coding in Go for several reasons. For a project that I'm working on at the moment, I needed to use OpenCV 3. Time to get them to work together!

I used the following articles for reference:

In my example code, I open a video and display it on the screen in a window. All of this is done using OpenCV calls from C++. Here is the C++ code. First, we include the OpenCV libraries and some other niceties:

#include <iostream>
#include <unistd.h>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

Next, we use a little trick from article 2 (calling C++ from C):

#ifdef __cplusplus
#define EXTERNC extern “C”
#else
#define EXTERNC
#endif

Now, we write a C++ function that does everything we need to do using OpenCV 3 calls.

EXTERNC void openVideo(char * filename)
{
VideoCapture cap(filename);
Mat Uframe;
int frameCount=0;
 if(!cap.isOpened())
{
cout << "Cannot open the video file" << endl;
return;
}
 namedWindow("Video", CV_WINDOW_NORMAL);
 while(++frameCount)
{
bool bSuccess=cap.read(Uframe);
  if(!bSuccess)
{
cout << frameCount << " frames read" << endl;
break;
}
  imshow("Video", Uframe);
waitKey(1);
}
}

Note the following:

  • The definition of the function uses EXTERNC to make it C callable.
  • In this code, I’m using all local variables. When you work with several different functions that share the same variables (e.g. the video file that you opened or the Mat containing the current frame), you may need to work with global variables. But then your code cannot be parallelised using go routines.
  • The call to waitKey() is necessary to force OpenCV to open the window and display the image, as per the OpenCV documentation on imshow().

We then make a small header file to make this function callable from C. Make sure the C++ file has extension .cpp, but the header file has extension .h.

#ifndef OPENCV_IMPLEMENTATION_H
#define OPENCV_IMPLEMENTATION_H
#ifdef __cplusplus
#define EXTERNC extern “C”
#else
#define EXTERNC
#endif
EXTERNC void openVideo(char * filename);
#endif //OPENCV_IMPLEMENTATION_H

As you can see, we've used the same trick to make the function C callable from C as well as C++.

The hard work is now done! All we need to do, is to write a Go program to call this function. Also, the Go code needs a few settings to help the Go compiler find our libraries. To do that, we need an import "C" preceded by all the compiler parameters in a Go comment.

Here's the go code:

package main
/*
#cgo LDFLAGS: -L/usr/local/Cellar/opencv3/3.2.0/lib -lopencv_core -lopencv_video -lopencv_videoio -lopencv_highgui
#include <stdlib.h>
#include "openCV-implementation.h"
*/
import "C"
func main() {
theFilename := "Trip-08-48-33.h264"
C.openVideo(C.CString(theFilename))
}

A few remarks:

  • LDFLAGS contains all the linker settings. -L adds a library path for the OpenCV libraries. -l adds a specific library to the linked binary.
  • I am working on a Mac and I installed OpenCV using Brew. That’s why the location of the OpenCV libraries is in a subdirectory called Cellar. You should replace this with the location of your OpenCV lib directory.
  • I’m adding a number of OpenCV libraries manually with -lopencv_… If you need more or less, then modify this list. Look up which libraries you require in the OpenCV documentation.
  • If you need to import other Go packages, then use a separate import line after the import "C" line. The line to import "C" should be one single line that is literally spelled out like that. If you combine the C import in an import (...) manner, it won't work.
  • I'm converting the Go string into a C *charusing the provided C.Cstring() function. Very handy!

That's all there is to it!