如何从JNI C函数返回未知大小的数组到Android中的Java?

l7wslrjt  于 2023-08-03  发布在  Android
关注(0)|答案(2)|浏览(112)

我已经使用MATLAB Coder将MATLAB脚本转换为C。该脚本以uint 8格式的视频作为输入,使用自定义的YOLOv 4 DL网络执行对象检测任务以识别感兴趣的对象,并创建在对象边界内裁剪的新uint 8数组(视频)(参见下面的代码):

function [I] = Android3(Vrecorded)

%#codegen
% Loads and created pretrained YOLO v4 DL NW detector customized 
% to detect LED array.

detector = coder.loadDeepLearningNetwork("trainedDetector_23022023.mat");

% Read all frames as grayscale images
frames = reshape(Vrecorded(:,:,1,:), size(Vrecorded,1), size(Vrecorded,2), []);

% extrai o número de quadros do vídeo capturado (NoF - Number of Frames)
NoF = size(frames,3);

%Detect objects in an unknown image / video by using the pretrained YOLO v4
FrameForDetection = zeros(size(Vrecorded,1),size(Vrecorded,2),3);
FrameForDetection(:,:,:) = Vrecorded(:,:,:,1);

%object detector
[bboxes,~,~] = detect(detector,FrameForDetection);

% This cell contains the cropped frames containing RoI after YOLO v4 object
% detection algoritm process the frames
croppedFrameVectors = cell(1,1);

sizes = imcrop(frames(:,:,1),bboxes(1,:));

croppedFrames = zeros(size(sizes,1),size(sizes,2),NoF);

for n = 1:1
    croppedFrameVectors{n} = rand(0,0,0);
    for k = 1:NoF
        % crops the frames in each detected object according to bounding
        % boxes results captured on the first frame
        croppedFrames(:,:,k) = imcrop(frames(:,:,k),bboxes(1,:));
    end
    % Insert the cropped frames with bounding boxes within a cell to allow
    % multiple cropped objects within the same variable
    croppedFrameVectors{n} = croppedFrames;
    % Erase variable to crop another object due to bbox different
    % dimensions from one object to another (widht x height)
end

I = croppedFrameVectors{1};

字符串
其中Vrecorded是大小为240 x320 xF的视频矩阵,并且F取决于输入视频而变化。
在将其转换为C并尝试将其调整为JNI语法之后,这是我迄今为止所做的:

#include <jni.h>
/* Include files */
#include "main.h"
#include "Android3.h"
#include "Android3_emxAPI.h"
#include "Android3_terminate.h"
#include "Android3_types.h"
#include "rt_nonfinite.h"
#include <android/log.h>

JNIEXPORT jbyteArray JNICALL
Java_com_example_occ_MainActivity_teste(JNIEnv *env, jobject thiz, jbyteArray Vrecorded) {
    emxArray_real_T *b_I;
    //emxArray_uint8_T *Vrecorded;

    // Call the entry-point function 'Android3'
    emxInitArray_real_T(&b_I, 3);
    Android3(Vrecorded, b_I);

    // Clean up memory
    emxDestroyArray_uint8_T(Vrecorded);
    emxDestroyArray_real_T(b_I);
}


其中Android 3()是从MATLAB代码执行图像处理的函数。我的Java主应用程序是:

package com.example.occ;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

import com.example.occ.databinding.ActivityMainBinding;

import java.util.Arrays;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'occ' library on application startup.
    static {
        System.loadLibrary("occ");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        byte[] c = new MainActivity().teste(I);
        System.out.println("Value of matrix B is: " + Arrays.toString(c));

    }

    public native byte[] teste(byte[] I);
}


到目前为止,我有两个问题:
1.我将矩阵Vrecorded(uint 8格式)传递给JNI函数的逻辑是否正确(使用byteArray[])。该矩阵具有3个维度;
1.如果我在编译运行前不知道矩阵的大小,如何将矩阵'I'返回到Java,以便它可以显示在屏幕上**?**

qnzebej0

qnzebej01#

我看不出这一切是如何工作的。java byte[]是一个表示一维数组的对象。在JNI端,这是一个不透明的jbyteArray对象。我所说的“opaque”是指不能单独使用这个值,必须使用像GetByteArryaElements这样的JNI函数来获取指向实际字节数组(的副本)的指针。然而,Matlab代码将其视为四维数组。
例如,三维数组arr将是byte[][][]。在JNI侧,这是一个不透明的jobjectArray,其中层的类型依次为:

  • arr的类型为byte[][][],JNI类型为jobjectArray
  • arr[x]具有byte[][]类型和JNI类型jobjectArray
  • arr[x][y]具有byte[]类型和JNI类型jbyteArray

因此,在输入端有许多选项:

  • 你可以将视频作为一个平面byte[],在这种情况下,你需要使用Matlab emx API将其解压缩为一个四维向量。这需要提取每一层的维度(或者仅仅“知道”它们)。
  • 如果它真的是一个深度嵌套的数组,你必须逐层组装Matlab输入数组。这对垃圾收集器来说将是非常痛苦和沉重的。

在输出端,您遇到了相反的问题:你是把它表示成一个平面的一维数组,还是完全三维的(如果我没看错你的Matlab代码的话)数组,还是介于两者之间的?在我看来,最自然的方法是创建一个小的助手类Rect,用宽度和高度装饰一个byte[],然后返回一个Rect[][],其中每个输入帧有一个Rect[]
最后,如果性能在考虑范围内,您将需要查看直接字节缓冲区,它只是本地内存的块,您可以从Java传递到本地代码而无需复制。

rks48beu

rks48beu2#

我的意思是,你必须构建一个Java数组。我认为在将数组返回给java之前应该知道数组的大小。在JNI文档中查看如何创建Java基本类型之一的Java数组。
那么java是如何确定数组大小的呢?Java数组是对象,不像C中的数组那样简单(它只包含一个接一个的普通数组元素)数组有一个不可变的length元素和存储在其示例中的数组元素。只需深入研究JNDI文档,了解如何从C帮助程序库创建java数组。

相关问题