mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-01 17:10:26 +08:00
Building more robust OpenCV3's cv::solvePnPRansac() in OpenCV2 build
This commit is contained in:
@@ -75,6 +75,7 @@ IF(OpenCV_VERSION_MAJOR EQUAL 2)
|
||||
SET(SRC_FILES
|
||||
${SRC_FILES}
|
||||
opencv/Orb.cpp
|
||||
opencv/solvepnp.cpp
|
||||
)
|
||||
ENDIF(OpenCV_VERSION_MAJOR EQUAL 2)
|
||||
|
||||
|
||||
@@ -2073,6 +2073,12 @@ Transform Memory::computeTransform(
|
||||
Signature tmpFrom = *fromS;
|
||||
Signature tmpTo = *toS;
|
||||
|
||||
// make a guess with known correspondences
|
||||
RegistrationVis regVis(parameters_);
|
||||
tmpFrom.sensorData().setFeatures(std::vector<cv::KeyPoint>(), cv::Mat());
|
||||
tmpTo.sensorData().setFeatures(std::vector<cv::KeyPoint>(), cv::Mat());
|
||||
guess = regVis.computeTransformation(tmpFrom, tmpTo, guess, info);
|
||||
|
||||
if(_reextractLoopClosureFeatures)
|
||||
{
|
||||
tmpFrom.setWords(std::multimap<int, cv::KeyPoint>());
|
||||
@@ -2084,16 +2090,24 @@ Transform Memory::computeTransform(
|
||||
tmpTo.setWordsDescriptors(std::multimap<int, cv::Mat>());
|
||||
tmpTo.sensorData().setFeatures(std::vector<cv::KeyPoint>(), cv::Mat());
|
||||
}
|
||||
|
||||
if(!_registrationPipeline->isImageRequired())
|
||||
else
|
||||
{
|
||||
// no visual in the pipeline, make visual registration for guess
|
||||
RegistrationVis regVis(parameters_);
|
||||
guess = regVis.computeTransformation(tmpFrom, tmpTo, guess, info);
|
||||
// set back features
|
||||
tmpFrom.sensorData().setFeatures(fromS->sensorData().keypoints(), fromS->sensorData().descriptors());
|
||||
tmpTo.sensorData().setFeatures(toS->sensorData().keypoints(), toS->sensorData().descriptors());
|
||||
}
|
||||
else if(guess.isNull())
|
||||
|
||||
if(guess.isNull())
|
||||
{
|
||||
guess.setIdentity();
|
||||
if(!_registrationPipeline->isImageRequired())
|
||||
{
|
||||
// no visual in the pipeline, make visual registration for guess
|
||||
guess = regVis.computeTransformation(tmpFrom, tmpTo, guess, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
guess.setIdentity();
|
||||
}
|
||||
}
|
||||
|
||||
if(!guess.isNull())
|
||||
|
||||
5
corelib/src/opencv/readme.txt
Normal file
5
corelib/src/opencv/readme.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
These files are built only if RTAB-Map is built against OpenCV 2.
|
||||
|
||||
* Orb.cpp is a modified version of OpenCV2 Orb with FAST object from rtabmap (FAST with Grid adaptor).
|
||||
* solvepnp.cpp is a copy of the OpenCV3 version of solvePnPRansac.
|
||||
666
corelib/src/opencv/solvepnp.cpp
Normal file
666
corelib/src/opencv/solvepnp.cpp
Normal file
@@ -0,0 +1,666 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "solvepnp.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
namespace cv3
|
||||
{
|
||||
|
||||
class PnPRansacCallback : public PointSetRegistrator::Callback
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
PnPRansacCallback(Mat _cameraMatrix=Mat(3,3,CV_64F), Mat _distCoeffs=Mat(4,1,CV_64F), int _flags=CV_ITERATIVE,
|
||||
bool _useExtrinsicGuess=false, Mat _rvec=Mat(), Mat _tvec=Mat() )
|
||||
: cameraMatrix(_cameraMatrix), distCoeffs(_distCoeffs), flags(_flags), useExtrinsicGuess(_useExtrinsicGuess),
|
||||
rvec(_rvec), tvec(_tvec) {}
|
||||
|
||||
/* Pre: True */
|
||||
/* Post: compute _model with given points an return number of found models */
|
||||
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const
|
||||
{
|
||||
Mat opoints = _m1.getMat(), ipoints = _m2.getMat();
|
||||
|
||||
bool correspondence = solvePnP( _m1, _m2, cameraMatrix, distCoeffs,
|
||||
rvec, tvec, useExtrinsicGuess, flags );
|
||||
|
||||
Mat _local_model;
|
||||
hconcat(rvec, tvec, _local_model);
|
||||
_local_model.copyTo(_model);
|
||||
|
||||
return correspondence;
|
||||
}
|
||||
|
||||
/* Pre: True */
|
||||
/* Post: fill _err with projection errors */
|
||||
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const
|
||||
{
|
||||
|
||||
Mat opoints = _m1.getMat(), ipoints = _m2.getMat(), model = _model.getMat();
|
||||
|
||||
int i, count = opoints.checkVector(3);
|
||||
Mat _rvec = model.col(0);
|
||||
Mat _tvec = model.col(1);
|
||||
|
||||
|
||||
Mat projpoints(count, 2, CV_32FC1);
|
||||
projectPoints(opoints, _rvec, _tvec, cameraMatrix, distCoeffs, projpoints);
|
||||
|
||||
const Point2f* ipoints_ptr = ipoints.ptr<Point2f>();
|
||||
const Point2f* projpoints_ptr = projpoints.ptr<Point2f>();
|
||||
|
||||
_err.create(count, 1, CV_32FC1);
|
||||
float* err = _err.getMat().ptr<float>();
|
||||
|
||||
for ( i = 0; i < count; ++i)
|
||||
err[i] = (float)norm( ipoints_ptr[i] - projpoints_ptr[i] );
|
||||
|
||||
}
|
||||
|
||||
|
||||
Mat cameraMatrix;
|
||||
Mat distCoeffs;
|
||||
int flags;
|
||||
bool useExtrinsicGuess;
|
||||
Mat rvec;
|
||||
Mat tvec;
|
||||
};
|
||||
|
||||
bool solvePnPRansac(InputArray _opoints, InputArray _ipoints,
|
||||
InputArray _cameraMatrix, InputArray _distCoeffs,
|
||||
OutputArray _rvec, OutputArray _tvec, bool useExtrinsicGuess,
|
||||
int iterationsCount, float reprojectionError, double confidence,
|
||||
OutputArray _inliers, int flags)
|
||||
{
|
||||
|
||||
Mat opoints0 = _opoints.getMat(), ipoints0 = _ipoints.getMat();
|
||||
Mat opoints, ipoints;
|
||||
if( opoints0.depth() == CV_64F || !opoints0.isContinuous() )
|
||||
opoints0.convertTo(opoints, CV_32F);
|
||||
else
|
||||
opoints = opoints0;
|
||||
if( ipoints0.depth() == CV_64F || !ipoints0.isContinuous() )
|
||||
ipoints0.convertTo(ipoints, CV_32F);
|
||||
else
|
||||
ipoints = ipoints0;
|
||||
|
||||
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
|
||||
CV_Assert( npoints >= 0 && npoints == std::max(ipoints.checkVector(2, CV_32F), ipoints.checkVector(2, CV_64F)) );
|
||||
|
||||
CV_Assert(opoints.isContinuous());
|
||||
CV_Assert(opoints.depth() == CV_32F || opoints.depth() == CV_64F);
|
||||
CV_Assert((opoints.rows == 1 && opoints.channels() == 3) || opoints.cols*opoints.channels() == 3);
|
||||
CV_Assert(ipoints.isContinuous());
|
||||
CV_Assert(ipoints.depth() == CV_32F || ipoints.depth() == CV_64F);
|
||||
CV_Assert((ipoints.rows == 1 && ipoints.channels() == 2) || ipoints.cols*ipoints.channels() == 2);
|
||||
|
||||
_rvec.create(3, 1, CV_64FC1);
|
||||
_tvec.create(3, 1, CV_64FC1);
|
||||
|
||||
Mat rvec = useExtrinsicGuess ? _rvec.getMat() : Mat(3, 1, CV_64FC1);
|
||||
Mat tvec = useExtrinsicGuess ? _tvec.getMat() : Mat(3, 1, CV_64FC1);
|
||||
Mat cameraMatrix = _cameraMatrix.getMat(), distCoeffs = _distCoeffs.getMat();
|
||||
|
||||
int model_points = 5;
|
||||
int ransac_kernel_method = CV_EPNP;
|
||||
|
||||
if( npoints == 4 )
|
||||
{
|
||||
model_points = 4;
|
||||
ransac_kernel_method = CV_P3P;
|
||||
}
|
||||
|
||||
Ptr<PointSetRegistrator::Callback> cb; // pointer to callback
|
||||
cb = Ptr<PnPRansacCallback>(new PnPRansacCallback( cameraMatrix, distCoeffs, ransac_kernel_method, useExtrinsicGuess, rvec, tvec));
|
||||
|
||||
double param1 = reprojectionError; // reprojection error
|
||||
double param2 = confidence; // confidence
|
||||
int param3 = iterationsCount; // number maximum iterations
|
||||
|
||||
Mat _local_model(3, 2, CV_64FC1);
|
||||
Mat _mask_local_inliers(1, opoints.rows, CV_8UC1);
|
||||
|
||||
// call Ransac
|
||||
int result = createRANSACPointSetRegistrator(cb, model_points,
|
||||
param1, param2, param3)->run(opoints, ipoints, _local_model, _mask_local_inliers);
|
||||
|
||||
if( result > 0 )
|
||||
{
|
||||
std::vector<Point3d> opoints_inliers;
|
||||
std::vector<Point2d> ipoints_inliers;
|
||||
opoints.convertTo(opoints_inliers, CV_64F);
|
||||
ipoints.convertTo(ipoints_inliers, CV_64F);
|
||||
|
||||
const uchar* mask = _mask_local_inliers.ptr<uchar>();
|
||||
int npoints1 = compressElems(&opoints_inliers[0], mask, 1, npoints);
|
||||
compressElems(&ipoints_inliers[0], mask, 1, npoints);
|
||||
|
||||
opoints_inliers.resize(npoints1);
|
||||
ipoints_inliers.resize(npoints1);
|
||||
result = solvePnP(opoints_inliers, ipoints_inliers, cameraMatrix,
|
||||
distCoeffs, rvec, tvec, false, flags == CV_P3P ? CV_EPNP : flags) ? 1 : -1;
|
||||
}
|
||||
|
||||
if( result <= 0 || _local_model.rows <= 0)
|
||||
{
|
||||
rvec.copyTo(_rvec); // output rotation vector
|
||||
tvec.copyTo(_tvec); // output translation vector
|
||||
|
||||
if( _inliers.needed() )
|
||||
_inliers.release();
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_local_model.col(0).copyTo(_rvec); // output rotation vector
|
||||
_local_model.col(1).copyTo(_tvec); // output translation vector
|
||||
}
|
||||
|
||||
if(_inliers.needed())
|
||||
{
|
||||
Mat _local_inliers;
|
||||
for (int i = 0; i < npoints; ++i)
|
||||
{
|
||||
if((int)_mask_local_inliers.at<uchar>(i) != 0) // inliers mask
|
||||
_local_inliers.push_back(i); // output inliers vector
|
||||
}
|
||||
_local_inliers.copyTo(_inliers);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int RANSACUpdateNumIters( double p, double ep, int modelPoints, int maxIters )
|
||||
{
|
||||
if( modelPoints <= 0 )
|
||||
CV_Error( 0, "the number of model points should be positive" );
|
||||
|
||||
p = MAX(p, 0.);
|
||||
p = MIN(p, 1.);
|
||||
ep = MAX(ep, 0.);
|
||||
ep = MIN(ep, 1.);
|
||||
|
||||
// avoid inf's & nan's
|
||||
double num = MAX(1. - p, DBL_MIN);
|
||||
double denom = 1. - std::pow(1. - ep, modelPoints);
|
||||
if( denom < DBL_MIN )
|
||||
return 0;
|
||||
|
||||
num = std::log(num);
|
||||
denom = std::log(denom);
|
||||
|
||||
return denom >= 0 || -num >= maxIters*(-denom) ? maxIters : cvRound(num/denom);
|
||||
}
|
||||
|
||||
class RANSACPointSetRegistrator : public PointSetRegistrator
|
||||
{
|
||||
public:
|
||||
RANSACPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb=Ptr<PointSetRegistrator::Callback>(),
|
||||
int _modelPoints=0, double _threshold=0, double _confidence=0.99, int _maxIters=1000)
|
||||
: cb(_cb), modelPoints(_modelPoints), threshold(_threshold), confidence(_confidence), maxIters(_maxIters)
|
||||
{
|
||||
checkPartialSubsets = false;
|
||||
}
|
||||
|
||||
int findInliers( const Mat& m1, const Mat& m2, const Mat& model, Mat& err, Mat& mask, double thresh ) const
|
||||
{
|
||||
cb->computeError( m1, m2, model, err );
|
||||
mask.create(err.size(), CV_8U);
|
||||
|
||||
CV_Assert( err.isContinuous() && err.type() == CV_32F && mask.isContinuous() && mask.type() == CV_8U);
|
||||
const float* errptr = err.ptr<float>();
|
||||
uchar* maskptr = mask.ptr<uchar>();
|
||||
float t = (float)(thresh*thresh);
|
||||
int i, n = (int)err.total(), nz = 0;
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
int f = errptr[i] <= t;
|
||||
maskptr[i] = (uchar)f;
|
||||
nz += f;
|
||||
}
|
||||
return nz;
|
||||
}
|
||||
|
||||
bool getSubset( const Mat& m1, const Mat& m2,
|
||||
Mat& ms1, Mat& ms2, RNG& rng,
|
||||
int maxAttempts=1000 ) const
|
||||
{
|
||||
cv::AutoBuffer<int> _idx(modelPoints);
|
||||
int* idx = _idx;
|
||||
int i = 0, j, k, iters = 0;
|
||||
int esz1 = (int)m1.elemSize(), esz2 = (int)m2.elemSize();
|
||||
int d1 = m1.channels() > 1 ? m1.channels() : m1.cols;
|
||||
int d2 = m2.channels() > 1 ? m2.channels() : m2.cols;
|
||||
int count = m1.checkVector(d1), count2 = m2.checkVector(d2);
|
||||
const int *m1ptr = m1.ptr<int>(), *m2ptr = m2.ptr<int>();
|
||||
|
||||
ms1.create(modelPoints, 1, CV_MAKETYPE(m1.depth(), d1));
|
||||
ms2.create(modelPoints, 1, CV_MAKETYPE(m2.depth(), d2));
|
||||
|
||||
int *ms1ptr = ms1.ptr<int>(), *ms2ptr = ms2.ptr<int>();
|
||||
|
||||
CV_Assert( count >= modelPoints && count == count2 );
|
||||
CV_Assert( (esz1 % sizeof(int)) == 0 && (esz2 % sizeof(int)) == 0 );
|
||||
esz1 /= sizeof(int);
|
||||
esz2 /= sizeof(int);
|
||||
|
||||
for(; iters < maxAttempts; iters++)
|
||||
{
|
||||
for( i = 0; i < modelPoints && iters < maxAttempts; )
|
||||
{
|
||||
int idx_i = 0;
|
||||
for(;;)
|
||||
{
|
||||
idx_i = idx[i] = rng.uniform(0, count);
|
||||
for( j = 0; j < i; j++ )
|
||||
if( idx_i == idx[j] )
|
||||
break;
|
||||
if( j == i )
|
||||
break;
|
||||
}
|
||||
for( k = 0; k < esz1; k++ )
|
||||
ms1ptr[i*esz1 + k] = m1ptr[idx_i*esz1 + k];
|
||||
for( k = 0; k < esz2; k++ )
|
||||
ms2ptr[i*esz2 + k] = m2ptr[idx_i*esz2 + k];
|
||||
if( checkPartialSubsets && !cb->checkSubset( ms1, ms2, i+1 ))
|
||||
{
|
||||
// we may have selected some bad points;
|
||||
// so, let's remove some of them randomly
|
||||
i = rng.uniform(0, i+1);
|
||||
iters++;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if( !checkPartialSubsets && i == modelPoints && !cb->checkSubset(ms1, ms2, i))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
return i == modelPoints && iters < maxAttempts;
|
||||
}
|
||||
|
||||
bool run(InputArray _m1, InputArray _m2, OutputArray _model, OutputArray _mask) const
|
||||
{
|
||||
bool result = false;
|
||||
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
|
||||
Mat err, mask, model, bestModel, ms1, ms2;
|
||||
|
||||
int iter, niters = MAX(maxIters, 1);
|
||||
int d1 = m1.channels() > 1 ? m1.channels() : m1.cols;
|
||||
int d2 = m2.channels() > 1 ? m2.channels() : m2.cols;
|
||||
int count = m1.checkVector(d1), count2 = m2.checkVector(d2), maxGoodCount = 0;
|
||||
|
||||
RNG rng((uint64)-1);
|
||||
|
||||
CV_Assert( cb );
|
||||
CV_Assert( confidence > 0 && confidence < 1 );
|
||||
|
||||
CV_Assert( count >= 0 && count2 == count );
|
||||
if( count < modelPoints )
|
||||
return false;
|
||||
|
||||
Mat bestMask0, bestMask;
|
||||
|
||||
if( _mask.needed() )
|
||||
{
|
||||
_mask.create(count, 1, CV_8U, -1, true);
|
||||
bestMask0 = bestMask = _mask.getMat();
|
||||
CV_Assert( (bestMask.cols == 1 || bestMask.rows == 1) && (int)bestMask.total() == count );
|
||||
}
|
||||
else
|
||||
{
|
||||
bestMask.create(count, 1, CV_8U);
|
||||
bestMask0 = bestMask;
|
||||
}
|
||||
|
||||
if( count == modelPoints )
|
||||
{
|
||||
if( cb->runKernel(m1, m2, bestModel) <= 0 )
|
||||
return false;
|
||||
bestModel.copyTo(_model);
|
||||
bestMask.setTo(Scalar::all(1));
|
||||
return true;
|
||||
}
|
||||
|
||||
for( iter = 0; iter < niters; iter++ )
|
||||
{
|
||||
int i, goodCount, nmodels;
|
||||
if( count > modelPoints )
|
||||
{
|
||||
bool found = getSubset( m1, m2, ms1, ms2, rng, 10000 );
|
||||
if( !found )
|
||||
{
|
||||
if( iter == 0 )
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nmodels = cb->runKernel( ms1, ms2, model );
|
||||
if( nmodels <= 0 )
|
||||
continue;
|
||||
CV_Assert( model.rows % nmodels == 0 );
|
||||
Size modelSize(model.cols, model.rows/nmodels);
|
||||
|
||||
for( i = 0; i < nmodels; i++ )
|
||||
{
|
||||
Mat model_i = model.rowRange( i*modelSize.height, (i+1)*modelSize.height );
|
||||
goodCount = findInliers( m1, m2, model_i, err, mask, threshold );
|
||||
|
||||
if( goodCount > MAX(maxGoodCount, modelPoints-1) )
|
||||
{
|
||||
std::swap(mask, bestMask);
|
||||
model_i.copyTo(bestModel);
|
||||
maxGoodCount = goodCount;
|
||||
niters = RANSACUpdateNumIters( confidence, (double)(count - goodCount)/count, modelPoints, niters );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( maxGoodCount > 0 )
|
||||
{
|
||||
if( bestMask.data != bestMask0.data )
|
||||
{
|
||||
if( bestMask.size() == bestMask0.size() )
|
||||
bestMask.copyTo(bestMask0);
|
||||
else
|
||||
transpose(bestMask, bestMask0);
|
||||
}
|
||||
bestModel.copyTo(_model);
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
_model.release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void setCallback(const Ptr<PointSetRegistrator::Callback>& _cb) { cb = _cb; }
|
||||
|
||||
Ptr<PointSetRegistrator::Callback> cb;
|
||||
int modelPoints;
|
||||
bool checkPartialSubsets;
|
||||
double threshold;
|
||||
double confidence;
|
||||
int maxIters;
|
||||
};
|
||||
|
||||
class LMeDSPointSetRegistrator : public RANSACPointSetRegistrator
|
||||
{
|
||||
public:
|
||||
LMeDSPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb=Ptr<PointSetRegistrator::Callback>(),
|
||||
int _modelPoints=0, double _confidence=0.99, int _maxIters=1000)
|
||||
: RANSACPointSetRegistrator(_cb, _modelPoints, 0, _confidence, _maxIters) {}
|
||||
|
||||
bool run(InputArray _m1, InputArray _m2, OutputArray _model, OutputArray _mask) const
|
||||
{
|
||||
const double outlierRatio = 0.45;
|
||||
bool result = false;
|
||||
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
|
||||
Mat ms1, ms2, err, errf, model, bestModel, mask, mask0;
|
||||
|
||||
int d1 = m1.channels() > 1 ? m1.channels() : m1.cols;
|
||||
int d2 = m2.channels() > 1 ? m2.channels() : m2.cols;
|
||||
int count = m1.checkVector(d1), count2 = m2.checkVector(d2);
|
||||
double minMedian = DBL_MAX, sigma;
|
||||
|
||||
RNG rng((uint64)-1);
|
||||
|
||||
CV_Assert( cb );
|
||||
CV_Assert( confidence > 0 && confidence < 1 );
|
||||
|
||||
CV_Assert( count >= 0 && count2 == count );
|
||||
if( count < modelPoints )
|
||||
return false;
|
||||
|
||||
if( _mask.needed() )
|
||||
{
|
||||
_mask.create(count, 1, CV_8U, -1, true);
|
||||
mask0 = mask = _mask.getMat();
|
||||
CV_Assert( (mask.cols == 1 || mask.rows == 1) && (int)mask.total() == count );
|
||||
}
|
||||
|
||||
if( count == modelPoints )
|
||||
{
|
||||
if( cb->runKernel(m1, m2, bestModel) <= 0 )
|
||||
return false;
|
||||
bestModel.copyTo(_model);
|
||||
mask.setTo(Scalar::all(1));
|
||||
return true;
|
||||
}
|
||||
|
||||
int iter, niters = RANSACUpdateNumIters(confidence, outlierRatio, modelPoints, maxIters);
|
||||
niters = MAX(niters, 3);
|
||||
|
||||
for( iter = 0; iter < niters; iter++ )
|
||||
{
|
||||
int i, nmodels;
|
||||
if( count > modelPoints )
|
||||
{
|
||||
bool found = getSubset( m1, m2, ms1, ms2, rng );
|
||||
if( !found )
|
||||
{
|
||||
if( iter == 0 )
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nmodels = cb->runKernel( ms1, ms2, model );
|
||||
if( nmodels <= 0 )
|
||||
continue;
|
||||
|
||||
CV_Assert( model.rows % nmodels == 0 );
|
||||
Size modelSize(model.cols, model.rows/nmodels);
|
||||
|
||||
for( i = 0; i < nmodels; i++ )
|
||||
{
|
||||
Mat model_i = model.rowRange( i*modelSize.height, (i+1)*modelSize.height );
|
||||
cb->computeError( m1, m2, model_i, err );
|
||||
if( err.depth() != CV_32F )
|
||||
err.convertTo(errf, CV_32F);
|
||||
else
|
||||
errf = err;
|
||||
CV_Assert( errf.isContinuous() && errf.type() == CV_32F && (int)errf.total() == count );
|
||||
std::sort(errf.ptr<int>(), errf.ptr<int>() + count);
|
||||
|
||||
double median = count % 2 != 0 ?
|
||||
errf.at<float>(count/2) : (errf.at<float>(count/2-1) + errf.at<float>(count/2))*0.5;
|
||||
|
||||
if( median < minMedian )
|
||||
{
|
||||
minMedian = median;
|
||||
model_i.copyTo(bestModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( minMedian < DBL_MAX )
|
||||
{
|
||||
sigma = 2.5*1.4826*(1 + 5./(count - modelPoints))*std::sqrt(minMedian);
|
||||
sigma = MAX( sigma, 0.001 );
|
||||
|
||||
count = findInliers( m1, m2, bestModel, err, mask, sigma );
|
||||
if( _mask.needed() && mask0.data != mask.data )
|
||||
{
|
||||
if( mask0.size() == mask.size() )
|
||||
mask.copyTo(mask0);
|
||||
else
|
||||
transpose(mask, mask0);
|
||||
}
|
||||
bestModel.copyTo(_model);
|
||||
result = count >= modelPoints;
|
||||
}
|
||||
else
|
||||
_model.release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Ptr<PointSetRegistrator> createRANSACPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb,
|
||||
int _modelPoints, double _threshold,
|
||||
double _confidence, int _maxIters)
|
||||
{
|
||||
return Ptr<PointSetRegistrator>(
|
||||
new RANSACPointSetRegistrator(_cb, _modelPoints, _threshold, _confidence, _maxIters));
|
||||
}
|
||||
|
||||
|
||||
Ptr<PointSetRegistrator> createLMeDSPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb,
|
||||
int _modelPoints, double _confidence, int _maxIters)
|
||||
{
|
||||
return Ptr<PointSetRegistrator>(
|
||||
new LMeDSPointSetRegistrator(_cb, _modelPoints, _confidence, _maxIters));
|
||||
}
|
||||
|
||||
|
||||
class Affine3DEstimatorCallback : public PointSetRegistrator::Callback
|
||||
{
|
||||
public:
|
||||
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const
|
||||
{
|
||||
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
|
||||
const Point3f* from = m1.ptr<Point3f>();
|
||||
const Point3f* to = m2.ptr<Point3f>();
|
||||
|
||||
const int N = 12;
|
||||
double buf[N*N + N + N];
|
||||
Mat A(N, N, CV_64F, &buf[0]);
|
||||
Mat B(N, 1, CV_64F, &buf[0] + N*N);
|
||||
Mat X(N, 1, CV_64F, &buf[0] + N*N + N);
|
||||
double* Adata = A.ptr<double>();
|
||||
double* Bdata = B.ptr<double>();
|
||||
A = Scalar::all(0);
|
||||
|
||||
for( int i = 0; i < (N/3); i++ )
|
||||
{
|
||||
Bdata[i*3] = to[i].x;
|
||||
Bdata[i*3+1] = to[i].y;
|
||||
Bdata[i*3+2] = to[i].z;
|
||||
|
||||
double *aptr = Adata + i*3*N;
|
||||
for(int k = 0; k < 3; ++k)
|
||||
{
|
||||
aptr[0] = from[i].x;
|
||||
aptr[1] = from[i].y;
|
||||
aptr[2] = from[i].z;
|
||||
aptr[3] = 1.0;
|
||||
aptr += 16;
|
||||
}
|
||||
}
|
||||
|
||||
solve(A, B, X, DECOMP_SVD);
|
||||
X.reshape(1, 3).copyTo(_model);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const
|
||||
{
|
||||
Mat m1 = _m1.getMat(), m2 = _m2.getMat(), model = _model.getMat();
|
||||
const Point3f* from = m1.ptr<Point3f>();
|
||||
const Point3f* to = m2.ptr<Point3f>();
|
||||
const double* F = model.ptr<double>();
|
||||
|
||||
int count = m1.checkVector(3);
|
||||
CV_Assert( count > 0 );
|
||||
|
||||
_err.create(count, 1, CV_32F);
|
||||
Mat err = _err.getMat();
|
||||
float* errptr = err.ptr<float>();
|
||||
|
||||
for(int i = 0; i < count; i++ )
|
||||
{
|
||||
const Point3f& f = from[i];
|
||||
const Point3f& t = to[i];
|
||||
|
||||
double a = F[0]*f.x + F[1]*f.y + F[ 2]*f.z + F[ 3] - t.x;
|
||||
double b = F[4]*f.x + F[5]*f.y + F[ 6]*f.z + F[ 7] - t.y;
|
||||
double c = F[8]*f.x + F[9]*f.y + F[10]*f.z + F[11] - t.z;
|
||||
|
||||
errptr[i] = (float)std::sqrt(a*a + b*b + c*c);
|
||||
}
|
||||
}
|
||||
|
||||
bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const
|
||||
{
|
||||
const float threshold = 0.996f;
|
||||
Mat ms1 = _ms1.getMat(), ms2 = _ms2.getMat();
|
||||
|
||||
for( int inp = 1; inp <= 2; inp++ )
|
||||
{
|
||||
int j, k, i = count - 1;
|
||||
const Mat* msi = inp == 1 ? &ms1 : &ms2;
|
||||
const Point3f* ptr = msi->ptr<Point3f>();
|
||||
|
||||
CV_Assert( count <= msi->rows );
|
||||
|
||||
// check that the i-th selected point does not belong
|
||||
// to a line connecting some previously selected points
|
||||
for(j = 0; j < i; ++j)
|
||||
{
|
||||
Point3f d1 = ptr[j] - ptr[i];
|
||||
float n1 = d1.x*d1.x + d1.y*d1.y;
|
||||
|
||||
for(k = 0; k < j; ++k)
|
||||
{
|
||||
Point3f d2 = ptr[k] - ptr[i];
|
||||
float denom = (d2.x*d2.x + d2.y*d2.y)*n1;
|
||||
float num = d1.x*d2.x + d1.y*d2.y;
|
||||
|
||||
if( num*num > threshold*threshold*denom )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
153
corelib/src/opencv/solvepnp.h
Normal file
153
corelib/src/opencv/solvepnp.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef RTABMAP_CORELIB_SRC_OPENCV_SOLVEPNP_H_
|
||||
#define RTABMAP_CORELIB_SRC_OPENCV_SOLVEPNP_H_
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/calib3d/calib3d.hpp>
|
||||
|
||||
namespace cv3 {
|
||||
|
||||
|
||||
|
||||
|
||||
/** @brief Finds an object pose from 3D-2D point correspondences using the RANSAC scheme.
|
||||
|
||||
@param objectPoints Array of object points in the object coordinate space, 3xN/Nx3 1-channel or
|
||||
1xN/Nx1 3-channel, where N is the number of points. vector\<Point3f\> can be also passed here.
|
||||
@param imagePoints Array of corresponding image points, 2xN/Nx2 1-channel or 1xN/Nx1 2-channel,
|
||||
where N is the number of points. vector\<Point2f\> can be also passed here.
|
||||
@param cameraMatrix Input camera matrix \f$A = \vecthreethree{fx}{0}{cx}{0}{fy}{cy}{0}{0}{1}\f$ .
|
||||
@param distCoeffs Input vector of distortion coefficients
|
||||
\f$(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6 [, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]])\f$ of
|
||||
4, 5, 8, 12 or 14 elements. If the vector is NULL/empty, the zero distortion coefficients are
|
||||
assumed.
|
||||
@param rvec Output rotation vector (see Rodrigues ) that, together with tvec , brings points from
|
||||
the model coordinate system to the camera coordinate system.
|
||||
@param tvec Output translation vector.
|
||||
@param useExtrinsicGuess Parameter used for SOLVEPNP_ITERATIVE. If true (1), the function uses
|
||||
the provided rvec and tvec values as initial approximations of the rotation and translation
|
||||
vectors, respectively, and further optimizes them.
|
||||
@param iterationsCount Number of iterations.
|
||||
@param reprojectionError Inlier threshold value used by the RANSAC procedure. The parameter value
|
||||
is the maximum allowed distance between the observed and computed point projections to consider it
|
||||
an inlier.
|
||||
@param confidence The probability that the algorithm produces a useful result.
|
||||
@param inliers Output vector that contains indices of inliers in objectPoints and imagePoints .
|
||||
@param flags Method for solving a PnP problem (see solvePnP ).
|
||||
|
||||
The function estimates an object pose given a set of object points, their corresponding image
|
||||
projections, as well as the camera matrix and the distortion coefficients. This function finds such
|
||||
a pose that minimizes reprojection error, that is, the sum of squared distances between the observed
|
||||
projections imagePoints and the projected (using projectPoints ) objectPoints. The use of RANSAC
|
||||
makes the function resistant to outliers.
|
||||
|
||||
@note
|
||||
- An example of how to use solvePNPRansac for object detection can be found at
|
||||
opencv_source_code/samples/cpp/tutorial_code/calib3d/real_time_pose_estimation/
|
||||
*/
|
||||
bool solvePnPRansac( cv::InputArray objectPoints, cv::InputArray imagePoints,
|
||||
cv::InputArray cameraMatrix, cv::InputArray distCoeffs,
|
||||
cv::OutputArray rvec, cv::OutputArray tvec,
|
||||
bool useExtrinsicGuess = false, int iterationsCount = 100,
|
||||
float reprojectionError = 8.0, double confidence = 0.99,
|
||||
cv::OutputArray inliers = cv::noArray(), int flags = CV_ITERATIVE );
|
||||
|
||||
int RANSACUpdateNumIters( double p, double ep, int modelPoints, int maxIters );
|
||||
|
||||
class LMSolver : public cv::Algorithm
|
||||
{
|
||||
public:
|
||||
class Callback
|
||||
{
|
||||
public:
|
||||
virtual ~Callback() {}
|
||||
virtual bool compute(cv::InputArray param, cv::OutputArray err, cv::OutputArray J) const = 0;
|
||||
};
|
||||
|
||||
virtual void setCallback(const cv::Ptr<LMSolver::Callback>& cb) = 0;
|
||||
virtual int run(cv::InputOutputArray _param0) const = 0;
|
||||
};
|
||||
|
||||
cv::Ptr<LMSolver> createLMSolver(const cv::Ptr<LMSolver::Callback>& cb, int maxIters);
|
||||
|
||||
class PointSetRegistrator : public cv::Algorithm
|
||||
{
|
||||
public:
|
||||
class Callback
|
||||
{
|
||||
public:
|
||||
virtual ~Callback() {}
|
||||
virtual int runKernel(cv::InputArray m1, cv::InputArray m2, cv::OutputArray model) const = 0;
|
||||
virtual void computeError(cv::InputArray m1, cv::InputArray m2, cv::InputArray model, cv::OutputArray err) const = 0;
|
||||
virtual bool checkSubset(cv::InputArray, cv::InputArray, int) const { return true; }
|
||||
};
|
||||
|
||||
virtual void setCallback(const cv::Ptr<PointSetRegistrator::Callback>& cb) = 0;
|
||||
virtual bool run(cv::InputArray m1, cv::InputArray m2, cv::OutputArray model, cv::OutputArray mask) const = 0;
|
||||
};
|
||||
|
||||
cv::Ptr<PointSetRegistrator> createRANSACPointSetRegistrator(const cv::Ptr<PointSetRegistrator::Callback>& cb,
|
||||
int modelPoints, double threshold,
|
||||
double confidence=0.99, int maxIters=1000 );
|
||||
|
||||
cv::Ptr<PointSetRegistrator> createLMeDSPointSetRegistrator(const cv::Ptr<PointSetRegistrator::Callback>& cb,
|
||||
int modelPoints, double confidence=0.99, int maxIters=1000 );
|
||||
|
||||
template<typename T> inline int compressElems( T* ptr, const uchar* mask, int mstep, int count )
|
||||
{
|
||||
int i, j;
|
||||
for( i = j = 0; i < count; i++ )
|
||||
if( mask[i*mstep] )
|
||||
{
|
||||
if( i > j )
|
||||
ptr[j] = ptr[i];
|
||||
j++;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* RTABMAP_CORELIB_SRC_OPENCV_SOLVEPNP_H_ */
|
||||
@@ -37,6 +37,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "rtabmap/core/OptimizerG2O.h"
|
||||
|
||||
#if CV_MAJOR_VERSION < 3
|
||||
#include "opencv/solvepnp.h"
|
||||
#endif
|
||||
|
||||
namespace rtabmap
|
||||
{
|
||||
|
||||
@@ -121,7 +125,7 @@ Transform estimateMotion3DTo2D(
|
||||
true,
|
||||
iterations,
|
||||
reprojError,
|
||||
0, // min inliers
|
||||
minInliers, // min inliers
|
||||
inliers,
|
||||
flagsPnP,
|
||||
refineIterations);
|
||||
@@ -323,7 +327,15 @@ void solvePnPRansac(
|
||||
int refineIterations,
|
||||
float refineSigma)
|
||||
{
|
||||
cv::solvePnPRansac(
|
||||
if(minInliersCount < 4)
|
||||
{
|
||||
minInliersCount = 4;
|
||||
}
|
||||
#if CV_MAJOR_VERSION < 3
|
||||
cv3::solvePnPRansac( //use OpenCV3 version of solvePnPRansac in OpenCV2
|
||||
#else
|
||||
cv::solvePnPRansac( // use directly version from OpenCV 3
|
||||
#endif
|
||||
objectPoints,
|
||||
imagePoints,
|
||||
cameraMatrix,
|
||||
@@ -333,16 +345,12 @@ void solvePnPRansac(
|
||||
useExtrinsicGuess,
|
||||
iterationsCount,
|
||||
reprojectionError,
|
||||
#if CV_MAJOR_VERSION < 3
|
||||
minInliersCount, // min inliers
|
||||
#else
|
||||
0.99, // confidence
|
||||
#endif
|
||||
inliers,
|
||||
flags);
|
||||
|
||||
float inlierThreshold = reprojectionError;
|
||||
if(inliers.size() >= 4 && refineIterations>0)
|
||||
if(inliers.size() >= minInliersCount && refineIterations>0)
|
||||
{
|
||||
float error_threshold = inlierThreshold;
|
||||
int refine_iterations = 0;
|
||||
@@ -381,7 +389,7 @@ void solvePnPRansac(
|
||||
UDEBUG("RANSAC refineModel: Number of inliers found (before/after): %d/%d, with an error threshold of %f.",
|
||||
(int)prev_inliers.size (), (int)new_inliers.size (), error_threshold);
|
||||
|
||||
if (new_inliers.size() < 4)
|
||||
if (new_inliers.size() < minInliersCount)
|
||||
{
|
||||
++refine_iterations;
|
||||
if (refine_iterations >= refineIterations)
|
||||
@@ -405,7 +413,7 @@ void solvePnPRansac(
|
||||
if (new_inliers.size () != prev_inliers.size ())
|
||||
{
|
||||
// Check if the number of inliers is oscillating in between two values
|
||||
if (inliers_sizes.size () >= 4)
|
||||
if (inliers_sizes.size () >= minInliersCount)
|
||||
{
|
||||
if (inliers_sizes[inliers_sizes.size () - 1] == inliers_sizes[inliers_sizes.size () - 3] &&
|
||||
inliers_sizes[inliers_sizes.size () - 2] == inliers_sizes[inliers_sizes.size () - 4])
|
||||
@@ -432,9 +440,9 @@ void solvePnPRansac(
|
||||
while (inlier_changed && ++refine_iterations < refineIterations);
|
||||
|
||||
// If the new set of inliers is empty, we didn't do a good job refining
|
||||
if (new_inliers.empty ())
|
||||
if (prev_inliers.size() < minInliersCount)
|
||||
{
|
||||
UWARN ("RANSAC refineModel: Refinement failed: got an empty set of inliers!");
|
||||
UWARN ("RANSAC refineModel: Refinement failed: got very low inliers (%d)!", (int)prev_inliers.size());
|
||||
}
|
||||
|
||||
if (oscillating)
|
||||
|
||||
Reference in New Issue
Block a user