mirror of
https://github.com/introlab/rtabmap.git
synced 2026-10-06 18:17:47 +08:00
* added doc and tests for util2d.h * updated cmake-ros ci * Added util3d.h doc and tests * util3d_transforms.h: Added doc and tests * util3d_filtering.h: started doc and test * util3d_filtering.h: more tests and doc * Added more doc/tests * finished util3d_filtering doc and tests * added test for util2d::depthBleedingFiltering * Added util3d_registration tests * Added util3d_features.h doc/tests * added doc/tests for util3d_correspondences.h * added doc/gtest for util3d_mapping.h (missing hpp functions) * finished testing util3d_mapping.hpp * Added util3d_motion_estimation.h tests (2D->3D done) * finished util3d_motion_estimation.h tests * minimal util3d_surface.h * Added Transform and VisualWord tests * Added doc for CameraModel and StereoCameraModel * Added more logs in ros ci * Passing tests on fical * improved all devcontainer * added devcontainer kilted, fixed source setup.bash, removed ldconfig in ros-cmake workflow * cleanup * source ros * Added utilite tests * Added testing to appveyor, github actions cancellable on re-commit on same branch * appveyor testing without all targets * appveyor: specifying ALL_BUILD target * Fixed Util2dTest.NMSImageBoundsRespected test * Fixing PCL Indices error on old pcl * Added VWDictionary tests and doc. Fixed LSH not working (fix from https://github.com/flann-lib/flann/pull/472 * fixing some appveyor CI errors, added test to check dictionary serialization against all type * Added StereoDense, StereoBM and StereoSGBM doc and tests * Added Stereo tests * Added CameraModel and StereoCameraModel tests * Added doc and test for Statistics * Added doc/tests for Signature * Added doc/test for SensorEvent, added doc for SensorCaptureInfo * Added doc to SensorData * Added SensorData tests * Added SensorCapture and SensorCaptureThread doc and tests * fixed sensordata test * updated SSC test and doc * Added doc and tests for BayesFilter class * Enabled testing on mac, updated windows testing like on linux * added test_link * fixed unresolved on windows * fixed ThreadHandle error on macos ci * Added GPS and GeodeticCoords tests * Added tests for compression * Added Odometry tests (base class only) * Added DBDriver tests * Added coverage report * uniformized test names * fixing concurancy and coverage ci * dont built tools, examples and app for coverage build * fixed report tool rebuilt without qt compilation error * updated coverage option * updated coverage config * added doc CI job * fixing windows and mac ci errors * Added DBDriverSqlite3 tests * Added IMU tests * Added Graph tests * fixing flaky macos test * Added IMUThread and IMUFilter tests * Added Landmarks tests * Added LASWriter tests * fixing seed flaky test * fixing flaky macos timing tests * Added LocalGrid tests * Added LocalGridMaker tests * fixing ci errors * Added GlobalMap tests * Added doc for EnvSensor * Added Features2D tests * Added Registration tests * Added RegistrationVis tests * Added doc for Rtabmap and Memory classes * Added Memory and Rtabmap tests * making some tests less flaky * lcov 1.14 support * updated compatible tool arguments * Added integration tests (RGB-D, Stereo, Lidar2d, Lidar3d) * More octomap checks * Refactored how/when python interpretor is created to simplify library usage * Added python tests * fixed some flaky tests * suppressed some third party related warnings * fixed ceres tests * more flaky fixes * Fixing tests without libpointmatcher * Added RANSAC rejection filter to PCL ICP * fixing multi platform flakiness * Added test to detect regression * Fixing windows pcl link error * fixed some macos flakiness * bigger 2D2D registration error on opencv 4.6.0 * flakiness * fixing flaky tests on windows and mac * flaky thread test on slow mac VM * windows slow test * fixing more ci erros * fxing temp dir on windows * Added Optimizer tests and discovered some bugs (fixed) * fixing flaky tests in mac and windows * Added Optimizer doc * Added GTSAM BA, updated Ceres to use g2o ba parameters. Renamed g2o's ba related parameters to Optimizer group and used by both gtsam and ceres. * fixing build without gtsam * fixing home dir * fixing python ci isssues * Added multicam ba tests * Added Ceres multicam BA support * Aligned BundleAdjustment parameters with Optimizer/Strategy to avoid confusion in the code * Added BA integration test * Added robust graph optimization integration test * Added loop3it test * Added stereo20Hz test * Added smartfactor gtsam * Fixed bugged check and warn if python didn't return any descriptors * Fixing gtsam version build issues * fixing tilt on windows ci * loosing ceres integration test for ci * mac ci flakiness * updating missing param in gui * updating test bound for mac * added appearance-based tests, set min gftt quality to quality level * testing more stuff * improving features2d tests * ci flakiness * fixing flaky ci * ci fixes * flaky fixes * Added RegistrationIcp tests * Added icp integration test with real-worl corridor like env * intermediate nodes * fixing enum * Updated test to catch #1714 * Fixed 2d corridor failing on pcl * flaky pnp test * flaky brisk test * Set rtabmap_integration test as long * updating loop closure test * flaky ci tests * TEsting roundtrip g2o/toro save/load * loosing test bound * fixed cuda capable checks * flaky tests * Debugging test hanging * more debugging stuff * updating limit * windows: disabled cuda on ci to avoid incompatible driver issue. Fixing a bad test mem allocation * trying fixing cuda hanging issue * fixing ci flakyness * flaky tests * Updated BOW flaky tests by checking min precision/recall instead of recall@100precision. Fixed signature test * CameraModel::load() test initRectificationMap param * test dbdriver load dictionary idsOnly * Memory: test keepLinkedInDb param * added dummyDictionary tests * test intermediate nodes count * Added MarkerDetector tests * reverted breaking change of UMutex and USemaphore * Features2d: fixed compiltion warnings with clang about override * clang warnings * fixing test build with pcl 1.8 * g2o and gtsam build errors on android * opencv5 test fixes * disabled testing for ios and android builds * normalized endline characters for easier diff * added LF CRLF rule * bump 0.23.10. fixing doc version * Publish rtabmap website doc from ci * fixing MSCVC build error * macos icp flaky test * fixing ceres macos test bound * ficing more flaky tests * fixing opencv5 related test errors. Also fixed an actual bug in ENU_WGS84ToGeocentric_WGS84() * added comment about mrpt change * removed rosdoc2 (will add it for rtabmap_ros later) * fixing website style * updated download links * locally deployable website with api * sweep doxygen issues * improved/revised doxygen main pages * removed examples empty page * Updated doxygen style * more concise doxygen groups * added api link on main readme * fixing utilite test error * fixing CommonFilteringGroundNormalsUp test * updated precisionRecall test bounds for Freak and brief descriptors * fixing scale check in ba tests * disabled tests on windows cuda build (missing dlls amd runner cannot test cuda anyway) * ceres: missing suitesparse dep in windows ci * adjusting recall thr for fast/freak * ficing more flaky tests * fixing flaky tests * disabled coverage in ros ci * Enable integration tests for ros ci jobs * loosing up some threshold for failing tests * trigger cache * fixing test data in ros ci. Updated flaky test for mac * slaking some test limit * Fixed rtabmap-detectMoreLoopClosures inverted output value * loosing up sift recall on mac * optimizer re-ordered distribution for reproducible results (mac g2o) * macos dump test crash log * combining all tests to save time on shared library reload. Also fixed Logs with missing arguments. * Added ENABLE_FORMAT_ERRORS cmake option * do test only one time * fixed all format warnings * format security android build errors * less verbose tests * updated ImuUThread test * fixed a log * Fixed libpointmatcher 2d normals eigen issue * Fixing libpointmatcher conversion issues * fixing libpointmatcher test on windows ci * cleanup comments, relax some test thr * disabled sequoia-intel ci build (too flaky, would need extensive testing directly on that machine)
3050 lines
97 KiB
C++
3050 lines
97 KiB
C++
/*
|
|
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions 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.
|
|
* Neither the name of the Universite de Sherbrooke nor the
|
|
names of its contributors may 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 COPYRIGHT HOLDER 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.
|
|
*/
|
|
|
|
#include "rtabmap/core/Features2d.h"
|
|
#include "rtabmap/core/util3d.h"
|
|
#include "rtabmap/core/util3d_features.h"
|
|
#include "rtabmap/core/Stereo.h"
|
|
#include "rtabmap/core/util2d.h"
|
|
#include "rtabmap/utilite/UStl.h"
|
|
#include "rtabmap/utilite/UConversion.h"
|
|
#include "rtabmap/utilite/ULogger.h"
|
|
#include "rtabmap/utilite/UMath.h"
|
|
#include "rtabmap/utilite/ULogger.h"
|
|
#include "rtabmap/utilite/UTimer.h"
|
|
#include <opencv2/core/version.hpp>
|
|
#include <opencv2/opencv_modules.hpp>
|
|
|
|
#ifdef RTABMAP_ORB_OCTREE
|
|
#include "opencv/ORBextractor.h"
|
|
#endif
|
|
|
|
#ifdef RTABMAP_TORCH
|
|
#include "superpoint_torch/SuperPoint.h"
|
|
#endif
|
|
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
|
|
#include "superpoint_rpautrat/SuperpointRpautrat.h"
|
|
#endif
|
|
|
|
#ifdef RTABMAP_PYTHON
|
|
#include "python/PyDetector.h"
|
|
#endif
|
|
|
|
#if CV_MAJOR_VERSION < 3
|
|
#include "opencv/Orb.h"
|
|
#ifdef HAVE_OPENCV_GPU
|
|
#include <opencv2/gpu/gpu.hpp>
|
|
#endif
|
|
#else
|
|
#include <opencv2/core/cuda.hpp>
|
|
#endif
|
|
|
|
#ifdef HAVE_OPENCV_NONFREE
|
|
#if CV_MAJOR_VERSION == 2 && CV_MINOR_VERSION >=4
|
|
#include <opencv2/nonfree/gpu.hpp>
|
|
#include <opencv2/nonfree/features2d.hpp>
|
|
#endif
|
|
#endif
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
#include <opencv2/xfeatures2d.hpp>
|
|
#include <opencv2/xfeatures2d/nonfree.hpp>
|
|
#include <opencv2/xfeatures2d/cuda.hpp>
|
|
#endif
|
|
#ifdef HAVE_OPENCV_CUDAFEATURES2D
|
|
#include <opencv2/cudafeatures2d.hpp>
|
|
#endif
|
|
#ifdef HAVE_OPENCV_CUDAIMGPROC
|
|
#include <opencv2/cudaimgproc.hpp>
|
|
#endif
|
|
|
|
#ifdef RTABMAP_FASTCV
|
|
#include <fastcv.h>
|
|
#endif
|
|
|
|
#ifdef RTABMAP_CUDASIFT
|
|
#include <cudasift/cudaImage.h>
|
|
#include <cudasift/cudaSift.h>
|
|
#endif
|
|
namespace rtabmap {
|
|
|
|
void Feature2D::filterKeypointsByDepth(
|
|
std::vector<cv::KeyPoint> & keypoints,
|
|
const cv::Mat & depth,
|
|
float minDepth,
|
|
float maxDepth)
|
|
{
|
|
cv::Mat descriptors;
|
|
filterKeypointsByDepth(keypoints, descriptors, depth, minDepth, maxDepth);
|
|
}
|
|
|
|
void Feature2D::filterKeypointsByDepth(
|
|
std::vector<cv::KeyPoint> & keypoints,
|
|
cv::Mat & descriptors,
|
|
const cv::Mat & depth,
|
|
float minDepth,
|
|
float maxDepth)
|
|
{
|
|
UASSERT(minDepth >= 0.0f);
|
|
UASSERT(maxDepth <= 0.0f || maxDepth > minDepth);
|
|
if(!depth.empty() && (descriptors.empty() || descriptors.rows == (int)keypoints.size()))
|
|
{
|
|
std::vector<cv::KeyPoint> output(keypoints.size());
|
|
std::vector<int> indexes(keypoints.size(), 0);
|
|
int oi=0;
|
|
bool isInMM = depth.type() == CV_16UC1;
|
|
for(unsigned int i=0; i<keypoints.size(); ++i)
|
|
{
|
|
int u = int(keypoints[i].pt.x+0.5f);
|
|
int v = int(keypoints[i].pt.y+0.5f);
|
|
if(u >=0 && u<depth.cols && v >=0 && v<depth.rows)
|
|
{
|
|
float d = isInMM?(float)depth.at<uint16_t>(v,u)*0.001f:depth.at<float>(v,u);
|
|
if(uIsFinite(d) && d>minDepth && (maxDepth <= 0.0f || d < maxDepth))
|
|
{
|
|
output[oi++] = keypoints[i];
|
|
indexes[i] = 1;
|
|
}
|
|
}
|
|
}
|
|
output.resize(oi);
|
|
keypoints = output;
|
|
|
|
if(!descriptors.empty() && (int)keypoints.size() != descriptors.rows)
|
|
{
|
|
if(keypoints.size() == 0)
|
|
{
|
|
descriptors = cv::Mat();
|
|
}
|
|
else
|
|
{
|
|
cv::Mat newDescriptors((int)keypoints.size(), descriptors.cols, descriptors.type());
|
|
int di = 0;
|
|
for(unsigned int i=0; i<indexes.size(); ++i)
|
|
{
|
|
if(indexes[i] == 1)
|
|
{
|
|
if(descriptors.type() == CV_32FC1)
|
|
{
|
|
memcpy(newDescriptors.ptr<float>(di++), descriptors.ptr<float>(i), descriptors.cols*sizeof(float));
|
|
}
|
|
else // CV_8UC1
|
|
{
|
|
memcpy(newDescriptors.ptr<char>(di++), descriptors.ptr<char>(i), descriptors.cols*sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
descriptors = newDescriptors;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Feature2D::filterKeypointsByDepth(
|
|
std::vector<cv::KeyPoint> & keypoints,
|
|
cv::Mat & descriptors,
|
|
std::vector<cv::Point3f> & keypoints3D,
|
|
float minDepth,
|
|
float maxDepth)
|
|
{
|
|
UDEBUG("");
|
|
//remove all keypoints/descriptors with no valid 3D points
|
|
UASSERT(((int)keypoints.size() == descriptors.rows || descriptors.empty()) &&
|
|
keypoints3D.size() == keypoints.size());
|
|
std::vector<cv::KeyPoint> validKeypoints(keypoints.size());
|
|
std::vector<cv::Point3f> validKeypoints3D(keypoints.size());
|
|
cv::Mat validDescriptors(descriptors.size(), descriptors.type());
|
|
|
|
int oi=0;
|
|
float minDepthSqr = minDepth * minDepth;
|
|
float maxDepthSqr = maxDepth * maxDepth;
|
|
for(unsigned int i=0; i<keypoints3D.size(); ++i)
|
|
{
|
|
cv::Point3f & pt = keypoints3D[i];
|
|
if(util3d::isFinite(pt))
|
|
{
|
|
float distSqr = pt.x*pt.x+pt.y*pt.y+pt.z*pt.z;
|
|
if(distSqr >= minDepthSqr && (maxDepthSqr==0.0f || distSqr <= maxDepthSqr))
|
|
{
|
|
validKeypoints[oi] = keypoints[i];
|
|
validKeypoints3D[oi] = pt;
|
|
if(!descriptors.empty())
|
|
{
|
|
descriptors.row(i).copyTo(validDescriptors.row(oi));
|
|
}
|
|
++oi;
|
|
}
|
|
}
|
|
}
|
|
UDEBUG("Removed %d invalid 3D points", (int)keypoints3D.size()-oi);
|
|
validKeypoints.resize(oi);
|
|
validKeypoints3D.resize(oi);
|
|
keypoints = validKeypoints;
|
|
keypoints3D = validKeypoints3D;
|
|
if(!descriptors.empty())
|
|
{
|
|
descriptors = validDescriptors.rowRange(0, oi).clone();
|
|
}
|
|
}
|
|
|
|
void Feature2D::filterKeypointsByDisparity(
|
|
std::vector<cv::KeyPoint> & keypoints,
|
|
const cv::Mat & disparity,
|
|
float minDisparity)
|
|
{
|
|
cv::Mat descriptors;
|
|
filterKeypointsByDisparity(keypoints, descriptors, disparity, minDisparity);
|
|
}
|
|
|
|
void Feature2D::filterKeypointsByDisparity(
|
|
std::vector<cv::KeyPoint> & keypoints,
|
|
cv::Mat & descriptors,
|
|
const cv::Mat & disparity,
|
|
float minDisparity)
|
|
{
|
|
if(!disparity.empty() && minDisparity > 0.0f && (descriptors.empty() || descriptors.rows == (int)keypoints.size()))
|
|
{
|
|
std::vector<cv::KeyPoint> output(keypoints.size());
|
|
std::vector<int> indexes(keypoints.size(), 0);
|
|
int oi=0;
|
|
for(unsigned int i=0; i<keypoints.size(); ++i)
|
|
{
|
|
int u = int(keypoints[i].pt.x+0.5f);
|
|
int v = int(keypoints[i].pt.y+0.5f);
|
|
if(u >=0 && u<disparity.cols && v >=0 && v<disparity.rows)
|
|
{
|
|
float d = disparity.type() == CV_16SC1?float(disparity.at<short>(v,u))/16.0f:disparity.at<float>(v,u);
|
|
if(d!=0.0f && uIsFinite(d) && d >= minDisparity)
|
|
{
|
|
output[oi++] = keypoints[i];
|
|
indexes[i] = 1;
|
|
}
|
|
}
|
|
}
|
|
output.resize(oi);
|
|
keypoints = output;
|
|
|
|
if(!descriptors.empty() && (int)keypoints.size() != descriptors.rows)
|
|
{
|
|
if(keypoints.size() == 0)
|
|
{
|
|
descriptors = cv::Mat();
|
|
}
|
|
else
|
|
{
|
|
cv::Mat newDescriptors((int)keypoints.size(), descriptors.cols, descriptors.type());
|
|
int di = 0;
|
|
for(unsigned int i=0; i<indexes.size(); ++i)
|
|
{
|
|
if(indexes[i] == 1)
|
|
{
|
|
if(descriptors.type() == CV_32FC1)
|
|
{
|
|
memcpy(newDescriptors.ptr<float>(di++), descriptors.ptr<float>(i), descriptors.cols*sizeof(float));
|
|
}
|
|
else // CV_8UC1
|
|
{
|
|
memcpy(newDescriptors.ptr<char>(di++), descriptors.ptr<char>(i), descriptors.cols*sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
descriptors = newDescriptors;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Feature2D::limitKeypoints(std::vector<cv::KeyPoint> & keypoints, int maxKeypoints, const cv::Size & imageSize, bool ssc)
|
|
{
|
|
cv::Mat descriptors;
|
|
limitKeypoints(keypoints, descriptors, maxKeypoints, imageSize, ssc);
|
|
}
|
|
|
|
void Feature2D::limitKeypoints(std::vector<cv::KeyPoint> & keypoints, cv::Mat & descriptors, int maxKeypoints, const cv::Size & imageSize, bool ssc)
|
|
{
|
|
std::vector<cv::Point3f> keypoints3D;
|
|
limitKeypoints(keypoints, keypoints3D, descriptors, maxKeypoints, imageSize, ssc);
|
|
}
|
|
|
|
void Feature2D::limitKeypoints(std::vector<cv::KeyPoint> & keypoints, std::vector<cv::Point3f> & keypoints3D, cv::Mat & descriptors, int maxKeypoints, const cv::Size & imageSize, bool ssc)
|
|
{
|
|
UASSERT_MSG((int)keypoints.size() == descriptors.rows || descriptors.rows == 0, uFormat("keypoints=%d descriptors=%d", (int)keypoints.size(), descriptors.rows).c_str());
|
|
UASSERT_MSG(keypoints.size() == keypoints3D.size() || keypoints3D.size() == 0, uFormat("keypoints=%d keypoints3D=%d", (int)keypoints.size(), (int)keypoints3D.size()).c_str());
|
|
if(maxKeypoints > 0 && (int)keypoints.size() > maxKeypoints)
|
|
{
|
|
UTimer timer;
|
|
int removed;
|
|
std::vector<cv::KeyPoint> kptsTmp;
|
|
std::vector<cv::Point3f> kpts3DTmp;
|
|
cv::Mat descriptorsTmp;
|
|
if(ssc)
|
|
{
|
|
ULOGGER_DEBUG("too many words (%d), removing words with SSC", (int)keypoints.size());
|
|
|
|
// Sorting keypoints by deacreasing order of strength
|
|
std::vector<float> responseVector;
|
|
for (unsigned int i = 0; i < keypoints.size(); i++)
|
|
{
|
|
responseVector.push_back(keypoints[i].response);
|
|
}
|
|
std::vector<int> indx(responseVector.size());
|
|
std::iota(std::begin(indx), std::end(indx), 0);
|
|
|
|
#if CV_MAJOR_VERSION >= 4
|
|
cv::sortIdx(responseVector, indx, cv::SORT_DESCENDING);
|
|
#else
|
|
cv::sortIdx(responseVector, indx, CV_SORT_DESCENDING);
|
|
#endif
|
|
|
|
static constexpr float tolerance = 0.1;
|
|
auto ResultVec = util2d::SSC(keypoints, maxKeypoints, tolerance, imageSize.width, imageSize.height, indx);
|
|
removed = keypoints.size()-ResultVec.size();
|
|
// retrieve final keypoints
|
|
kptsTmp.resize(ResultVec.size());
|
|
if(!keypoints3D.empty())
|
|
{
|
|
kpts3DTmp.resize(ResultVec.size());
|
|
}
|
|
if(descriptors.rows)
|
|
{
|
|
descriptorsTmp = cv::Mat(ResultVec.size(), descriptors.cols, descriptors.type());
|
|
}
|
|
for(unsigned int k=0; k<ResultVec.size(); ++k)
|
|
{
|
|
kptsTmp[k] = keypoints[ResultVec[k]];
|
|
if(keypoints3D.size())
|
|
{
|
|
kpts3DTmp[k] = keypoints3D[ResultVec[k]];
|
|
}
|
|
if(descriptors.rows)
|
|
{
|
|
if(descriptors.type() == CV_32FC1)
|
|
{
|
|
memcpy(descriptorsTmp.ptr<float>(k), descriptors.ptr<float>(ResultVec[k]), descriptors.cols*sizeof(float));
|
|
}
|
|
else
|
|
{
|
|
memcpy(descriptorsTmp.ptr<char>(k), descriptors.ptr<char>(ResultVec[k]), descriptors.cols*sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_DEBUG("too many words (%d), removing words with the hessian threshold", (int)keypoints.size());
|
|
// Remove words under the new hessian threshold
|
|
|
|
// Sort words by hessian
|
|
std::multimap<float, int> hessianMap; // <hessian,id>
|
|
for(unsigned int i = 0; i <keypoints.size(); ++i)
|
|
{
|
|
//Keep track of the data, to be easier to manage the data in the next step
|
|
hessianMap.insert(std::pair<float, int>(fabs(keypoints[i].response), i));
|
|
}
|
|
|
|
// Remove them from the signature
|
|
removed = (int)hessianMap.size()-maxKeypoints;
|
|
std::multimap<float, int>::reverse_iterator iter = hessianMap.rbegin();
|
|
kptsTmp.resize(maxKeypoints);
|
|
if(!keypoints3D.empty())
|
|
{
|
|
kpts3DTmp.resize(maxKeypoints);
|
|
}
|
|
if(descriptors.rows)
|
|
{
|
|
descriptorsTmp = cv::Mat(maxKeypoints, descriptors.cols, descriptors.type());
|
|
}
|
|
for(unsigned int k=0; k<kptsTmp.size() && iter!=hessianMap.rend(); ++k, ++iter)
|
|
{
|
|
kptsTmp[k] = keypoints[iter->second];
|
|
if(keypoints3D.size())
|
|
{
|
|
kpts3DTmp[k] = keypoints3D[iter->second];
|
|
}
|
|
if(descriptors.rows)
|
|
{
|
|
if(descriptors.type() == CV_32FC1)
|
|
{
|
|
memcpy(descriptorsTmp.ptr<float>(k), descriptors.ptr<float>(iter->second), descriptors.cols*sizeof(float));
|
|
}
|
|
else
|
|
{
|
|
memcpy(descriptorsTmp.ptr<char>(k), descriptors.ptr<char>(iter->second), descriptors.cols*sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ULOGGER_DEBUG("%d keypoints removed, (kept %d), minimum response=%f", removed, (int)kptsTmp.size(), !ssc&&kptsTmp.size()?kptsTmp.back().response:0.0f);
|
|
ULOGGER_DEBUG("removing words time = %f s", timer.ticks());
|
|
keypoints = kptsTmp;
|
|
keypoints3D = kpts3DTmp;
|
|
if(descriptors.rows)
|
|
{
|
|
descriptors = descriptorsTmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Feature2D::limitKeypoints(const std::vector<cv::KeyPoint> & keypoints, std::vector<bool> & inliers, int maxKeypoints, const cv::Size & imageSize, bool ssc)
|
|
{
|
|
if(maxKeypoints > 0 && (int)keypoints.size() > maxKeypoints)
|
|
{
|
|
UTimer timer;
|
|
float minimumHessian = 0.0f;
|
|
int removed;
|
|
inliers.resize(keypoints.size(), false);
|
|
if(ssc)
|
|
{
|
|
ULOGGER_DEBUG("too many words (%d), removing words with SSC", (int)keypoints.size());
|
|
|
|
// Sorting keypoints by deacreasing order of strength
|
|
std::vector<float> responseVector;
|
|
for (unsigned int i = 0; i < keypoints.size(); i++)
|
|
{
|
|
responseVector.push_back(keypoints[i].response);
|
|
}
|
|
std::vector<int> indx(responseVector.size());
|
|
std::iota(std::begin(indx), std::end(indx), 0);
|
|
|
|
#if CV_MAJOR_VERSION >= 4
|
|
cv::sortIdx(responseVector, indx, cv::SORT_DESCENDING);
|
|
#else
|
|
cv::sortIdx(responseVector, indx, CV_SORT_DESCENDING);
|
|
#endif
|
|
|
|
static constexpr float tolerance = 0.1;
|
|
auto ResultVec = util2d::SSC(keypoints, maxKeypoints, tolerance, imageSize.width, imageSize.height, indx);
|
|
removed = keypoints.size()-ResultVec.size();
|
|
for(unsigned int k=0; k<ResultVec.size(); ++k)
|
|
{
|
|
inliers[ResultVec[k]] = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_DEBUG("too much words (%d), removing words with the hessian threshold", (int)keypoints.size());
|
|
// Remove words under the new hessian threshold
|
|
|
|
// Sort words by hessian
|
|
std::multimap<float, int> hessianMap; // <hessian,id>
|
|
for(unsigned int i = 0; i<keypoints.size(); ++i)
|
|
{
|
|
//Keep track of the data, to be easier to manage the data in the next step
|
|
hessianMap.insert(std::pair<float, int>(fabs(keypoints[i].response), i));
|
|
}
|
|
|
|
// Keep keypoints with highest response
|
|
removed = (int)hessianMap.size()-maxKeypoints;
|
|
std::multimap<float, int>::reverse_iterator iter = hessianMap.rbegin();
|
|
for(int k=0; k<maxKeypoints && iter!=hessianMap.rend(); ++k, ++iter)
|
|
{
|
|
inliers[iter->second] = true;
|
|
minimumHessian = iter->first;
|
|
}
|
|
}
|
|
ULOGGER_DEBUG("%d keypoints removed, (kept %d), minimum response=%f", removed, (int)(keypoints.size()-removed), minimumHessian);
|
|
ULOGGER_DEBUG("filter keypoints time = %f s", timer.ticks());
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_DEBUG("keeping all %d keypoints", (int)keypoints.size());
|
|
inliers.resize(keypoints.size(), true);
|
|
}
|
|
}
|
|
|
|
void Feature2D::limitKeypoints(const std::vector<cv::KeyPoint> & keypoints, std::vector<bool> & inliers, int maxKeypoints, const cv::Size & imageSize, int gridRows, int gridCols, bool ssc)
|
|
{
|
|
if(maxKeypoints <= 0 || (int)keypoints.size() <= maxKeypoints)
|
|
{
|
|
inliers.resize(keypoints.size(), true);
|
|
return;
|
|
}
|
|
UASSERT(gridCols>=1 && gridRows >=1);
|
|
UASSERT(imageSize.height>gridRows && imageSize.width>gridCols);
|
|
int rowSize = imageSize.height / gridRows;
|
|
int colSize = imageSize.width / gridCols;
|
|
int maxKeypointsPerCell = maxKeypoints / (gridRows * gridCols);
|
|
std::vector<std::vector<cv::KeyPoint> > keypointsPerCell(gridRows * gridCols);
|
|
std::vector<std::vector<int> > indexesPerCell(gridRows * gridCols);
|
|
for(size_t i=0; i<keypoints.size(); ++i)
|
|
{
|
|
int cellRow = int(keypoints[i].pt.y)/rowSize;
|
|
int cellCol = int(keypoints[i].pt.x)/colSize;
|
|
UASSERT(cellRow >=0 && cellRow < gridRows);
|
|
UASSERT(cellCol >=0 && cellCol < gridCols);
|
|
|
|
keypointsPerCell[cellRow*gridCols + cellCol].push_back(keypoints[i]);
|
|
indexesPerCell[cellRow*gridCols + cellCol].push_back(i);
|
|
}
|
|
inliers.resize(keypoints.size(), false);
|
|
for(size_t i=0; i<keypointsPerCell.size(); ++i)
|
|
{
|
|
std::vector<bool> inliersCell;
|
|
limitKeypoints(keypointsPerCell[i], inliersCell, maxKeypointsPerCell, cv::Size(colSize, rowSize), ssc);
|
|
for(size_t j=0; j<inliersCell.size(); ++j)
|
|
{
|
|
if(inliersCell[j])
|
|
{
|
|
inliers.at(indexesPerCell[i][j]) = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cv::Rect Feature2D::computeRoi(const cv::Mat & image, const std::string & roiRatios)
|
|
{
|
|
return util2d::computeRoi(image, roiRatios);
|
|
}
|
|
|
|
cv::Rect Feature2D::computeRoi(const cv::Mat & image, const std::vector<float> & roiRatios)
|
|
{
|
|
return util2d::computeRoi(image, roiRatios);
|
|
}
|
|
|
|
/////////////////////
|
|
// Feature2D
|
|
/////////////////////
|
|
Feature2D::Feature2D(const ParametersMap & parameters) :
|
|
maxFeatures_(Parameters::defaultKpMaxFeatures()),
|
|
SSC_(Parameters::defaultKpSSC()),
|
|
_maxDepth(Parameters::defaultKpMaxDepth()),
|
|
_minDepth(Parameters::defaultKpMinDepth()),
|
|
_roiRatios(std::vector<float>(4, 0.0f)),
|
|
_subPixWinSize(Parameters::defaultKpSubPixWinSize()),
|
|
_subPixIterations(Parameters::defaultKpSubPixIterations()),
|
|
_subPixEps(Parameters::defaultKpSubPixEps()),
|
|
gridRows_(Parameters::defaultKpGridRows()),
|
|
gridCols_(Parameters::defaultKpGridCols())
|
|
{
|
|
_stereo = new Stereo(parameters);
|
|
this->parseParameters(parameters);
|
|
}
|
|
Feature2D::~Feature2D()
|
|
{
|
|
delete _stereo;
|
|
}
|
|
void Feature2D::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
uInsert(parameters_, parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kKpMaxFeatures(), maxFeatures_);
|
|
Parameters::parse(parameters, Parameters::kKpSSC(), SSC_);
|
|
Parameters::parse(parameters, Parameters::kKpMaxDepth(), _maxDepth);
|
|
Parameters::parse(parameters, Parameters::kKpMinDepth(), _minDepth);
|
|
Parameters::parse(parameters, Parameters::kKpSubPixWinSize(), _subPixWinSize);
|
|
Parameters::parse(parameters, Parameters::kKpSubPixIterations(), _subPixIterations);
|
|
Parameters::parse(parameters, Parameters::kKpSubPixEps(), _subPixEps);
|
|
Parameters::parse(parameters, Parameters::kKpGridRows(), gridRows_);
|
|
Parameters::parse(parameters, Parameters::kKpGridCols(), gridCols_);
|
|
|
|
UASSERT(gridRows_ >= 1 && gridCols_>=1);
|
|
|
|
// convert ROI from string to vector
|
|
ParametersMap::const_iterator iter;
|
|
if((iter=parameters.find(Parameters::kKpRoiRatios())) != parameters.end())
|
|
{
|
|
std::list<std::string> strValues = uSplit(iter->second, ' ');
|
|
if(strValues.size() != 4)
|
|
{
|
|
ULOGGER_ERROR("The number of values must be 4 (roi=\"%s\")", iter->second.c_str());
|
|
}
|
|
else
|
|
{
|
|
std::vector<float> tmpValues(4);
|
|
unsigned int i=0;
|
|
for(std::list<std::string>::iterator jter = strValues.begin(); jter!=strValues.end(); ++jter)
|
|
{
|
|
tmpValues[i] = uStr2Float(*jter);
|
|
++i;
|
|
}
|
|
|
|
if(tmpValues[0] >= 0 && tmpValues[0] < 1 && tmpValues[0] < 1.0f-tmpValues[1] &&
|
|
tmpValues[1] >= 0 && tmpValues[1] < 1 && tmpValues[1] < 1.0f-tmpValues[0] &&
|
|
tmpValues[2] >= 0 && tmpValues[2] < 1 && tmpValues[2] < 1.0f-tmpValues[3] &&
|
|
tmpValues[3] >= 0 && tmpValues[3] < 1 && tmpValues[3] < 1.0f-tmpValues[2])
|
|
{
|
|
_roiRatios = tmpValues;
|
|
}
|
|
else
|
|
{
|
|
ULOGGER_ERROR("The roi ratios are not valid (roi=\"%s\")", iter->second.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
//stereo
|
|
UASSERT(_stereo != 0);
|
|
if((iter=parameters.find(Parameters::kStereoOpticalFlow())) != parameters.end())
|
|
{
|
|
delete _stereo;
|
|
_stereo = Stereo::create(parameters_);
|
|
}
|
|
else
|
|
{
|
|
_stereo->parseParameters(parameters);
|
|
}
|
|
}
|
|
Feature2D * Feature2D::create(const ParametersMap & parameters)
|
|
{
|
|
int type = Parameters::defaultKpDetectorStrategy();
|
|
Parameters::parse(parameters, Parameters::kKpDetectorStrategy(), type);
|
|
return create((Feature2D::Type)type, parameters);
|
|
}
|
|
|
|
bool Feature2D::isAvailable(Feature2D::Type type)
|
|
{
|
|
// kFeatureUndef is a sentinel ("strategy not specified"); create() falls
|
|
// through to a default backend, so the type isn't really "available" as
|
|
// requested.
|
|
if(type == kFeatureUndef)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// SURF / SIFT / SURF-FREAK / SURF-DAISY require either OpenCV < 3.4.11
|
|
// (built-in) OR the xfeatures2d module + RTABMAP_NONFREE for OpenCV >= 3.4.11.
|
|
#if CV_MAJOR_VERSION < 3 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION <= 3) || (CV_MAJOR_VERSION == 3 && (CV_MINOR_VERSION < 4 || (CV_MINOR_VERSION==4 && CV_SUBMINOR_VERSION<11)))
|
|
#ifndef RTABMAP_NONFREE
|
|
if(type == kFeatureSurf || type == kFeatureSift || type == kFeatureSurfFreak || type == kFeatureSurfDaisy)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
#else
|
|
#ifndef RTABMAP_NONFREE
|
|
if(type == kFeatureSurf || type == kFeatureSurfFreak || type == kFeatureSurfDaisy)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(HAVE_OPENCV_XFEATURES2D) && CV_MAJOR_VERSION >= 3
|
|
if(type == kFeatureFastBrief ||
|
|
type == kFeatureFastFreak ||
|
|
type == kFeatureGfttBrief ||
|
|
type == kFeatureGfttFreak ||
|
|
type == kFeatureSurfFreak ||
|
|
type == kFeatureGfttDaisy ||
|
|
type == kFeatureSurfDaisy)
|
|
{
|
|
return false;
|
|
}
|
|
#elif CV_MAJOR_VERSION < 3
|
|
if(type == kFeatureKaze ||
|
|
type == kFeatureGfttDaisy ||
|
|
type == kFeatureSurfDaisy)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#ifndef RTABMAP_ORB_OCTREE
|
|
if(type == kFeatureOrbOctree) return false;
|
|
#endif
|
|
#ifndef RTABMAP_TORCH
|
|
if(type == kFeatureSuperPointTorch) return false;
|
|
#endif
|
|
#if !defined(RTABMAP_TORCH) || !defined(RTABMAP_PYTHON)
|
|
if(type == kFeatureSuperPointRpautrat) return false;
|
|
#endif
|
|
#ifndef RTABMAP_PYTHON
|
|
if(type == kFeaturePyDetector) return false;
|
|
#endif
|
|
return true;
|
|
}
|
|
Feature2D * Feature2D::create(Feature2D::Type type, const ParametersMap & parameters)
|
|
{
|
|
|
|
// NONFREE checks
|
|
#if CV_MAJOR_VERSION < 3 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION <= 3) || (CV_MAJOR_VERSION == 3 && (CV_MINOR_VERSION < 4 || (CV_MINOR_VERSION==4 && CV_SUBMINOR_VERSION<11)))
|
|
|
|
#ifndef RTABMAP_NONFREE
|
|
if(type == Feature2D::kFeatureSurf || type == Feature2D::kFeatureSift || type == Feature2D::kFeatureSurfFreak || type == Feature2D::kFeatureSurfDaisy)
|
|
{
|
|
#if CV_MAJOR_VERSION < 3
|
|
UWARN("SURF and SIFT features cannot be used because OpenCV was not built with nonfree module. GFTT/ORB is used instead.");
|
|
#else
|
|
UWARN("SURF and SIFT features cannot be used because OpenCV was not built with xfeatures2d module. GFTT/ORB is used instead.");
|
|
#endif
|
|
type = Feature2D::kFeatureGfttOrb;
|
|
}
|
|
#endif
|
|
|
|
#else // >= 4.4.0 >= 3.4.11
|
|
|
|
#ifndef RTABMAP_NONFREE
|
|
if(type == Feature2D::kFeatureSurf)
|
|
{
|
|
UWARN("SURF features cannot be used because OpenCV was not built with nonfree module. SIFT is used instead.");
|
|
type = Feature2D::kFeatureSift;
|
|
}
|
|
else if(type == Feature2D::kFeatureSurfFreak || type == Feature2D::kFeatureSurfDaisy)
|
|
{
|
|
UWARN("SURF detector cannot be used because OpenCV was not built with nonfree module. GFTT/ORB is used instead.");
|
|
type = Feature2D::kFeatureGfttOrb;
|
|
}
|
|
#endif
|
|
|
|
#endif // >= 4.4.0 >= 3.4.11
|
|
|
|
#if !defined(HAVE_OPENCV_XFEATURES2D) && CV_MAJOR_VERSION >= 3
|
|
if(type == Feature2D::kFeatureFastBrief ||
|
|
type == Feature2D::kFeatureFastFreak ||
|
|
type == Feature2D::kFeatureGfttBrief ||
|
|
type == Feature2D::kFeatureGfttFreak ||
|
|
type == Feature2D::kFeatureSurfFreak ||
|
|
type == Feature2D::kFeatureGfttDaisy ||
|
|
type == Feature2D::kFeatureSurfDaisy)
|
|
{
|
|
UWARN("BRIEF, FREAK and DAISY features cannot be used because OpenCV was not built with xfeatures2d module. GFTT/ORB is used instead.");
|
|
type = Feature2D::kFeatureGfttOrb;
|
|
}
|
|
#elif CV_MAJOR_VERSION < 3
|
|
if(type == Feature2D::kFeatureKaze)
|
|
{
|
|
#ifdef RTABMAP_NONFREE
|
|
UWARN("KAZE detector/descriptor can be used only with OpenCV3. SURF is used instead.");
|
|
type = Feature2D::kFeatureSurf;
|
|
#else
|
|
UWARN("KAZE detector/descriptor can be used only with OpenCV3. GFTT/ORB is used instead.");
|
|
type = Feature2D::kFeatureGfttOrb;
|
|
#endif
|
|
}
|
|
if(type == Feature2D::kFeatureGfttDaisy || type == Feature2D::kFeatureSurfDaisy)
|
|
{
|
|
UWARN("DAISY detector/descriptor can be used only with OpenCV3. GFTT/BRIEF is used instead.");
|
|
type = Feature2D::kFeatureGfttBrief;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef RTABMAP_ORB_OCTREE
|
|
if(type == Feature2D::kFeatureOrbOctree)
|
|
{
|
|
UWARN("ORB OcTree feature cannot be used as RTAB-Map is not built with the option enabled. GFTT/ORB is used instead.");
|
|
type = Feature2D::kFeatureGfttOrb;
|
|
}
|
|
#endif
|
|
|
|
#ifndef RTABMAP_TORCH
|
|
if(type == Feature2D::kFeatureSuperPointTorch)
|
|
{
|
|
UWARN("SuperPoint Torch feature cannot be used as RTAB-Map is not built with the option enabled. GFTT/ORB is used instead.");
|
|
type = Feature2D::kFeatureGfttOrb;
|
|
}
|
|
#endif
|
|
|
|
Feature2D * feature2D = 0;
|
|
switch(type)
|
|
{
|
|
case Feature2D::kFeatureSurf:
|
|
feature2D = new SURF(parameters);
|
|
break;
|
|
case Feature2D::kFeatureSift:
|
|
feature2D = new SIFT(parameters);
|
|
break;
|
|
case Feature2D::kFeatureOrb:
|
|
feature2D = new ORB(parameters);
|
|
break;
|
|
case Feature2D::kFeatureFastBrief:
|
|
feature2D = new FAST_BRIEF(parameters);
|
|
break;
|
|
case Feature2D::kFeatureFastFreak:
|
|
feature2D = new FAST_FREAK(parameters);
|
|
break;
|
|
case Feature2D::kFeatureGfttFreak:
|
|
feature2D = new GFTT_FREAK(parameters);
|
|
break;
|
|
case Feature2D::kFeatureGfttBrief:
|
|
feature2D = new GFTT_BRIEF(parameters);
|
|
break;
|
|
case Feature2D::kFeatureGfttOrb:
|
|
feature2D = new GFTT_ORB(parameters);
|
|
break;
|
|
case Feature2D::kFeatureBrisk:
|
|
feature2D = new BRISK(parameters);
|
|
break;
|
|
case Feature2D::kFeatureKaze:
|
|
feature2D = new KAZE(parameters);
|
|
break;
|
|
case Feature2D::kFeatureOrbOctree:
|
|
feature2D = new ORBOctree(parameters);
|
|
break;
|
|
#ifdef RTABMAP_TORCH
|
|
case Feature2D::kFeatureSuperPointTorch:
|
|
feature2D = new SuperPointTorch(parameters);
|
|
break;
|
|
#endif
|
|
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
|
|
case Feature2D::kFeatureSuperPointRpautrat:
|
|
feature2D = new SuperPointRpautrat(parameters);
|
|
break;
|
|
#endif
|
|
case Feature2D::kFeatureSurfFreak:
|
|
feature2D = new SURF_FREAK(parameters);
|
|
break;
|
|
case Feature2D::kFeatureGfttDaisy:
|
|
feature2D = new GFTT_DAISY(parameters);
|
|
break;
|
|
case Feature2D::kFeatureSurfDaisy:
|
|
feature2D = new SURF_DAISY(parameters);
|
|
break;
|
|
#ifdef RTABMAP_PYTHON
|
|
case Feature2D::kFeaturePyDetector:
|
|
feature2D = new PyDetector(parameters);
|
|
break;
|
|
#endif
|
|
#ifdef RTABMAP_NONFREE
|
|
default:
|
|
feature2D = new SURF(parameters);
|
|
type = Feature2D::kFeatureSurf;
|
|
break;
|
|
#else
|
|
default:
|
|
feature2D = new ORB(parameters);
|
|
type = Feature2D::kFeatureGfttOrb;
|
|
break;
|
|
#endif
|
|
|
|
}
|
|
return feature2D;
|
|
}
|
|
|
|
std::vector<cv::KeyPoint> Feature2D::generateKeypoints(const cv::Mat & image, const cv::Mat & maskIn)
|
|
{
|
|
UASSERT(!image.empty());
|
|
UASSERT(image.type() == CV_8UC1);
|
|
|
|
cv::Mat mask;
|
|
if(!maskIn.empty())
|
|
{
|
|
if(maskIn.type()==CV_16UC1 || maskIn.type() == CV_32FC1)
|
|
{
|
|
mask = cv::Mat::zeros(maskIn.rows, maskIn.cols, CV_8UC1);
|
|
for(int i=0; i<(int)mask.total(); ++i)
|
|
{
|
|
float value = 0.0f;
|
|
if(maskIn.type()==CV_16UC1)
|
|
{
|
|
if(((unsigned short*)maskIn.data)[i] > 0 &&
|
|
((unsigned short*)maskIn.data)[i] < std::numeric_limits<unsigned short>::max())
|
|
{
|
|
value = float(((unsigned short*)maskIn.data)[i])*0.001f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
value = ((float*)maskIn.data)[i];
|
|
}
|
|
|
|
if(value>_minDepth &&
|
|
(_maxDepth == 0.0f || value <= _maxDepth) &&
|
|
uIsFinite(value))
|
|
{
|
|
((unsigned char*)mask.data)[i] = 255; // ORB uses 255 to handle pyramids
|
|
}
|
|
}
|
|
}
|
|
else if(maskIn.type()==CV_8UC1)
|
|
{
|
|
// assume a standard mask
|
|
mask = maskIn;
|
|
}
|
|
else
|
|
{
|
|
UERROR("Wrong mask type (%d)! Should be 8UC1, 16UC1 or 32FC1.", maskIn.type());
|
|
}
|
|
}
|
|
|
|
UASSERT(mask.empty() || (mask.cols == image.cols && mask.rows == image.rows));
|
|
|
|
std::vector<cv::KeyPoint> keypoints;
|
|
UTimer timer;
|
|
cv::Rect globalRoi = Feature2D::computeRoi(image, _roiRatios);
|
|
if(!(globalRoi.width && globalRoi.height))
|
|
{
|
|
globalRoi = cv::Rect(0,0,image.cols, image.rows);
|
|
}
|
|
|
|
// Get keypoints
|
|
int rowSize = globalRoi.height / gridRows_;
|
|
int colSize = globalRoi.width / gridCols_;
|
|
int maxFeatures = maxFeatures_ / (gridRows_ * gridCols_);
|
|
for (int i = 0; i<gridRows_; ++i)
|
|
{
|
|
for (int j = 0; j<gridCols_; ++j)
|
|
{
|
|
cv::Rect roi(globalRoi.x + j*colSize, globalRoi.y + i*rowSize, colSize, rowSize);
|
|
std::vector<cv::KeyPoint> subKeypoints;
|
|
subKeypoints = this->generateKeypointsImpl(image, roi, mask);
|
|
if (this->getType() != Feature2D::Type::kFeaturePyDetector && this->getType() != Feature2D::Type::kFeatureSuperPointRpautrat)
|
|
{
|
|
limitKeypoints(subKeypoints, maxFeatures, roi.size(), this->getSSC());
|
|
}
|
|
if(roi.x || roi.y)
|
|
{
|
|
// Adjust keypoint position to raw image
|
|
for(std::vector<cv::KeyPoint>::iterator iter=subKeypoints.begin(); iter!=subKeypoints.end(); ++iter)
|
|
{
|
|
iter->pt.x += roi.x;
|
|
iter->pt.y += roi.y;
|
|
}
|
|
}
|
|
keypoints.insert( keypoints.end(), subKeypoints.begin(), subKeypoints.end() );
|
|
}
|
|
}
|
|
UDEBUG("Keypoints extraction time = %f s, keypoints extracted = %d (grid=%dx%d, mask empty=%d)",
|
|
timer.ticks(), (int)keypoints.size(), gridCols_, gridRows_, mask.empty()?1:0);
|
|
|
|
if(keypoints.size() && _subPixWinSize > 0 && _subPixIterations > 0)
|
|
{
|
|
std::vector<cv::Point2f> corners;
|
|
cv::KeyPoint::convert(keypoints, corners);
|
|
cv::cornerSubPix( image, corners,
|
|
cv::Size( _subPixWinSize, _subPixWinSize ),
|
|
cv::Size( -1, -1 ),
|
|
cv::TermCriteria( cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, _subPixIterations, _subPixEps ) );
|
|
|
|
for(unsigned int i=0;i<corners.size(); ++i)
|
|
{
|
|
keypoints[i].pt = corners[i];
|
|
}
|
|
UDEBUG("subpixel time = %f s", timer.ticks());
|
|
}
|
|
|
|
return keypoints;
|
|
}
|
|
|
|
cv::Mat Feature2D::generateDescriptors(
|
|
const cv::Mat & image,
|
|
std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
cv::Mat descriptors;
|
|
if(keypoints.size())
|
|
{
|
|
UASSERT(!image.empty());
|
|
UASSERT(image.type() == CV_8UC1);
|
|
descriptors = generateDescriptorsImpl(image, keypoints);
|
|
if(descriptors.rows != (int)keypoints.size())
|
|
{
|
|
UWARN("Descriptor extraction returned %d rows for %d keypoints — "
|
|
"clearing keypoints to keep them in sync.",
|
|
descriptors.rows, (int)keypoints.size());
|
|
keypoints.clear();
|
|
descriptors = cv::Mat();
|
|
}
|
|
else {
|
|
UDEBUG("Descriptors extracted = %d, remaining kpts=%d", descriptors.rows, (int)keypoints.size());
|
|
}
|
|
}
|
|
return descriptors;
|
|
}
|
|
|
|
std::vector<cv::Point3f> Feature2D::generateKeypoints3D(
|
|
const SensorData & data,
|
|
const std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
std::vector<cv::Point3f> keypoints3D;
|
|
if(keypoints.size())
|
|
{
|
|
if(!data.rightRaw().empty() && !data.imageRaw().empty() &&
|
|
!data.stereoCameraModels().empty() &&
|
|
data.stereoCameraModels()[0].isValidForProjection())
|
|
{
|
|
//stereo
|
|
cv::Mat imageLeft = data.imageRaw();
|
|
cv::Mat imageRight = data.rightRaw();
|
|
#ifdef HAVE_OPENCV_CUDEV
|
|
cv::cuda::GpuMat d_imageLeft;
|
|
cv::cuda::GpuMat d_imageRight;
|
|
if(_stereo->isGpuEnabled())
|
|
{
|
|
d_imageLeft = data.imageRawGpu();
|
|
if(d_imageLeft.empty()) {
|
|
d_imageLeft = cv::cuda::GpuMat(imageLeft);
|
|
}
|
|
// convert to grayscale if not already
|
|
if(d_imageLeft.channels() > 1) {
|
|
cv::cuda::GpuMat tmp;
|
|
cv::cuda::cvtColor(d_imageLeft, tmp, cv::COLOR_BGR2GRAY);
|
|
d_imageLeft = tmp;
|
|
}
|
|
|
|
d_imageRight = data.depthOrRightRawGpu();
|
|
if(d_imageRight.empty()) {
|
|
d_imageRight = cv::cuda::GpuMat(imageRight);
|
|
}
|
|
// convert to grayscale if not already
|
|
if(d_imageRight.channels() > 1) {
|
|
cv::cuda::GpuMat tmp;
|
|
cv::cuda::cvtColor(d_imageRight, tmp, cv::COLOR_BGR2GRAY);
|
|
d_imageRight = tmp;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// convert to grayscale
|
|
if(imageLeft.channels() > 1)
|
|
{
|
|
cv::cvtColor(data.imageRaw(), imageLeft, cv::COLOR_BGR2GRAY);
|
|
}
|
|
if(imageRight.channels() > 1)
|
|
{
|
|
cv::cvtColor(data.rightRaw(), imageRight, cv::COLOR_BGR2GRAY);
|
|
}
|
|
}
|
|
|
|
std::vector<cv::Point2f> leftCorners;
|
|
cv::KeyPoint::convert(keypoints, leftCorners);
|
|
|
|
std::vector<cv::Point2f> rightCorners;
|
|
|
|
if(data.stereoCameraModels().size() == 1)
|
|
{
|
|
std::vector<unsigned char> status;
|
|
#ifdef HAVE_OPENCV_CUDEV
|
|
if(_stereo->isGpuEnabled())
|
|
{
|
|
rightCorners = _stereo->computeCorrespondences(
|
|
d_imageLeft,
|
|
d_imageRight,
|
|
leftCorners,
|
|
status);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
rightCorners = _stereo->computeCorrespondences(
|
|
imageLeft,
|
|
imageRight,
|
|
leftCorners,
|
|
status);
|
|
}
|
|
|
|
if(ULogger::level() >= ULogger::kWarning)
|
|
{
|
|
int rejected = 0;
|
|
for(size_t i=0; i<status.size(); ++i)
|
|
{
|
|
if(status[i]==0)
|
|
{
|
|
++rejected;
|
|
}
|
|
}
|
|
if(rejected > (int)status.size()/2)
|
|
{
|
|
UWARN("A large number (%d/%d) of stereo correspondences are rejected! "
|
|
"Optical flow may have failed because images are not calibrated, "
|
|
"the background is too far (no disparity between the images), "
|
|
"maximum disparity may be too small (%f) or that exposure between "
|
|
"left and right images is too different.",
|
|
rejected,
|
|
(int)status.size(),
|
|
_stereo->maxDisparity());
|
|
}
|
|
}
|
|
|
|
keypoints3D = util3d::generateKeypoints3DStereo(
|
|
leftCorners,
|
|
rightCorners,
|
|
data.stereoCameraModels()[0],
|
|
status,
|
|
_minDepth,
|
|
_maxDepth);
|
|
}
|
|
else
|
|
{
|
|
int subImageWith = imageLeft.cols / data.stereoCameraModels().size();
|
|
UASSERT(imageLeft.cols % subImageWith == 0);
|
|
std::vector<std::vector<cv::Point2f> > subLeftCorners(data.stereoCameraModels().size());
|
|
std::vector<std::vector<int> > subIndex(data.stereoCameraModels().size());
|
|
// Assign keypoints per camera
|
|
for(size_t i=0; i<leftCorners.size(); ++i)
|
|
{
|
|
int cameraIndex = int(leftCorners[i].x / subImageWith);
|
|
leftCorners[i].x -= cameraIndex*subImageWith;
|
|
subLeftCorners[cameraIndex].push_back(leftCorners[i]);
|
|
subIndex[cameraIndex].push_back(i);
|
|
}
|
|
|
|
keypoints3D.resize(keypoints.size());
|
|
int total = 0;
|
|
int rejected = 0;
|
|
for(size_t i=0; i<data.stereoCameraModels().size(); ++i)
|
|
{
|
|
if(!subLeftCorners[i].empty())
|
|
{
|
|
std::vector<unsigned char> status;
|
|
#ifdef HAVE_OPENCV_CUDEV
|
|
if(_stereo->isGpuEnabled())
|
|
{
|
|
rightCorners = _stereo->computeCorrespondences(
|
|
d_imageLeft.colRange(cv::Range(subImageWith*i, subImageWith*(i+1))),
|
|
d_imageRight.colRange(cv::Range(subImageWith*i, subImageWith*(i+1))),
|
|
subLeftCorners[i],
|
|
status);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
rightCorners = _stereo->computeCorrespondences(
|
|
imageLeft.colRange(cv::Range(subImageWith*i, subImageWith*(i+1))),
|
|
imageRight.colRange(cv::Range(subImageWith*i, subImageWith*(i+1))),
|
|
subLeftCorners[i],
|
|
status);
|
|
}
|
|
|
|
std::vector<cv::Point3f> subKeypoints3D = util3d::generateKeypoints3DStereo(
|
|
subLeftCorners[i],
|
|
rightCorners,
|
|
data.stereoCameraModels()[i],
|
|
status,
|
|
_minDepth,
|
|
_maxDepth);
|
|
|
|
if(ULogger::level() >= ULogger::kWarning)
|
|
{
|
|
for(size_t i=0; i<status.size(); ++i)
|
|
{
|
|
if(status[i]==0)
|
|
{
|
|
++rejected;
|
|
}
|
|
}
|
|
total+=status.size();
|
|
}
|
|
|
|
UASSERT(subIndex[i].size() == subKeypoints3D.size());
|
|
for(size_t j=0; j<subKeypoints3D.size(); ++j)
|
|
{
|
|
keypoints3D[subIndex[i][j]] = subKeypoints3D[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ULogger::level() >= ULogger::kWarning)
|
|
{
|
|
if(rejected > total/2)
|
|
{
|
|
UWARN("A large number (%d/%d) of stereo correspondences are rejected! "
|
|
"Optical flow may have failed because images are not calibrated, "
|
|
"the background is too far (no disparity between the images), "
|
|
"maximum disparity may be too small (%f) or that exposure between "
|
|
"left and right images is too different.",
|
|
rejected,
|
|
total,
|
|
_stereo->maxDisparity());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(!data.depthRaw().empty() && data.cameraModels().size())
|
|
{
|
|
keypoints3D = util3d::generateKeypoints3DDepth(
|
|
keypoints,
|
|
data.depthOrRightRaw(),
|
|
data.cameraModels(),
|
|
_minDepth,
|
|
_maxDepth);
|
|
}
|
|
}
|
|
|
|
return keypoints3D;
|
|
}
|
|
|
|
//////////////////////////
|
|
//SURF
|
|
//////////////////////////
|
|
SURF::SURF(const ParametersMap & parameters) :
|
|
hessianThreshold_(Parameters::defaultSURFHessianThreshold()),
|
|
nOctaves_(Parameters::defaultSURFOctaves()),
|
|
nOctaveLayers_(Parameters::defaultSURFOctaveLayers()),
|
|
extended_(Parameters::defaultSURFExtended()),
|
|
upright_(Parameters::defaultSURFUpright()),
|
|
gpuKeypointsRatio_(Parameters::defaultSURFGpuKeypointsRatio()),
|
|
gpuVersion_(Parameters::defaultSURFGpuVersion())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
SURF::~SURF()
|
|
{
|
|
}
|
|
|
|
void SURF::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
Feature2D::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kSURFExtended(), extended_);
|
|
Parameters::parse(parameters, Parameters::kSURFHessianThreshold(), hessianThreshold_);
|
|
Parameters::parse(parameters, Parameters::kSURFOctaveLayers(), nOctaveLayers_);
|
|
Parameters::parse(parameters, Parameters::kSURFOctaves(), nOctaves_);
|
|
Parameters::parse(parameters, Parameters::kSURFUpright(), upright_);
|
|
Parameters::parse(parameters, Parameters::kSURFGpuKeypointsRatio(), gpuKeypointsRatio_);
|
|
Parameters::parse(parameters, Parameters::kSURFGpuVersion(), gpuVersion_);
|
|
|
|
#ifdef RTABMAP_NONFREE
|
|
#if CV_MAJOR_VERSION < 3
|
|
if(gpuVersion_ && cv::gpu::getCudaEnabledDeviceCount() <= 0)
|
|
{
|
|
UWARN("GPU version of SURF not available! Using CPU version instead...");
|
|
gpuVersion_ = false;
|
|
}
|
|
#else
|
|
if(gpuVersion_ && cv::cuda::getCudaEnabledDeviceCount() <= 0)
|
|
{
|
|
UWARN("GPU version of SURF not available! Using CPU version instead...");
|
|
gpuVersion_ = false;
|
|
}
|
|
#endif
|
|
if(gpuVersion_)
|
|
{
|
|
_gpuSurf = cv::Ptr<CV_SURF_GPU>(new CV_SURF_GPU(hessianThreshold_, nOctaves_, nOctaveLayers_, extended_, gpuKeypointsRatio_, upright_));
|
|
}
|
|
else
|
|
{
|
|
#if CV_MAJOR_VERSION < 3
|
|
_surf = cv::Ptr<CV_SURF>(new CV_SURF(hessianThreshold_, nOctaves_, nOctaveLayers_, extended_, upright_));
|
|
#else
|
|
_surf = CV_SURF::create(hessianThreshold_, nOctaves_, nOctaveLayers_, extended_, upright_);
|
|
#endif
|
|
}
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV nonfree module so SURF cannot be used!");
|
|
#endif
|
|
}
|
|
|
|
bool SURF::isGpuAvailable() const
|
|
{
|
|
#ifdef RTABMAP_NONFREE
|
|
#if CV_MAJOR_VERSION < 3
|
|
return cv::gpu::getCudaEnabledDeviceCount() > 0;
|
|
#else
|
|
return cv::cuda::getCudaEnabledDeviceCount() > 0;
|
|
#endif
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
std::vector<cv::KeyPoint> SURF::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
std::vector<cv::KeyPoint> keypoints;
|
|
|
|
#ifdef RTABMAP_NONFREE
|
|
cv::Mat imgRoi(image, roi);
|
|
cv::Mat maskRoi;
|
|
if(!mask.empty())
|
|
{
|
|
maskRoi = cv::Mat(mask, roi);
|
|
}
|
|
if(gpuVersion_)
|
|
{
|
|
#if CV_MAJOR_VERSION < 3
|
|
cv::gpu::GpuMat imgGpu(imgRoi);
|
|
cv::gpu::GpuMat maskGpu(maskRoi);
|
|
(*_gpuSurf.obj)(imgGpu, maskGpu, keypoints);
|
|
#else
|
|
cv::cuda::GpuMat imgGpu(imgRoi);
|
|
cv::cuda::GpuMat maskGpu(maskRoi);
|
|
(*_gpuSurf.get())(imgGpu, maskGpu, keypoints);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
_surf->detect(imgRoi, keypoints, maskRoi);
|
|
}
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV nonfree module so SURF cannot be used!");
|
|
#endif
|
|
return keypoints;
|
|
}
|
|
|
|
cv::Mat SURF::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
#ifdef RTABMAP_NONFREE
|
|
if(gpuVersion_)
|
|
{
|
|
#if CV_MAJOR_VERSION < 3
|
|
cv::gpu::GpuMat imgGpu(image);
|
|
cv::gpu::GpuMat descriptorsGPU;
|
|
(*_gpuSurf.obj)(imgGpu, cv::gpu::GpuMat(), keypoints, descriptorsGPU, true);
|
|
#else
|
|
cv::cuda::GpuMat imgGpu(image);
|
|
cv::cuda::GpuMat descriptorsGPU;
|
|
(*_gpuSurf.get())(imgGpu, cv::cuda::GpuMat(), keypoints, descriptorsGPU, true);
|
|
#endif
|
|
|
|
// Download descriptors
|
|
if (descriptorsGPU.empty())
|
|
descriptors = cv::Mat();
|
|
else
|
|
{
|
|
UASSERT(descriptorsGPU.type() == CV_32F);
|
|
descriptors = cv::Mat(descriptorsGPU.size(), CV_32F);
|
|
descriptorsGPU.download(descriptors);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_surf->compute(image, keypoints, descriptors);
|
|
}
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV nonfree module so SURF cannot be used!");
|
|
#endif
|
|
|
|
return descriptors;
|
|
}
|
|
|
|
//////////////////////////
|
|
//SIFT
|
|
//////////////////////////
|
|
SIFT::SIFT(const ParametersMap & parameters) :
|
|
nOctaveLayers_(Parameters::defaultSIFTNOctaveLayers()),
|
|
contrastThreshold_(Parameters::defaultSIFTContrastThreshold()),
|
|
edgeThreshold_(Parameters::defaultSIFTEdgeThreshold()),
|
|
sigma_(Parameters::defaultSIFTSigma()),
|
|
preciseUpscale_(Parameters::defaultSIFTPreciseUpscale()),
|
|
rootSIFT_(Parameters::defaultSIFTRootSIFT()),
|
|
gpu_(Parameters::defaultSIFTGpu()),
|
|
gaussianThreshold_(Parameters::defaultSIFTGaussianThreshold()),
|
|
maxGaussianThreshold_(Parameters::defaultSIFTMaxGaussianThreshold()),
|
|
upscale_(Parameters::defaultSIFTUpscale()),
|
|
cudaSiftData_(0),
|
|
cudaSiftMemory_(0),
|
|
cudaSiftUpscaling_(upscale_)
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
SIFT::~SIFT()
|
|
{
|
|
#ifdef RTABMAP_CUDASIFT
|
|
if(cudaSiftData_) {
|
|
FreeSiftData(*cudaSiftData_);
|
|
delete cudaSiftData_;
|
|
}
|
|
if(cudaSiftMemory_) {
|
|
FreeSiftTempMemory(cudaSiftMemory_);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void SIFT::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
Feature2D::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kSIFTContrastThreshold(), contrastThreshold_);
|
|
Parameters::parse(parameters, Parameters::kSIFTEdgeThreshold(), edgeThreshold_);
|
|
Parameters::parse(parameters, Parameters::kSIFTNOctaveLayers(), nOctaveLayers_);
|
|
Parameters::parse(parameters, Parameters::kSIFTSigma(), sigma_);
|
|
Parameters::parse(parameters, Parameters::kSIFTPreciseUpscale(), preciseUpscale_);
|
|
Parameters::parse(parameters, Parameters::kSIFTRootSIFT(), rootSIFT_);
|
|
Parameters::parse(parameters, Parameters::kSIFTGpu(), gpu_);
|
|
Parameters::parse(parameters, Parameters::kSIFTGaussianThreshold(), gaussianThreshold_);
|
|
Parameters::parse(parameters, Parameters::kSIFTMaxGaussianThreshold(), maxGaussianThreshold_);
|
|
Parameters::parse(parameters, Parameters::kSIFTUpscale(), upscale_);
|
|
|
|
if(gpu_)
|
|
{
|
|
#ifdef RTABMAP_CUDASIFT
|
|
// Check if there is a cuda device
|
|
if(cudaSiftData_==0)
|
|
{
|
|
if(InitCuda(0, ULogger::level() == ULogger::kDebug)) {
|
|
UDEBUG("Init SiftData");
|
|
cudaSiftData_ = new SiftData();
|
|
InitSiftData(*cudaSiftData_, 8192, true, true);
|
|
}
|
|
else{
|
|
UWARN("No cuda device(s) detected, CudaSift is not available! Using SIFT CPU version instead.");
|
|
gpu_ = false;
|
|
}
|
|
}
|
|
#else
|
|
UWARN("RTAB-Map is not built with CudaSift so %s cannot be used!", Parameters::kSIFTGpu().c_str());
|
|
gpu_ = false;
|
|
#endif
|
|
}
|
|
|
|
if(!gpu_)
|
|
{
|
|
#if CV_MAJOR_VERSION < 3 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION <= 3) || (CV_MAJOR_VERSION == 3 && (CV_MINOR_VERSION < 4 || (CV_MINOR_VERSION==4 && CV_SUBMINOR_VERSION<11)))
|
|
#ifdef RTABMAP_NONFREE
|
|
#if CV_MAJOR_VERSION < 3
|
|
sift_ = cv::Ptr<CV_SIFT>(new CV_SIFT(this->getMaxFeatures(), nOctaveLayers_, contrastThreshold_, edgeThreshold_, sigma_));
|
|
#else
|
|
sift_ = CV_SIFT::create(this->getMaxFeatures(), nOctaveLayers_, contrastThreshold_, edgeThreshold_, sigma_);
|
|
#endif
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV nonfree module so SIFT cannot be used!");
|
|
#endif
|
|
#elif CV_MAJOR_VERSION>4 || (CV_MAJOR_VERSION==4 && CV_MINOR_VERSION>=8)// >=4.8
|
|
sift_ = CV_SIFT::create(this->getMaxFeatures(), nOctaveLayers_, contrastThreshold_, edgeThreshold_, sigma_, preciseUpscale_);
|
|
#else // >=4.4, >=3.4.11
|
|
sift_ = CV_SIFT::create(this->getMaxFeatures(), nOctaveLayers_, contrastThreshold_, edgeThreshold_, sigma_);
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
bool SIFT::isGpuAvailable() const
|
|
{
|
|
#ifdef RTABMAP_CUDASIFT
|
|
return cv::cuda::getCudaEnabledDeviceCount() > 0;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
std::vector<cv::KeyPoint> SIFT::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
std::vector<cv::KeyPoint> keypoints;
|
|
cv::Mat imgRoi(image, roi);
|
|
cv::Mat maskRoi;
|
|
if(!mask.empty())
|
|
{
|
|
maskRoi = cv::Mat(mask, roi);
|
|
}
|
|
#ifdef RTABMAP_CUDASIFT
|
|
if(gpu_)
|
|
{
|
|
/* Read image using OpenCV and convert to floating point. */
|
|
int w = imgRoi.cols;
|
|
int h = imgRoi.rows;
|
|
cv::Mat img_h;
|
|
imgRoi.convertTo(img_h, CV_32FC1);
|
|
CudaImage img_d;
|
|
img_d.Allocate(w, h, iAlignUp(w, 128), false, NULL, (float*)img_h.data);
|
|
img_d.Download();
|
|
|
|
// Compute number of octaves like OpenCV based on resolution
|
|
// ref: https://github.com/opencv/opencv/blob/4d665419992dda6e40364f741ae4765176b64bb0/modules/features2d/src/sift.dispatch.cpp#L538
|
|
// *** stack smashing detected *** if "-2" term is higher
|
|
int numOctaves = cvRound(std::log( (double)std::min(w*(upscale_?2:1), h*(upscale_?2:1)) ) / std::log(2.) - (upscale_?3:2));
|
|
if(numOctaves < 1) {
|
|
numOctaves = 1;
|
|
}
|
|
else if (numOctaves>7)
|
|
{
|
|
numOctaves = 7; // hard-coded limit in CudaSift
|
|
}
|
|
float initBlur = sigma_; /* Amount of initial Gaussian blurring in standard deviations */
|
|
float thresh = gaussianThreshold_; /* Threshold on difference of Gaussians for feature pruning */
|
|
float edgeLimit = edgeThreshold_;
|
|
float minScale = 0.0f; /* Minimum acceptable scale to remove fine-scale features */
|
|
UDEBUG("numOctaves=%d initBlur=%f thresh=%f edgeLimit=%f minScale=%f upScale=%s w=%d h=%d", numOctaves, initBlur, thresh, edgeLimit, minScale, upscale_?"true":"false", w, h);
|
|
|
|
if(cudaSiftMemory_ && (cudaSiftMemorySize_ != cv::Size(w, h) || cudaSiftUpscaling_ != upscale_)) {
|
|
// Resolution changed, reset buffer
|
|
FreeSiftTempMemory(cudaSiftMemory_);
|
|
cudaSiftMemory_ = 0;
|
|
}
|
|
|
|
if(cudaSiftMemory_ == 0) {
|
|
cudaSiftMemory_ = AllocSiftTempMemory(w, h, numOctaves, upscale_);
|
|
UASSERT(cudaSiftMemory_ != 0);
|
|
cudaSiftMemorySize_ = cv::Size(w, h);
|
|
cudaSiftUpscaling_ = upscale_;
|
|
}
|
|
|
|
ExtractSift(*cudaSiftData_, img_d, numOctaves, initBlur, thresh, edgeLimit, minScale, upscale_, cudaSiftMemory_);
|
|
UDEBUG("%d features extracted", cudaSiftData_->numPts);
|
|
|
|
// Convert CudaSift into OpenCV format
|
|
cudaSiftDescriptors_ = cv::Mat();
|
|
if(cudaSiftData_->numPts)
|
|
{
|
|
keypoints.resize(cudaSiftData_->numPts);
|
|
cudaSiftDescriptors_ = cv::Mat(cudaSiftData_->numPts, 128, CV_32FC1);
|
|
size_t k=0;
|
|
for(int i=0; i<cudaSiftData_->numPts; ++i)
|
|
{
|
|
// Ignore keypoints with invalid descriptors
|
|
float *desc = cudaSiftData_->h_data[i].data;
|
|
if(desc[0] != 0 && desc[0] == desc[63] && desc[0] == desc[127])
|
|
{
|
|
//UWARN("Invalid decsriptor? skipping: %f,%f,%f", cudaSiftData_->h_data[i].xpos, cudaSiftData_->h_data[i].ypos, cudaSiftData_->h_data[i].scale);
|
|
//std::cout << cv::Mat(1, 128*4, CV_8UC1, desc) << std::endl;
|
|
continue;
|
|
}
|
|
// Ignore keypoints not in the mask
|
|
if(!maskRoi.empty() && maskRoi.at<unsigned char>(cudaSiftData_->h_data[i].ypos, cudaSiftData_->h_data[i].xpos) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(i>0 &&
|
|
cudaSiftData_->h_data[i].subsampling == cudaSiftData_->h_data[i-1].subsampling &&
|
|
fabs(cudaSiftData_->h_data[i].xpos-cudaSiftData_->h_data[i-1].xpos) +
|
|
fabs(cudaSiftData_->h_data[i].xpos-cudaSiftData_->h_data[i-1].ypos) < 0.1f)
|
|
{
|
|
// Same feature, skip doubles
|
|
continue;
|
|
}
|
|
|
|
float response = abs(cudaSiftData_->h_data[i].sharpness);
|
|
if(maxGaussianThreshold_>gaussianThreshold_ && response > maxGaussianThreshold_)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
cv::Mat(1, 128, CV_32FC1, desc).copyTo(cudaSiftDescriptors_.row(k));
|
|
keypoints[k].pt.x = cudaSiftData_->h_data[i].xpos;
|
|
keypoints[k].pt.y = cudaSiftData_->h_data[i].ypos;
|
|
keypoints[k].size = 2.0f*cudaSiftData_->h_data[i].scale; // x2 because the scale is more like a radius than a diameter, see CudaSift's ExtractSiftDescriptors function to see how they convert scale to patch size
|
|
keypoints[k].angle = cudaSiftData_->h_data[i].orientation;
|
|
keypoints[k].response = response;
|
|
keypoints[k].octave = log2(cudaSiftData_->h_data[i].subsampling)-(upscale_?1:0);
|
|
++k;
|
|
}
|
|
if(k < keypoints.size())
|
|
{
|
|
UDEBUG("keypoints extracted = %d, valid=%d", (int)keypoints.size(), (int)k);
|
|
keypoints.resize(k);
|
|
cudaSiftDescriptors_.resize(k);
|
|
}
|
|
if(this->getMaxFeatures() != 0 && this->getMaxFeatures() < (int)keypoints.size())
|
|
{
|
|
// Call limitKeypoints() now to filter the descriptors.
|
|
this->limitKeypoints(keypoints, cudaSiftDescriptors_, this->getMaxFeatures(), cv::Size(w,h), this->getSSC());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#if CV_MAJOR_VERSION < 3 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION <= 3) || (CV_MAJOR_VERSION == 3 && (CV_MINOR_VERSION < 4 || (CV_MINOR_VERSION==4 && CV_SUBMINOR_VERSION<11)))
|
|
#ifdef RTABMAP_NONFREE
|
|
sift_->detect(imgRoi, keypoints, maskRoi); // Opencv keypoints
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV nonfree module so SIFT cannot be used!");
|
|
#endif
|
|
#else // >=4.4, >=3.4.11
|
|
sift_->detect(imgRoi, keypoints, maskRoi); // Opencv keypoints
|
|
#endif
|
|
}
|
|
return keypoints;
|
|
}
|
|
|
|
cv::Mat SIFT::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
cv::Mat descriptors;
|
|
#ifdef RTABMAP_CUDASIFT
|
|
if(gpu_)
|
|
{
|
|
if((int)keypoints.size() == cudaSiftDescriptors_.rows)
|
|
{
|
|
descriptors = cudaSiftDescriptors_.clone();
|
|
}
|
|
else
|
|
{
|
|
UERROR("CudaSift: keypoints size %ld is not equal to extracted descriptors size %d", keypoints.size(), cudaSiftDescriptors_.rows);
|
|
return cv::Mat();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
#if CV_MAJOR_VERSION < 3 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION <= 3) || (CV_MAJOR_VERSION == 3 && (CV_MINOR_VERSION < 4 || (CV_MINOR_VERSION==4 && CV_SUBMINOR_VERSION<11)))
|
|
#ifdef RTABMAP_NONFREE
|
|
sift_->compute(image, keypoints, descriptors);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV nonfree module so SIFT cannot be used!");
|
|
#endif
|
|
#else // >=4.4, >=3.4.11
|
|
sift_->compute(image, keypoints, descriptors);
|
|
#endif
|
|
|
|
#ifdef RTABMAP_CUDASIFT
|
|
}
|
|
#endif
|
|
|
|
if( rootSIFT_ && !descriptors.empty())
|
|
{
|
|
UDEBUG("Performing RootSIFT...");
|
|
// see http://www.pyimagesearch.com/2015/04/13/implementing-rootsift-in-python-and-opencv/
|
|
// apply the Hellinger kernel by first L1-normalizing and taking the
|
|
// square-root
|
|
for(int i=0; i<descriptors.rows; ++i)
|
|
{
|
|
// By taking the L1 norm, followed by the square-root, we have
|
|
// already L2 normalized the feature vector and further normalization
|
|
// is not needed.
|
|
descriptors.row(i) = descriptors.row(i) / cv::sum(descriptors.row(i))[0];
|
|
cv::sqrt(descriptors.row(i), descriptors.row(i));
|
|
}
|
|
}
|
|
return descriptors;
|
|
}
|
|
|
|
//////////////////////////
|
|
//ORB
|
|
//////////////////////////
|
|
ORB::ORB(const ParametersMap & parameters) :
|
|
scaleFactor_(Parameters::defaultORBScaleFactor()),
|
|
nLevels_(Parameters::defaultORBNLevels()),
|
|
edgeThreshold_(Parameters::defaultORBEdgeThreshold()),
|
|
firstLevel_(Parameters::defaultORBFirstLevel()),
|
|
WTA_K_(Parameters::defaultORBWTA_K()),
|
|
scoreType_(Parameters::defaultORBScoreType()),
|
|
patchSize_(Parameters::defaultORBPatchSize()),
|
|
gpu_(Parameters::defaultORBGpu()),
|
|
fastThreshold_(Parameters::defaultFASTThreshold()),
|
|
nonmaxSuppresion_(Parameters::defaultFASTNonmaxSuppression())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
ORB::~ORB()
|
|
{
|
|
}
|
|
|
|
void ORB::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
Feature2D::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kORBScaleFactor(), scaleFactor_);
|
|
Parameters::parse(parameters, Parameters::kORBNLevels(), nLevels_);
|
|
Parameters::parse(parameters, Parameters::kORBEdgeThreshold(), edgeThreshold_);
|
|
Parameters::parse(parameters, Parameters::kORBFirstLevel(), firstLevel_);
|
|
Parameters::parse(parameters, Parameters::kORBWTA_K(), WTA_K_);
|
|
Parameters::parse(parameters, Parameters::kORBScoreType(), scoreType_);
|
|
Parameters::parse(parameters, Parameters::kORBPatchSize(), patchSize_);
|
|
Parameters::parse(parameters, Parameters::kORBGpu(), gpu_);
|
|
|
|
Parameters::parse(parameters, Parameters::kFASTThreshold(), fastThreshold_);
|
|
Parameters::parse(parameters, Parameters::kFASTNonmaxSuppression(), nonmaxSuppresion_);
|
|
|
|
#if CV_MAJOR_VERSION < 3
|
|
#ifdef HAVE_OPENCV_GPU
|
|
if(gpu_ && cv::gpu::getCudaEnabledDeviceCount() <= 0)
|
|
{
|
|
UWARN("GPU version of ORB not available! Using CPU version instead...");
|
|
gpu_ = false;
|
|
}
|
|
#else
|
|
if(gpu_)
|
|
{
|
|
UWARN("GPU version of ORB not available (OpenCV not built with gpu/cuda module)! Using CPU version instead...");
|
|
gpu_ = false;
|
|
}
|
|
#endif
|
|
#else
|
|
#ifndef HAVE_OPENCV_CUDAFEATURES2D
|
|
if(gpu_)
|
|
{
|
|
UWARN("GPU version of ORB not available (OpenCV cudafeatures2d module)! Using CPU version instead...");
|
|
gpu_ = false;
|
|
}
|
|
#endif
|
|
if(gpu_ && cv::cuda::getCudaEnabledDeviceCount() <= 0)
|
|
{
|
|
UWARN("GPU version of ORB not available (no GPU found)! Using CPU version instead...");
|
|
gpu_ = false;
|
|
}
|
|
#endif
|
|
if(gpu_)
|
|
{
|
|
#if CV_MAJOR_VERSION < 3
|
|
#ifdef HAVE_OPENCV_GPU
|
|
_gpuOrb = cv::Ptr<CV_ORB_GPU>(new CV_ORB_GPU(this->getMaxFeatures(), scaleFactor_, nLevels_, edgeThreshold_, firstLevel_, WTA_K_, scoreType_, patchSize_));
|
|
_gpuOrb->setFastParams(fastThreshold_, nonmaxSuppresion_);
|
|
#else
|
|
UFATAL("not supposed to be here");
|
|
#endif
|
|
#else
|
|
#ifdef HAVE_OPENCV_CUDAFEATURES2D
|
|
_gpuOrb = CV_ORB_GPU::create(this->getMaxFeatures(), scaleFactor_, nLevels_, edgeThreshold_, firstLevel_, WTA_K_, scoreType_, patchSize_, fastThreshold_);
|
|
#endif
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if CV_MAJOR_VERSION < 3
|
|
_orb = cv::Ptr<CV_ORB>(new CV_ORB(this->getMaxFeatures(), scaleFactor_, nLevels_, edgeThreshold_, firstLevel_, WTA_K_, scoreType_, patchSize_, parameters));
|
|
#elif CV_MAJOR_VERSION > 3
|
|
_orb = CV_ORB::create(this->getMaxFeatures(), scaleFactor_, nLevels_, edgeThreshold_, firstLevel_, WTA_K_, (cv::ORB::ScoreType)scoreType_, patchSize_, fastThreshold_);
|
|
#else
|
|
_orb = CV_ORB::create(this->getMaxFeatures(), scaleFactor_, nLevels_, edgeThreshold_, firstLevel_, WTA_K_, scoreType_, patchSize_, fastThreshold_);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool ORB::isGpuAvailable() const
|
|
{
|
|
#ifdef HAVE_OPENCV_CUDAFEATURES2D
|
|
return cv::cuda::getCudaEnabledDeviceCount() > 0;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
std::vector<cv::KeyPoint> ORB::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
std::vector<cv::KeyPoint> keypoints;
|
|
cv::Mat imgRoi(image, roi);
|
|
cv::Mat maskRoi;
|
|
if(!mask.empty())
|
|
{
|
|
maskRoi = cv::Mat(mask, roi);
|
|
}
|
|
|
|
if(gpu_)
|
|
{
|
|
#if CV_MAJOR_VERSION < 3
|
|
#ifdef HAVE_OPENCV_GPU
|
|
cv::gpu::GpuMat imgGpu(imgRoi);
|
|
cv::gpu::GpuMat maskGpu(maskRoi);
|
|
(*_gpuOrb.obj)(imgGpu, maskGpu, keypoints);
|
|
#else
|
|
UERROR("Cannot use ORBGPU because OpenCV is not built with gpu module.");
|
|
#endif
|
|
#else
|
|
#ifdef HAVE_OPENCV_CUDAFEATURES2D
|
|
cv::cuda::GpuMat d_image(imgRoi);
|
|
cv::cuda::GpuMat d_mask(maskRoi);
|
|
try {
|
|
_gpuOrb->detectAndCompute(d_image, d_mask, keypoints, cv::cuda::GpuMat(), false);
|
|
} catch (cv::Exception& e) {
|
|
const char* err_msg = e.what();
|
|
UWARN("OpenCV exception caught: %s", err_msg);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
_orb->detect(imgRoi, keypoints, maskRoi);
|
|
}
|
|
|
|
return keypoints;
|
|
}
|
|
|
|
cv::Mat ORB::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
if(image.empty())
|
|
{
|
|
ULOGGER_ERROR("Image is null ?!?");
|
|
return descriptors;
|
|
}
|
|
if(gpu_)
|
|
{
|
|
#if CV_MAJOR_VERSION < 3
|
|
#ifdef HAVE_OPENCV_GPU
|
|
cv::gpu::GpuMat imgGpu(image);
|
|
cv::gpu::GpuMat descriptorsGPU;
|
|
(*_gpuOrb.obj)(imgGpu, cv::gpu::GpuMat(), keypoints, descriptorsGPU);
|
|
// Download descriptors
|
|
if (descriptorsGPU.empty())
|
|
descriptors = cv::Mat();
|
|
else
|
|
{
|
|
UASSERT(descriptorsGPU.type() == CV_32F);
|
|
descriptors = cv::Mat(descriptorsGPU.size(), CV_32F);
|
|
descriptorsGPU.download(descriptors);
|
|
}
|
|
#else
|
|
UERROR("GPU version of ORB not available (OpenCV not built with gpu/cuda module)! Using CPU version instead...");
|
|
#endif
|
|
#else
|
|
#ifdef HAVE_OPENCV_CUDAFEATURES2D
|
|
cv::cuda::GpuMat d_image(image);
|
|
cv::cuda::GpuMat d_descriptors;
|
|
try {
|
|
_gpuOrb->detectAndCompute(d_image, cv::cuda::GpuMat(), keypoints, d_descriptors, true);
|
|
} catch (cv::Exception& e) {
|
|
const char* err_msg = e.what();
|
|
UWARN("OpenCV exception caught: %s", err_msg);
|
|
}
|
|
// Download descriptors
|
|
if (d_descriptors.empty())
|
|
descriptors = cv::Mat();
|
|
else
|
|
{
|
|
UASSERT(d_descriptors.type() == CV_32F || d_descriptors.type() == CV_8U);
|
|
d_descriptors.download(descriptors);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
_orb->compute(image, keypoints, descriptors);
|
|
}
|
|
|
|
return descriptors;
|
|
}
|
|
|
|
//////////////////////////
|
|
//FAST
|
|
//////////////////////////
|
|
FAST::FAST(const ParametersMap & parameters) :
|
|
threshold_(Parameters::defaultFASTThreshold()),
|
|
nonmaxSuppression_(Parameters::defaultFASTNonmaxSuppression()),
|
|
gpu_(Parameters::defaultFASTGpu()),
|
|
gpuKeypointsRatio_(Parameters::defaultFASTGpuKeypointsRatio()),
|
|
minThreshold_(Parameters::defaultFASTMinThreshold()),
|
|
maxThreshold_(Parameters::defaultFASTMaxThreshold()),
|
|
gridRows_(Parameters::defaultFASTGridRows()),
|
|
gridCols_(Parameters::defaultFASTGridCols()),
|
|
fastCV_(Parameters::defaultFASTCV()),
|
|
fastCVinit_(false),
|
|
fastCVMaxFeatures_(10000),
|
|
fastCVLastImageHeight_(0)
|
|
{
|
|
#ifdef RTABMAP_FASTCV
|
|
char sVersion[128] = { 0 };
|
|
fcvGetVersion(sVersion, 128);
|
|
UINFO("fastcv version = %s", sVersion);
|
|
int ix;
|
|
if ((ix = fcvSetOperationMode(FASTCV_OP_PERFORMANCE)))
|
|
{
|
|
UERROR("fcvSetOperationMode return=%d, OpenCV FAST will be used instead!", ix);
|
|
fastCV_ = 0;
|
|
}
|
|
else
|
|
{
|
|
fcvMemInit();
|
|
|
|
if (!(fastCVCorners_ = (uint32_t*)fcvMemAlloc(fastCVMaxFeatures_ * sizeof(uint32_t) * 2, 16)) ||
|
|
!(fastCVCornerScores_ = (uint32_t*)fcvMemAlloc( fastCVMaxFeatures_ * sizeof(uint32_t), 16 )))
|
|
{
|
|
UERROR("could not alloc fastcv mem, using opencv fast instead!");
|
|
|
|
if (fastCVCorners_)
|
|
{
|
|
fcvMemFree(fastCVCorners_);
|
|
fastCVCorners_ = NULL;
|
|
}
|
|
if (fastCVCornerScores_)
|
|
{
|
|
fcvMemFree(fastCVCornerScores_);
|
|
fastCVCornerScores_ = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fastCVinit_ = true;
|
|
}
|
|
}
|
|
#endif
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
FAST::~FAST()
|
|
{
|
|
#ifdef RTABMAP_FASTCV
|
|
if(fastCVinit_)
|
|
{
|
|
fcvMemDeInit();
|
|
|
|
if (fastCVCorners_)
|
|
fcvMemFree(fastCVCorners_);
|
|
if (fastCVCornerScores_)
|
|
fcvMemFree(fastCVCornerScores_);
|
|
if (fastCVTempBuf_)
|
|
fcvMemFree(fastCVTempBuf_);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FAST::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
Feature2D::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kFASTThreshold(), threshold_);
|
|
Parameters::parse(parameters, Parameters::kFASTNonmaxSuppression(), nonmaxSuppression_);
|
|
Parameters::parse(parameters, Parameters::kFASTGpu(), gpu_);
|
|
Parameters::parse(parameters, Parameters::kFASTGpuKeypointsRatio(), gpuKeypointsRatio_);
|
|
|
|
Parameters::parse(parameters, Parameters::kFASTMinThreshold(), minThreshold_);
|
|
Parameters::parse(parameters, Parameters::kFASTMaxThreshold(), maxThreshold_);
|
|
Parameters::parse(parameters, Parameters::kFASTGridRows(), gridRows_);
|
|
Parameters::parse(parameters, Parameters::kFASTGridCols(), gridCols_);
|
|
|
|
Parameters::parse(parameters, Parameters::kFASTCV(), fastCV_);
|
|
UASSERT(fastCV_ == 0 || fastCV_ == 9 || fastCV_ == 10);
|
|
|
|
UASSERT_MSG(threshold_ >= minThreshold_, uFormat("%d vs %d", threshold_, minThreshold_).c_str());
|
|
UASSERT_MSG(threshold_ <= maxThreshold_, uFormat("%d vs %d", threshold_, maxThreshold_).c_str());
|
|
|
|
#if CV_MAJOR_VERSION < 3
|
|
#ifdef HAVE_OPENCV_GPU
|
|
if(gpu_ && cv::gpu::getCudaEnabledDeviceCount() <= 0)
|
|
{
|
|
UWARN("GPU version of FAST not available! Using CPU version instead...");
|
|
gpu_ = false;
|
|
}
|
|
#else
|
|
if(gpu_)
|
|
{
|
|
UWARN("GPU version of FAST not available (OpenCV not built with gpu/cuda module)! Using CPU version instead...");
|
|
gpu_ = false;
|
|
}
|
|
#endif
|
|
#else
|
|
#ifdef HAVE_OPENCV_CUDAFEATURES2D
|
|
if(gpu_ && cv::cuda::getCudaEnabledDeviceCount() <= 0)
|
|
{
|
|
UWARN("GPU version of FAST not available! Using CPU version instead...");
|
|
gpu_ = false;
|
|
}
|
|
#else
|
|
if(gpu_)
|
|
{
|
|
UWARN("GPU version of FAST not available (OpenCV cudafeatures2d module)! Using CPU version instead...");
|
|
gpu_ = false;
|
|
}
|
|
#endif
|
|
#endif
|
|
if(gpu_)
|
|
{
|
|
#if CV_MAJOR_VERSION < 3
|
|
#ifdef HAVE_OPENCV_GPU
|
|
_gpuFast = new CV_FAST_GPU(threshold_, nonmaxSuppression_, gpuKeypointsRatio_);
|
|
#else
|
|
UFATAL("not supposed to be here!");
|
|
#endif
|
|
#else
|
|
#ifdef HAVE_OPENCV_CUDAFEATURES2D
|
|
UFATAL("not implemented");
|
|
#endif
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if CV_MAJOR_VERSION < 3
|
|
if(gridRows_ > 0 && gridCols_ > 0)
|
|
{
|
|
UDEBUG("grid max features = %d", this->getMaxFeatures());
|
|
cv::Ptr<cv::FeatureDetector> fastAdjuster = cv::Ptr<cv::FastAdjuster>(new cv::FastAdjuster(threshold_, nonmaxSuppression_, minThreshold_, maxThreshold_));
|
|
_fast = cv::Ptr<cv::FeatureDetector>(new cv::GridAdaptedFeatureDetector(fastAdjuster, this->getMaxFeatures(), gridRows_, gridCols_));
|
|
}
|
|
else
|
|
{
|
|
if(gridRows_ > 0)
|
|
{
|
|
UWARN("Parameter \"%s\" is set (value=%d) but not \"%s\"! Grid adaptor will not be added.",
|
|
Parameters::kFASTGridRows().c_str(), gridRows_, Parameters::kFASTGridCols().c_str());
|
|
}
|
|
else if(gridCols_ > 0)
|
|
{
|
|
UWARN("Parameter \"%s\" is set (value=%d) but not \"%s\"! Grid adaptor will not be added.",
|
|
Parameters::kFASTGridCols().c_str(), gridCols_, Parameters::kFASTGridRows().c_str());
|
|
}
|
|
_fast = cv::Ptr<cv::FeatureDetector>(new CV_FAST(threshold_, nonmaxSuppression_));
|
|
}
|
|
#else
|
|
_fast = CV_FAST::create(threshold_, nonmaxSuppression_);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool FAST::isGpuAvailable() const
|
|
{
|
|
// Not implemented
|
|
return false;
|
|
}
|
|
|
|
std::vector<cv::KeyPoint> FAST::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
std::vector<cv::KeyPoint> keypoints;
|
|
|
|
#ifdef RTABMAP_FASTCV
|
|
if(fastCV_>0)
|
|
{
|
|
// Note: mask not supported, it should be the inverse of the current mask used (0=where to extract)
|
|
uint32_t nCorners = 0;
|
|
|
|
UASSERT(fastCVCorners_ != NULL && fastCVCornerScores_ != NULL);
|
|
if (nonmaxSuppression_)
|
|
{
|
|
if(fastCVTempBuf_==NULL || (fastCVTempBuf_!= NULL && fastCVLastImageHeight_!= image.rows))
|
|
{
|
|
if (fastCVTempBuf_)
|
|
{
|
|
fcvMemFree(fastCVTempBuf_);
|
|
fastCVTempBuf_ = NULL;
|
|
}
|
|
if(!(fastCVTempBuf_ = (uint32_t*)fcvMemAlloc( (3*fastCVMaxFeatures_+image.rows+1)*4, 16 )))
|
|
{
|
|
UERROR("could not alloc fastcv mem for temp buf (%s=true)", Parameters::kFASTNonmaxSuppression().c_str());
|
|
fastCVLastImageHeight_ = 0;
|
|
return keypoints;
|
|
}
|
|
fastCVLastImageHeight_ = image.rows;
|
|
}
|
|
}
|
|
|
|
// image.data should be 128 bits aligned
|
|
UDEBUG("%dx%d (step=%d) thr=%d maxFeatures=%d", image.cols, image.rows, image.step1(), threshold_, fastCVMaxFeatures_);
|
|
if(fastCV_ == 10)
|
|
{
|
|
fcvCornerFast10Scoreu8(image.data, image.cols, image.rows, 0, threshold_, 0, fastCVCorners_, fastCVCornerScores_, fastCVMaxFeatures_, &nCorners, nonmaxSuppression_?1:0, fastCVTempBuf_);
|
|
}
|
|
else
|
|
{
|
|
fcvCornerFast9Scoreu8_v2(image.data, image.cols, image.rows, image.step1(), threshold_, 0, fastCVCorners_, fastCVCornerScores_, fastCVMaxFeatures_, &nCorners, nonmaxSuppression_?1:0, fastCVTempBuf_);
|
|
}
|
|
UDEBUG("number of corners found = %d:", nCorners);
|
|
keypoints.resize(nCorners);
|
|
for (uint32_t i = 0; i < nCorners; i++)
|
|
{
|
|
keypoints[i].pt.x = fastCVCorners_[i * 2];
|
|
keypoints[i].pt.y = fastCVCorners_[(i * 2) + 1];
|
|
keypoints[i].size = 3;
|
|
keypoints[i].response = fastCVCornerScores_[i];
|
|
}
|
|
|
|
if(this->getMaxFeatures() > 0)
|
|
{
|
|
this->limitKeypoints(keypoints, this->getMaxFeatures());
|
|
}
|
|
return keypoints;
|
|
}
|
|
#endif
|
|
|
|
if(fastCV_>0)
|
|
{
|
|
UWARN( "RTAB-Map is not built with FastCV support. OpenCV's FAST is used instead. "
|
|
"Please set %s to 0. This message will only appear once.",
|
|
Parameters::kFASTCV().c_str());
|
|
fastCV_ = 0;
|
|
}
|
|
|
|
cv::Mat imgRoi(image, roi);
|
|
cv::Mat maskRoi;
|
|
if(!mask.empty())
|
|
{
|
|
maskRoi = cv::Mat(mask, roi);
|
|
}
|
|
if(gpu_)
|
|
{
|
|
#if CV_MAJOR_VERSION < 3
|
|
#ifdef HAVE_OPENCV_GPU
|
|
cv::gpu::GpuMat imgGpu(imgRoi);
|
|
cv::gpu::GpuMat maskGpu(maskRoi);
|
|
(*_gpuFast.obj)(imgGpu, maskGpu, keypoints);
|
|
#else
|
|
UERROR("Cannot use FAST GPU because OpenCV is not built with gpu module.");
|
|
#endif
|
|
#else
|
|
#ifdef HAVE_OPENCV_CUDAFEATURES2D
|
|
UFATAL("not implemented");
|
|
#endif
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
_fast->detect(imgRoi, keypoints, maskRoi); // Opencv keypoints
|
|
}
|
|
return keypoints;
|
|
}
|
|
|
|
//////////////////////////
|
|
//FAST-BRIEF
|
|
//////////////////////////
|
|
FAST_BRIEF::FAST_BRIEF(const ParametersMap & parameters) :
|
|
FAST(parameters),
|
|
bytes_(Parameters::defaultBRIEFBytes())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
FAST_BRIEF::~FAST_BRIEF()
|
|
{
|
|
}
|
|
|
|
void FAST_BRIEF::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
FAST::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kBRIEFBytes(), bytes_);
|
|
#if CV_MAJOR_VERSION < 3
|
|
_brief = cv::Ptr<CV_BRIEF>(new CV_BRIEF(bytes_));
|
|
#else
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_brief = CV_BRIEF::create(bytes_);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so Brief cannot be used!");
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
cv::Mat FAST_BRIEF::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
#if CV_MAJOR_VERSION < 3
|
|
_brief->compute(image, keypoints, descriptors);
|
|
#else
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_brief->compute(image, keypoints, descriptors);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so Brief cannot be used!");
|
|
#endif
|
|
#endif
|
|
return descriptors;
|
|
}
|
|
|
|
//////////////////////////
|
|
//FAST-FREAK
|
|
//////////////////////////
|
|
FAST_FREAK::FAST_FREAK(const ParametersMap & parameters) :
|
|
FAST(parameters),
|
|
orientationNormalized_(Parameters::defaultFREAKOrientationNormalized()),
|
|
scaleNormalized_(Parameters::defaultFREAKScaleNormalized()),
|
|
patternScale_(Parameters::defaultFREAKPatternScale()),
|
|
nOctaves_(Parameters::defaultFREAKNOctaves())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
FAST_FREAK::~FAST_FREAK()
|
|
{
|
|
}
|
|
|
|
void FAST_FREAK::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
FAST::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kFREAKOrientationNormalized(), orientationNormalized_);
|
|
Parameters::parse(parameters, Parameters::kFREAKScaleNormalized(), scaleNormalized_);
|
|
Parameters::parse(parameters, Parameters::kFREAKPatternScale(), patternScale_);
|
|
Parameters::parse(parameters, Parameters::kFREAKNOctaves(), nOctaves_);
|
|
|
|
#if CV_MAJOR_VERSION < 3
|
|
_freak = cv::Ptr<CV_FREAK>(new CV_FREAK(orientationNormalized_, scaleNormalized_, patternScale_, nOctaves_));
|
|
#else
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_freak = CV_FREAK::create(orientationNormalized_, scaleNormalized_, patternScale_, nOctaves_);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so Freak cannot be used!");
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
cv::Mat FAST_FREAK::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
#if CV_MAJOR_VERSION < 3
|
|
_freak->compute(image, keypoints, descriptors);
|
|
#else
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_freak->compute(image, keypoints, descriptors);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so Freak cannot be used!");
|
|
#endif
|
|
#endif
|
|
return descriptors;
|
|
}
|
|
|
|
//////////////////////////
|
|
//GFTT
|
|
//////////////////////////
|
|
GFTT::GFTT(const ParametersMap & parameters) :
|
|
_qualityLevel(Parameters::defaultGFTTQualityLevel()),
|
|
_minDistance(Parameters::defaultGFTTMinDistance()),
|
|
_blockSize(Parameters::defaultGFTTBlockSize()),
|
|
_useHarrisDetector(Parameters::defaultGFTTUseHarrisDetector()),
|
|
_k(Parameters::defaultGFTTK()),
|
|
_gpu(Parameters::defaultGFTTGpu())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
GFTT::~GFTT()
|
|
{
|
|
}
|
|
|
|
void GFTT::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
Feature2D::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kGFTTQualityLevel(), _qualityLevel);
|
|
Parameters::parse(parameters, Parameters::kGFTTMinDistance(), _minDistance);
|
|
Parameters::parse(parameters, Parameters::kGFTTBlockSize(), _blockSize);
|
|
Parameters::parse(parameters, Parameters::kGFTTUseHarrisDetector(), _useHarrisDetector);
|
|
Parameters::parse(parameters, Parameters::kGFTTK(), _k);
|
|
Parameters::parse(parameters, Parameters::kGFTTGpu(), _gpu);
|
|
|
|
#if CV_MAJOR_VERSION < 3
|
|
if(_gpu)
|
|
{
|
|
UWARN("GPU version of GFTT is not implemented for OpenCV<3! Using CPU version instead...");
|
|
_gpu = false;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_OPENCV_CUDAIMGPROC
|
|
if(_gpu && cv::cuda::getCudaEnabledDeviceCount() <= 0)
|
|
{
|
|
UWARN("GPU version of GFTT not available! Using CPU version instead...");
|
|
_gpu = false;
|
|
}
|
|
#else
|
|
if(_gpu)
|
|
{
|
|
UWARN("GPU version of GFTT not available (OpenCV cudaimageproc module)! Using CPU version instead...");
|
|
_gpu = false;
|
|
}
|
|
#endif
|
|
if(_gpu)
|
|
{
|
|
#ifdef HAVE_OPENCV_CUDAIMGPROC
|
|
_gpuGftt = cv::cuda::createGoodFeaturesToTrackDetector(CV_8UC1, this->getMaxFeatures(), _qualityLevel, _minDistance, _blockSize, _useHarrisDetector ,_k);
|
|
#else
|
|
UFATAL("not supposed to be here!");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if CV_MAJOR_VERSION < 3
|
|
_gftt = cv::Ptr<CV_GFTT>(new CV_GFTT(this->getMaxFeatures(), _qualityLevel, _minDistance, _blockSize, _useHarrisDetector ,_k));
|
|
#else
|
|
_gftt = CV_GFTT::create(this->getMaxFeatures(), _qualityLevel, _minDistance, _blockSize, _useHarrisDetector ,_k);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool GFTT::isGpuAvailable() const
|
|
{
|
|
#ifdef HAVE_OPENCV_CUDAIMGPROC
|
|
return cv::cuda::getCudaEnabledDeviceCount() > 0;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
std::vector<cv::KeyPoint> GFTT::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
std::vector<cv::KeyPoint> keypoints;
|
|
cv::Mat imgRoi(image, roi);
|
|
cv::Mat maskRoi;
|
|
if(!mask.empty())
|
|
{
|
|
maskRoi = cv::Mat(mask, roi);
|
|
}
|
|
|
|
#if CV_MAJOR_VERSION >= 3 && defined(HAVE_OPENCV_CUDAIMGPROC)
|
|
if(_gpu)
|
|
{
|
|
cv::cuda::GpuMat imgGpu(imgRoi);
|
|
cv::cuda::GpuMat maskGpu(maskRoi);
|
|
cv::cuda::GpuMat cornersGpu;
|
|
_gpuGftt->detect(imgGpu, cornersGpu, maskGpu);
|
|
std::vector<cv::Point2f> corners(cornersGpu.cols);
|
|
cv::Mat cornersMat(1, cornersGpu.cols, CV_32FC2, (void*)&corners[0]);
|
|
cornersGpu.download(cornersMat);
|
|
cv::KeyPoint::convert(corners, keypoints, _blockSize);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
_gftt->detect(imgRoi, keypoints, maskRoi); // Opencv keypoints
|
|
}
|
|
|
|
if(!_useHarrisDetector && _qualityLevel>0.0)
|
|
{
|
|
std::vector<cv::KeyPoint> bestKeypoints;
|
|
bestKeypoints.reserve(keypoints.size());
|
|
for(size_t i=0; i<keypoints.size(); ++i)
|
|
{
|
|
if(keypoints[i].response > _qualityLevel)
|
|
{
|
|
bestKeypoints.push_back(keypoints[i]);
|
|
}
|
|
}
|
|
|
|
return bestKeypoints;
|
|
}
|
|
return keypoints;
|
|
}
|
|
|
|
//////////////////////////
|
|
//FAST-BRIEF
|
|
//////////////////////////
|
|
GFTT_BRIEF::GFTT_BRIEF(const ParametersMap & parameters) :
|
|
GFTT(parameters),
|
|
bytes_(Parameters::defaultBRIEFBytes())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
GFTT_BRIEF::~GFTT_BRIEF()
|
|
{
|
|
}
|
|
|
|
void GFTT_BRIEF::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
GFTT::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kBRIEFBytes(), bytes_);
|
|
#if CV_MAJOR_VERSION < 3
|
|
_brief = cv::Ptr<CV_BRIEF>(new CV_BRIEF(bytes_));
|
|
#else
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_brief = CV_BRIEF::create(bytes_);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so Brief cannot be used!");
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
cv::Mat GFTT_BRIEF::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
#if CV_MAJOR_VERSION < 3
|
|
_brief->compute(image, keypoints, descriptors);
|
|
#else
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_brief->compute(image, keypoints, descriptors);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so Brief cannot be used!");
|
|
#endif
|
|
#endif
|
|
return descriptors;
|
|
}
|
|
|
|
//////////////////////////
|
|
//GFTT-FREAK
|
|
//////////////////////////
|
|
GFTT_FREAK::GFTT_FREAK(const ParametersMap & parameters) :
|
|
GFTT(parameters),
|
|
orientationNormalized_(Parameters::defaultFREAKOrientationNormalized()),
|
|
scaleNormalized_(Parameters::defaultFREAKScaleNormalized()),
|
|
patternScale_(Parameters::defaultFREAKPatternScale()),
|
|
nOctaves_(Parameters::defaultFREAKNOctaves())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
GFTT_FREAK::~GFTT_FREAK()
|
|
{
|
|
}
|
|
|
|
void GFTT_FREAK::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
GFTT::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kFREAKOrientationNormalized(), orientationNormalized_);
|
|
Parameters::parse(parameters, Parameters::kFREAKScaleNormalized(), scaleNormalized_);
|
|
Parameters::parse(parameters, Parameters::kFREAKPatternScale(), patternScale_);
|
|
Parameters::parse(parameters, Parameters::kFREAKNOctaves(), nOctaves_);
|
|
|
|
#if CV_MAJOR_VERSION < 3
|
|
_freak = cv::Ptr<CV_FREAK>(new CV_FREAK(orientationNormalized_, scaleNormalized_, patternScale_, nOctaves_));
|
|
#else
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_freak = CV_FREAK::create(orientationNormalized_, scaleNormalized_, patternScale_, nOctaves_);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so Freak cannot be used!");
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
cv::Mat GFTT_FREAK::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
#if CV_MAJOR_VERSION < 3
|
|
_freak->compute(image, keypoints, descriptors);
|
|
#else
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_freak->compute(image, keypoints, descriptors);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so Freak cannot be used!");
|
|
#endif
|
|
#endif
|
|
return descriptors;
|
|
}
|
|
|
|
//////////////////////////
|
|
//SURF-FREAK
|
|
//////////////////////////
|
|
SURF_FREAK::SURF_FREAK(const ParametersMap & parameters) :
|
|
SURF(parameters),
|
|
orientationNormalized_(Parameters::defaultFREAKOrientationNormalized()),
|
|
scaleNormalized_(Parameters::defaultFREAKScaleNormalized()),
|
|
patternScale_(Parameters::defaultFREAKPatternScale()),
|
|
nOctaves_(Parameters::defaultFREAKNOctaves())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
SURF_FREAK::~SURF_FREAK()
|
|
{
|
|
}
|
|
|
|
void SURF_FREAK::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
SURF::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kFREAKOrientationNormalized(), orientationNormalized_);
|
|
Parameters::parse(parameters, Parameters::kFREAKScaleNormalized(), scaleNormalized_);
|
|
Parameters::parse(parameters, Parameters::kFREAKPatternScale(), patternScale_);
|
|
Parameters::parse(parameters, Parameters::kFREAKNOctaves(), nOctaves_);
|
|
|
|
#if CV_MAJOR_VERSION < 3
|
|
_freak = cv::Ptr<CV_FREAK>(new CV_FREAK(orientationNormalized_, scaleNormalized_, patternScale_, nOctaves_));
|
|
#else
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_freak = CV_FREAK::create(orientationNormalized_, scaleNormalized_, patternScale_, nOctaves_);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so Freak cannot be used!");
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
cv::Mat SURF_FREAK::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
#if CV_MAJOR_VERSION < 3
|
|
_freak->compute(image, keypoints, descriptors);
|
|
#else
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_freak->compute(image, keypoints, descriptors);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so Freak cannot be used!");
|
|
#endif
|
|
#endif
|
|
return descriptors;
|
|
}
|
|
|
|
//////////////////////////
|
|
//GFTT-ORB
|
|
//////////////////////////
|
|
GFTT_ORB::GFTT_ORB(const ParametersMap & parameters) :
|
|
GFTT(parameters),
|
|
_orb(parameters)
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
GFTT_ORB::~GFTT_ORB()
|
|
{
|
|
}
|
|
|
|
void GFTT_ORB::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
GFTT::parseParameters(parameters);
|
|
_orb.parseParameters(parameters);
|
|
}
|
|
|
|
cv::Mat GFTT_ORB::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
return _orb.generateDescriptors(image, keypoints);
|
|
}
|
|
|
|
//////////////////////////
|
|
//BRISK
|
|
//////////////////////////
|
|
BRISK::BRISK(const ParametersMap & parameters) :
|
|
thresh_(Parameters::defaultBRISKThresh()),
|
|
octaves_(Parameters::defaultBRISKOctaves()),
|
|
patternScale_(Parameters::defaultBRISKPatternScale())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
BRISK::~BRISK()
|
|
{
|
|
}
|
|
|
|
void BRISK::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
Feature2D::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kBRISKThresh(), thresh_);
|
|
Parameters::parse(parameters, Parameters::kBRISKOctaves(), octaves_);
|
|
Parameters::parse(parameters, Parameters::kBRISKPatternScale(), patternScale_);
|
|
#if CV_MAJOR_VERSION > 4
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
brisk_ = CV_BRISK::create(thresh_, octaves_, patternScale_);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so BRISK cannot be used!");
|
|
#endif
|
|
#elif CV_MAJOR_VERSION < 3
|
|
brisk_ = cv::Ptr<CV_BRISK>(new CV_BRISK(thresh_, octaves_, patternScale_));
|
|
#else
|
|
brisk_ = CV_BRISK::create(thresh_, octaves_, patternScale_);
|
|
#endif
|
|
}
|
|
|
|
std::vector<cv::KeyPoint> BRISK::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
std::vector<cv::KeyPoint> keypoints;
|
|
#if CV_MAJOR_VERSION < 5 || (CV_MAJOR_VERSION > 4 && defined(HAVE_OPENCV_XFEATURES2D))
|
|
cv::Mat imgRoi(image, roi);
|
|
cv::Mat maskRoi;
|
|
if(!mask.empty())
|
|
{
|
|
maskRoi = cv::Mat(mask, roi);
|
|
}
|
|
brisk_->detect(imgRoi, keypoints, maskRoi); // Opencv keypoints
|
|
#else
|
|
UWARN("RTAB-Map is not built with BRISK feature support!");
|
|
#endif
|
|
return keypoints;
|
|
}
|
|
|
|
cv::Mat BRISK::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
#if CV_MAJOR_VERSION < 5 || (CV_MAJOR_VERSION > 4 && defined(HAVE_OPENCV_XFEATURES2D))
|
|
brisk_->compute(image, keypoints, descriptors);
|
|
#else
|
|
UWARN("RTAB-Map is not built with BRISK feature support!");
|
|
#endif
|
|
return descriptors;
|
|
}
|
|
|
|
//////////////////////////
|
|
//KAZE
|
|
//////////////////////////
|
|
KAZE::KAZE(const ParametersMap & parameters) :
|
|
extended_(Parameters::defaultKAZEExtended()),
|
|
upright_(Parameters::defaultKAZEUpright()),
|
|
threshold_(Parameters::defaultKAZEThreshold()),
|
|
nOctaves_(Parameters::defaultKAZENOctaves()),
|
|
nOctaveLayers_(Parameters::defaultKAZENOctaveLayers()),
|
|
diffusivity_(Parameters::defaultKAZEDiffusivity())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
KAZE::~KAZE()
|
|
{
|
|
}
|
|
|
|
void KAZE::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
Feature2D::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kKAZEExtended(), extended_);
|
|
Parameters::parse(parameters, Parameters::kKAZEUpright(), upright_);
|
|
Parameters::parse(parameters, Parameters::kKAZEThreshold(), threshold_);
|
|
Parameters::parse(parameters, Parameters::kKAZENOctaves(), nOctaves_);
|
|
Parameters::parse(parameters, Parameters::kKAZENOctaveLayers(), nOctaveLayers_);
|
|
Parameters::parse(parameters, Parameters::kKAZEDiffusivity(), diffusivity_);
|
|
|
|
#if CV_MAJOR_VERSION > 4
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
kaze_ = CV_KAZE::create(extended_, upright_, threshold_, nOctaves_, nOctaveLayers_, (CV_KAZE::DiffusivityType)diffusivity_);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so KAZE cannot be used!");
|
|
#endif
|
|
#elif CV_MAJOR_VERSION > 3
|
|
kaze_ = CV_KAZE::create(extended_, upright_, threshold_, nOctaves_, nOctaveLayers_, (CV_KAZE::DiffusivityType)diffusivity_);
|
|
#elif CV_MAJOR_VERSION > 2
|
|
kaze_ = CV_KAZE::create(extended_, upright_, threshold_, nOctaves_, nOctaveLayers_, diffusivity_);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV3 so Kaze feature cannot be used!");
|
|
#endif
|
|
}
|
|
|
|
std::vector<cv::KeyPoint> KAZE::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
std::vector<cv::KeyPoint> keypoints;
|
|
#if (CV_MAJOR_VERSION > 2 && CV_MAJOR_VERSION < 5) || (CV_MAJOR_VERSION > 4 && defined(HAVE_OPENCV_XFEATURES2D))
|
|
cv::Mat imgRoi(image, roi);
|
|
cv::Mat maskRoi;
|
|
if (!mask.empty())
|
|
{
|
|
maskRoi = cv::Mat(mask, roi);
|
|
}
|
|
kaze_->detect(imgRoi, keypoints, maskRoi); // Opencv keypoints
|
|
#else
|
|
UWARN("RTAB-Map is not built with Kaze feature support!");
|
|
#endif
|
|
return keypoints;
|
|
}
|
|
|
|
cv::Mat KAZE::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
#if (CV_MAJOR_VERSION > 2 && CV_MAJOR_VERSION < 5) || (CV_MAJOR_VERSION > 4 && defined(HAVE_OPENCV_XFEATURES2D))
|
|
kaze_->compute(image, keypoints, descriptors);
|
|
#else
|
|
UWARN("RTAB-Map is not built with Kaze feature support!");
|
|
#endif
|
|
return descriptors;
|
|
}
|
|
|
|
//////////////////////////
|
|
//ORBOctree
|
|
//////////////////////////
|
|
ORBOctree::ORBOctree(const ParametersMap & parameters) :
|
|
scaleFactor_(Parameters::defaultORBScaleFactor()),
|
|
nLevels_(Parameters::defaultORBNLevels()),
|
|
patchSize_(Parameters::defaultORBPatchSize()),
|
|
edgeThreshold_(Parameters::defaultORBEdgeThreshold()),
|
|
fastThreshold_(Parameters::defaultFASTThreshold()),
|
|
fastMinThreshold_(Parameters::defaultFASTMinThreshold())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
ORBOctree::~ORBOctree()
|
|
{
|
|
}
|
|
|
|
void ORBOctree::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
Feature2D::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kORBScaleFactor(), scaleFactor_);
|
|
Parameters::parse(parameters, Parameters::kORBNLevels(), nLevels_);
|
|
Parameters::parse(parameters, Parameters::kORBPatchSize(), patchSize_);
|
|
Parameters::parse(parameters, Parameters::kORBEdgeThreshold(), edgeThreshold_);
|
|
|
|
Parameters::parse(parameters, Parameters::kFASTThreshold(), fastThreshold_);
|
|
Parameters::parse(parameters, Parameters::kFASTMinThreshold(), fastMinThreshold_);
|
|
|
|
#ifdef RTABMAP_ORB_OCTREE
|
|
_orb = cv::Ptr<ORBextractor>(new ORBextractor(this->getMaxFeatures(), scaleFactor_, nLevels_, fastThreshold_, fastMinThreshold_, patchSize_, edgeThreshold_));
|
|
#else
|
|
UWARN("RTAB-Map is not built with ORB OcTree option enabled so ORB OcTree feature cannot be used!");
|
|
#endif
|
|
}
|
|
|
|
std::vector<cv::KeyPoint> ORBOctree::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
|
|
{
|
|
std::vector<cv::KeyPoint> keypoints;
|
|
descriptors_ = cv::Mat();
|
|
#ifdef RTABMAP_ORB_OCTREE
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat imgRoi(image, roi);
|
|
cv::Mat maskRoi;
|
|
if(!mask.empty())
|
|
{
|
|
maskRoi = cv::Mat(mask, roi);
|
|
}
|
|
|
|
(*_orb)(imgRoi, maskRoi, keypoints, descriptors_);
|
|
|
|
// OrbOctree ignores the mask, so we have to apply it manually here
|
|
if(!keypoints.empty() && !maskRoi.empty())
|
|
{
|
|
std::vector<cv::KeyPoint> validKeypoints;
|
|
validKeypoints.reserve(keypoints.size());
|
|
cv::Mat validDescriptors;
|
|
for(size_t i=0; i<keypoints.size(); ++i)
|
|
{
|
|
if(maskRoi.at<unsigned char>(keypoints[i].pt.y+roi.y, keypoints[i].pt.x+roi.x) != 0)
|
|
{
|
|
validKeypoints.push_back(keypoints[i]);
|
|
validDescriptors.push_back(descriptors_.row(i));
|
|
}
|
|
}
|
|
keypoints = validKeypoints;
|
|
descriptors_ = validDescriptors;
|
|
}
|
|
|
|
if((int)keypoints.size() > this->getMaxFeatures())
|
|
{
|
|
limitKeypoints(keypoints, descriptors_, this->getMaxFeatures(), roi.size(), this->getSSC());
|
|
}
|
|
#else
|
|
UWARN("RTAB-Map is not built with ORB OcTree option enabled so ORB OcTree feature cannot be used!");
|
|
#endif
|
|
return keypoints;
|
|
}
|
|
|
|
cv::Mat ORBOctree::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
#ifdef RTABMAP_ORB_OCTREE
|
|
UASSERT_MSG((int)keypoints.size() == descriptors_.rows, uFormat("keypoints=%d descriptors=%d", (int)keypoints.size(), descriptors_.rows).c_str());
|
|
#else
|
|
UWARN("RTAB-Map is not built with ORB OcTree option enabled so ORB OcTree feature cannot be used!");
|
|
#endif
|
|
return descriptors_;
|
|
}
|
|
|
|
//////////////////////////
|
|
//SuperPointTorch
|
|
//////////////////////////
|
|
SuperPointTorch::SuperPointTorch(const ParametersMap & parameters) :
|
|
path_(Parameters::defaultSuperPointModelPath()),
|
|
threshold_(Parameters::defaultSuperPointThreshold()),
|
|
nms_(Parameters::defaultSuperPointNMS()),
|
|
minDistance_(Parameters::defaultSuperPointNMSRadius()),
|
|
cuda_(Parameters::defaultSuperPointCuda())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
SuperPointTorch::~SuperPointTorch()
|
|
{
|
|
}
|
|
|
|
bool SuperPointTorch::isGpuAvailable() const
|
|
{
|
|
#ifdef RTABMAP_TORCH
|
|
return torch::cuda::is_available();
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void SuperPointTorch::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
Feature2D::parseParameters(parameters);
|
|
|
|
std::string previousPath = path_;
|
|
#ifdef RTABMAP_TORCH
|
|
bool previousCuda = cuda_;
|
|
#endif
|
|
Parameters::parse(parameters, Parameters::kSuperPointModelPath(), path_);
|
|
Parameters::parse(parameters, Parameters::kSuperPointThreshold(), threshold_);
|
|
Parameters::parse(parameters, Parameters::kSuperPointNMS(), nms_);
|
|
Parameters::parse(parameters, Parameters::kSuperPointNMSRadius(), minDistance_);
|
|
Parameters::parse(parameters, Parameters::kSuperPointCuda(), cuda_);
|
|
|
|
#ifdef RTABMAP_TORCH
|
|
if(superPoint_.get() == 0 || path_.compare(previousPath) != 0 || previousCuda != cuda_)
|
|
{
|
|
superPoint_ = cv::Ptr<SPDetector>(new SPDetector(path_, threshold_, nms_, minDistance_, cuda_));
|
|
}
|
|
else
|
|
{
|
|
superPoint_->setThreshold(threshold_);
|
|
superPoint_->SetNMS(nms_);
|
|
superPoint_->setMinDistance(minDistance_);
|
|
}
|
|
#else
|
|
UWARN("RTAB-Map is not built with Torch support so SuperPoint Torch feature cannot be used!");
|
|
#endif
|
|
}
|
|
|
|
std::vector<cv::KeyPoint> SuperPointTorch::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
|
|
{
|
|
#ifdef RTABMAP_TORCH
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
if(roi.x!=0 || roi.y !=0 || roi.width!=image.cols || roi.height!=image.rows)
|
|
{
|
|
UERROR("SuperPoint: Not supporting ROI (%d,%d,%d,%d). Make sure %s, %s, %s, %s, %s, %s are all set to default values.",
|
|
roi.x, roi.y, roi.width, roi.height,
|
|
Parameters::kKpRoiRatios().c_str(),
|
|
Parameters::kVisRoiRatios().c_str(),
|
|
Parameters::kVisGridRows().c_str(),
|
|
Parameters::kVisGridCols().c_str(),
|
|
Parameters::kKpGridRows().c_str(),
|
|
Parameters::kKpGridCols().c_str());
|
|
return std::vector<cv::KeyPoint>();
|
|
}
|
|
return superPoint_->detect(image, mask);
|
|
#else
|
|
UWARN("RTAB-Map is not built with Torch support so SuperPoint Torch feature cannot be used!");
|
|
return std::vector<cv::KeyPoint>();
|
|
#endif
|
|
}
|
|
|
|
cv::Mat SuperPointTorch::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
#ifdef RTABMAP_TORCH
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
if(!keypoints.empty())
|
|
{
|
|
descriptors = superPoint_->compute(keypoints);
|
|
if(descriptors.empty())
|
|
{
|
|
// superpoint may have been reset between keypoint detection and now,
|
|
// re-detect features to re-inialize the descriptors matrix, then
|
|
// re-extract descriptors with original keypoints.
|
|
UWARN("Re-initializing superpoint on that image to extract descriptors");
|
|
if(!superPoint_->detect(image).empty())
|
|
{
|
|
descriptors = superPoint_->compute(keypoints);
|
|
if(descriptors.rows == (int)keypoints.size())
|
|
{
|
|
UWARN("Sucessfully re-initialized superpoint, returning %d descriptors.", descriptors.rows);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UWARN("Failed to re-initialize superpoint on that image, returning empty descriptors.");
|
|
}
|
|
}
|
|
}
|
|
return descriptors;
|
|
#else
|
|
UWARN("RTAB-Map is not built with Torch support so SuperPoint Torch feature cannot be used!");
|
|
return cv::Mat();
|
|
#endif
|
|
}
|
|
|
|
|
|
//////////////////////////
|
|
//SuperPointRpautrat
|
|
//////////////////////////
|
|
SuperPointRpautrat::SuperPointRpautrat(const ParametersMap & parameters) :
|
|
superpointWeightsPath_(Parameters::defaultSuperPointRpautratWeightsPath()),
|
|
superpointModelPath_(Parameters::defaultSuperPointRpautratModelPath()),
|
|
outputDir_(""),
|
|
threshold_(Parameters::defaultSuperPointRpautratThreshold()),
|
|
nms_(Parameters::defaultSuperPointRpautratNMS()),
|
|
minDistance_(Parameters::defaultSuperPointRpautratNMSRadius()),
|
|
cuda_(Parameters::defaultSuperPointRpautratCuda())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
SuperPointRpautrat::~SuperPointRpautrat()
|
|
{
|
|
}
|
|
|
|
bool SuperPointRpautrat::isGpuAvailable() const
|
|
{
|
|
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
|
|
return torch::cuda::is_available();
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void SuperPointRpautrat::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
Feature2D::parseParameters(parameters);
|
|
|
|
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
|
|
std::string previousWeightsPath = superpointWeightsPath_;
|
|
std::string previousModelPath = superpointModelPath_;
|
|
bool previousCuda = cuda_;
|
|
float previousThreshold = threshold_;
|
|
bool previousNms = nms_;
|
|
int previousMinDistance = minDistance_;
|
|
|
|
Parameters::parse(parameters, Parameters::kSuperPointRpautratWeightsPath(), superpointWeightsPath_);
|
|
Parameters::parse(parameters, Parameters::kSuperPointRpautratModelPath(), superpointModelPath_);
|
|
Parameters::parse(parameters, Parameters::kSuperPointRpautratThreshold(), threshold_);
|
|
Parameters::parse(parameters, Parameters::kSuperPointRpautratNMS(), nms_);
|
|
Parameters::parse(parameters, Parameters::kSuperPointRpautratNMSRadius(), minDistance_);
|
|
Parameters::parse(parameters, Parameters::kSuperPointRpautratCuda(), cuda_);
|
|
Parameters::parse(parameters, Parameters::kRtabmapWorkingDirectory(), outputDir_);
|
|
|
|
// If working directory is not set, use the default
|
|
if(outputDir_.empty())
|
|
{
|
|
outputDir_ = Parameters::createDefaultWorkingDirectory();
|
|
}
|
|
|
|
// Reinitialize detector if model-affecting parameters changed
|
|
if(superPoint_.get() == 0 ||
|
|
superpointWeightsPath_.compare(previousWeightsPath) != 0 ||
|
|
superpointModelPath_.compare(previousModelPath) != 0 ||
|
|
previousCuda != cuda_ ||
|
|
previousThreshold != threshold_ ||
|
|
previousNms != nms_ ||
|
|
previousMinDistance != minDistance_)
|
|
{
|
|
superPoint_ = cv::Ptr<SPDetectorRpautrat>(new SPDetectorRpautrat(superpointWeightsPath_, superpointModelPath_, outputDir_, threshold_, nms_, minDistance_, cuda_, this->getMaxFeatures(), this->getSSC()));
|
|
}
|
|
else if(superPoint_.get() != 0)
|
|
{
|
|
// Update post-processing parameters without reinitializing
|
|
superPoint_->setMaxFeatures(this->getMaxFeatures());
|
|
superPoint_->setSSC(this->getSSC());
|
|
}
|
|
#else
|
|
UWARN("RTAB-Map is not built with Torch support so SuperPoint Rpautrat feature cannot be used!");
|
|
#endif
|
|
}
|
|
|
|
std::vector<cv::KeyPoint> SuperPointRpautrat::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
|
|
{
|
|
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
if(roi.x!=0 || roi.y !=0 || roi.width!=image.cols || roi.height!=image.rows)
|
|
{
|
|
UERROR("SuperPoint Rpautrat: Not supporting ROI (%d,%d,%d,%d). Make sure %s, %s, %s, %s, %s, %s are all set to default values.",
|
|
roi.x, roi.y, roi.width, roi.height,
|
|
Parameters::kKpRoiRatios().c_str(),
|
|
Parameters::kVisRoiRatios().c_str(),
|
|
Parameters::kVisGridRows().c_str(),
|
|
Parameters::kVisGridCols().c_str(),
|
|
Parameters::kKpGridRows().c_str(),
|
|
Parameters::kKpGridCols().c_str());
|
|
return std::vector<cv::KeyPoint>();
|
|
}
|
|
return superPoint_->detect(image, mask);
|
|
#else
|
|
UWARN("RTAB-Map is not built with Torch support so SuperPoint Rpautrat feature cannot be used!");
|
|
return std::vector<cv::KeyPoint>();
|
|
#endif
|
|
}
|
|
|
|
cv::Mat SuperPointRpautrat::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
if(!keypoints.empty())
|
|
{
|
|
descriptors = superPoint_->compute(keypoints);
|
|
if(descriptors.empty())
|
|
{
|
|
// superpoint may have been reset between keypoint detection and now,
|
|
// re-detect features to re-inialize the descriptors matrix, then
|
|
// re-extract descriptors with original keypoints.
|
|
UWARN("Re-initializing superpoint on that image to extract descriptors");
|
|
if(!superPoint_->detect(image).empty())
|
|
{
|
|
descriptors = superPoint_->compute(keypoints);
|
|
if(descriptors.rows == (int)keypoints.size())
|
|
{
|
|
UWARN("Sucessfully re-initialized superpoint, returning %d descriptors.", descriptors.rows);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UWARN("Failed to re-initialize superpoint on that image, returning empty descriptors.");
|
|
}
|
|
}
|
|
}
|
|
return descriptors;
|
|
#else
|
|
UWARN("RTAB-Map is not built with Torch support so SuperPoint Rpautrat feature cannot be used!");
|
|
return cv::Mat();
|
|
#endif
|
|
}
|
|
|
|
|
|
//////////////////////////
|
|
//GFTT-DAISY
|
|
//////////////////////////
|
|
GFTT_DAISY::GFTT_DAISY(const ParametersMap & parameters) :
|
|
GFTT(parameters),
|
|
orientationNormalized_(Parameters::defaultFREAKOrientationNormalized()),
|
|
scaleNormalized_(Parameters::defaultFREAKScaleNormalized()),
|
|
patternScale_(Parameters::defaultFREAKPatternScale()),
|
|
nOctaves_(Parameters::defaultFREAKNOctaves())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
GFTT_DAISY::~GFTT_DAISY()
|
|
{
|
|
}
|
|
|
|
void GFTT_DAISY::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
GFTT::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kFREAKOrientationNormalized(), orientationNormalized_);
|
|
Parameters::parse(parameters, Parameters::kFREAKScaleNormalized(), scaleNormalized_);
|
|
Parameters::parse(parameters, Parameters::kFREAKPatternScale(), patternScale_);
|
|
Parameters::parse(parameters, Parameters::kFREAKNOctaves(), nOctaves_);
|
|
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_daisy = CV_DAISY::create();
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so DAISY cannot be used!");
|
|
#endif
|
|
}
|
|
|
|
cv::Mat GFTT_DAISY::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_daisy->compute(image, keypoints, descriptors);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so DAISY cannot be used!");
|
|
#endif
|
|
return descriptors;
|
|
}
|
|
|
|
//////////////////////////
|
|
//SURF-DAISY
|
|
//////////////////////////
|
|
SURF_DAISY::SURF_DAISY(const ParametersMap & parameters) :
|
|
SURF(parameters),
|
|
orientationNormalized_(Parameters::defaultFREAKOrientationNormalized()),
|
|
scaleNormalized_(Parameters::defaultFREAKScaleNormalized()),
|
|
patternScale_(Parameters::defaultFREAKPatternScale()),
|
|
nOctaves_(Parameters::defaultFREAKNOctaves())
|
|
{
|
|
parseParameters(parameters);
|
|
}
|
|
|
|
SURF_DAISY::~SURF_DAISY()
|
|
{
|
|
}
|
|
|
|
void SURF_DAISY::parseParameters(const ParametersMap & parameters)
|
|
{
|
|
SURF::parseParameters(parameters);
|
|
|
|
Parameters::parse(parameters, Parameters::kFREAKOrientationNormalized(), orientationNormalized_);
|
|
Parameters::parse(parameters, Parameters::kFREAKScaleNormalized(), scaleNormalized_);
|
|
Parameters::parse(parameters, Parameters::kFREAKPatternScale(), patternScale_);
|
|
Parameters::parse(parameters, Parameters::kFREAKNOctaves(), nOctaves_);
|
|
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_daisy = CV_DAISY::create();
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so DAISY cannot be used!");
|
|
#endif
|
|
}
|
|
|
|
cv::Mat SURF_DAISY::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
|
{
|
|
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
|
cv::Mat descriptors;
|
|
#ifdef HAVE_OPENCV_XFEATURES2D
|
|
_daisy->compute(image, keypoints, descriptors);
|
|
#else
|
|
UWARN("RTAB-Map is not built with OpenCV xfeatures2d module so DAISY cannot be used!");
|
|
#endif
|
|
return descriptors;
|
|
}
|
|
|
|
}
|