在 Android Java 中将 16 位深度帧从 Intel Realsense D455 转换为 OpenCV Mat

Converting 16 bit depth frame from Intel Realsense D455 to OpenCV Mat in Android Java

我正在尝试将从英特尔实感 D455 相机获得的 DepthFrame 对象转换为 Java 中的 OpenCV Mat 对象。我可以使用 DepthFrame.getDistance(x,y) 获取像素的目标深度,但我试图获取整个矩阵,以便我可以获得以米为单位的距离值,类似于 their Github repo 中的示例代码,这是在 C++ 中。

我可以使用以下函数将从相机流(VideoFrame 或彩色 DepthFrame)获得的任何彩色图像转换为 Mat,因为它们每像素 8 位:

    public static Mat VideoFrame2Mat(final VideoFrame frame) {
        Mat frameMat = new Mat(frame.getHeight(), frame.getWidth(), CV_8UC3);
        final int bufferSize = (int)(frameMat.total() * frameMat.elemSize());
        byte[] dataBuffer = new byte[bufferSize];
        frame.getData(dataBuffer);
        ByteBuffer.wrap(dataBuffer).order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().get(dataBuffer);
        frameMat.put(0,0, dataBuffer);
        return frameMat;
    }

然而,未着色的 DepthFrame 值是每个像素 16 位,当 CV_8UC1 替换为 CV_16UC1 时,上述代码会出错。出现错误是因为在OpenCV函数Mat.put(row, col, data[])的Java包装器中,有一个类型检查只允许处理8位Mat

    // javadoc:Mat::put(row,col,data)
    public int put(int row, int col, byte[] data) {
        int t = type();
        if (data == null || data.length % CvType.channels(t) != 0)
            throw new UnsupportedOperationException(
                    "Provided data element number (" +
                            (data == null ? 0 : data.length) +
                            ") should be multiple of the Mat channels count (" +
                            CvType.channels(t) + ")");
        if (CvType.depth(t) == CvType.CV_8U || CvType.depth(t) == CvType.CV_8S) {
            return nPutB(nativeObj, row, col, data.length, data);
        }
        throw new UnsupportedOperationException("Mat data type is not compatible: " + t);
    }

因此我尝试使用接受数组的Mat的构造函数并编写了以下方法:

    public static Mat DepthFrame2Mat(final DepthFrame frame) {
        byte[] dataBuffer = new byte[frame.getDataSize()];
        frame.getData(dataBuffer);
        ByteBuffer buffer = ByteBuffer.wrap(dataBuffer).order(ByteOrder.LITTLE_ENDIAN).asReadOnlyBuffer().get(dataBuffer);

        Log.d(TAG, String.format("DepthFrame2Mat: w: %s h: %s capacity: %s remaining %s framedatasize: %s databufferlen: %s framedepth: %s type: %s " ,
                frame.getWidth(), frame.getHeight(), buffer.capacity(), buffer.remaining(), frame.getDataSize(), dataBuffer.length, frame.getBitsPerPixel(), frame.getProfile().getFormat()));
        
        return new Mat(frame.getHeight(), frame.getWidth(), CV_16UC1, buffer);
    }

但现在我一直收到错误 E/cv::error(): OpenCV(4.5.5) Error: Assertion failed (total() == 0 || data != NULL) in Mat, file /build/master_pack-android/opencv/modules/core/src/matrix.cpp, line 428

使用函数中看到的 log 命令,我正在检查 data 是否为空或 null,但事实并非如此。此外,DepthFrame 位深度和类型似乎也是正确的:

D/CvHelpers: DepthFrame2Mat: w: 640 h: 480 capacity: 614400 remaining 0 framedatasize: 614400 databufferlen: 614400 framedepth: 16 type: Z16

出现此错误的原因可能是什么?有没有更好的方法来处理这种转换?

注:SO问题such as this和网上的例子我都查过了,不过都是C++的。我不想仅仅为了创建 Mat.

添加 JNI 支持

即使不是直接的 OpenCV API 解决方案,在 Java 中将字节数组转换为短数组似乎也可行:

    public static Mat DepthFrame2Mat(final DepthFrame frame) {
        Mat frameMat = new Mat(frame.getHeight(), frame.getWidth(), CV_16UC1);
        final int bufferSize = (int)(frameMat.total() * frameMat.elemSize());
        byte[] dataBuffer = new byte[bufferSize];
        short[] s = new short[dataBuffer.length / 2];
        frame.getData(dataBuffer);
        ByteBuffer.wrap(dataBuffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(s);
        frameMat.put(0,0, s);
        return frameMat;
    }