36 #ifdef HAVE_OPENCV_ARUCO 37 markerLength_ = Parameters::defaultMarkerLength();
38 maxDepthError_ = Parameters::defaultMarkerMaxDepthError();
39 maxRange_ = Parameters::defaultMarkerMaxRange();
40 minRange_ = Parameters::defaultMarkerMinRange();
41 dictionaryId_ = Parameters::defaultMarkerDictionary();
42 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7) 43 detectorParams_.reset(
new cv::aruco::DetectorParameters());
44 #elif CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION >=2) 45 detectorParams_ = cv::aruco::DetectorParameters::create();
47 detectorParams_.reset(
new cv::aruco::DetectorParameters());
49 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7) 50 detectorParams_->cornerRefinementMethod = (cv::aruco::CornerRefineMethod) Parameters::defaultMarkerCornerRefinementMethod();
51 #elif CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION >=3) 52 detectorParams_->cornerRefinementMethod = Parameters::defaultMarkerCornerRefinementMethod();
54 detectorParams_->doCornerRefinement = Parameters::defaultMarkerCornerRefinementMethod()!=0;
66 #ifdef HAVE_OPENCV_ARUCO 67 detectorParams_->adaptiveThreshWinSizeMin = 3;
68 detectorParams_->adaptiveThreshWinSizeMax = 23;
69 detectorParams_->adaptiveThreshWinSizeStep = 10;
70 detectorParams_->adaptiveThreshConstant = 7;
71 detectorParams_->minMarkerPerimeterRate = 0.03;
72 detectorParams_->maxMarkerPerimeterRate = 4.0;
73 detectorParams_->polygonalApproxAccuracyRate = 0.03;
74 detectorParams_->minCornerDistanceRate = 0.05;
75 detectorParams_->minDistanceToBorder = 3;
76 detectorParams_->minMarkerDistanceRate = 0.05;
77 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7) 78 int cornerRefinementMethod;
79 Parameters::parse(parameters, Parameters::kMarkerCornerRefinementMethod(), cornerRefinementMethod);
80 detectorParams_->cornerRefinementMethod = (cv::aruco::CornerRefineMethod)cornerRefinementMethod;
81 #elif CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION >=3) 82 Parameters::parse(parameters, Parameters::kMarkerCornerRefinementMethod(), detectorParams_->cornerRefinementMethod);
84 int doCornerRefinement = detectorParams_->doCornerRefinement?1:0;
85 Parameters::parse(parameters, Parameters::kMarkerCornerRefinementMethod(), doCornerRefinement);
86 detectorParams_->doCornerRefinement = doCornerRefinement!=0;
88 detectorParams_->cornerRefinementWinSize = 5;
89 detectorParams_->cornerRefinementMaxIterations = 30;
90 detectorParams_->cornerRefinementMinAccuracy = 0.1;
91 detectorParams_->markerBorderBits = 1;
92 detectorParams_->perspectiveRemovePixelPerCell = 4;
93 detectorParams_->perspectiveRemoveIgnoredMarginPerCell = 0.13;
94 detectorParams_->maxErroneousBitsInBorderRate = 0.35;
95 detectorParams_->minOtsuStdDev = 5.0;
96 detectorParams_->errorCorrectionRate = 0.6;
99 Parameters::parse(parameters, Parameters::kMarkerMaxDepthError(), maxDepthError_);
103 #if CV_MAJOR_VERSION < 3 || (CV_MAJOR_VERSION == 3 && (CV_MINOR_VERSION <4 || (CV_MINOR_VERSION ==4 && CV_SUBMINOR_VERSION<2))) 104 if(dictionaryId_ >= 17)
106 UERROR(
"Cannot set AprilTag dictionary. OpenCV version should be at least 3.4.2, " 107 "current version is %s. Setting %s to default (%d)",
109 Parameters::kMarkerDictionary().c_str(),
110 Parameters::defaultMarkerDictionary());
111 dictionaryId_ = Parameters::defaultMarkerDictionary();
114 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7) 115 dictionary_.reset(
new cv::aruco::Dictionary());
116 *dictionary_ = cv::aruco::getPredefinedDictionary(cv::aruco::PredefinedDictionaryType(dictionaryId_));
117 #elif CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION >=2) 118 dictionary_ = cv::aruco::getPredefinedDictionary(cv::aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId_));
120 dictionary_.reset(
new cv::aruco::Dictionary());
121 *dictionary_ = cv::aruco::getPredefinedDictionary(cv::aruco::PREDEFINED_DICTIONARY_NAME(dictionaryId_));
128 std::map<int, Transform> detections;
129 std::map<int, MarkerInfo> infos =
detect(image, model, depth, std::map<int, float>(), imageWithDetections);
131 for(std::map<int, MarkerInfo>::iterator iter=infos.begin(); iter!=infos.end(); ++iter)
133 detections.insert(std::make_pair(iter->first, iter->second.pose()));
137 *markerLengthOut = iter->second.length();
145 const std::vector<CameraModel> & models,
146 const cv::Mat & depth,
147 const std::map<int, float> & markerLengths,
148 cv::Mat * imageWithDetections)
150 UASSERT(!models.empty() && !image.empty());
151 UASSERT(
int((image.cols/models.size())*models.size()) == image.cols);
152 UASSERT(
int((depth.cols/models.size())*models.size()) == depth.cols);
153 int subRGBWidth = image.cols/models.size();
154 int subDepthWidth = depth.cols/models.size();
156 std::map<int, MarkerInfo> allInfo;
157 for(
size_t i=0; i<models.size(); ++i)
159 cv::Mat subImage(image, cv::Rect(subRGBWidth*i, 0, subRGBWidth, image.rows));
162 subDepth = cv::Mat(depth, cv::Rect(subDepthWidth*i, 0, subDepthWidth, depth.rows));
164 cv::Mat subImageWithDetections;
165 std::map<int, MarkerInfo> subInfo =
detect(subImage, model, subDepth, markerLengths, imageWithDetections?&subImageWithDetections:0);
168 for(std::map<int, MarkerInfo>::iterator iter=subInfo.begin(); iter!=subInfo.end(); ++iter)
170 std::pair<std::map<int, MarkerInfo>::iterator,
bool> inserted = allInfo.insert(*iter);
173 UWARN(
"Marker %d already added by another camera, ignoring detection from camera %d", iter->first, i);
179 allInfo.insert(subInfo.begin(), subInfo.end());
181 if(imageWithDetections)
185 *imageWithDetections = image.clone();
187 if(!subImageWithDetections.empty())
189 subImageWithDetections.copyTo(cv::Mat(*imageWithDetections, cv::Rect(subRGBWidth*i, 0, subRGBWidth, image.rows)));
198 const cv::Mat & depth,
199 const std::map<int, float> & markerLengths,
200 cv::Mat * imageWithDetections)
202 if(!image.empty() && image.cols != model.
imageWidth())
204 UERROR(
"This method cannot handle multi-camera marker detection, use the other function version supporting it.");
205 return std::map<int, MarkerInfo>();
208 std::map<int, MarkerInfo> detections;
210 #ifdef HAVE_OPENCV_ARUCO 212 std::vector< int > ids;
213 std::vector< std::vector< cv::Point2f > > corners, rejected;
214 std::vector< cv::Vec3d > rvecs, tvecs;
217 #if CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION >=2) 218 cv::aruco::detectMarkers(image, dictionary_, corners, ids, detectorParams_, rejected);
220 cv::aruco::detectMarkers(image, *dictionary_, corners, ids, *detectorParams_, rejected);
222 UDEBUG(
"Markers detected=%d rejected=%d", (
int)ids.size(), (int)rejected.size());
225 float rgbToDepthFactorX = 1.0f;
226 float rgbToDepthFactorY = 1.0f;
232 else if(markerLength_ == 0)
236 UERROR(
"Depth image is empty, please set %s parameter to non-null.", Parameters::kMarkerLength().c_str());
241 cv::aruco::estimatePoseSingleMarkers(corners, markerLength_<=0.0?1.0
f:markerLength_, model.
K(), model.
D(), rvecs, tvecs);
242 std::vector<float> scales;
243 for(
size_t i=0; i<ids.size(); ++i)
246 std::map<int, float>::const_iterator findIter = markerLengths.find(ids[i]);
247 if(!depth.empty() && (markerLength_ == 0 || (markerLength_<0 && findIter==markerLengths.end())))
249 float d =
util2d::getDepth(depth, (corners[i][0].
x + (corners[i][2].
x-corners[i][0].
x)/2.0
f)*rgbToDepthFactorX, (corners[i][0].y + (corners[i][2].y-corners[i][0].y)/2.0
f)*rgbToDepthFactorY,
true, 0.02
f,
true);
250 float d1 =
util2d::getDepth(depth, corners[i][0].x*rgbToDepthFactorX, corners[i][0].y*rgbToDepthFactorY,
true, 0.02
f,
true);
251 float d2 =
util2d::getDepth(depth, corners[i][1].x*rgbToDepthFactorX, corners[i][1].y*rgbToDepthFactorY,
true, 0.02
f,
true);
252 float d3 =
util2d::getDepth(depth, corners[i][2].x*rgbToDepthFactorX, corners[i][2].y*rgbToDepthFactorY,
true, 0.02
f,
true);
253 float d4 =
util2d::getDepth(depth, corners[i][3].x*rgbToDepthFactorX, corners[i][3].y*rgbToDepthFactorY,
true, 0.02
f,
true);
257 if(d>0 && d1>0 && d2>0 && d3>0 && d4>0)
259 float scale = d/tvecs[i].val[2];
261 if( fabs(d-d1) < maxDepthError_ &&
262 fabs(d-d2) < maxDepthError_ &&
263 fabs(d-d3) < maxDepthError_ &&
264 fabs(d-d4) < maxDepthError_)
267 scales.push_back(length);
268 tvecs[i] *= scales.back();
269 UWARN(
"Automatic marker length estimation: id=%d depth=%fm length=%fm", ids[i], d, scales.back());
273 UWARN(
"The four marker's corners should be " 274 "perpendicular to camera to estimate correctly " 275 "the marker's length. Errors: %f, %f, %f > %fm (%s). Four corners: %f %f %f %f (middle=%f). " 276 "Parameter %s can be set to non-null to skip automatic " 277 "marker length estimation. Detections are ignored.",
278 fabs(d1-d2), fabs(d1-d3), fabs(d1-d4), maxDepthError_, Parameters::kMarkerMaxDepthError().c_str(),
280 Parameters::kMarkerLength().c_str());
286 UWARN(
"Some depth values (%f,%f,%f,%f, middle=%f) cannot be detected on the " 287 "marker's corners, cannot initialize marker length. " 288 "Parameter %s can be set to non-null to skip automatic " 289 "marker length estimation. Detections are ignored.",
291 Parameters::kMarkerLength().c_str());
295 else if(markerLength_ < 0)
297 if(findIter!=markerLengths.end())
299 length = findIter->second;
304 UWARN(
"Cannot find marker length for marker %d, ignoring this marker (count=%d)", ids[i], (
int)markerLengths.size());
308 else if(markerLength_ > 0)
310 length = markerLength_;
319 if((maxRange_ <= 0 || tvecs[i].val[2] < maxRange_) &&
320 (minRange_ <= 0 || tvecs[i].val[2] > minRange_))
323 cv::Rodrigues(rvecs[i], R);
324 Transform t(R.at<
double>(0,0), R.at<
double>(0,1), R.at<
double>(0,2), tvecs[i].val[0],
325 R.at<
double>(1,0), R.at<
double>(1,1), R.at<
double>(1,2), tvecs[i].val[1],
326 R.at<
double>(2,0), R.at<
double>(2,1), R.at<
double>(2,2), tvecs[i].val[2]);
328 detections.insert(std::make_pair(ids[i],
MarkerInfo(ids[i], length, pose)));
332 if(markerLength_ == 0 && !scales.empty())
335 float maxError = 0.0f;
336 for(
size_t i=0; i<scales.size(); ++i)
340 float error = fabs(scales[i]-scales[0]);
343 UWARN(
"The marker's length detected between 2 of the " 344 "markers doesn't match (%fm vs %fm)." 345 "Parameter %s can be set to non-null to skip automatic " 346 "marker length estimation. Detections are ignored.",
347 scales[i], scales[0],
348 Parameters::kMarkerLength().c_str());
359 markerLength_ = sum/float(scales.size());
360 UWARN(
"Final marker length estimated = %fm, max error=%fm (used for subsequent detections)", markerLength_, maxError);
364 if(imageWithDetections)
366 image.copyTo(*imageWithDetections);
369 cv::aruco::drawDetectedMarkers(*imageWithDetections, corners, ids);
371 for(
unsigned int i = 0; i < ids.size(); i++)
373 std::map<int, MarkerInfo>::iterator iter = detections.find(ids[i]);
374 if(iter!=detections.end())
376 #if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && (CV_MINOR_VERSION >1 || (CV_MINOR_VERSION==1 && CV_PATCH_VERSION>=1))) 377 cv::drawFrameAxes(*imageWithDetections, model.
K(), model.
D(), rvecs[i], tvecs[i], iter->second.length() * 0.5f);
379 cv::aruco::drawAxis(*imageWithDetections, model.
K(), model.
D(), rvecs[i], tvecs[i], iter->second.length() * 0.5f);
387 UERROR(
"RTAB-Map is not built with \"aruco\" module from OpenCV.");
static bool parse(const ParametersMap ¶meters, const std::string &key, bool &value)
std::map< int, MarkerInfo > detect(const cv::Mat &image, const std::vector< CameraModel > &models, const cv::Mat &depth=cv::Mat(), const std::map< int, float > &markerLengths=std::map< int, float >(), cv::Mat *imageWithDetections=0)
std::map< std::string, std::string > ParametersMap
#define UASSERT(condition)
const Transform & localTransform() const
static ULogger::Level level()
MarkerDetector(const ParametersMap ¶meters=ParametersMap())
virtual ~MarkerDetector()
ULogger class and convenient macros.
void parseParameters(const ParametersMap ¶meters)
float RTABMAP_EXP getDepth(const cv::Mat &depthImage, float x, float y, bool smoothing, float depthErrorRatio=0.02f, bool estWithNeighborsIfNull=false)
GLM_FUNC_DECL genType::value_type length(genType const &x)