在 JNI 中将字节数组转换为浮点向量时遇到错误

Facing error in converting byte array to vector of float in JNI

在我的本机代码中,我生成了一个浮点向量,需要通过将其转换为字节数组(使用小端模式)将其发送到 java 部分。后来我重新发送这个字节数组,需要将它转换回原来的浮点向量。我找不到确切的示例并编写了下面的代码,一次将获取 4 个字节值并将其转换为浮点数并将其添加到浮点数的最终向量中。我不会修改任何数据,只是执行一些计算,因此需要速度快,并尽可能避免内存复制。

目前它警告我 "Using unsigned char for signed value of type 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 结束 我初始化一个所需长度的 byte[](4 * 浮点数) B) 将其作为 jbyteArray 传递给 JNI 函数

现在,如何从 C++ 端填充这个 byteArray

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

这行得通吗?

来自https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html 我看到jbyte 是一个带符号的8 位类型。所以在你的联盟中使用 signed char 是有意义的。警告应该消失了。修复后,会出现一个问题,即浮点数在本机端的表示方式与在 java 端的表示方式相同。

不幸的是,更新后的代码也不起作用。

但首先,让我们解决一下您给 @Botje.

java end just stores the data in the database and maybe send this data further to servers

这是两个重要的限制。首先,如果您的数据库接口仅将字节数组作为 blob,这将阻止您使用 DirectByteBuffer。其次,如果数组可能被发送到不同的机器,您必须确保浮点值存储在那里与生成字节数组的机器完全一样。大多数情况下,这 won't be a problem,但您最好在部署分布式解决方案之前进行检查。

现在,回到您的 JNI 代码。实际上不需要在 Java 端预分配数组。 FaceRecognizeGenerateFeatureData() 方法可以简单地 return 它创建的新字节数组:

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 引擎生成一个无法映射到外部缓冲区的向量;另一方面,您 can 包装缓冲区以填充 vecFloatFeatureDataNew 向量而不进行复制。我相信这种优化不会很重要,但会减少繁琐的代码。