I tried implementing direct convolution of two audio files using JNI on Android.. So far, I ve done this:
JNIEXPORT jint JNICALL Java_com_example_directconv_MainActivity_convolve (JNIEnv * env, jobject obj, jdoubleArray signal1, jdoubleArray signal2, jdoubleArray output)
{
jdouble *sig1, *sig2, *out;
// jboolean isCopy1, isCopy2, isCopy3;
int i,j;
jsize n,m;
sig1=(*env)->NewDoubleArray(env,n);
sig2=(*env)->NewDoubleArray(env,m);
out=(*env)->NewDoubleArray(env,m);
n=(*env)->GetArrayLength(env, sig1);
m=(*env)->GetArrayLength(env,sig2);
sig1=(*env)->GetDoubleArrayElements(env, signal1,NULL);
sig2=(*env)->GetDoubleArrayElements(env, signal2,NULL);
out=(*env)->GetDoubleArrayElements(env, output, NULL);
if (sig1 != NULL || sig2!=NULL) {
memcpy(signal1,sig1,n);
memcpy(signal2,sig2,m);
(*env)->ReleaseDoubleArrayElements(env,signal1,sig1,JNI_ABORT);
(*env)->ReleaseDoubleArrayElements(env,signal2,sig2,JNI_ABORT);
}
for(i=0; i<n;i++)
{
out[i]=0;
for(j=0;j<m;j++)
{
out[i]+=sig1[i-j]*sig2[j];
}
}
(*env)->ReleaseDoubleArrayElements(env,output,out,0);
return 1;}
And on the java side:
public class MainActivity extends Activity {
static
{
System.loadLibrary("DirectConv");
}
File externalDir1=Environment.getExternalStorageDirectory();
File externalDir2=Environment.getExternalStorageDirectory();
File f1=new File(externalDir1.getAbsolutePath()+"/Test"+File.separator+"wav2.wav");
File f2=new File(externalDir2.getAbsolutePath()+"/Test"+File.separator+"wav2.wav");
int res;
public native int convolve(double[]signal1, double[]signal2, double[]output);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
//---------------------------------------------read files
double[]raw1=read(f1);
double[]raw2=read(f2);
double[]out=new double[raw1.length];
res=convolve(raw1,raw2,out);
for(int i=0;i<out.length;i++)
Log.i("out", "out "+ out[i]);
}
The resulting array (out) is all zeros. I don't see what I did wrong. Any help?
Your handling of JNI arrays is all wrong. First off, there's no reason for you to be creating any new arrays here. YOur inputs and outputs are passed down, what you want to do is convert the inputs to C arrays, work on them in pure C, then convert your output array to a Java array.
Secondly, you don't need the memcpys. Calling getDoubleArrayElements does it for you.
Third, you need to put the values in out back into output via setDoubleArrayRegion
Fourth, you need to clean up your memory usage. If you don't, you're going to memory leak and eventually die as there's a limit of 256 (or is it 512? Been too long I forget) Java objects pinned to C.
Your code should look more like:
JNIEXPORT jint JNICALL Java_com_example_directconv_MainActivity_convolve (JNIEnv * env, jobject obj, jdoubleArray signal1, jdoubleArray signal2, jdoubleArray output)
{
double *sig1, *sig2, *out;
int i,j;
jsize n,m;
n=(*env)->GetArrayLength(env, sig1);
m=(*env)->GetArrayLength(env,sig2);
sig1=(*env)->GetDoubleArrayElements(env, signal1,NULL);
sig2=(*env)->GetDoubleArrayElements(env, signal2,NULL);
for(i=0; i<n;i++)
{
out[i]=0;
for(j=0;j<m;j++)
{
out[i]+=sig1[i-j]*sig2[j];
}
}
(*env)->SetDoubleArrayRegion(env, output, 0, n, out);
(*env)->ReleaseDoubleArrayElements(env,signal1,sig1,JNI_ABORT);
(*env)->ReleaseDoubleArrayElements(env,signal2,sig2,JNI_ABORT);
return 1;}
I'm going to assume your convolution math is right, its way too late for me to start remembering that stuff :)
I figured out the problem. I added files sizes as parameters like this:
JNIEXPORT jint JNICALL Java_com_example_directconv_MainActivity_convolve (JNIEnv * env, jobject obj, jdoubleArray signal1, jdoubleArray signal2, jdoubleArray output, jint size1, jint size2)
{
jdouble *sig1, *sig2, *out;
int i,j,k;
jint n,m;
n=size1;
m=size2;
sig1=(*env)->GetDoubleArrayElements(env, signal1,NULL);
sig2=(*env)->GetDoubleArrayElements(env, signal2,NULL);
out=(*env)->GetDoubleArrayElements(env,output,NULL);
for(i=0; i<n;i++)
{
out[i]=0;
for(j=0;j<m;j++)
{
if(i-j>=0)
out[i]+=sig1[i-j]*sig2[j];
}
}
(*env)->SetDoubleArrayRegion(env, output, 0, n, out);
(*env)->ReleaseDoubleArrayElements(env,signal1,sig1,JNI_ABORT);
(*env)->ReleaseDoubleArrayElements(env,signal2,sig2,JNI_ABORT);
return 1;
}
Related
I'm currently developping an android app with android studio, for this project i need to use a custom library written in c/c++ .
So i in order to do that i needed to use NDK.
The library contain methods that i need to implements in order to have access to specifics fonctions in android
My question is : how can I call my jni method inside the method existing in c
which will call a java method to store session key in the android system
So for a pratical exemple since it's maybe not clear:
In a file called lib_exemple.c i have three methods
the first one is the initialisation call to the library (jni to library c) -->working
JNIEXPORT void JNICALL
Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1init
(JNIEnv *env, jobject instance) {
lib_Example_init('0'); // c func of the library
}
then the second one is the jni who call a java method (jni to java) -> working
JNIEXPORT jint
JNICALL Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1store_1session_1key
(JNIEnv * env, jobject jobject1, jbyte jbyte1, jchar jchar1, jbyte jbyte2){
jclass clazz = (*env)->FindClass(env, "com/example/libExemple/Libs/ExempleLib");
jmethodID mCurrentActivityId = (*env)->GetMethodID(env, clazz, "KeyStoreSessionKey", "(BCB)I");
jint result = (*env)->CallIntMethod(env, jobject1, mCurrentActivityId, jbyte1, jchar1, jbyte2);
return result;
}
and the third method is the one the library c have in it (library C to jni)
int lib_Exemple_store_session_key(uint8_t Session, P_KEY_ST_T pKey, uint8_t keyType) {
//i want to call jni func here , so the librairy can access the native android function
return 0;
}
then to configure the ndk in a file called ExempleLib.java i have defined
static {
System.loadLibrary("LibExemple");
}
then the prototype of the initialisation of the library
public native void libExemple_init();
the prototype of the first method in lib_exemple.c
public native int lib_Exemple_store_session_key(byte pKey, char key_length, byte keyIndex);
and the java fonction called by it
protected int KeyStoreSessionKey(byte pKey, char key_length, byte keyIndex) {
...
}
my mainActivity contain
ExempleLib LibFunc = new ExempleLib();
Log.d(TAG, "init lib" );
LibFunc.lib_Exemple_init();
My goal is that after initialising my library and call an init function , it can call the first method in the second method of lib_exemple.c (the one used in the library )
thanks
EDIT :
The solution of my problem is when i initialize the lib , i need to save jvm
so i can access the JNIenv in the library method directly
jobject Savedinstance = NULL;
static JavaVM *cachedJVM;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
cachedJVM = jvm;
return JNI_VERSION_1_6;
}
JNIEnv *AttachJava() {
JavaVMAttachArgs args = {JNI_VERSION_1_6, 0, 0};
JNIEnv *java;
(*cachedJVM)->AttachCurrentThread(cachedJVM, &java, &args);
return java;
}
JNIEXPORT jint JNICALL
Java_com_example_vgosselin_libExemple_Libs_ExempleLib_lib_1init(JNIEnv *env,
jobject instance) {
Savedinstance = instance;
void *element = 0;
jint result;
result = lib_Exemple_init_();
return result;
}
so i can call the java method in
int lib_Exemple_store_session_key(uint8_t Session, P_KEY_ST_T pKey, uint8_t keyType) {
JNIEnv *NewEnv = AttachJava();
jclass clazz = (*NewEnv)->FindClass(NewEnv,"com/example/vgosselin/libExemple/Libs/ExempleLib");
jmethodID mCurrentActivityId = (*NewEnv)->GetMethodID(NewEnv,clazz,"KeystoreStoreKey","([BCB)I");
jint result;
jbyteArray Data = 0;
jchar Key = 0;
jbyte Type = 0;
result = (*NewEnv)->CallIntMethod(NewEnv, Savedinstance, mCurrentActivityId, Data, Key, Type);
return result;
}
and now i don't need the method in jni syntax anymore
JNIEXPORT jint JNICALL Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1store_1session_1key
I a Library in C that I'm leveraging for an Android application. This library has an audio stream that it occasionally flushes. When this happens it calls a write callback function of my design.
My intent is to have that C callback call a method on a specific Java Object which will handle stuff with the strem.
Currently I have code like so:
methodID compressionHandler=0;
jobject compressionHandlerClass;
int audioBufferChunkSize;
static JavaVM *gJavaVM;
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
gJavaVM = vm;
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL
Java_com_my_code_init(JNIEnv* env, jobject obj, /*classpath of the class we want to call against*/jstring compressedAudioHandlerPath, /*class instance we want to call against*/jobject callbackClass) {
......
// this is a global ref as per:
//http://stackoverflow.com/questions/14765776/jni-error-app-bug-accessed-stale-local-reference-0xbc00021-index-8-in-a-tabl
compressionHandlerClass = (*env)->NewGlobalRef(env,callbackClass);
// name of the class
const char *classLocation;
// convert jString to c String
classLocation = (*env)->GetStringUTFChars( env, compressedAudioHandlerPath , NULL ) ;
// tmp variable for holding the class location, relates to the above issue with garbage collection
jclass clazz = (*env)->FindClass(env, classLocation);
// the actual method that we want to call, this gets used in the writeCallback
compressionHandler = (*env)->GetMethodID(env, clazz, "handleCompressedAudio", "([B)V");
......
}
The callback method looks like so:
void writeCallback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) {
JNIEnv *env;
int isAttached = 0;
if ((status = (*gJavaVM)->GetEnv(gJavaVM, (void**)&env, JNI_VERSION_1_6)) < 0) {
if ((status = (*gJavaVM)->AttachCurrentThread(gJavaVM, &env, NULL)) < 0) {
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
isAttached = 1;
}
if(*env!=0 && compressionHandler!=0){
jbyteArray arr = (*env)->NewByteArray(env,bytes);
(*env)->SetByteArrayRegion(env,arr, 0, bytes, (jbyte*)buffer);
(*env)->CallVoidMethod(env,compressionHandlerClass, compressionHandler,arr);
free(arr);
free(env);
free(isAttached);
}
}
I'm getting crashes at the CallVoidMethod, that signature of which is an interface implemented by whatever object I pass in:
public interface CompressedAudioHandler {
void handleCompressedAudio(byte[] buff);
}
I suspect that I am improperly attaining/keep references to these objects, but I haven't found a great way to handle that. Any advice on how I can more correctly handle this?
I need get byte array from jni to Java.
Ex: I have a byte array byte[] a = {1,2,3,4,5,6}
JNIEXPORT jbyteArray JNICALL Java_com_vn_getArray (JNIEnv *env, jobject obj) {
jbyte[] a = {1,2,3,4,5,6};
return a;
}
I do not know how to return a byte array from jni.
Can someone help me? Please!
In Java, an array is an object. So to hand a byte array from C or C++ over to java you will need to instantiate a jbyteArray, and return that. Instead of a C array. To solve that, see the following code:
JNIEXPORT jbyteArray JNICALL Java_Test_returnArray
(JNIEnv *env, jobject This)
{
jbyte a[] = {1,2,3,4,5,6};
jbyteArray ret = env->NewByteArray(6);
env->SetByteArrayRegion (ret, 0, 6, a);
return ret;
}
Based on this link
I do like that and it's working
JNIEXPORT jbyteArray JNICALL Java_com_vn_getArray(JNIEnv *env, jobject obj) {
jbyte byteUrl[] = {1,2,3,3,4};
int sizeByteUrl = 5;
jbyteArray data = (*env)->NewByteArray(env, sizeByteUrl);
if (data == NULL) {
return NULL; // out of memory error thrown
}
// creat bytes from byteUrl
jbyte *bytes = (*env)->GetByteArrayElements(env, data, 0);
int i;
for (i = 0; i < sizeByteUrl; i++) {
bytes[i] = byteUrl[i];
}
// move from the temp structure to the java structure
(*env)->SetByteArrayRegion(env, data, 0, sizeByteUrl, bytes);
return data;
}
I am a student. Recently I've been building a Face recognize project using opencv but I dont know where to start.
I successfully build my Face detection using OpenCv4Android by reading opencv face detection sample.
Now I start to build Face recognize (using LBPH algorithm) part, I read Opencv document and search google for tutorial that I can actually follow but i failed (there lots of tutorial using javacv but I want to use OpenCv4Android instead) :(
can anyone help me with the step by step tutorial about what should i do to using face recognize in OpenCV4Android SDK? Big thanks to you.
Additional:
I find out about FaceRecognizer.java class in opencv/contrib
I find facerec.java in OpenCV4android folder
I read somewhere and try the method FaceRecognize model = createLBPHFaceRecognizer() ---> but the method createLBPHFaceRecognizer() return error not found. where can i find and use this method?
Please help me what I need to do next? A lot of thanks!!!!!
The createFisherFaceRecognizer() method (as well as the other 2 createXXXFaceRecognizer()) were skipped during the java wrapper code generation, it's a known, yet unsolved problem.
The best solution could be implementing it with jni/ndk. You will have to build:
an .so file with the native c++ code, let's call it facerec.so an
additional java wrapper class calling that, FisherFaceRecognizer.java
sadly, can't help much with ndk(no such thing here), but it worked nicely on desktop/eclipse (the dll/so would go right into your project folder), so here's the code (quite a wall of it).
// --- 8< --------- facerec.cpp -------------------------------
#include "jni.h"
#include "opencv2/contrib/contrib.hpp"
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jlong JNICALL Java_FisherFaceRecognizer_createFisherFaceRecognizer_10(JNIEnv* env, jclass);
JNIEXPORT jlong JNICALL Java_FisherFaceRecognizer_createFisherFaceRecognizer_10(JNIEnv* env, jclass) {
try {
cv::Ptr<cv::FaceRecognizer> pfr = cv::createFisherFaceRecognizer();
pfr.addref(); // this is for the 2.4 branch, 3.0 would need a different treatment here
return (jlong) pfr.obj;
} catch (...) {
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, "sorry, dave..");
}
return 0;
}
JNIEXPORT jlong JNICALL Java_FisherFaceRecognizer_createFisherFaceRecognizer_11(JNIEnv* env, jclass, jint num_components);
JNIEXPORT jlong JNICALL Java_FisherFaceRecognizer_createFisherFaceRecognizer_11(JNIEnv* env, jclass, jint num_components) {
try {
cv::Ptr<cv::FaceRecognizer> pfr = cv::createFisherFaceRecognizer(num_components);
pfr.addref();
return (jlong) pfr.obj;
} catch (...) {
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, "sorry, dave..");
}
return 0;
}
JNIEXPORT jlong JNICALL Java_FisherFaceRecognizer_createFisherFaceRecognizer_12(JNIEnv* env, jclass, jint num_components, jdouble threshold);
JNIEXPORT jlong JNICALL Java_FisherFaceRecognizer_createFisherFaceRecognizer_12(JNIEnv* env, jclass, jint num_components, jdouble threshold) {
try {
cv::Ptr<cv::FaceRecognizer> pfr = cv::createFisherFaceRecognizer(num_components,threshold);
pfr.addref();
return (jlong) pfr.obj;
} catch (...) {
jclass je = env->FindClass("java/lang/Exception");
env->ThrowNew(je, "sorry, dave..");
}
return 0;
}
#ifdef __cplusplus
}
#endif
// --- 8< --------- FisherFaceRecognizer.java -----------------
import org.opencv.contrib.FaceRecognizer;
import org.opencv.core.Core;
public class FisherFaceRecognizer extends FaceRecognizer {
static{ System.loadLibrary("facerec"); }
private static native long createFisherFaceRecognizer_0();
private static native long createFisherFaceRecognizer_1(int num_components);
private static native long createFisherFaceRecognizer_2(int num_components, double threshold);
public FisherFaceRecognizer () {
super(createFisherFaceRecognizer_0());
}
public FisherFaceRecognizer (int num_components) {
super(createFisherFaceRecognizer_1(num_components));
}
public FisherFaceRecognizer (int num_components, double threshold) {
super(createFisherFaceRecognizer_2(num_components, threshold));
}
}
once you got all this compiled (congrats!!), you would call it like this:
FaceRecognizer facerec = new FisherFaceRecognizer();
facerec.load("/sdcard/smile.yml"); // note, that it can't read from apk or zip, so you need to copy it somewhere
Mat img = ...//test face
int [] label = new int[1];
double [] conf = new double[1];
facerec.predict(img, label, conf);
Background
I'm working with byte arrays in JNI. And I can't get length of jbyteArray. I'm writing code in eclipse in Windows 7.
Java code:
private native int Enroll( byte[] pSeed );
JNI code:
In JNI I have a struct that have two members unsigned long length and unsigned char data[1]
typedef struct blobData_s {
unsigned long length;
unsigned char data[1];
} blobData_t;
Now as my JNI function get as argument jbyteArray jpSeed i want to get the length of jpSeed and set it as length member of struct.
JNIEXPORT jint JNICALL Java_com_Test_Enroll( JNIEnv* env, jobject thiz, jbyteArray jpSeed ){
blobData_t* bd = malloc( sizeof(blobData_t) );
bd->length = **Question 1**
bd->data[1] = jbyteArray;
}
Question 1: How I can get the length of jpSeed in JNI ?
Question 2: Will this code works correct bd.data[1] = jbyteArray; ?
You can use GetArrayLength(JNIEnv* env, jbyteArray array) Read here.
Not sure what you want to do, I assume you want the content of jpSeed in bd.data[1].
Anyways, accessing the contents of a byte array, should be done with GetByteArrayElements(...).
Solution
Answer on Question 1. As jpSeed is jbyteArray it mean that you can get it's length by calling GetByteArrayElements( ... ) functions that declared in JNI ( you can read documentation here ) here the right code will be:
JNIEXPORT jint JNICALL Java_com_Test_Enroll( JNIEnv* env, jobject thiz, jbyteArray jpSeed ){
blobData_t* bd = malloc( sizeof(blobData_t) );
bd->length = (*env)->GetArrayLength( env, jpSeed );
.......
}
Answer on Question 2. This bd->data[1] = jbyteArray; code is not correct as it will not be compiled the right solution for this part is:
JNIEXPORT jint JNICALL Java_com_Test_Enroll( JNIEnv* env, jobject thiz, jbyteArray jpSeed ){
blobData_t* bd = malloc( sizeof(blobData_t) );
.......
jbyte* bytes = (*env)->GetByteArrayElements( env, jpSeed, 0 );
bd->data[1] = bytes[1];
}
And don't forgot to release.