【问题标题】:Android JNI: convert cv::Mat to jbyteArrayAndroid JNI:将 cv::Mat 转换为 jbyteArray
【发布时间】:2018-09-08 04:46:33
【问题描述】:

我有 C/C++ 源代码(在 Windows 上运行良好)将输入帧转换为新帧。

现在我想将此 C/C++ 源代码移植到我的 Android 应用程序以转换设备 Camera2 的帧。

我将从 Java 应用程序中获取 Camera2 的连续字节 [] 帧,然后将其输入到 JNI 以接收转换后的字节 new_frame。

所以我在 JNI 中的流程必须是:

  1. 将 byte[] 帧从 Java 转换为 JNI jarrayByte

  2. 将 jarrayByte 转换为 jyte*

  3. 从 jbyte* 创建新的 cv::Mat 原件

  4. 将输入 cv::Mat 原始转换为使用 C/C++ 函数转换的新 cv::Mat

  5. 现在,我需要将 cv::Mat 转换为 jByteArray 以返回 Java 函数 => 我卡在这一步

所以我的问题是如何将 cv::mat 转换回 jbyteArray 以返回应用程序的 Java 层?

extern "C" JNIEXPORT jbyteArray
JNICALL
Java_com_xyz_NativeUtil_convertFrame(JNIEnv *env, jobject obj,
                                                                  jbyteArray yuvFrame) {
// convert jByteArray to jbyte*
jbyte* yuvByte = env->GetByteArrayElements(yuvFrame, 0);
// convert jbyte* to cv::Mat
cv::Mat frame_original_yuv = cv::Mat(height + height/2, width, CV_8UC1, yuvByte);
// convert YUV cv::Mat to RGBA cv::Mat
cv::Mat frame_original_rgba = cv::Mat(height, width, CV_8UC4);
cv::cvtColor(frame_original_yuv, frame_original_rgba, CV_YUV2RGBA_NV21);
env->ReleaseByteArrayElements(yuvFrame, yuvByte, 0);
// convert frame
cv::Mat frame_converted = convert_frame(frame_original_rgba);
frame_original_yuv.release();

if (frame_converted .empty()) {
    frame_converted = frame_original_rgba;
}
frame_original_rgba.release();
// convert cv::Mat to jbyteArray
jbyteArray result = ???

return result;

}

我的 Java 代码:

mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = reader.acquireLatestImage();
                if (image == null)
                    return;

                Image.Plane Y = image.getPlanes()[0];
                Image.Plane U = image.getPlanes()[1];
                Image.Plane V = image.getPlanes()[2];

                int Yb = Y.getBuffer().remaining();
                int Ub = U.getBuffer().remaining();
                int Vb = V.getBuffer().remaining();

                byte[] yuvFrame = new byte[Yb + Ub + Vb];

                Y.getBuffer().get(yuvFrame, 0, Yb);
                U.getBuffer().get(yuvFrame, Yb, Ub);
                V.getBuffer().get(yuvFrame, Yb + Ub, Vb);

                image.close();
            }
        }, mBackgroundHandler);

