fixed issue 18 : Mutiple detection of the same object

git-svn-id: http://find-object.googlecode.com/svn/trunk/find_object@205 620bd6b2-0a58-f614-fd9a-1bd335dccda9
This commit is contained in:
matlabbe 2014-04-11 19:24:59 +00:00
parent ada571b679
commit 444a001533
4 changed files with 137 additions and 41 deletions

View File

@ -992,7 +992,7 @@ protected:
{ {
h_ = findHomography(mpts_1, h_ = findHomography(mpts_1,
mpts_2, mpts_2,
cv::RANSAC, Settings::getHomographyMethod(),
Settings::getHomography_ransacReprojThr(), Settings::getHomography_ransacReprojThr(),
outlierMask_); outlierMask_);
@ -1007,6 +1007,14 @@ protected:
++outliers_; ++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()); //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 objectsDescriptors_[0].type() == descriptors.type()) // binary descriptor issue, if the dataTree is not yet updated with modified settings
{ {
QVector<QMultiMap<int, int> > matches(objects_.size()); // Map< ObjectDescriptorIndex, SceneDescriptorIndex > QVector<QMultiMap<int, int> > matches(objects_.size()); // Map< ObjectDescriptorIndex, SceneDescriptorIndex >
QVector<int> matchesId(objects_.size(), -1);
float minMatchedDistance = -1.0f; float minMatchedDistance = -1.0f;
float maxMatchedDistance = -1.0f; float maxMatchedDistance = -1.0f;
@ -1223,7 +1232,7 @@ void MainWindow::update(const cv::Mat & image)
QMap<int, float> scores; QMap<int, float> scores;
int maxScoreId = -1; int maxScoreId = -1;
int maxScore = 0; int maxScore = 0;
QMap<int, QPair<QRect, QTransform> > objectsDetected; QMultiMap<int, QPair<QRect, QTransform> > objectsDetected;
if(Settings::getHomography_homographyComputed()) if(Settings::getHomography_homographyComputed())
{ {
@ -1239,7 +1248,8 @@ void MainWindow::update(const cv::Mat & image)
for(int k=i; k<i+threadCounts && k<matches.size(); ++k) for(int k=i; k<i+threadCounts && k<matches.size(); ++k)
{ {
threads.push_back(new HomographyThread(&matches[k], k, &objects_.at(k)->keypoints(), &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(); threads.back()->start();
} }
@ -1257,55 +1267,101 @@ void MainWindow::update(const cv::Mat & image)
{ {
if(threads[j]->getInliers() >= Settings::getHomography_minimumInliers()) if(threads[j]->getInliers() >= Settings::getHomography_minimumInliers())
{ {
if(this->isVisible())
{
for(unsigned int k=0; k<threads[j]->getOutlierMask().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(); const cv::Mat & H = threads[j]->getHomography();
QTransform hTransform( QTransform hTransform(
H.at<double>(0,0), H.at<double>(1,0), H.at<double>(2,0), H.at<double>(0,0), H.at<double>(1,0), H.at<double>(2,0),
H.at<double>(0,1), H.at<double>(1,1), H.at<double>(2,1), H.at<double>(0,1), H.at<double>(1,1), H.at<double>(2,1),
H.at<double>(0,2), H.at<double>(1,2), H.at<double>(2,2)); H.at<double>(0,2), H.at<double>(1,2), H.at<double>(2,2));
// find center of object int distance = Settings::getGeneral_multiDetectionRadius(); // in pixels
QRect rect = objects_.at(index)->pixmap().rect(); if(Settings::getGeneral_multiDetection())
objectsDetected.insert(objects_.at(index)->id(), QPair<QRect, QTransform>(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())
{ {
label->setText(QString("%1 in %2 out").arg(threads[j]->getInliers()).arg(threads[j]->getOutliers())); // Remove inliers and recompute homography
QPen rectPen(color); QMultiMap<int, int> newMatches;
rectPen.setWidth(4); for(unsigned int k=0; k<threads[j]->getOutlierMask().size();++k)
QGraphicsRectItem * rectItem = new QGraphicsRectItem(rect); {
rectItem->setPen(rectPen); if(!threads[j]->getOutlierMask().at(k))
rectItem->setTransform(hTransform); {
ui_->imageView_source->addRect(rectItem); 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<int, QPair<QRect, QTransform> >::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; k<threads[j]->getOutlierMask().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<QRect, QTransform>(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())); 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 //scores
for(int i=0; i<matches.size(); ++i) for(int i=0; i<matches.size(); ++i)
{ {
scores.insert(objects_.at(i)->id(), 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()) if(maxScoreId == -1 || maxScore < matches[i].size())
{ {
maxScoreId = objects_.at(i)->id(); maxScoreId = objects_.at(objectIndex)->id();
maxScore = matches[i].size(); maxScore = matches[i].size();
} }
} }

View File

@ -74,7 +74,7 @@ private slots:
void moveCameraFrame(int frame); void moveCameraFrame(int frame);
signals: signals:
void objectsFound(const QMap<int, QPair<QRect, QTransform> > &); void objectsFound(const QMultiMap<int, QPair<QRect, QTransform> > &);
private: private:
void addObjectFromFile(const QString & filePath); void addObjectFromFile(const QString & filePath);

View File

@ -9,6 +9,7 @@
#include <QtCore/QDir> #include <QtCore/QDir>
#include <stdio.h> #include <stdio.h>
#include <opencv2/nonfree/features2d.hpp> #include <opencv2/nonfree/features2d.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#define VERBOSE 0 #define VERBOSE 0
@ -519,3 +520,32 @@ cv::flann::SearchParams Settings::getFlannSearchParams()
getNearestNeighbor_9search_sorted()); 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;
}

View File

@ -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, 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, 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, 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, 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, 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, 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: public:
virtual ~Settings(){} virtual ~Settings(){}
@ -207,6 +211,8 @@ public:
static cvflann::flann_distance_t getFlannDistanceType(); static cvflann::flann_distance_t getFlannDistanceType();
static cv::flann::SearchParams getFlannSearchParams(); static cv::flann::SearchParams getFlannSearchParams();
static int getHomographyMethod();
private: private:
Settings(){} Settings(){}