首页 > 其他分享 >保存深度值——小端序,位数,Android

保存深度值——小端序,位数,Android

时间:2023-11-15 22:11:22浏览次数:38  
标签:short 0000 0010 int 小端序 width depth 位数 Android

保存深度值——小端序,位数,Android

accuireDepthImage

华为Mate Pro系列基本上前置摄像头都是有tof的,也就是能够得到场景的深度信息,在华为的AR engine里提供了一个方法可以读取场景的深度值。

image-20231115194245868

不过其官方文档里对这个方法的介绍很少,寥寥数语,前期也在这里踩了一些坑。Google的AR core对这个深度值做了详细的介绍:

image-20231115194742052 image-20231115195139089

得到的深度图是16位的,其中高3位是置信度,低13位是采样得到的深度值,并且排列顺序是小端序。第一张图说设高3位为0,但是我看了一下,其实是第二种情况。

知道这个信息之后,我们便可以使用如下代码保存深度值(二进制文件):

Image depthImage = arFrame.acquireDepthImage();
File f = new File(dir, numFrameStr + "_depth16.bin");

if(depthImage.getFormat() != ImageFormat.DEPTH16)
    throw new RuntimeException("Expected image format is DEPTH16, but is:"+depthImage.getFormat());

ByteBuffer buffer = depthImage.getPlanes()[0].getBuffer();
try {
    FileChannel fc = new FileOutputStream(f).getChannel();
    fc.write(buffer);
    fc.close();
} catch (IOException e) {
    e.printStackTrace();
    Log.i(TAG, "Error writing image depth16: " +f.getPath());
}

将二进制文件打开看一下:

image-20231115200524323

文件是以16进制保存的,所以每四个数字代表一个深度值。取0x0020,0x2242转化为十进制的深度值看一下。

16进制 0x0020 0x2242 0xba42
二进制 0000 0000 0010 0000 0010 0010 0100 0010 1011 1010 0100 0010
由于是小端序,将高位字节拿到前面 0010 0000 0000 0000 0100 0010 0010 0010 0100 0010 1011 1010
将高三位的置信度设为0 0000 0000 0000 0000 0000 0010 0010 0010 0000 0010 1011 1010
十进制 0mm 546mm 698mm

最后我们将这个二进制文件转化为格式为CU_16的灰度图看一下:

image-20231115203107035

效果还不错。

depthImag保存为图像

前面的保存的二进制文件是保存了置信度信息的,如果想要保存深度图就需要把高3位置信度信息设为0,才能保存。代码如下:

Image depthImg = arFrame.acquireDepthImage();
int width = depthImg.getWidth();
int height = depthImg.getHeight();

//ShortBuffer shortBuffer = depthImg.getPlanes()[0].getBuffer().asShortBuffer();
ShortBuffer shortBuffer = depthImg.getPlanes()[0].getBuffer().order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
Bitmap disBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
for (int i = 0; i < height; i++) {
    for (int j = 0; j < width; j++) {
        int index = (i * width + j);
        shortBuffer.position(index);
        short depthSample = shortBuffer.get();
        // 获取深度值后13位
        short depthRange = (short) (depthSample & 0x1FFF); 
        
        // 拆分short数据成两个8位数据
        int highByte = (depthRange & 0xFF00) >> 8;  // 获取高8位
        int lowByte = depthRange & 0x00FF;  // 获取低8位
        disBitmap.setPixel(j, i, Color.argb(255 , highByte, lowByte, 0));
    }
}
try {
        FileOutputStream out = new FileOutputStream(file);
        disBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
        out.flush();
        out.close();
        //GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
    } catch (Exception e) {
        e.printStackTrace();
    }

恢复深度值的时候就可以读出R通道的值,然后左移八位(乘256),再加上G通道的值。

注意这里有一个非常隐晦的BUG,就是如果使用的是第5行被注释的代码保存ShortBuffer,得到的将是大端序的值,现在一般的机器都是小端序列,如果使用大端序这将会导致一些错误

可以看到小端法使用的是ShortBuffer shortBuffer = depthImg.getPlanes()[0].getBuffer().order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();

其值是准确的,而使用ShortBuffer shortBuffer = depthImg.getPlanes()[0].getBuffer().asShortBuffer();得到了错误的结果。

image-20231115215740325

有些遗憾的是,我发现论坛里HMS 小助手提供的代码是有问题的:

image-20231115220111223

这里应该使用 ShortBuffer shortDepthBuffer = plane.getBuffer().order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();

论坛里这个朋友也遇到了类似的问题:

image-20231115220255520

最后建议在java中配置一下opencv直接保存为16位灰度图(推荐)

public static void writeDepth16binInPng16GrayscaleTum(String bin, int width, int height, String png) throws IOException {
    byte[] bytes = Files.readAllBytes(Paths.get(bin));
    ByteBuffer buffer = ByteBuffer.wrap(bytes);
    buffer = buffer.order(ByteOrder.LITTLE_ENDIAN);
    ShortBuffer sBuffer = buffer.asShortBuffer();
    short[] depthTum = new short[width*height];

    Mat mat = Mat.eye(height, width, CvType.CV_16UC1); //max is 65536 == 65meters / 16 bits = 2 bytes

    int i=0;
    for (int h = 0; h < height; h++) {
        for (int w = 0; w < width; w++) {
            short depthSample = sBuffer.get(); //depth16[y*width + x];
            short depthMm = (short) (depthSample & 0x1FFF);
//                short depthConfidence = (short) ((depthSample >> 13) & 0x7);
            depthTum[h*width+w] = (short)(depthMm * 5); //tum rgbd is 5==1mm / 5000==1m
        }
    }

    mat.put(0, 0, depthTum);
    Imgcodecs.imwrite(png, mat);
    //buffer.clear();
}

code

read_bin.cpp

//
// Created by xin on 23-11-15.
//
#include <iostream>
#include <opencv2/opencv.hpp>
#include <fstream>
#include <vector>


int main() {
    // 文件路径
    std::string file_path = "/home/xin/Code/CLionProjects/depth_image/img/another/0_depth16.bin";

    std::ifstream file(file_path, std::ios::binary);
    if (!file.is_open()) {
        std::cerr << "Failed to open the file." << std::endl;
        return 1;
    }

    // 读取文件内容到 vector
    std::vector<uint16_t> depth_values;
    uint16_t value;

    while (file.read(reinterpret_cast<char*>(&value), sizeof(value))) {
        value = value & uint16_t(0x1FFF); // 这个是真实的深度值
        value *= 5; // 为了更好的可视化,使得灰度图更亮一些
        depth_values.push_back(value);
    }

    file.close();

    cv::Mat depth_image(180, 240, CV_16UC1);
    // 将 depth_values 复制到 depth_image 中
    std::memcpy(depth_image.data, depth_values.data(), depth_values.size() * sizeof(uint16_t));

    cv::imwrite("DepthImage.png", depth_image);
    cv::waitKey(0);

    return 0;
}

标签:short,0000,0010,int,小端序,width,depth,位数,Android
From: https://www.cnblogs.com/curiositywang/p/17834954.html

相关文章

  • 互联网增速按下暂停键,裁员降本增效,未来Android程序员还有哪些机会?
    前言据说现在很多程序员都找不到工作了,原因相信大家都知道,就是据说互联网行业现在发展停滞了,很多公司为了降本增效,所以就拿高薪的程序员开刀,导致一下子涌现出很多失业的程序员,这些程序员一时之间无法被普通公司消化掉,从而导致了很多程序员找不到工作。但好在经过一段时间的消化,似乎......
  • 代码安全之代码混淆及加固(Android)
    ​代码安全之代码混淆及加固(Android)......
  • android开发Flutter Text自动换行实现
    flutter自动换行处理flutter自动换行有几种场景:column中,row中在Column中的Text不用任何处理,能够自动换行。在Row中的Text需要用Expanded包裹。因为文字是水平方向排放的,和Row的计算规则冲突,需要使用Expanded包裹,表示剩下的给Text,Text自然知道自身宽度,才能实现自动换行。Row(cro......
  • 关于Android Notification 点击后不跳转Activity的问题
    折腾了很久点击Notification不跳转除了Android26以上 设置channelid还有个比较细的点,没有注意if(Build.VERSION.SDK_INT>=26){channel=newNotificationChannel("my_channel_02","MyMsg",Notificati......
  • Android性能优化:一份详细的布局优化实战指南,太特么重要了
    前言对于Android开发者来说,仅掌握基本的应用开发技能是远远不够的。无论是在工作中还是面试过程中,掌握大量的性能优化知识对于提升应用体验至关重要。Android性能优化主要涵盖以下方面:启动优化、渲染优化、内存优化、网络优化、卡顿检测与优化、耗电优化、安装包体积优化以及安全问......
  • Android并发编程高级面试题汇总(含详细解析 四)
    Android并发编程高级面试题汇总最全最细面试题讲解持续更新中......
  • NDK生成so文件,进行Android端串口通信
    开篇说明1、NDK构建工具的安装2、ndk-build所需构建文件Android.mk、Application.mk(官方链接:ndk-build脚本 | AndroidNDK | AndroidDevelopers(google.cn))3、java定义动态调用接口、生成头文件4、C++串口通信5、Java提供相关接口,生成jar包......
  • Android app的暗黑模式适配实现
    原文地址:Androidapp的暗黑模式适配实现-Stars-One的杂货小窝很久之前放在草稿箱的一篇简单笔记,是之前蓝奏云批量下载工具Android版本实现暗黑主题的适配记录本文所说的这里的暗黑主题,应该只支持Android10系统,不过我手头的Flyme系统(Android9)上测试也有效果,其他低版本......
  • Android——自定义组件
    自定义组件共分为:自定义组合控件,自定义View,自定义ViewGroup自定义ViewGroup一共七步:1.继承ViewGroup,将统一调用第三的构造方法。重写onMeasure(),onLayout()方法,创建数据集合对象,创建孩子行列集合对象。编写常量横向外边距,纵向外边距,组件属性2.创建孩子,设置孩子,添加......
  • Android安卓 match_parent与match_parent区别
    Android安卓match_parent与match_parent区别 在Android中,match_parent和fill_parent是布局参数(layoutparameters)中的两个常用属性,它们在XML中用于定义一个视图(View)的尺寸。在最新的Android版本中,fill_parent已经被废弃,而match_parent用于替代。**match_parent:**这个属......