Ошибка при преобразовании байтового массива в вектор с плавающей точкой в ​​JNI - PullRequest
1 голос
/ 16 марта 2020

В своем нативном коде я генерирую вектор с плавающей точкой, и мне нужно отправить его в java часть путем преобразования его в байтовый массив (используя схему с прямым порядком байтов). Позже я пересылаю этот байтовый массив и мне нужно преобразовать его обратно в исходный вектор с плавающей точкой. Я не смог найти точный пример и написал ниже код, который будет принимать 4-байтовые значения за раз и будет преобразовывать его в float и добавлять его в конечный вектор float. Я не буду изменять какие-либо данные, просто выполню некоторые вычисления, поэтому нужно, чтобы они были быстрыми и, если возможно, избегали копирования памяти, где это возможно.

В настоящее время я предупреждаю, что «Использование знака без знака для значения со знаком типа jbyte ". Кто-нибудь может подсказать мне, как поступить?

JNIEXPORT jfloat JNICALL Java_com_xyzxyzxcyzxczxczc(JNIEnv *env, jclass type, jlong hEngineHandle, jbyteArray feature1){
try {
    PeopleCounting *obj = (PeopleCounting *) hEngineHandle;
    jbyte *f1 = (jbyte *)env->GetByteArrayElements(feature1, NULL);
    if(obj->faceRecognitionByteArraySize == 0){ // Setting it once for future use as it not going to change for my use case
        obj->faceRecognitionByteArraySize = env->GetArrayLength(feature1);
    }

    union UStuff
    {
        float   f;
        unsigned char   c[4];
    };


    UStuff f1bb;

    std::vector<float> f1vec;

    //Convert every 4 bytes to float using a union
    for (int i = 0; i < obj->faceRecognitionByteArraySize; i+=4){
        //Going backwards - due to endianness

        // Warning here. // Using unsigned char for signed value of type jbyte
        f1bb.c[3] = f1[i];
        f1bb.c[2] = f1[i+1];
        f1bb.c[1] = f1[i+2];
        f1bb.c[0] = f1[i+3];

        f1vec.push_back(f1bb.f);

    }

    // release it
    env->ReleaseByteArrayElements(feature1, f1, 0 );

 // Work with f1vec data
}

ОБНОВЛЕНИЯ: Как предполагает @Alex, и потребителем, и производителем байтового массива будет C ++, тогда нет необходимости в какой-либо последовательности. Таким образом, подход, который я собираюсь предпринять, выглядит следующим образом:

A) Java end Я инициализирую байт [] необходимой длины (4 * число значений с плавающей запятой). B) Передайте это как jbyteArray в функцию JNI.

Теперь, Как заполнить этот byteArray из конца C ++ ?

JNIEXPORT void JNICALL Java_com_xyz_FaceRecognizeGenerateFeatureData(JNIEnv *env, jclass type, jlong hEngineHandle, jlong addrAlignedFaceMat, jbyteArray featureData){
try {
    PeopleCounting *obj = (PeopleCounting *) hEngineHandle;
    Mat *pMat = (Mat *) addrAlignedFaceMat;

    vector<float> vecFloatFeatureData = obj->faceRecognizeGenerateFeatureData(*pMat);
    void* data = env->GetDirectBufferAddress(featureData); // How to fill the byteArray with values from vecFloatFeatureData? (If requied I can have a constant having the length of the array or number of actual float values i.e. len of array/4

C) Теперь, позже мне нужно снова использовать эти данные, передав эту от Java до C ++. Поэтому передача jbyteArray в функцию JNI

JNIEXPORT jfloat JNICALL Java_com_xyz_ConsumeData(JNIEnv *env, jclass type, jlong hEngineHandle, jbyteArray feature1){
 try {
  PeopleCounting *obj = (PeopleCounting *) hEngineHandle;

  void* data = env->GetDirectBufferAddress(featureData);
  float *floatBuffer = (float *) data1;
  vector<float> vecFloatFeature1Data(floatBuffer, floatBuffer + obj->_faceRecognitionByteArraySize); // _faceRecognitionByteArraySize contains the byte array size i.e. 4*no. of floats

Будет ли это работать?

Ответы [ 2 ]

1 голос
/ 16 марта 2020

К сожалению, обновленный код также не будет работать.

Но сначала давайте обратимся к ответу , который вы дали @ Botje .

java end просто сохраняет данные в базе данных и, возможно, отправляет эти данные дальше на серверы

Это два существенных ограничения. Во-первых, если ваш интерфейс базы данных принимает только байтовые массивы в качестве больших двоичных объектов, это помешает вам использовать DirectByteBuffer . Во-вторых, если массив может быть отправлен на другой компьютер, вы должны быть уверены, что значения с плавающей запятой хранятся там точно так же, как на компьютере, который создал массив байтов. В основном, это не будет проблемой , но вам лучше проверить перед развертыванием вашего распределенного решения.

Теперь вернемся к вашему JNI-коду. На самом деле нет необходимости предварительно выделять массив на стороне Java. Метод FaceRecognizeGenerateFeatureData() может просто возвращать новый байтовый массив, который он создает:

JNIEXPORT jbyteArray JNICALL Java_com_xyz_FaceRecognizeGenerateFeatureData(JNIEnv *env, jclass type,
  jlong hEngineHandle, jlong addrAlignedFaceMat) {

    PeopleCounting *obj = (PeopleCounting *) hEngineHandle;
    Mat *pMat = (Mat *) addrAlignedFaceMat;

    vector<float> vecFloatFeatureData = obj->faceRecognizeGenerateFeatureData(*pMat);
    jbyte* dataBytes = reinterpret_cast<jbyte*>(vecFloatFeatureData.data());
    size_t dataLen = vecFloatFeatureData.size()*sizeof(vecFloatFeatureData[0]);
    jbyteArray featureData = env->NewByteArray(dataLen);
    env->SetByteArrayRegion(featureData, 0, dataLen, dataBytes);
    return featureData;
}

Десериализация может использовать дополнительный GetByteArrayRegion () и избегать двойного копирования:

JNIEXPORT jfloat JNICALL Java_com_xyz_ConsumeData(JNIEnv *env, jclass type,
  jlong hEngineHandle, jbyteArray featureData) {

    PeopleCounting *obj = (PeopleCounting *) hEngineHandle;
    size_t dataLen = env->GetArrayLength(featureData);
    vector<float> vecFloatFeatureDataNew(dataLen/sizeof(float));
    jbyte* dataBytes = reinterpret_cast<jbyte*>(vecFloatFeatureDataNew.data());
    env->GetByteArrayRegion(featureData, 0, dataLen, dataBytes);
    …

Обратите внимание, что с этим архитектура, вы могли бы немного выиграть от использования DirectByteBuffer вместо байтового массива. Ваш движок PeopleCounting создает вектор, который нельзя сопоставить с внешним буфером; с другой стороны, вы можете обернуть буфер, чтобы заполнить вектор vecFloatFeatureDataNew без копирования. Я считаю, что эта оптимизация не будет существенной, но приведет к менее громоздкому коду.

0 голосов
/ 16 марта 2020

С https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html Я вижу, что jbyte - это 8-битный тип со знаком. Поэтому имеет смысл использовать подписанный символ в вашем союзе. Предупреждения должны быть удалены тогда. После исправления возникает проблема, если поплавки представлены на нативной стороне так же, как на стороне java.

...