Android JNI:将cv :: Mat转换为jbyteArray

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android JNI:将cv :: Mat转换为jbyteArray相关的知识,希望对你有一定的参考价值。

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

现在我想将这个C / C ++源代码移植到我的android应用程序来转换设备Camera2的框架。

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

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

  1. 将byte []框架从Java转换为JNI jarrayByte
  2. 将jarrayByte转换为jyte *
  3. 从jbyte *创建新的cv :: Mat原创*
  4. 将输入cv :: Mat orignal转换为使用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);
答案

让我们假设您要将本机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;
}

以上是关于Android JNI:将cv :: Mat转换为jbyteArray的主要内容,如果未能解决你的问题,请参考以下文章

安卓Android Studio JNI开发问题澄清与汇总

将 Magick::Image 转换为 cv::Mat

将一行 cv::Mat 转换为 std::vector

请教,jni调用,类型转换.用opencv进行静态人脸检测

将 cv::Mat 转换为 vector<int>

使用原始数据将 QImage 转换为 cv::Mat