Write a blog post they said. Talk about SWIG they said. Make it short they said.
Well, for those of you that don’t know, the latter two are mutually exclusive which results in a blog post of excess length. But, on the upside, you can just skip our gibberish and move to the essential code parts.
This blog post is all about SWIG (the Simplified Wrapper and Interface Generator). Unfortunately, for those of you that like pretty pictures, there won’t be any.
Now, having lost half of the readers with this previous sentence, if this blog post is not about images and computer vision, what is it about anyway, and why is it posted here? After all, this is supposed to be a computer vision blog.
SWIG & JNI
SWIG is (almost) the solution to problems that many companies and developers developing for more than one mobile platform face, namely how to hold a shared core code (C++ in this case), and access it from different host languages.
While it is fairly easy to access C++ code on iOS, doing the same from Android proves to be a more complicated task. Luckily, we are going to give an answer to the question: How to glue JAVA and C++ together?
- Create your own cross-platform OCR app / mobile scanning app with our free demo & OCR SDK trial!
We are going to do so by making use of the Java Native Interface (JNI), together with SWIG. SWIG wraps and connects C++ code via the JNI to Java code. (For those of you reading carefully, yes, this is a SEO sentence. It will be the only one. Promise!)
Something about SWIG
In general, SWIG needs a C++ input file (holding the code you want to be wrapped), and an interface file, telling SWIG how to wrap things. The output of SWIG is JAVA code, with which you are able to access C++ objects and functions.
The interface file consists of many directives, some more complicated, some less, some better documented, some less. You can find a lot of them here at SWIG.
If you want to implement the example in this blog post yourself, you need to download and install the swig tool.
The SWIG command looks like this
swig -java -c++ -package io.anyline.swig.example -outdir src/io/anyline/swig/example -o wrap/swig_example_wrap.cpp swig_example.i
java and -c++ mean that we want to wrap C++ code to JAVA
package defines the resulting package name
outdir the output directory for the JAVA files
o the output file for the C++ wrapper code
and finally your interface file.
Note: SWIG is available for many programming languages. We will focus on JAVA for this blog post.
We will start with an easy example, providing you with the basics of
- How to wrap your classes
- Wrapping input and output parameters for functions
- How to bind the parameters to existing JAVA implementations
Our example scenario will be the following. Suppose we have a C++ core (called
MySwigExample), which receives an image as an input (an OpenCV Mat), performs some operation, and returns a C++ result (conveniently called
MySwigResult), holding a thresholded image (OpenCV Mat) and a detected region of interest (OpenCV Rect) in the image.
The best part is: we don’t have to care about any actual C++ implementation. There is none. All we need are the header files with the function declarations. Because from the JAVA point of view, we only need a mechanism to call the functions and retrieve the results.
Okay, let’s start with the C++ header of our example
For simplicity (some would call it laziness), the code is stripped to its pure essentials.
What we want to achieve is to create an empty
MySwigResult object and a
MySwigExample object, call the
run function, and retrieve the
cv::Rect of the
MySwigResult object. And, of course, we want to do all that from JAVA.
To tell SWIG what to do, we need the SWIG interface file. The first simple interface version, just defines a module name (which is used for the generated JNI class names), and tells SWIG to include our C++ header.
Running this with the swig command will produce six JAVA files. The two JAVA implementations
MySwigResult.java, as well as the wrapper code in
swig_example.java (yep – empty) and
There are two JAVA files left which are called
SWIGTYPE_p_cv__Rect.java. That is because SWIG does not know how to handle the OpenCV classes in JAVA, and just creates its own wrapper for them. But don’t worry, we’ll get there.
If you take a look inside the generated JAVA classes, you will see that they call methods in
swig_exampleJNI, which are native methods. These methods actually call the C++ code, residing in
wrap/swig_example_wrap.cpp. In there, the C++ objects are created, and a pointer to them is returned to the JAVA side. So all the created objects on the JAVA side are really just wrappers, not implementing any logic, but calling their respective counterparts on the C++ side.
Long story short, if we call a JAVA constructor for
MySwigResult, a C++
MySwigResult object is created, and the pointer is stored in the JAVA wrapper.
Connect the OpenCV Classes with their JAVA Counterparts
So far so good, but it gets better. Now that we have our wrapped classes, it would be nice to connect the OpenCV classes that are used in the C++ part with their JAVA counterparts. One option would be to have SWIG wrap the C++ implementation of OpenCV by adding the respective header files to the SWIG interface file, but we don’t need all of the classes.
Fortunately, there is an elegant way to establish this connection.
First, let’s have a look at the JAVA implementation of
After taking a closer look, we discover that a Mat is actually also a C++ object, where the JAVA part is simply a wrapper, holding a
public final long nativeObj. This is the same as
swigCPtr field in our wrapped classes. So simply a pointer to the C++ object it wraps.
This means, that whenever we want to pass a Mat from JAVA to C++, or the other way around, we can simply pass this pointer because the object already exists in the C++ environment.
To tell SWIG how to do that, we use typemaps. A type map, the name kind of gives it away, is a directive telling SWIG how one type (in C++) should be mapped to another type (in JAVA).
And the following is the typemap for Mat:
jstype typemap tells SWIG that we want to use
org.opencv.core.Mat for the
javain typemap tells SWIG how to pass the JAVA object to its intermediary JNI class. In this case, we call the
getNativeObjAddr() function on the JAVA
Mat, to obtain the pointer address for the C++
cv::Mat and pass it along.
Next up, the
jtype typemap tells SWIG that a
long will be passed to the
long is the pointer we obtained in the previous step.
long becomes a
jlong (the corresponding JNI native type – just don’t mind, we think of it as a long) in the
jni typemap when passed on to the SWIG C++ function (located in
And finally, we have to tell SWIG how to create the
cv::Mat in the C++ function out of the pointer we just passed along. That is done via the
in typemap, where we simply tell SWIG to cast that pointer to a
And… done! We are now able to pass a Mat from JAVA to C++. This is it. All the “magic”. We’re aware of the fact that this is not easy to understand when you try it the first time. But give it some time. Get a coffee and think about it for a day or two. Trust us, it will make sense after a while!
To make it even better, passing a
cv::Mat back to JAVA is a piece of cake! Since we already have all the
jni typemaps, all we have to do is tell SWIG how to handle the pointer that is returned from the
swig_exampleJNI.java methods. And OpenCV does us a real favour there, since we can simply call the constructor of the JAVA
Mat and pass the pointer.
Done. If you told your colleagues that it would take quite some time to create a wrapper to pass a Mat from C++ to JAVA, you just earned a lot of free time with this one-liner.
If you add the typemaps to the interface file (be aware to add it before the
%include statement – otherwise it will be useless), and run the swig command again, you will see that the
SWIGTYPE_p_cv__Mat.java is gone and the JAVA parameters are replaced by a neat
But, quite frankly, OpenCV made it easy for us to create the Mat typemap. We will now create typemaps for the OpenCV Rect, which does not have a pointer to C++ anywhere near its code.
The Rect (a working title heading)
By now, you have studied the JAVA code of a OpenCV Rect to find a loophole and be able to skip this part of the blog post. If you found one, great, and please tell us about it. If you didn’t, I am afraid you are stuck with me here. Don’t worry, I promise it gets “fun” (as fun as wrapping a
cv::Rect from C++ to JAVA can be…)
What we are going to do is, unlike in the previous part about
Mat, copy the
cv::Rect from C++ to JAVA, rather than sending pointers to the object around. This also means, that the wrapped
Rect in JAVA will not be linked to the C++ object. So if the C++ object is modified, the JAVA version will not know anything about it, and live happily in its JAVA world, not caring about this C++ everyone keeps talking about.
So let’s have a look at the corresponding typemaps:
The C++ part of the JNI (
swig_example_wrap.cpp) is going to return a
jobject this time, instead of a pointer.
What we need to do now, is tell SWIG how to create that JAVA object within its C++ code.
The answer to that lies within the
out typemap. Simply put, we ask the JNI environment
jenv for the JAVA class of the
Rect, ask what the constructor is, and call the constructor with the
height parameters of our C++
cv::Rect. The so obtained object is then simply returned, and we just pass it on in the
Note: The JNIEnv is provided as a parameter to all JNI C++ functions. It is a struct holding pointers to all JNI functions. Feel free to read more about it here (https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html)
Note2: Yes, this code does not include any security checks. So if a method does not exist, you might run into a problem – see “laziness” before.
Run the swig command again, and you will see that the
getRegionOfInterest() method now returns a
But, the setter for regionOfInterest and the constructor still require the swig generated
SWIGTYPE_p_cv__Rect as parameters. That is because we didn’t define the
in typemap in our interface file.
We could either write the
in typemap, or just ignore these methods. We decided democratically that we are going to ignore them.
Well, why can we ignore these methods anyway?
First, the main part of our program runs in C++, so we only need the JAVA side to be able to access the results of the program. We are never going to set any parameter on
MySwigResult from JAVA. Neither are we going to construct a
MySwigResult object with a thresholded image and a region of interest in the constructor – simply because you should take care of that part in C++.
So much for the why part, now we’ll look at the how part. And that is easy as well:
Thankfully the SWIG guys were pretty straight forward regarding their naming conventions.
%ignore followed by the class and the function signature tells SWIG skip this functions when creating the wrapper code.
Run the swig command again, and you will see that all the auto-generated SWIG classes for our OpenCV objects are gone.
Final Steps or “Please Wrap It Up Already, I’m Tired”
That’s the last part, then we are done. You might have realized by now that the generated wrappers for
MySwigResult lack the import statements for the OpenCV classes. You could just add them manually, but once you run the swig command again, the changes would be lost inevitably. There might be some of you saying now “I’m going to remember that, don’t worry. Bye!”
For all others – and the ones that read the comment at the top of the SWIG generated classes – there is a simple SWIG directive that adds the imports to your generated JAVA classes. Believe it or not, the naming convention is straightforward:
This adds the import statements to the beginning of your generated JAVA classes. Actually there is nothing more to say about this. And that also concludes our first blog post about SWIG and its typemaps.
Summary: SWIG is a Real Benefit in Many Situations
SWIG is pretty powerful if you know how to use it, and what to use when, and respect the sequence of your directives, and so on. And there are a couple of real experts on SWIG out there (looking in your direction, Flexo from stack overflow!) and this is just a simple example, trying to give you an introduction to SWIG and how to benefit from it.
But despite its complexity, SWIG can be a real benefit in many situations. If you look at the code that SWIG created, you will get the point. Imagine to write and maintain this code yourself.