Saturday, April 5, 2008

The Idiot's Way of Wrapping C/C++ OpenCV code for Java using SWIG

For the longest time I tried many times to wrap the C/C++ code that I have for face detection and recognition using OpenCV for Java using SWIG with no success. Being the idiot that I am, I finally learned why I couldn't do it after my friend, Martin Lukac, who has been working with the same issue for a while before me, explained that you can't wrap a C/C++ code which has some custom functions and data types/objects (i.e. from OpenCV) just like that. SWIG was only made to work with primitive data types of C/C++ (or other languages like Perl or Python) - which is obvious: you shouldn't expect the guys who made SWIG covers all the custom libraries out there, there are thousands and thousands of them! The only way that I would be able to wrap a C/C++ code which contains OpenCV data types and functions is that I would have to define those OpenCV data types/objects and functions for SWIG (i.e. in the interface file) myself. That would take me weeks even months to figure out! Fortunately, there is an easier way...

My solution: Create a proxy class.

That is, create a C++ class, which accesses the C/C++ functions in the main code which uses the OpenCV data types/objects and/or functions. So it looks like this: C++ class --> main C/C++ code --> OpenCV data types/objects and/or functions. Only the class will be wrapped, so SWIG does not have to deal with the OpenCV data types/objects and functions.

So in the class, I include the main C/C++ code using: #include "...", and for the class' methods, just access the main C/C++ code's functions statically (i.e. directly call the functions). For example:

My main C/C++ code:

/*
* File name: main_capture.cpp
* This program will do a live feed from a webcam and display it, until the ESC key is pressed
*/

#include "cv.h"
#include "highgui.h"

int initate_capture() {

IplImage* frame; // OpenCV data type for images
CvCapture* capture; // OpenCV object for camera capture

// Initialize capture from camera
capture = cvCreateCameraCapture( -1 ); // "-1" -> capture from any detected camera

// If there is no camera connected/detected...
if ( !capture ) {
fprintf( stderr, "ERROR, capture is NULL\n "); // ... show error ...
cvWaitKey(0); // ... wait for any key to be pressed ...
return -1; // ... and quit the program.
}

// If capture is successful, create a window named "Capture" to display the captured image
cvNamedWindow( "Capture", CV_WINDOW_AUTOSIZE );

// Create loop...
while (1) {

frame = cvQueryFrame( capture ); // Capture from camera...
cvShowImage( "Captue", frame ); // Show captured image in the window "Capture"

if ( (cvWaitKey(10) & 255 ) == 27 ) break; // Exit loop when ESC is pressed
}

// Clear some used memory before quitting the program
cvReleaseCapture( &capture );
cvDestroyWindow( "Capture" );
return 0;
}

// End of main_capture.cpp


My C++ proxy class:

/*
* File name: main_capture_class.cc
*/

#ifndef main_capture_class
#define main_capture_class

#include "main_capture.cpp" // Include the main C/C++ program

using std namespace;

class Capture {

// Declare the class methods
public:
void do_capture(); // This one is public

};

void Capture::do_capture() {
initiate_capture(); // Calls the function "initiate_capture()" in main_capture.cpp
}

#endif

// End of main_capture_class.cc


If you want to test if the class works, add this at the end of the class code (before the #endif):

int main() {
Capture c; // Instantiate the class Capture
c.do_capture(); // Call the "do_capture()" method of class Capture
}


Notice a couple of things here:
  1. I don't have a "main()" function in the main program. I don't need to, since the program will be run by some other program, unless I want to test the main program by itself, then I would need the "main()" function. However, I have to have a function that I can easily call from the class (depends on what you define as "easily" - my definition is where I don't have to worry about passing around variables and return types. Eventually, we will need/want to have something returned from the main program, e.g. name, or some other values).
  2. There are no OpenCV stuffs in the class - LEAVE THOSE IN THE MAIN PROGRAM!
  3. I try to keep the functions as simple as possible, i.e. not a lot of variable passing - this will make the functions easier to call from Java. Notice that the class' code has no variable passing, all the return types are void, and is very short (and sweet),
  4. The class' extension is .cc, for some reason SWIG doesn't like it when the extension is .cpp for C++

The next step is to create all the interface file (.i) for the class for SWIG, but first (as it is called in the SWIG tutorial website), I like to use a header file (.h) of the class, and include it in the interface file. So the header file for the class:

// File name: main_capture_class.h

class Capture {
public:
void do_capture();
}

// End of main_capture_class.h


Now the interface file:

// File name: main_capture_class.i
%module capture

%{
#include "main_capture_class.h"
%}

// List of all the methods of the class
%Capture::do_capture();

%include "main_capture_class.h"

// End of main_capture_class.i


So to recap, you need four files:
  1. The main C/C++ code where you use the OpenCV data types/objects and/or functions (main_capture.cpp)
  2. The proxy class that calls the functions in main_capture.cpp (main_capture_class.cc)
  3. The header file for the class (main_capture_class.h)
  4. The interface file for the class (main_capture_class.i)

So now start wrapping up! Using the interface file ... etc. etc. After the whole thing is done, you should have several files such as:
- Capture.java --> this is your usable Java class
- captureJNI.java
- captureJNI.class
- main_capture_class.o
- main_capture_class_wrap.cxx
- main_capture_class_wrap.o
- libcapture.so


Note that 'libcapture.so' is the shared object (hence, the '.so' extension) library, and the name 'libcapture' depends on what you define as the output name when you finally create the shared object after all the compilation steps are done.

If you want to test it, make a test Java file, for example:

// File name: CaptureTest.java

public class CaptureTest {

// CaptureTest constructor
CaptureTest() {
System.out.println("Running CaptureTest...\n");
System.loadLibrary("capture"); // Load the library, i.e. shared object "libcapture.so"
// Notice that the "lib-" and the ".so" extension are omitted
}

// CaptureTest method: doCapture()
public void doCapture() {
Capture c = new Capture(); // Instantiate the Capture object (class)
c.do_capture(); // Call the Capture method "do_capture()"
}

// Main routine
public static void main( String [] args ) {
CaptureTest ct = new CaptureTest(); // Instantiate the CaptureTest class
ct.doCapture(); // Start capturing...
}
}

// End of CaptureTest.java


Now I'm not claiming that this is THE way to do it, or the most elegant way, but it's certainly a sure-fire one. Give it a shot!

For more information, consult:
http://www.swig.org/tutorial.html --> for basic SWIG wrapping tutorial (for other languages as well)
http://www.swig.org/Doc1.3/Java.html --> for details on SWIG wrapping C++ for Java