mirror of
https://github.com/opencv/opencv_contrib.git
synced 2025-10-19 02:16:34 +08:00

* empty kinfu module created * KinFu: skeleton is done * some intermediate state fixed * fixed normal calculation * bilinear depth interp: fixing missing data * TSDF integration optimized * TSDF: adding constness * utils: isNaN; Intr::Projector const reference fixed * TSDF raycast: quality improvements * TSDF fetchCloud is done * render() added * ICP implemented * debug code moved to demo.cpp * less TODOs * partial refactoring * TSDF: fetchPoints() and fetchNormals() rewritten in parallel manner * platform choose added * reordered * data types isolated off the platform * minor fixes * ScopeTime added * fixed iterations gathering * volume::integrate() parallelized but works slow (big overhead for * raycast is done in parallel * got rid of kftype and p3type * fetchNormals() fixed * less code duplication * nan check reduced, interpolate() refactored to fetchVoxel() * ICP: optimizations * TSDF: bilinear specialized * TSDF: voxelSizeInv pushed away * TSDF: interpolation optimized * TSDF::integrate: parallel_for now works fast * Frame::render: pow -> float ipow<int p>(x) * ICP::getAb: parallel_for * ICP::getAb: time print disabled * ICP::getAb::bilinear: 2 calls joined * refactored, extra functions removed * optimized to use only 27 elems * ICP::getAb: better optimized * Points and Normals data type expanded to 4 channels * ICP optimized (doesn't work) * ICP::getAb is on intrinsics and it works * NaN check is faster * ICP::getAB: minors * added non-SIMD code as fallback * TSDF::fetchVoxel and interpolation: got rid of coord check * TSDF::fetchVoxel: refactor * TSDF::raycast: local copies of members * TSDF::interpolate: refactored for simplier vectorization * TSDF::getNormal: refactored for simplier vectorization * minor * include "intrin.hpp" moved to precomp.hpp * TSDF::coords shifts moved to class body * TSDF::getNormal vectorized * TSDF::getNormal: little improvements * TSDF::interpolate: little improvements * TSDF::raycast vectorized * more to precomp.hpp * TSDF: minor optimizations * TSDF::raycast cycles optimized * TSDF::fetchPointsNormals instead of separate p and n * TSDF::bilinearInterpolate: little speedup * TSDF::interpolate: speed up * TSDF::interpolate: more compact code * TSDF::getNormal and raycast main cycle made faster * ICP: few improvements * Frame: a lot of parts parallelized * TSDF::fetchPointsNormals minor improvements * TSDF::integrate and bilinear vectorized * TSDF::interpolate and getNormal: interpolation vectorized * ICP: minor changes * gradientDeltaFactor removed, coarseParams() added * TSDF::raycast: fixed bug with tmin/tmax * minors * baseZ fixed * ICP: interpolation fixed, non-parallelized code fixed * TSDF::interpolate: bilinear fixed, less artifacts * TSDF: minor refactoring * TSDF: some members moved to parent class * added tests for KinFu * KinFu documented * docs fixed * warnings fixed * license added, overrides added * minors * ScopeTime moved to separate file * less memory allocations * demo improved, java binding disabled * viz module made optional * fix to demo * frameGenerator interface: got rid of refs to cv::Ptr * demo made interactive * trying to fix build * trying to fix warnings * warning fixed * demo fixed * raycast_step_factor tuned * legal info added * don't reset if ICP failed * refactoring: KinFu::operator() => update() * KinFu::KinFuParams => ::Params * get/setParams * fetch => get * all src moved to cv::kinfu namespace * struct Intr made internal * kinfu_module merged into rgbd module * License preambule updated * minors * frame.* renamed to kinfu_frame.* * warnings fixed * more warnings fixed * RGBD normals: a fix against Inf/Nan values * FastICP: fixed transformation direction * RGBD Odometry tests: added epsilon for id transform; minors * RGBD Odometry tests enabled * modules list fixed
249 lines
7.8 KiB
C++
249 lines
7.8 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_WillowGarage.md file found in this module's directory
|
|
|
|
#include <opencv2/rgbd.hpp>
|
|
|
|
#include <opencv2/highgui.hpp>
|
|
#include <opencv2/calib3d.hpp>
|
|
#include <opencv2/imgproc.hpp>
|
|
#include <opencv2/core/utility.hpp>
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
|
|
using namespace std;
|
|
using namespace cv;
|
|
using namespace cv::rgbd;
|
|
|
|
#define BILATERAL_FILTER 0// if 1 then bilateral filter will be used for the depth
|
|
|
|
class MyTickMeter
|
|
{
|
|
public:
|
|
MyTickMeter() { reset(); }
|
|
void start() { startTime = getTickCount(); }
|
|
void stop()
|
|
{
|
|
int64 time = getTickCount();
|
|
if ( startTime == 0 )
|
|
return;
|
|
++counter;
|
|
sumTime += ( time - startTime );
|
|
startTime = 0;
|
|
}
|
|
|
|
int64 getTimeTicks() const { return sumTime; }
|
|
double getTimeSec() const { return (double)getTimeTicks()/getTickFrequency(); }
|
|
int64 getCounter() const { return counter; }
|
|
|
|
void reset() { startTime = sumTime = 0; counter = 0; }
|
|
private:
|
|
int64 counter;
|
|
int64 sumTime;
|
|
int64 startTime;
|
|
};
|
|
|
|
static
|
|
void writeResults( const string& filename, const vector<string>& timestamps, const vector<Mat>& Rt )
|
|
{
|
|
CV_Assert( timestamps.size() == Rt.size() );
|
|
|
|
ofstream file( filename.c_str() );
|
|
if( !file.is_open() )
|
|
return;
|
|
|
|
cout.precision(4);
|
|
for( size_t i = 0; i < Rt.size(); i++ )
|
|
{
|
|
const Mat& Rt_curr = Rt[i];
|
|
if( Rt_curr.empty() )
|
|
continue;
|
|
|
|
CV_Assert( Rt_curr.type() == CV_64FC1 );
|
|
|
|
Mat R = Rt_curr(Rect(0,0,3,3)), rvec;
|
|
Rodrigues(R, rvec);
|
|
double alpha = norm( rvec );
|
|
if(alpha > DBL_MIN)
|
|
rvec = rvec / alpha;
|
|
|
|
double cos_alpha2 = std::cos(0.5 * alpha);
|
|
double sin_alpha2 = std::sin(0.5 * alpha);
|
|
|
|
rvec *= sin_alpha2;
|
|
|
|
CV_Assert( rvec.type() == CV_64FC1 );
|
|
// timestamp tx ty tz qx qy qz qw
|
|
file << timestamps[i] << " " << fixed
|
|
<< Rt_curr.at<double>(0,3) << " " << Rt_curr.at<double>(1,3) << " " << Rt_curr.at<double>(2,3) << " "
|
|
<< rvec.at<double>(0) << " " << rvec.at<double>(1) << " " << rvec.at<double>(2) << " " << cos_alpha2 << endl;
|
|
|
|
}
|
|
file.close();
|
|
}
|
|
|
|
static
|
|
void setCameraMatrixFreiburg1(float& fx, float& fy, float& cx, float& cy)
|
|
{
|
|
fx = 517.3f; fy = 516.5f; cx = 318.6f; cy = 255.3f;
|
|
}
|
|
|
|
static
|
|
void setCameraMatrixFreiburg2(float& fx, float& fy, float& cx, float& cy)
|
|
{
|
|
fx = 520.9f; fy = 521.0f; cx = 325.1f; cy = 249.7f;
|
|
}
|
|
|
|
/*
|
|
* This sample helps to evaluate odometry on TUM datasets and benchmark http://vision.in.tum.de/data/datasets/rgbd-dataset.
|
|
* At this link you can find instructions for evaluation. The sample runs some opencv odometry and saves a camera trajectory
|
|
* to file of format that the benchmark requires. Saved file can be used for online evaluation.
|
|
*/
|
|
int main(int argc, char** argv)
|
|
{
|
|
if(argc != 4)
|
|
{
|
|
cout << "Format: file_with_rgb_depth_pairs trajectory_file odometry_name [Rgbd or ICP or RgbdICP or FastICP]" << endl;
|
|
return -1;
|
|
}
|
|
|
|
vector<string> timestamps;
|
|
vector<Mat> Rts;
|
|
|
|
const string filename = argv[1];
|
|
ifstream file( filename.c_str() );
|
|
if( !file.is_open() )
|
|
return -1;
|
|
|
|
char dlmrt = '/';
|
|
size_t pos = filename.rfind(dlmrt);
|
|
string dirname = pos == string::npos ? "" : filename.substr(0, pos) + dlmrt;
|
|
|
|
const int timestampLength = 17;
|
|
const int rgbPathLehgth = 17+8;
|
|
const int depthPathLehgth = 17+10;
|
|
|
|
float fx = 525.0f, // default
|
|
fy = 525.0f,
|
|
cx = 319.5f,
|
|
cy = 239.5f;
|
|
if(filename.find("freiburg1") != string::npos)
|
|
setCameraMatrixFreiburg1(fx, fy, cx, cy);
|
|
if(filename.find("freiburg2") != string::npos)
|
|
setCameraMatrixFreiburg2(fx, fy, cx, cy);
|
|
Mat cameraMatrix = Mat::eye(3,3,CV_32FC1);
|
|
{
|
|
cameraMatrix.at<float>(0,0) = fx;
|
|
cameraMatrix.at<float>(1,1) = fy;
|
|
cameraMatrix.at<float>(0,2) = cx;
|
|
cameraMatrix.at<float>(1,2) = cy;
|
|
}
|
|
|
|
Ptr<OdometryFrame> frame_prev = Ptr<OdometryFrame>(new OdometryFrame()),
|
|
frame_curr = Ptr<OdometryFrame>(new OdometryFrame());
|
|
Ptr<Odometry> odometry = Odometry::create(string(argv[3]) + "Odometry");
|
|
if(odometry.empty())
|
|
{
|
|
cout << "Can not create Odometry algorithm. Check the passed odometry name." << endl;
|
|
return -1;
|
|
}
|
|
odometry->setCameraMatrix(cameraMatrix);
|
|
|
|
MyTickMeter gtm;
|
|
int count = 0;
|
|
for(int i = 0; !file.eof(); i++)
|
|
{
|
|
string str;
|
|
std::getline(file, str);
|
|
if(str.empty()) break;
|
|
if(str.at(0) == '#') continue; /* comment */
|
|
|
|
Mat image, depth;
|
|
// Read one pair (rgb and depth)
|
|
// example: 1305031453.359684 rgb/1305031453.359684.png 1305031453.374112 depth/1305031453.374112.png
|
|
#if BILATERAL_FILTER
|
|
MyTickMeter tm_bilateral_filter;
|
|
#endif
|
|
{
|
|
string rgbFilename = str.substr(timestampLength + 1, rgbPathLehgth );
|
|
string timestap = str.substr(0, timestampLength);
|
|
string depthFilename = str.substr(2*timestampLength + rgbPathLehgth + 3, depthPathLehgth );
|
|
|
|
image = imread(dirname + rgbFilename);
|
|
depth = imread(dirname + depthFilename, -1);
|
|
|
|
CV_Assert(!image.empty());
|
|
CV_Assert(!depth.empty());
|
|
CV_Assert(depth.type() == CV_16UC1);
|
|
|
|
cout << i << " " << rgbFilename << " " << depthFilename << endl;
|
|
|
|
// scale depth
|
|
Mat depth_flt;
|
|
depth.convertTo(depth_flt, CV_32FC1, 1.f/5000.f);
|
|
#if !BILATERAL_FILTER
|
|
depth_flt.setTo(std::numeric_limits<float>::quiet_NaN(), depth == 0);
|
|
depth = depth_flt;
|
|
#else
|
|
tm_bilateral_filter.start();
|
|
depth = Mat(depth_flt.size(), CV_32FC1, Scalar(0));
|
|
const double depth_sigma = 0.03;
|
|
const double space_sigma = 4.5; // in pixels
|
|
Mat invalidDepthMask = depth_flt == 0.f;
|
|
depth_flt.setTo(-5*depth_sigma, invalidDepthMask);
|
|
bilateralFilter(depth_flt, depth, -1, depth_sigma, space_sigma);
|
|
depth.setTo(std::numeric_limits<float>::quiet_NaN(), invalidDepthMask);
|
|
tm_bilateral_filter.stop();
|
|
cout << "Time filter " << tm_bilateral_filter.getTimeSec() << endl;
|
|
#endif
|
|
timestamps.push_back( timestap );
|
|
}
|
|
|
|
{
|
|
Mat gray;
|
|
cvtColor(image, gray, COLOR_BGR2GRAY);
|
|
frame_curr->image = gray;
|
|
frame_curr->depth = depth;
|
|
|
|
Mat Rt;
|
|
if(!Rts.empty())
|
|
{
|
|
MyTickMeter tm;
|
|
tm.start();
|
|
gtm.start();
|
|
bool res = odometry->compute(frame_curr, frame_prev, Rt);
|
|
gtm.stop();
|
|
tm.stop();
|
|
count++;
|
|
cout << "Time " << tm.getTimeSec() << endl;
|
|
#if BILATERAL_FILTER
|
|
cout << "Time ratio " << tm_bilateral_filter.getTimeSec() / tm.getTimeSec() << endl;
|
|
#endif
|
|
if(!res)
|
|
Rt = Mat::eye(4,4,CV_64FC1);
|
|
}
|
|
|
|
if( Rts.empty() )
|
|
Rts.push_back(Mat::eye(4,4,CV_64FC1));
|
|
else
|
|
{
|
|
Mat& prevRt = *Rts.rbegin();
|
|
cout << "Rt " << Rt << endl;
|
|
Rts.push_back( prevRt * Rt );
|
|
}
|
|
|
|
if(!frame_prev.empty())
|
|
frame_prev->release();
|
|
std::swap(frame_prev, frame_curr);
|
|
}
|
|
}
|
|
|
|
std::cout << "Average time " << gtm.getTimeSec()/count << std::endl;
|
|
writeResults(argv[2], timestamps, Rts);
|
|
|
|
return 0;
|
|
}
|