diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index dc0b5246..8311c05d 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -992,7 +992,7 @@ protected: { h_ = findHomography(mpts_1, mpts_2, - cv::RANSAC, + Settings::getHomographyMethod(), Settings::getHomography_ransacReprojThr(), outlierMask_); @@ -1007,6 +1007,14 @@ protected: ++outliers_; } } + if(Settings::getHomography_ignoreWhenAllInliers()) + { + // ignore homography when all features are inliers + if(inliers_ == (int)outlierMask_.size()) + { + h_ = cv::Mat(); + } + } } //printf("Homography Object %d time=%d ms\n", objectIndex_, time.elapsed()); @@ -1091,6 +1099,7 @@ void MainWindow::update(const cv::Mat & image) objectsDescriptors_[0].type() == descriptors.type()) // binary descriptor issue, if the dataTree is not yet updated with modified settings { QVector > matches(objects_.size()); // Map< ObjectDescriptorIndex, SceneDescriptorIndex > + QVector matchesId(objects_.size(), -1); float minMatchedDistance = -1.0f; float maxMatchedDistance = -1.0f; @@ -1223,7 +1232,7 @@ void MainWindow::update(const cv::Mat & image) QMap scores; int maxScoreId = -1; int maxScore = 0; - QMap > objectsDetected; + QMultiMap > objectsDetected; if(Settings::getHomography_homographyComputed()) { @@ -1239,7 +1248,8 @@ void MainWindow::update(const cv::Mat & image) for(int k=i; kkeypoints(), &keypoints)); + int objectId = matchesId[k] >=0 ? matchesId[k]:k; // the first matches ids correspond object index + threads.push_back(new HomographyThread(&matches[k], objectId, &objects_.at(objectId)->keypoints(), &keypoints)); threads.back()->start(); } @@ -1257,55 +1267,101 @@ void MainWindow::update(const cv::Mat & image) { if(threads[j]->getInliers() >= Settings::getHomography_minimumInliers()) { - if(this->isVisible()) - { - for(unsigned int k=0; kgetOutlierMask().size();++k) - { - if(threads[j]->getOutlierMask().at(k)) - { - objects_.at(index)->setKptColor(threads[j]->getIndexesA().at(k), color); - ui_->imageView_source->setKptColor(threads[j]->getIndexesB().at(k), color); - } - else - { - objects_.at(index)->setKptColor(threads[j]->getIndexesA().at(k), QColor(0,0,0)); - } - } - } - const cv::Mat & H = threads[j]->getHomography(); QTransform hTransform( H.at(0,0), H.at(1,0), H.at(2,0), H.at(0,1), H.at(1,1), H.at(2,1), H.at(0,2), H.at(1,2), H.at(2,2)); - // find center of object - QRect rect = objects_.at(index)->pixmap().rect(); - objectsDetected.insert(objects_.at(index)->id(), QPair(rect, hTransform)); - // Example getting the center of the object in the scene using the homography - //QPoint pos(rect.width()/2, rect.height()/2); - //hTransform.map(pos) - - // add rectangle - if(this->isVisible()) + int distance = Settings::getGeneral_multiDetectionRadius(); // in pixels + if(Settings::getGeneral_multiDetection()) { - label->setText(QString("%1 in %2 out").arg(threads[j]->getInliers()).arg(threads[j]->getOutliers())); - QPen rectPen(color); - rectPen.setWidth(4); - QGraphicsRectItem * rectItem = new QGraphicsRectItem(rect); - rectItem->setPen(rectPen); - rectItem->setTransform(hTransform); - ui_->imageView_source->addRect(rectItem); + // Remove inliers and recompute homography + QMultiMap newMatches; + for(unsigned int k=0; kgetOutlierMask().size();++k) + { + if(!threads[j]->getOutlierMask().at(k)) + { + newMatches.insert(threads[j]->getIndexesA().at(k), threads[j]->getIndexesB().at(k)); + } + } + matches.push_back(newMatches); + matchesId.push_back(index); + + // compute distance from previous added same objects... + QMultiMap >::iterator objIter = objectsDetected.find(objects_.at(index)->id()); + for(;objIter!=objectsDetected.end() && objIter.key() == objects_.at(index)->id(); ++objIter) + { + qreal dx = objIter.value().second.m31() - hTransform.m31(); + qreal dy = objIter.value().second.m32() - hTransform.m32(); + int d = (int)sqrt(dx*dx + dy*dy); + if(d < distance) + { + distance = d; + } + } + } + + if(distance >= Settings::getGeneral_multiDetectionRadius()) + { + if(this->isVisible()) + { + for(unsigned int k=0; kgetOutlierMask().size();++k) + { + if(threads[j]->getOutlierMask().at(k)) + { + objects_.at(index)->setKptColor(threads[j]->getIndexesA().at(k), color); + ui_->imageView_source->setKptColor(threads[j]->getIndexesB().at(k), color); + } + else if(!objectsDetected.contains(objects_.at(index)->id())) + { + objects_.at(index)->setKptColor(threads[j]->getIndexesA().at(k), QColor(0,0,0)); + } + } + } + + QRect rect = objects_.at(index)->pixmap().rect(); + objectsDetected.insert(objects_.at(index)->id(), QPair(rect, hTransform)); + // Example getting the center of the object in the scene using the homography + //QPoint pos(rect.width()/2, rect.height()/2); + //hTransform.map(pos) + + // add rectangle + if(this->isVisible()) + { + if(objectsDetected.count(objects_.at(index)->id()) > 1) + { + // if a homography is already found, set the objects count + label->setText(QString("%1 objects found").arg(objectsDetected.count(objects_.at(index)->id()))); + } + else + { + label->setText(QString("%1 in %2 out").arg(threads[j]->getInliers()).arg(threads[j]->getOutliers())); + } + QPen rectPen(color); + rectPen.setWidth(4); + QGraphicsRectItem * rectItem = new QGraphicsRectItem(rect); + rectItem->setPen(rectPen); + rectItem->setTransform(hTransform); + ui_->imageView_source->addRect(rectItem); + } } } - else + else if(this->isVisible() && objectsDetected.count(objects_.at(index)->id()) == 0) { label->setText(QString("Too low inliers (%1 in %2 out)").arg(threads[j]->getInliers()).arg(threads[j]->getOutliers())); } } - else + else if(this->isVisible() && objectsDetected.count(objects_.at(index)->id()) == 0) { - label->setText(QString("Too low matches (%1)").arg(matches[index].size())); + if(threads[j]->getInliers() >= Settings::getHomography_minimumInliers()) + { + label->setText(QString("Ignored, all inliers (%1 in %2 out)").arg(matches[index].size()).arg(threads[j]->getOutliers())); + } + else + { + label->setText(QString("Too low matches (%1)").arg(matches[index].size())); + } } } } @@ -1330,10 +1386,14 @@ void MainWindow::update(const cv::Mat & image) //scores for(int i=0; iid(), matches[i].size()); + int objectIndex = matchesId.at(i) >= 0? matchesId.at(i): i; + if(!scores.contains(objects_.at(objectIndex)->id())) + { + scores.insert(objects_.at(objectIndex)->id(), matches[i].size()); + } if(maxScoreId == -1 || maxScore < matches[i].size()) { - maxScoreId = objects_.at(i)->id(); + maxScoreId = objects_.at(objectIndex)->id(); maxScore = matches[i].size(); } } diff --git a/src/MainWindow.h b/src/MainWindow.h index 218ee638..69062b84 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -74,7 +74,7 @@ private slots: void moveCameraFrame(int frame); signals: - void objectsFound(const QMap > &); + void objectsFound(const QMultiMap > &); private: void addObjectFromFile(const QString & filePath); diff --git a/src/Settings.cpp b/src/Settings.cpp index 50506e4e..39f296c0 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #define VERBOSE 0 @@ -519,3 +520,32 @@ cv::flann::SearchParams Settings::getFlannSearchParams() getNearestNeighbor_9search_sorted()); } +int Settings::getHomographyMethod() +{ + int method = cv::RANSAC; + QString str = getHomography_method(); + QStringList split = str.split(':'); + if(split.size()==2) + { + bool ok = false; + int index = split.first().toInt(&ok); + if(ok) + { + QStringList strategies = split.last().split(';'); + if(strategies.size() == 2 && index>=0 && index<2) + { + switch(method) + { + case 0: + method = cv::LMEDS; + break; + default: + method = cv::RANSAC; + break; + } + } + } + } + if(VERBOSE)printf("Settings::getHomographyMethod() method=%d\n", method); + return method; +} diff --git a/src/Settings.h b/src/Settings.h index 17294e2a..4db69d7b 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -173,10 +173,14 @@ class Settings PARAMETER(General, invertedSearch, bool, false, "In contrast to classic Find-Object usage (where we match descriptors from the objects to those in a vocabulary created with descriptors extracted from the scene), we create a vocabulary from all the objects' descriptors and we match scene's descriptors to this vocabulary. It is the inverted search mode."); PARAMETER(General, controlsShown, bool, false, "Show play/image seek controls (useful with video file and directory of images modes)."); PARAMETER(General, threads, int, 1, "Number of threads used for objects matching and homography computation. 0 means as many threads as objects. On InvertedSearch mode, multi-threading has only effect on homography computation."); + PARAMETER(General, multiDetection, bool, false, "Multiple detection of the same object."); + PARAMETER(General, multiDetectionRadius, int, 30, "Ignore detection of the same object in X pixels radius of the previous detections."); PARAMETER(Homography, homographyComputed, bool, true, "Compute homography? On ROS, this is required to publish objects detected."); + PARAMETER(Homography, method, QString, "1:LMEDS;RANSAC", "Type of the robust estimation algorithm: least-median algorithm or RANSAC algorithm."); PARAMETER(Homography, ransacReprojThr, double, 1.0, "Maximum allowed reprojection error to treat a point pair as an inlier (used in the RANSAC method only). It usually makes sense to set this parameter somewhere in the range of 1 to 10."); PARAMETER(Homography, minimumInliers, int, 10, "Minimum inliers to accept the homography."); + PARAMETER(Homography, ignoreWhenAllInliers, bool, false, "Ignore homography when all features are inliers (sometimes when the homography doesn't converge, it returns the best homography with all features as inliers)."); public: virtual ~Settings(){} @@ -207,6 +211,8 @@ public: static cvflann::flann_distance_t getFlannDistanceType(); static cv::flann::SearchParams getFlannSearchParams(); + static int getHomographyMethod(); + private: Settings(){}