我们知道华为AREngine可以根据接口acquireDepthImage()获取得到深度图,获取得到的深度图是DEPTH16格式的,AREngine中又没有给出例子怎样根据深度图求得深度信息,吐槽一下文档不够详细。
Image | 在camera状态为tracking状态下,获取当前帧对应的深度图像,返回图像格式为DEPTH16,只能在下一次ARSession.update()前使用。 |
DEPTH16格式是Android dense depth image format.
每个像素是16位,代表深度摄像机或类似传感器的深度范围测量。16位样本由置信度值和实际测距测量组成。
置信度值是对样本正确性的估计。它编码在样本的3个最高有效位中,值0表示100%置信度,值1表示0%置信度,值2表示1/7,值3表示2/7,依此类推。
当由照相机拍摄时,射程的单位是毫米。
例如,以下示例从DEPTH16格式图像的第一个像素中提取范围和置信度,并将置信度转换为介于0和1.f之间的浮点值,其中1.f表示最大置信度:
ShortBuffer shortDepthBuffer = img.getPlanes()[0].getBuffer().asShortBuffer();
short depthSample = shortDepthBuffer.get()
short depthRange = (short) (depthSample & 0x1FFF);
short depthConfidence = (short) ((depthSample >> 13) & 0x7);
float depthPercentage = depthConfidence == 0 ? 1.f : (depthConfidence - 1) / 7.f;
好了,我们已经知道DEPTH16格式的含义,那我们怎样在opengl中以纹理的方式在片元着色器里面求得相机坐标下的深度呢?
1、创建纹理ID,并且纹理可以传入无符号整型,这一定要注意GL_TEXTURE_MIN_FILTER与GL_TEXTURE_MAG_FILTER一定不要使用GL_NEAREST,否则整型数字传入不到纹理。
public void createOnGlThread() {
int[] textureId = new int[1];
glGenTextures(1, textureId, 0);
depthTextureId = textureId[0];
glBindTexture(GL_TEXTURE_2D, depthTextureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
2、每次更新纹理,因为我们获取的是16位的,所以glTexImage2D第三个参数使用GL_R16UI, 第7个参数使用GL_RED_INTEGER,第8个参数使用GL_UNSIGNED_SHORT,参数不要使用错,否则很容易纹理写入不了数据。
public void update(final ARFrame frame) {
try {
Image depthImage = frame.acquireDepthImage();
depthTextureWidth = depthImage.getWidth();
depthTextureHeight = depthImage.getHeight();
// String msg = String.format("width: %s, height: %s", depthTextureWidth, depthTextureHeight);
// Log.d("render", msg);
glBindTexture(GL_TEXTURE_2D, depthTextureId);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_R16UI,
depthTextureWidth,
depthTextureHeight,
0,
GL_RED_INTEGER,
GL_UNSIGNED_SHORT,
depthImage.getPlanes()[0].getBuffer().asShortBuffer());
depthImage.close();
} catch (Exception e) {
Log.d(TAG, "XXXXXXXXXXXX" + e);
// This normally means that depth data is not available yet.
}
}
3、数据传入到纹理中了,看看shader中怎样处理纹理,获取相机坐标下的深度,单位是毫米。这一定注意,我们使用usampler2D类型接收纹理。
precision highp usampler2D;
layout (binding=2) uniform usampler2D bgDepthTexture;
float GetBgDepthMillimeters(in vec2 depth_uv) {
uvec3 rawDepth = texture(bgDepthTexture, depth_uv).xyz;
int depthRange = int(rawDepth.r) & 0x1FFF;
int depthConfidence = ((int(rawDepth.r) >> 13) & 0x7);
float depthPercentage = depthConfidence == 0 ? 1.0 : float(depthConfidence - 1) / 7.0;
float depth = depthPercentage > 0.1 ? float(depthRange) : FAR * 1000.0;
depth = max(depth, NEAR * 1000.0);
depth = min(depth, FAR * 1000.0);
return depth;
}
这样就获得了相机坐标下的深度信息。
参考文档:
https://developer.android.com/reference/android/graphics/ImageFormat#DEPTH16
https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml