Converting OpenCV code from C++ to Java

2020-07-11 05:41发布

问题:

I'm currently trying to migrate a bit of legacy code from iPhone to Android. This code uses the OpenCV library to do some image processing. Overall it goes well, but I'm stuck on one line of code I have no idea how can be converted into Java code:

Scalar dMean;
Scalar scalar;
std::vector<Mat> channels;
split(mat, channels);
for(int i=0; i<3; ++i) {
   channels[i] += dMean[i];
}

The question is - what should be used instead of the += operator in Java code to add a Scalar object to a Mat?

回答1:

Note: take this answer with a grain of salt, I haven't fully tested this ;)

OPTION1:

The most direct way, and if you are only going to process a few pixels, is by using your_mat.put(row, col, data) and your_mat.get(row, col).

Because the put() method does not accept Scalar objects as the data parameter, you have to convert the Scalar to something that put() accepts.

So if your Scalar is (1,2,3) maybe an int array int[] scalar = {1,2,3}; should do the trick.

int[] scalar = ... // convert from Scalar object

// assuming the result of get() is an int[], sum both arrays:
int[] data = your_mat.get(row, col) + scalar // <- pseudo-code alert :D

your_mat.put(row, col, data);

OPTION2:

But the recommended way, for a lot of pixel processing, is to first convert a Mat to a Java primitive, process the primitive and then convert it back to Mat. This is to avoid too many JNI calls, this method does 2 JNI calls while the former makes one per put/get.

The corresponding Java primitive array type depends on the Mat type:

CV_8U and CV_8S -> byte[];
CV_16U and CV_16S -> short[];
CV_32S -> int[];
CV_32F -> float[];
CV_64F-> double[];

So the code will be something like this:

// assuming Mat it's of CV_32S type
int buff[] = new int[your_mat.total() * your_mat.channels()];

your_mat.get(0, 0, buff);

// buff is now Mat converted to int[]

your_mat.put(0, 0, buff); // after processing buff, convert it back to Mat type

OPTION 3:

Ok so those solutions are pretty ugly, this one is not the most effective but it's a little less ugly, in a Java way:

List<Integer> scalarList = ... // your conversion from a Scalar to a List
List<Integer> channelsList = new ArrayList<Integer>();

Converters.Mat_to_vector_int(channels, channelsList); // this is an existing OpenCV4Android converter

// process channelsList and scalarList, store in channelsList

channels = Converters.vector_int_to_Mat(channelsList); // after processing, convert it back to Mat type

Now that I think about it option 3 is quite similar to option 2, if OpenCV's Converters work similar internally as the option 2 conversion.