当前位置: 首页 > ds >正文

【Depth与RGB对齐算法(D2C)】

本文介绍一下RGBD相机中深度图像与RGB图像的对齐算法。大部分RGBD相机是自带RGB与深度图像对齐功能的,开发者可以很方便地调用。但有的开发者使用的RGBD相机是自己用独立的RGB相机与深度相机组建而成的,这时候通常要自己实现RGB与深度对齐的算法。本篇博客的算法主要参考Realsense SDK中的代码。

D2C算法的核心,是解决深度图像从一个相机的图像坐标系到另外一个相机图像坐标系的映射问题,特别是在两个相机分辨率、焦距不同的情况下,如何又快又好地实现深度图像的映射。

实现D2C对齐的前提是需要知道两个相机的内参和相对外参。两个相机内参和相对外参可以通过标定获得。

一个简单直接的想法是,首先根据深度相机的内参,将深度图像转化为深度相机坐标系中的点云,然后将点云根据深度相机和RGB相机的相对外参,转换到RGB相机坐标系,最后根据RGB相机的内参,投影转换后的点云到RGB相机的图像坐标系,把点云Z值赋给最近邻的整像素。

用数学公式描述这一过程。首先,根据深度相机内参,将深度Depth\text{Depth}Depth转换为点云PDepthP_{\text{Depth}}PDepth
PDepth=[(u−cx)/fx∗Depth(u,v)(v−cy)/fy∗Depth(u,v)Depth(u,v)]P_{\text{Depth}}=\begin{bmatrix}(u-c_x)/f_x*\text{Depth}(u,v)\\(v-c_y)/f_y*\text{Depth}(u,v)\\\text{Depth}(u,v)\end{bmatrix}PDepth=(ucx)/fxDepth(u,v)(vcy)/fyDepth(u,v)Depth(u,v)其中,(u,v)(u,v)(u,v)为深度相机的图像坐标,fx,fy,cx,cyf_x,f_y,c_x,c_yfx,fy,cx,cy为深度相机内参的焦距和主点,Depth\text{Depth}Depth为深度值。其次,根据相对外参,将深度相机坐标系中的点云PDepthP_{\text{Depth}}PDepth转换为RGB相机坐标系中的点云PRGBP_{\text{RGB}}PRGB
PRGB=[XRGBYRGBZRGB]=R⋅PDepth+TP_{\text{RGB}} = \begin{bmatrix} X_{\text{RGB}}\\ Y_{\text{RGB}}\\ Z_{\text{RGB}} \end{bmatrix}=R \cdot P_{\text{Depth}}+TPRGB=XRGBYRGBZRGB=RPDepth+T其中,R,TR,TR,T为相对外参的旋转和平移矩阵。最后,将转换的点云PRGBP_{\text{RGB}}PRGB投影到RGB相机的图像坐标系,将转换点云的Z值赋值给最近邻的图像坐标
u′=fx′XRGB/ZRGB+cx′v′=fy′YRGB/ZRGB+cy′DepthAligned(round(u′),round(v′))=ZRGBu' =f_x'X_{\text{RGB}}/Z_{\text{RGB}}+c_x'\\ v' =f_y'Y_{\text{RGB}}/Z_{\text{RGB}}+c_y'\\ \text{Depth}_\text{Aligned}(\text{round}(u'),\text{round}(v')) = Z_{\text{RGB}}u=fxXRGB/ZRGB+cxv=fyYRGB/ZRGB+cyDepthAligned(round(u),round(v))=ZRGB其中,(u′,v′)(u',v')(u,v)为RGB相机的图像坐标,fx′,fy′,cx′,cy′f_x',f_y',c_x',c_y'fx,fy,cx,cy为RGB相机内参的焦距和主点,DepthAligned\text{Depth}_\text{Aligned}DepthAligned为对齐深度。

然而,由于RGB相机通常比深度相机有更高的图像分辨率,此方法得到的对齐深度图会存在很多空缺的像素。

本文介绍的算法是此想法的改进。核心思想是把深度图中的像素看成一个1x1大小矩形,而非一个点。对于深度图中所有的像素,计算以其为中心的矩形左上点和右下点在RGB相机图像坐标系中的映射坐标,将映射坐标所构成矩形区域中的所有像素,赋值为矩形左上点和右下点转换后点云Z值的均值。当像素已被赋值时,则取已有值和新赋值的最小值,以正确处理坐标转换带来的遮挡问题。

算法代码如下:

// d2c.h
#pragma once
#include <cstdint>struct Intrinsics { //内参int width, height; //图像长宽float fx, fy; //焦距float cx, cy; //主点
};struct Extrinsics { //外参float R[9]; //旋转矩阵float T[3]; //平移矩阵
};void DepthToColor(const uint16_t* depth, uint16_t* aligned_depth, const Intrinsics& depth_intrinsics, const Intrinsics& color_intrinsics, const Extrinsics& extrinsics);
//d2c.cpp
#include "d2c.h"
#include <algorithm>//将深度值根据深度相机内参转换为点云
static inline void DeprojectPointToPoint(float point[3], const Intrinsics& intrinsics, const float pixel[2], float depth) {point[0] = (pixel[0] - intrinsics.cx) * depth / intrinsics.fx;point[1] = (pixel[1] - intrinsics.cy) * depth / intrinsics.fy;point[2] = depth;
}
//根据外参,将深度相机坐标系中的点云转换到RGB相机坐标系
static inline void TransformPointToPoint(float to[3], const Extrinsics& extrinsics, const float from[3]) {to[0] = extrinsics.R[0] * from[0] + extrinsics.R[1] * from[1] + extrinsics.R[2] * from[2] + extrinsics.T[0];to[1] = extrinsics.R[3] * from[0] + extrinsics.R[4] * from[1] + extrinsics.R[5] * from[2] + extrinsics.T[1];to[2] = extrinsics.R[6] * from[0] + extrinsics.R[7] * from[1] + extrinsics.R[8] * from[2] + extrinsics.T[2];
}
//将RGB相机坐标系的点云投影为图像坐标
static inline void ProjectPointToPixel(float pixel[2], const Intrinsics& intrinsics, const float point[3]) {pixel[0] = (point[0] * intrinsics.fx / point[2]) + intrinsics.cx;pixel[1] = (point[1] * intrinsics.fy / point[2]) + intrinsics.cy;
}
//深度RGB对齐
void DepthToColor(const uint16_t* depth, uint16_t* aligned_depth, const Intrinsics& depth_intrinsics, const Intrinsics& color_intrinsics, const Extrinsics& extrinsics) {for (int depth_y = 0; depth_y < depth_intrinsics.height; ++depth_y) {int depth_pixel_index = depth_y * depth_intrinsics.width;for (int depth_x = 0; depth_x < depth_intrinsics.width; ++depth_x, ++depth_pixel_index) {//跳过深度值为0的像素if (uint16_t z = depth[depth_pixel_index]) {//将左上角点{ depth_x - 0.5f, depth_y - 0.5f }映射到RGB图像坐标float depth_pixel[2] = { depth_x - 0.5f, depth_y - 0.5f }, depth_point[3], other_point0[3], other_point1[3], other_pixel[2];DeprojectPointToPoint(depth_point, depth_intrinsics, depth_pixel, z);TransformPointToPoint(other_point0, extrinsics, depth_point);ProjectPointToPixel(other_pixel, color_intrinsics, other_point0);const int other_x0 = static_cast<int>(other_pixel[0] + 0.5f);//快速四舍五入const int other_y0 = static_cast<int>(other_pixel[1] + 0.5f);//将右下角点{ depth_x + 0.5f, depth_y + 0.5f }映射到RGB图像坐标depth_pixel[0] = depth_x + 0.5f; depth_pixel[1] = depth_y + 0.5f;DeprojectPointToPoint(depth_point, depth_intrinsics, depth_pixel, z);TransformPointToPoint(other_point1, extrinsics, depth_point);ProjectPointToPixel(other_pixel, color_intrinsics, other_point1);const int other_x1 = static_cast<int>(other_pixel[0] + 0.5f);const int other_y1 = static_cast<int>(other_pixel[1] + 0.5f);//映射后的矩形超出RGB图像范围则跳过if (other_x0 < 0 || other_y0 < 0 || other_x1 >= color_intrinsics.width || other_y1 >= color_intrinsics.height)continue;//计算转换点云的Z值均值const uint16_t new_z = (other_point0[2] + other_point1[2]) * 0.5;//对映射后矩阵中的像素进行赋值for (int y = other_y0; y <= other_y1; ++y) {for (int x = other_x0; x <= other_x1; ++x) {const int index = y * color_intrinsics.width + x;const uint16_t aligned_z = aligned_depth[index];aligned_depth[index] = aligned_z ? std::min(aligned_z, new_z) : new_z;//如果已有值,则取两者最小值}}}}}
}

算法运行结果:
在这里插入图片描述

http://www.xdnf.cn/news/19180.html

相关文章:

  • 首次创建Django项目初始化
  • 沙箱操作工具
  • 计算机组成原理3-3-1:无符号数乘法运算的硬件逻辑实现
  • 学习做动画6.瞄准偏移
  • JavaScript初识:给小白的第一堂编程课
  • 大数据毕业设计选题推荐-基于大数据的痴呆症预测数据可视化分析系统-Spark-Hadoop-Bigdata
  • openEuler常用操作指令
  • AT_abc407_e [ABC407E] Most Valuable Parentheses
  • 客户案例 | 国际知名内衣品牌x甄知科技,领航IT服务新征程
  • 算法题打卡力扣第15题:三数之和(mid)
  • 用 PyTorch 搭建 CNN 实现 MNIST 手写数字识别
  • QT:【第一个QT程序】【信号和槽】
  • 2025通用证书研究:方法论、岗位映射与四证对比
  • 腾讯云重保流程详解:从预案到复盘的全周期安全防护
  • 【练习九】Java实现加油站支付小程序:存款与消费
  • 大数据原生集群 (Hadoop3.X为核心) 本地测试环境搭建三
  • Unity游戏打包——Android打包环境(Mac下)
  • 0828 C++基础
  • PhotoshopImageGenerator:基于Photoshop的自动化图像数据集生成工具
  • BGP路由协议(一):基本概念
  • 每日五个pyecharts可视化图表日历图和箱线图:从入门到精通
  • 基于matplotlib库的python可视化:以北京市各区降雨量为例
  • 【开题答辩全过程】以 基于Spring Boot农产品运输服务平台为例,包含答辩的问题和答案
  • [论文阅读] 人工智能 + 软件工程 | 告别“隐藏陷阱”:领域预训练模型SmartBERT如何赋能智能合约安全
  • LoRA modules_to_save解析及卸载适配器(62)
  • 怎样将Word转成高质量的DITA
  • 构建AI智能体:十六、构建本地化AI应用:基于ModelScope与向量数据库的文本向量化
  • RGW层Op的组织
  • 【大前端】React Native(RN)跨端的原理
  • Day16_【机器学习—模型拟合问题】