Reputation: 53
Edit: Skip to part 2 for a simpler code.
Part 1: I am working on a project wherein I have to call Java routines from C++.
I use the following code to call the java method. The parameter is a string array from which the first 2 values are used to read a file from the hard drive decode it and return the data. For testing purpose, I am returning a 1D integer array containing only the first image in the file. There are more images in the file with Z depth and time sequence like in a video, ignoring those for now.
mid = m_pJVMInstance->m_pEnv->GetStaticMethodID(imageJ_cls, "getIntData", "([Ljava/lang/String;)[I");
if (mid == nullptr)
cerr << "ERROR: method not found !" << endl;
else {
jobjectArray arr = m_pJVMInstance->m_pEnv->NewObjectArray(2, // constructs java array of 2
m_pJVMInstance->m_pEnv->FindClass("java/lang/String"), // Strings
m_pJVMInstance->m_pEnv->NewStringUTF("str")); // each initialized with value "str"
m_pJVMInstance->m_pEnv->SetObjectArrayElement(arr, 0, m_pJVMInstance->m_pEnv->NewStringUTF("D:\Dev_Environment\Test_Files\\")); // change an element
m_pJVMInstance->m_pEnv->SetObjectArrayElement(arr, 1, m_pJVMInstance->m_pEnv->NewStringUTF("4D_1ch.lsm")); // change an element
jintArray depth = (jintArray)(m_pJVMInstance->m_pEnv->CallStaticIntMethod(imageJ_cls, mid, arr)); // call the method with the arr as argument.
m_pJVMInstance->m_pEnv->DeleteLocalRef(arr); // release the object
}
The method mid is getting the correct function. The Java code does some processing and returns a integer array to C++ for further processing.
The Java code:
public static int[] getIntData(String[] args) {
int[] test = new int[1];
test[0] = 1;
String dir = args[0];
String name = args[1];
String id = dir + name;
ImageProcessorReader ip_reader = new ImageProcessorReader(
new ChannelSeparator(LociPrefs.makeImageReader()));
try {
IJ.showStatus("Examining file " + name);
ip_reader.setId(id);
int num = ip_reader.getImageCount();
int width = ip_reader.getSizeX();
int height = ip_reader.getSizeY();
ImageStack stack = new ImageStack(width, height);
byte[][][] lookupTable = new byte[ip_reader.getSizeC()][][];
//TODO: Don't know how to handle multiple channels i.e RGb images currently.
// Adding all the slices into a 2D array and returning those values.
int[] test_array = new int[width*height];
int[][][] return_array = new int[height][width][num];
for (int i=0; i<num; i++) {
IJ.showStatus("Reading image plane #" + (i + 1) + "/" + num);
ImageProcessor ip = ip_reader.openProcessors(i)[0];
// Copying the value to the return array.
int[][] temp_array = ip.getIntArray();
for (int h=0; h < height; h++) {
for (int w = 0; w < width; w++) {
return_array[h][w][i] = temp_array[h][w];
if (i==0){
test_array[h*width + w] = temp_array[h][w];
}
}
}
//java.awt.image.BufferedImage awt_Ip = ip.getBufferedImage();
//ImageIO.write(awt_Ip, "jpg", new File("D:\\Dev_Environment\\Test_Files\\Test_Folder\\out" + Integer.toString(i) + ".jpg"));
stack.addSlice("" + (i + 1), ip);
int channel = ip_reader.getZCTCoords(i)[1];
lookupTable[channel] = ip_reader.get8BitLookupTable();
}
IJ.showStatus("Constructing image");
ImagePlus imp = new ImagePlus(name, stack);
// ImagePlus show is leading to java window not responding, maybe it is because this program is not a imageJ plugin but a standalone program.
//imp.show();
//ImagePlus colorizedImage = applyLookupTables(r, imp, lookupTable);
//r.close();
//colorizedImage.show();
IJ.showStatus("");
test[0] = 2;
return test;
}
catch (FormatException exc) {
IJ.error("Sorry, an error occurred: " + exc.getMessage());
test[0] = 3;
}
catch (IOException exc) {
IJ.error("Sorry, an error occurred: " + exc.getMessage());
test[0] = 4;
}
return test;
}
For testing only I added a test array of size 1 to check the output in C++. I am getting 0 as a return value in the depth variable.
My question is how do we return an integer array from Java to c++? Is jintArray the right way? Consequently, can I return a 3D array from Java to C++?
Edit Part 2: I did a test with new Java code and same C++ code, except now I am calling "getInDataS" from the java side. Here is the java code:
public static int[] getIntDataS(String[] args) {
int[] test = new int[10];
test[0] = 10;
test[1] = 10;
test[2] = 10;
test[3] = 10;
test[4] = 10;
test[5] = 10;
test[6] = 10;
test[7] = 10;
test[8] = 10;
test[9] = 10;
return test;
}
I am atleast getting some value in the depth variable but as soon as I try to read the values access violation is thrown. Bothe the lines of code throw the error. Here is the C++ Code:
jintArray depth = (jintArray)(m_pJVMInstance->m_pEnv->CallStaticIntMethod(imageJ_cls, mid, arr));
jsize len = m_pJVMInstance->m_pEnv->GetArrayLength(depth);
jint* body = m_pJVMInstance->m_pEnv->GetIntArrayElements(depth, 0);
Upvotes: 2
Views: 1207
Reputation: 57173
Returning int array works exactly as you describe, but the JNI function to call such Java method is not CallStaticIntMethod(), but CallStaticObjectMethod(). This is the most likely cause of the error you get.
To accept a 2D array (or more) in C, you declare it as jobjectArray, and get the object value of each element as jintArray.
Note that your C code leaks some local references, which may be a problem if it is called in a loop. It may be easier to wrap this code with PushLocalFrame() and PopLocalFrame().
Upvotes: 1