【问题讨论】:

    标签: android opencv java-native-interface


    【解决方案1】:

    假设您想将原生 cv::Mat 对象传递给 Java 并将其转换为 Bitmap

    在这种情况下,最好将其转换为 jintArray,因此我将使用它编写一个示例,但您可以轻松修改该代码以改用 jbyteArray

    请注意,在该代码中,我假设您的 cv::Mat 有 4 个通道 (bgra),请相应地进行更改。如果您的垫子是灰色 - 将 r, g, b 设置为相同的灰色,将 a 设置为 255。如果您的垫子是 bgr - 将 a 设置为 255。

    jintArray matToBitmapArray(JNIEnv *env, const cv::Mat &image) {
        jintArray resultImage = env->NewIntArray(image.total());
        jint *_data = new jint[image.total()];
        for (int i = 0; i < image.total(); i++) {
            char r = image.data[4 * i + 2];
            char g = image.data[4 * i + 1];
            char b = image.data[4 * i + 0];
            char a = image.data[4 * i + 3];
            _data[i] = (((jint) a << 24) & 0xFF000000) + (((jint) r << 16) & 0x00FF0000) +
                       (((jint) g << 8) & 0x0000FF00) + ((jint) b & 0x000000FF);
        }
        env->SetIntArrayRegion(resultImage, 0, image.total(), _data);
        delete[]_data;
    
        return resultImage;
    }
    

    BGR案例:

    jintArray matToBitmapArray(JNIEnv *env, const cv::Mat &image) {
        jintArray resultImage = env->NewIntArray(image.total());
        jint *_data = new jint[image.total()];
        for (int i = 0; i < image.total(); i++) {
            char r = image.data[3 * i + 2];
            char g = image.data[3 * i + 1];
            char b = image.data[3 * i + 0];
            char a = (char)255;
            _data[i] = (((jint) a << 24) & 0xFF000000) + (((jint) r << 16) & 0x00FF0000) +
                       (((jint) g << 8) & 0x0000FF00) + ((jint) b & 0x000000FF);
        }
        env->SetIntArrayRegion(resultImage, 0, image.total(), _data);
        delete[]_data;
    
        return resultImage;
    }
    

    灰色情况:

    jintArray matToBitmapArray(JNIEnv *env, const cv::Mat &image) {
        jintArray resultImage = env->NewIntArray(image.total());
        jint *_data = new jint[image.total()];
        for (int i = 0; i < image.total(); i++) {
            // Note that you can use better gray->rgba conversion
            char r = image.data[i];
            char g = image.data[i];
            char b = image.data[i];
            char a = (char)255;
            _data[i] = (((jint) a << 24) & 0xFF000000) + (((jint) r << 16) & 0x00FF0000) +
                       (((jint) g << 8) & 0x0000FF00) + ((jint) b & 0x000000FF);
        }
        env->SetIntArrayRegion(resultImage, 0, image.total(), _data);
        delete[]_data;
    
        return resultImage;
    }
    

    在java端

    Bitmap bitmap = Bitmap.createBitmap(ourNativeArray, width, height, Bitmap.Config.ARGB_8888);
    

    编辑

    考虑到您有 BGRA 4 通道图像,您可以将其转换为 jbyteArray

    jbyteArray matToByteArray(JNIEnv *env, const cv::Mat &image) {
        jbyteArray resultImage = env->NewByteArray(image.total() * 4);
        jbyte *_data = new jbyte[image.total() * 4];
        for (int i = 0; i < image.total() * 4; i++) {
            _data[i] = image.data[i];
        }
        env->SetByteArrayRegion(resultImage, 0, image.total() * 4, _data);
        delete[]_data;
    
        return resultImage;
    }
    

    【讨论】:

    • 非常感谢您的回答。我对 JNI 转换没有太多经验,那么“但是您可以轻松地修改该代码以改用 jbyteArray”是什么意思?只有将“jintArray”替换为“jbyteArray”?
    • 问题是 - 我不知道你为什么需要字节数组,所以这个问题对我来说有点不清楚。这实际上取决于您希望在输出数组中看到什么(您可能希望其中包含 jpeg 或 png 数据 - 我怎么知道)
    • 感谢 Dmitrii,我更新了我的问题以获取完整信息。你能给我建议吗?
    • 更新了答案
    • 谢谢 Dmitrii,您绝对是该领域的专家。我将您的代码合并到我的程序中,构建 NDK 然后运行应用程序。不幸的是,我的应用程序因错误消息“A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x9c840000 in tid 1619”而崩溃。我认为这是因为我的源代码在某处不正确。你能看看我上面的 JNI 源代码并发表你的评论吗?我需要释放完成的对象(错误,frame_original_)还是我的 cv::Mat 从 YUV 转换为 RGBA 不正确?我真的很赞同你的支持。
    猜你喜欢
    • 1970-01-01
    • 2013-11-11
    • 2012-12-29
    • 2014-05-07
    • 2011-06-07
    • 2016-04-13
    • 2011-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多