(以下主要以Open3D庫為例,解析TSDF演算法的具體流程)
前言
Open3D是Inter Visual Computing Lab的研究科學家Qianyi Zhou(前)和Jaesik Park共同完成的開源庫,支持處理3D數據的軟體快速開發。
http://www.open3d.org/docs/introduction.html
https://github.com/IntelVCL/Open3D
TSDF(truncated signed distance function)是一種利用結構化點雲數據並以參數表達表面的表面重建演算法。核心是將點雲數據映射到一個預先定義的三維立體空間中,並用截斷符號距離函數表示真實場景表面附近的區域,建立表面模型[1]。
TSDF演算法流程
先大致介紹下演算法流程,再對照下面具體代碼進行分析。
1.對於構造的立體中的每個體素g,轉化g為世界坐標系下得三維位置點p(推算);
2.由第i幀深度數據的相機位姿矩陣 ,由 求得p在相機坐標系下得映射點v;並由Kinect相機內參矩陣,反投影v點求深度圖像中的對應像素點x;
3.計算點p沿投影光線到最近表面的距離 。首先計算距離符號函數:
如果 大於0時,取
否則取
4.計算 對應的權重係數 : 其中θ為投影光線與表面法向量的夾角;
5.
進行融合求得截斷符號距離函數 與權重 ;
6.將 和 存儲於體素g中;
7.實現所有數據幀對應點雲融合。利用光線投影法找到的所有 值為0的體素點g,可以映射到世界坐標中提取出場景模型的表面點雲[2]。
Open3D中的TSDF
int main(int argc, char *argv[])
{
using namespace three;
auto volume = ScalableTSDFVolume::ScalableTSDFVolume(4.0 / 512.0, 0.04, TSDFVolumeColorType::RGB8);
// 聲明TSDF體的voxel_length(體素邊長),sdf_trunc(截斷距離),colorType(著色需要)
vector<Eigen::Matrix4d> Extrinsics;
GetExtrinsic(filename_log, Extrinsics); //獲取外參
for (int i = 0; i < 5; i++) {
Eigen::Matrix4d extrinsic = Extrinsics[i];
Image image_color, image_depth;
ReadImageFromJPG(filename_color, image_color);
ReadImageFromPNG(filename_depth, image_depth);
shared_ptr<RGBDImage> image_rgbd = CreateRGBDImageFromColorAndDepth(image_color, image_depth, 1000, 3.0, false);
// 融合生成 RGB + depth 格式圖
volume.Integrate(*image_rgbd, PinholeCameraIntrinsicParameters::PrimeSenseDefault, extrinsic); // 核心函數:利用該幀數據生成TSDF
}
auto mesh = volume.ExtractTriangleMesh();//三角化表面模型
mesh->ComputeVertexNormals();//計演算法向量
DrawGeometries({ mesh }, "Mesh");//顯示Mesh
return 1;
}
(用Open3D里的RGBD數據作測試,5幀RGBD)
void ScalableTSDFVolume::Integrate(const RGBDImage &image,
const PinholeCameraIntrinsic &intrinsic,
const Eigen::Matrix4d &extrinsic)
{
if ((image.depth_.num_of_channels_ != 1) ||
(image.depth_.bytes_per_channel_ != 4) ||
(image.depth_.width_ != intrinsic.width_) ||
(image.depth_.height_ != intrinsic.height_) ||
(color_type_ == TSDFVolumeColorType::RGB8 &&
image.color_.num_of_channels_ != 3) ||
(color_type_ == TSDFVolumeColorType::RGB8 &&
image.color_.bytes_per_channel_ != 1) ||
(color_type_ == TSDFVolumeColorType::Gray32 &&
image.color_.num_of_channels_ != 1) ||
(color_type_ == TSDFVolumeColorType::Gray32 &&
image.color_.bytes_per_channel_ != 4) ||
(color_type_ != TSDFVolumeColorType::None &&
image.color_.width_ != intrinsic.width_) ||
(color_type_ != TSDFVolumeColorType::None &&
image.color_.height_ != intrinsic.height_)) {
PrintWarning("[ScalableTSDFVolume::Integrate] Unsupported image format.
");
return;
}
auto depth2cameradistance = CreateDepthToCameraDistanceMultiplierFloatImage
intrinsic);
auto pointcloud = CreatePointCloudFromDepthImage(image.depth_, intrinsic,
extrinsic, 1000.0, 1000.0, depth_sampling_stride_);
std::unordered_set<Eigen::Vector3i, hash_eigen::hash<Eigen::Vector3i>>
touched_volume_units_;
for (const auto &point : pointcloud->points_) {
auto min_bound = LocateVolumeUnit(point - Eigen::Vector3d(
sdf_trunc_, sdf_trunc_, sdf_trunc_));
auto max_bound = LocateVolumeUnit(point + Eigen::Vector3d(
sdf_trunc_, sdf_trunc_, sdf_trunc_));
for (auto x = min_bound(0); x <= max_bound(0); x++) {
for (auto y = min_bound(1); y <= max_bound(1); y++) {
for (auto z = min_bound(2); z <= max_bound(2); z++) {
auto loc = Eigen::Vector3i(x, y, z);
if (touched_volume_units_.find(loc) ==
touched_volume_units_.end()) {
touched_volume_units_.insert(loc);
auto volume = OpenVolumeUnit(Eigen::Vector3i(x, y, z));
volume->IntegrateWithDepthToCameraDistanceMultiplier(
image, intrinsic, extrinsic,
*depth2cameradistance);
}
}
}
}
}
}
(取自Open3DsrcCoreIntegrationScalebleTSDFVolume.cpp)
1.CreateDepthToCameraDistanceMultiplierFloatImage():
創建一個和深度圖同樣大小的浮點圖,其中像素(i,j)的值為
由像平面坐標繫到相機坐標系的轉換關係: