mirror of
https://github.com/opencv/opencv_contrib.git
synced 2025-10-18 17:24:28 +08:00

[GSoC] Add Submaps and PoseGraph optimization for Large Scale Depth Fusion * - Add HashTSDF class - Implement Integrate function (untested) * Integration seems to be working, raycasting does not * Update integration code * Integration and Raycasting fixes, (both work now) * - Format code - Clean up comments and few fixes * Add Kinect Fusion backup file * - Add interpolation for vertices and normals (slow and unreliable!) - Format code - Delete kinfu_back.cpp * Bug fix for integration and noisy odometry * - Create volume abstract class - Address Review comments * - Add getPoints and getNormals function - Fix formatting according to comments - Move volume abstract class to include/opencv2/rgbd/ - Write factory method for creating TSDFVolumes - Small bug fixes - Minor fixes according to comments * - Add tests for hashTSDF - Fix raycasting bug causing to loop forever - Suppress warnings by explicit conversion - Disable hashTsdf test until we figure out memory leak - style changes - Add missing license in a few files, correct precomp.hpp usage * - Use CRTP based static polymorphism to choose between CPU and GPU for HashTSDF volume * Create submap and submapMgr Implement overlap_ratio check to create new submaps * Early draft of posegraph and submaps (Doesn't even compile) * Minor cleanup (no compilation) * Track all submaps (no posegraph update yet) * Return inliers from ICP for weighting the constraints (Huber threshold based inliers pending) * Add updating constraints between submaps and retain same current map * Fix constraints creation between submaps and allow for switching between submaps * - Fix bug in allocate volumeUnits - Simplify calculation of visibleBlocks * Remove inlier calculation in fast_icp (not required) * Modify readFile to allow reading other datasets easily * - Implement posegraph update, Gauss newton is unstable - Minor changes to Gauss newton and Sparse matrix. Residual still increases slightly over iterations * Implement simplified levenberg marquardt * Bug fixes for Levenberg Marquardt and minor changes * minor changes * Fixes, but Optimizer is still not well behaved * Working Ceres optimizer * - Reorganize IO code for samples in a separate file - Minor fix for Ceres preprocessor definition - Remove unused generatorJacobian, will be used for opencv implementation of levenberg marquardt - Doxygen docs fix - Minor preprocessor fixes * - Reorganize IO code for samples in a separate file - Minor fix for Ceres preprocessor definition - Remove unused generatorJacobian, will be used for opencv implementation of levenberg marquardt - Doxygen docs fix - Minor preprocessor fixes - Move inline functions to header, and make function params const references * - Add Python bindings for volume struct - Remove makeVolume(const VolumeParams&) Python binding due to compilation issues - Minor changes according to comments * - Remove dynafu::Params() since it is identical to kinfu::Params() - Use common functions for dynafu_demo - Suppress "unreachable code" in volume.cpp * Minor API changes * Minor * Remove CRTP for HashTSDF class * Bug fixes for HashTSDF integration
278 lines
8.3 KiB
C++
278 lines
8.3 KiB
C++
// This file is part of OpenCV project.
|
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
|
// of this distribution and at http://opencv.org/license.html
|
|
|
|
// This code is also subject to the license terms in the LICENSE_KinectFusion.md file found in this module's directory
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <opencv2/imgproc.hpp>
|
|
#include <opencv2/calib3d.hpp>
|
|
#include <opencv2/highgui.hpp>
|
|
#include <opencv2/rgbd/kinfu.hpp>
|
|
|
|
#include "io_utils.hpp"
|
|
|
|
using namespace cv;
|
|
using namespace cv::kinfu;
|
|
using namespace cv::io_utils;
|
|
|
|
#ifdef HAVE_OPENCV_VIZ
|
|
#include <opencv2/viz.hpp>
|
|
#endif
|
|
|
|
#ifdef HAVE_OPENCV_VIZ
|
|
const std::string vizWindowName = "cloud";
|
|
|
|
struct PauseCallbackArgs
|
|
{
|
|
PauseCallbackArgs(KinFu& _kf) : kf(_kf)
|
|
{ }
|
|
|
|
KinFu& kf;
|
|
};
|
|
|
|
void pauseCallback(const viz::MouseEvent& me, void* args);
|
|
void pauseCallback(const viz::MouseEvent& me, void* args)
|
|
{
|
|
if(me.type == viz::MouseEvent::Type::MouseMove ||
|
|
me.type == viz::MouseEvent::Type::MouseScrollDown ||
|
|
me.type == viz::MouseEvent::Type::MouseScrollUp)
|
|
{
|
|
PauseCallbackArgs pca = *((PauseCallbackArgs*)(args));
|
|
viz::Viz3d window(vizWindowName);
|
|
UMat rendered;
|
|
pca.kf.render(rendered, window.getViewerPose().matrix);
|
|
imshow("render", rendered);
|
|
waitKey(1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static const char* keys =
|
|
{
|
|
"{help h usage ? | | print this message }"
|
|
"{depth | | Path to depth.txt file listing a set of depth images }"
|
|
"{camera |0| Index of depth camera to be used as a depth source }"
|
|
"{coarse | | Run on coarse settings (fast but ugly) or on default (slow but looks better),"
|
|
" in coarse mode points and normals are displayed }"
|
|
"{useHashTSDF | | Use the newer hashtable based TSDFVolume (relatively fast) and for larger reconstructions}"
|
|
"{idle | | Do not run KinFu, just display depth frames }"
|
|
"{record | | Write depth frames to specified file list"
|
|
" (the same format as for the 'depth' key) }"
|
|
};
|
|
|
|
static const std::string message =
|
|
"\nThis demo uses live depth input or RGB-D dataset taken from"
|
|
"\nhttps://vision.in.tum.de/data/datasets/rgbd-dataset"
|
|
"\nto demonstrate KinectFusion implementation \n";
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
bool coarse = false;
|
|
bool idle = false;
|
|
bool useHashTSDF = false;
|
|
std::string recordPath;
|
|
|
|
CommandLineParser parser(argc, argv, keys);
|
|
parser.about(message);
|
|
|
|
if(!parser.check())
|
|
{
|
|
parser.printMessage();
|
|
parser.printErrors();
|
|
return -1;
|
|
}
|
|
|
|
if(parser.has("help"))
|
|
{
|
|
parser.printMessage();
|
|
return 0;
|
|
}
|
|
if(parser.has("coarse"))
|
|
{
|
|
coarse = true;
|
|
}
|
|
if(parser.has("record"))
|
|
{
|
|
recordPath = parser.get<String>("record");
|
|
}
|
|
if(parser.has("useHashTSDF"))
|
|
{
|
|
useHashTSDF = true;
|
|
}
|
|
if(parser.has("idle"))
|
|
{
|
|
idle = true;
|
|
}
|
|
|
|
Ptr<DepthSource> ds;
|
|
if (parser.has("depth"))
|
|
ds = makePtr<DepthSource>(parser.get<String>("depth"));
|
|
else
|
|
ds = makePtr<DepthSource>(parser.get<int>("camera"));
|
|
|
|
if (ds->empty())
|
|
{
|
|
std::cerr << "Failed to open depth source" << std::endl;
|
|
parser.printMessage();
|
|
return -1;
|
|
}
|
|
|
|
Ptr<DepthWriter> depthWriter;
|
|
if(!recordPath.empty())
|
|
depthWriter = makePtr<DepthWriter>(recordPath);
|
|
|
|
Ptr<Params> params;
|
|
Ptr<KinFu> kf;
|
|
|
|
if(coarse)
|
|
params = Params::coarseParams();
|
|
else
|
|
params = Params::defaultParams();
|
|
|
|
if(useHashTSDF)
|
|
params = Params::hashTSDFParams(coarse);
|
|
|
|
// These params can be different for each depth sensor
|
|
ds->updateParams(*params);
|
|
|
|
// Enables OpenCL explicitly (by default can be switched-off)
|
|
cv::setUseOptimized(true);
|
|
|
|
// Scene-specific params should be tuned for each scene individually
|
|
//float cubeSize = 1.f;
|
|
//params->voxelSize = cubeSize/params->volumeDims[0]; //meters
|
|
//params->tsdf_trunc_dist = 0.01f; //meters
|
|
//params->icpDistThresh = 0.01f; //meters
|
|
//params->volumePose = Affine3f().translate(Vec3f(-cubeSize/2.f, -cubeSize/2.f, 0.25f)); //meters
|
|
//params->tsdf_max_weight = 16;
|
|
|
|
if(!idle)
|
|
kf = KinFu::create(params);
|
|
|
|
#ifdef HAVE_OPENCV_VIZ
|
|
cv::viz::Viz3d window(vizWindowName);
|
|
window.setViewerPose(Affine3f::Identity());
|
|
bool pause = false;
|
|
#endif
|
|
|
|
UMat rendered;
|
|
UMat points;
|
|
UMat normals;
|
|
|
|
int64 prevTime = getTickCount();
|
|
|
|
for(UMat frame = ds->getDepth(); !frame.empty(); frame = ds->getDepth())
|
|
{
|
|
if(depthWriter)
|
|
depthWriter->append(frame);
|
|
|
|
#ifdef HAVE_OPENCV_VIZ
|
|
if(pause)
|
|
{
|
|
// doesn't happen in idle mode
|
|
kf->getCloud(points, normals);
|
|
if(!points.empty() && !normals.empty())
|
|
{
|
|
viz::WCloud cloudWidget(points, viz::Color::white());
|
|
viz::WCloudNormals cloudNormals(points, normals, /*level*/1, /*scale*/0.05, viz::Color::gray());
|
|
window.showWidget("cloud", cloudWidget);
|
|
window.showWidget("normals", cloudNormals);
|
|
|
|
Vec3d volSize = kf->getParams().voxelSize*Vec3d(kf->getParams().volumeDims);
|
|
window.showWidget("cube", viz::WCube(Vec3d::all(0),
|
|
volSize),
|
|
kf->getParams().volumePose);
|
|
PauseCallbackArgs pca(*kf);
|
|
window.registerMouseCallback(pauseCallback, (void*)&pca);
|
|
window.showWidget("text", viz::WText(cv::String("Move camera in this window. "
|
|
"Close the window or press Q to resume"), Point()));
|
|
window.spin();
|
|
window.removeWidget("text");
|
|
window.removeWidget("cloud");
|
|
window.removeWidget("normals");
|
|
window.registerMouseCallback(0);
|
|
}
|
|
|
|
pause = false;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
UMat cvt8;
|
|
float depthFactor = params->depthFactor;
|
|
convertScaleAbs(frame, cvt8, 0.25*256. / depthFactor);
|
|
if(!idle)
|
|
{
|
|
imshow("depth", cvt8);
|
|
|
|
if(!kf->update(frame))
|
|
{
|
|
kf->reset();
|
|
std::cout << "reset" << std::endl;
|
|
}
|
|
#ifdef HAVE_OPENCV_VIZ
|
|
else
|
|
{
|
|
if(coarse)
|
|
{
|
|
kf->getCloud(points, normals);
|
|
if(!points.empty() && !normals.empty())
|
|
{
|
|
viz::WCloud cloudWidget(points, viz::Color::white());
|
|
viz::WCloudNormals cloudNormals(points, normals, /*level*/1, /*scale*/0.05, viz::Color::gray());
|
|
window.showWidget("cloud", cloudWidget);
|
|
window.showWidget("normals", cloudNormals);
|
|
}
|
|
}
|
|
|
|
//window.showWidget("worldAxes", viz::WCoordinateSystem());
|
|
Vec3d volSize = kf->getParams().voxelSize*kf->getParams().volumeDims;
|
|
window.showWidget("cube", viz::WCube(Vec3d::all(0),
|
|
volSize),
|
|
kf->getParams().volumePose);
|
|
window.setViewerPose(kf->getPose());
|
|
window.spinOnce(1, true);
|
|
}
|
|
#endif
|
|
|
|
kf->render(rendered);
|
|
}
|
|
else
|
|
{
|
|
rendered = cvt8;
|
|
}
|
|
}
|
|
|
|
int64 newTime = getTickCount();
|
|
putText(rendered, cv::format("FPS: %2d press R to reset, P to pause, Q to quit",
|
|
(int)(getTickFrequency()/(newTime - prevTime))),
|
|
Point(0, rendered.rows-1), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 255));
|
|
prevTime = newTime;
|
|
|
|
imshow("render", rendered);
|
|
|
|
int c = waitKey(1);
|
|
switch (c)
|
|
{
|
|
case 'r':
|
|
if(!idle)
|
|
kf->reset();
|
|
break;
|
|
case 'q':
|
|
return 0;
|
|
#ifdef HAVE_OPENCV_VIZ
|
|
case 'p':
|
|
if(!idle)
|
|
pause = true;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|