Tuesday, May 27, 2008

How to pass data from java to C with incredible ease

From the moment I started to develop in java, because of the interaction with academic environmental modelers, I often had the problem of retrieving data with java, or having a GUI in java, but needing to use the data in a supercomplex C or fortran algorithm.
So I spent days and nights in learing and applying JNI techniques, hating them every day.
Then one day SWIG came around and helped a bit, but I yet find pure java development much more fun and maintainable :)

And then one day this guy on the imagio mailinglist pointed me to JNA... and I gave it a first simple try... and I have to share this...
no nightmare...
no worries...
just the time to write the code... INCREDIBLE :)

Since I am currently working on porting the GRASS binary raster format to imageio, i will do an example with that. I will read a GRASS raster and print its content in java, in C as pointer of doubles and as array of doubles.

Step one: get the jni.jar from the link above

Step two: write some java code that reads an image or, as in my case a raster map


public class PrintImageInC {

public PrintImageInC( String filePath ) {

try {
// create the grass reader
GrassBinaryImageReaderSpi readerSpi = new GrassBinaryImageReaderSpi();
ImageReader reader = readerSpi.createReaderInstance();
File file = new File(filePath);
reader.setInput(file);
int imageIndex = 0;

// set the novalue to use
GrassImageReadParam imageReadParam = new GrassImageReadParam();
imageReadParam.setNovalue(-9999.0);

// read the raster
BufferedImage bi = reader.read(imageIndex, imageReadParam);

// get the databuffer out of the raster and wrap the data array
DataBufferDouble dataBuffer = (DataBufferDouble) bi.getData().getDataBuffer();
double[] ds = dataBuffer.getBankData()[0];

int rows = bi.getHeight();
int cols = bi.getWidth();


WritableRaster raster = bi.getRaster();
System.out.println("Printing array in Java");
System.out.println("-----------------------------------");
for( int i = 0; i rows; i++ ) {
for( int j = 0; j cols; j++ ) {
System.out.print(raster.getSampleDouble(j, i, 0) + " ");
}
System.out.println();
}
System.out.println();

PrintLibrary.INSTANCE.printimageArray(ds, rows, cols);

DoubleBuffer wrapDs = DoubleBuffer.wrap(ds);
PrintLibrary.INSTANCE.printimagePtr(wrapDs, rows, cols);

} catch (IOException e) {
e.printStackTrace();
}
}

// here the magic is done
public interface PrintLibrary extends Library {
// load the C library
PrintLibrary INSTANCE = (PrintLibrary) Native.loadLibrary("imageioprint",
PrintLibrary.class);

// define prototypes
void printimageArray( double[] imgdata, int rows, int cols );
void printimagePtr( DoubleBuffer imgdata, int rows, int cols );
}

public static void main( String[] args ) {

String infile = "/home/moovida/grass/grassdb/flangitest/prova/cell/flowy";
new PrintImageInC(infile);
}

}


Step three: write some simple C code to print out the contents (here my testfile imageioprint.c)


#include stdio.h


void printimagePtr(double *imgdata, int rows, int cols)
{
int i = 0;
int j = 0;
printf ("Printing pointer in C\n");
printf ("--------------------------------------------\n");
for (i = 0; i rows; i++)
{
for (j = 0; j cols; j++)
{
printf ("%f ", *imgdata);
imgdata++;
}
printf("\n");
}
printf("\n");
}

void printimageArray(double imgdata[], int rows, int cols)
{
int i = 0;
int j = 0;
printf ("Printing array in C\n");
printf ("--------------------------------------------\n");
for (i = 0; i rows; i++)
{
for (j = 0; j cols; j++)
{
printf ("%f ", imgdata[ i*cols + j ]);
}
printf("\n");
}
printf("\n");
}


Step four: compile you C code to a shared library

gcc -shared -o libimageioprint.so imageioprint.c


Step five: supply the needed LD_LIBRARY_PATH to the java environment (i.e. the folder inside which the lib is located) and execute!




PS: JNA dynamically sets types, so it is less performant than JNI. So if you have to call a method millions of times, JNI will be the cross you have to carry, but in my case, with one single heavy raster to pass, the performance is no matter at all.

No comments: