Updated how the homography is validated (which should be more robust now). Added new parameters Homography/allCornersVisible, Homography/minAngle and General/autoPauseOnDetection.
git-svn-id: http://find-object.googlecode.com/svn/trunk/find_object@377 620bd6b2-0a58-f614-fd9a-1bd335dccda9
This commit is contained in:
+116
-43
@@ -17,6 +17,7 @@
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QTime>
|
||||
#include <QtGui/QGraphicsRectItem>
|
||||
#include <stdio.h>
|
||||
|
||||
FindObject::FindObject(QObject * parent) :
|
||||
@@ -510,7 +511,8 @@ public:
|
||||
matches_(matches),
|
||||
objectId_(objectId),
|
||||
kptsA_(kptsA),
|
||||
kptsB_(kptsB)
|
||||
kptsB_(kptsB),
|
||||
code_(DetectionInfo::kRejectedUndef)
|
||||
{
|
||||
Q_ASSERT(matches && kptsA && kptsB);
|
||||
}
|
||||
@@ -523,6 +525,7 @@ public:
|
||||
QMultiMap<int, int> getInliers() const {return inliers_;}
|
||||
QMultiMap<int, int> getOutliers() const {return outliers_;}
|
||||
const cv::Mat & getHomography() const {return h_;}
|
||||
DetectionInfo::RejectedCode rejectedCode() const {return code_;}
|
||||
|
||||
protected:
|
||||
virtual void run()
|
||||
@@ -565,15 +568,20 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
// ignore homography when all features are inliers
|
||||
if(inliers_.size() == (int)outlierMask_.size() && !h_.empty())
|
||||
{
|
||||
if(Settings::getHomography_ignoreWhenAllInliers() || cv::countNonZero(h_) < 1)
|
||||
{
|
||||
// ignore homography when all features are inliers
|
||||
h_ = cv::Mat();
|
||||
code_ = DetectionInfo::kRejectedAllInliers;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
code_ = DetectionInfo::kRejectedLowMatches;
|
||||
}
|
||||
|
||||
//UINFO("Homography Object %d time=%d ms", objectIndex_, time.elapsed());
|
||||
}
|
||||
@@ -582,6 +590,7 @@ private:
|
||||
int objectId_;
|
||||
const std::vector<cv::KeyPoint> * kptsA_;
|
||||
const std::vector<cv::KeyPoint> * kptsB_;
|
||||
DetectionInfo::RejectedCode code_;
|
||||
|
||||
std::vector<int> indexesA_;
|
||||
std::vector<int> indexesB_;
|
||||
@@ -860,65 +869,129 @@ bool FindObject::detect(const cv::Mat & image, DetectionInfo & info)
|
||||
threads[j]->wait();
|
||||
|
||||
int id = threads[j]->getObjectId();
|
||||
|
||||
if(!threads[j]->getHomography().empty())
|
||||
QTransform hTransform;
|
||||
DetectionInfo::RejectedCode code = DetectionInfo::kRejectedUndef;
|
||||
if(threads[j]->getHomography().empty())
|
||||
{
|
||||
if(threads[j]->getInliers().size() >= Settings::getHomography_minimumInliers())
|
||||
code = threads[j]->rejectedCode();
|
||||
}
|
||||
if(code == DetectionInfo::kRejectedUndef &&
|
||||
threads[j]->getInliers().size() < Settings::getHomography_minimumInliers() )
|
||||
{
|
||||
code = DetectionInfo::kRejectedLowInliers;
|
||||
}
|
||||
if(code == DetectionInfo::kRejectedUndef)
|
||||
{
|
||||
const cv::Mat & H = threads[j]->getHomography();
|
||||
hTransform = QTransform(
|
||||
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,2), H.at<double>(1,2), H.at<double>(2,2));
|
||||
|
||||
// is homography valid?
|
||||
// Here we use mapToScene() from QGraphicsItem instead
|
||||
// of QTransform::map() because if the homography is not valid,
|
||||
// huge errors are set by the QGraphicsItem and not by QTransform::map();
|
||||
QRectF objectRect = objects_.value(id)->rect();
|
||||
QGraphicsRectItem item(objectRect);
|
||||
item.setTransform(hTransform);
|
||||
QPolygonF rectH = item.mapToScene(item.rect());
|
||||
|
||||
// If a point is outside of 2x times the surface of the scene, homography is invalid.
|
||||
for(int p=0; p<rectH.size(); ++p)
|
||||
{
|
||||
const cv::Mat & H = threads[j]->getHomography();
|
||||
QTransform hTransform(
|
||||
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,2), H.at<double>(1,2), H.at<double>(2,2));
|
||||
|
||||
int distance = Settings::getGeneral_multiDetectionRadius(); // in pixels
|
||||
if(Settings::getGeneral_multiDetection())
|
||||
if(rectH.at(p).x() < -image.cols || rectH.at(p).x() > image.cols*2 ||
|
||||
rectH.at(p).y() < -image.rows || rectH.at(p).y() > image.rows*2)
|
||||
{
|
||||
// Get the outliers and recompute homography with them
|
||||
matchesList.push_back(threads[j]->getOutliers());
|
||||
matchesId.push_back(id);
|
||||
code= DetectionInfo::kRejectedNotValid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// compute distance from previous added same objects...
|
||||
QMultiMap<int, QTransform>::iterator objIter = info.objDetected_.find(id);
|
||||
for(;objIter!=info.objDetected_.end() && objIter.key() == id; ++objIter)
|
||||
// angle
|
||||
if(code == DetectionInfo::kRejectedUndef &&
|
||||
Settings::getHomography_minAngle() > 0)
|
||||
{
|
||||
for(int a=0; a<rectH.size(); ++a)
|
||||
{
|
||||
// Find the smaller angle
|
||||
QLineF ab(rectH.at(a).x(), rectH.at(a).y(), rectH.at((a+1)%4).x(), rectH.at((a+1)%4).y());
|
||||
QLineF cb(rectH.at((a+1)%4).x(), rectH.at((a+1)%4).y(), rectH.at((a+2)%4).x(), rectH.at((a+2)%4).y());
|
||||
float angle = ab.angle(cb);
|
||||
float minAngle = (float)Settings::getHomography_minAngle();
|
||||
if(angle < minAngle ||
|
||||
angle > 180.0-minAngle)
|
||||
{
|
||||
qreal dx = objIter.value().m31() - hTransform.m31();
|
||||
qreal dy = objIter.value().m32() - hTransform.m32();
|
||||
int d = (int)sqrt(dx*dx + dy*dy);
|
||||
if(d < distance)
|
||||
{
|
||||
distance = d;
|
||||
}
|
||||
code = DetectionInfo::kRejectedByAngle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// multi detection
|
||||
if(code == DetectionInfo::kRejectedUndef &&
|
||||
Settings::getGeneral_multiDetection())
|
||||
{
|
||||
int distance = Settings::getGeneral_multiDetectionRadius(); // in pixels
|
||||
// Get the outliers and recompute homography with them
|
||||
matchesList.push_back(threads[j]->getOutliers());
|
||||
matchesId.push_back(id);
|
||||
|
||||
// compute distance from previous added same objects...
|
||||
QMultiMap<int, QTransform>::iterator objIter = info.objDetected_.find(id);
|
||||
for(;objIter!=info.objDetected_.end() && objIter.key() == id; ++objIter)
|
||||
{
|
||||
qreal dx = objIter.value().m31() - hTransform.m31();
|
||||
qreal dy = objIter.value().m32() - hTransform.m32();
|
||||
int d = (int)sqrt(dx*dx + dy*dy);
|
||||
if(d < distance)
|
||||
{
|
||||
distance = d;
|
||||
}
|
||||
}
|
||||
|
||||
if(distance >= Settings::getGeneral_multiDetectionRadius())
|
||||
if(distance < Settings::getGeneral_multiDetectionRadius())
|
||||
{
|
||||
QRect rect = objects_.value(id)->rect();
|
||||
info.objDetected_.insert(id, hTransform);
|
||||
info.objDetectedSizes_.insert(id, rect.size());
|
||||
info.objDetectedInliers_.insert(id, threads[j]->getInliers());
|
||||
info.objDetectedOutliers_.insert(id, threads[j]->getOutliers());
|
||||
info.objDetectedInliersCount_.insert(id, threads[j]->getInliers().size());
|
||||
info.objDetectedOutliersCount_.insert(id, threads[j]->getOutliers().size());
|
||||
info.objDetectedFilenames_.insert(id, objects_.value(id)->filename());
|
||||
}
|
||||
else
|
||||
{
|
||||
info.rejectedInliers_.insert(id, threads[j]->getInliers());
|
||||
info.rejectedOutliers_.insert(id, threads[j]->getOutliers());
|
||||
code = DetectionInfo::kRejectedSuperposed;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// Corners visible
|
||||
if(code == DetectionInfo::kRejectedUndef &&
|
||||
Settings::getHomography_allCornersVisible())
|
||||
{
|
||||
info.rejectedInliers_.insert(id, threads[j]->getInliers());
|
||||
info.rejectedOutliers_.insert(id, threads[j]->getOutliers());
|
||||
// Now verify if all corners are in the scene
|
||||
QRectF sceneRect(0,0,image.cols, image.rows);
|
||||
for(int p=0; p<rectH.size(); ++p)
|
||||
{
|
||||
if(!sceneRect.contains(QPointF(rectH.at(p).x(), rectH.at(p).y())))
|
||||
{
|
||||
code = DetectionInfo::kRejectedCornersOutside;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(code == DetectionInfo::kRejectedUndef)
|
||||
{
|
||||
// Accepted!
|
||||
//std::cout << "H= " << threads[j]->getHomography() << std::endl;
|
||||
|
||||
info.objDetected_.insert(id, hTransform);
|
||||
info.objDetectedSizes_.insert(id, objects_.value(id)->rect().size());
|
||||
info.objDetectedInliers_.insert(id, threads[j]->getInliers());
|
||||
info.objDetectedOutliers_.insert(id, threads[j]->getOutliers());
|
||||
info.objDetectedInliersCount_.insert(id, threads[j]->getInliers().size());
|
||||
info.objDetectedOutliersCount_.insert(id, threads[j]->getOutliers().size());
|
||||
info.objDetectedFilenames_.insert(id, objects_.value(id)->filename());
|
||||
}
|
||||
else
|
||||
{
|
||||
//Rejected!
|
||||
info.rejectedInliers_.insert(id, threads[j]->getInliers());
|
||||
info.rejectedOutliers_.insert(id, threads[j]->getOutliers());
|
||||
info.rejectedCodes_.insert(id, code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-5
@@ -32,6 +32,7 @@
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QLineF>
|
||||
|
||||
#include <QtGui/QFileDialog>
|
||||
#include <QtGui/QMessageBox>
|
||||
@@ -129,7 +130,7 @@ MainWindow::MainWindow(FindObject * findObject, Camera * camera, QWidget * paren
|
||||
ui_->menuView->addAction(ui_->dockWidget_plot->toggleViewAction());
|
||||
connect(ui_->toolBox, SIGNAL(parametersChanged(const QStringList &)), this, SLOT(notifyParametersChanged(const QStringList &)));
|
||||
|
||||
ui_->imageView_source->setGraphicsViewMode(false);
|
||||
ui_->imageView_source->setGraphicsViewMode(true);
|
||||
ui_->imageView_source->setTextLabel(tr("Press \"space\" to start the camera..."));
|
||||
ui_->imageView_source->setMirrorView(Settings::getGeneral_mirrorView());
|
||||
connect((QCheckBox*)ui_->toolBox->getParameterWidget(Settings::kGeneral_mirrorView()),
|
||||
@@ -963,22 +964,39 @@ void MainWindow::update(const cv::Mat & image)
|
||||
QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1detection").arg(id));
|
||||
QMultiMap<int, int> rejectedInliers = info.rejectedInliers_.value(id);
|
||||
QMultiMap<int, int> rejectedOutliers = info.rejectedOutliers_.value(id);
|
||||
if(jter.value().size() < Settings::getHomography_minimumInliers())
|
||||
int rejectedCode = info.rejectedCodes_.value(id);
|
||||
if(rejectedCode == DetectionInfo::kRejectedLowMatches)
|
||||
{
|
||||
label->setText(QString("Too low matches (%1)").arg(jter.value().size()));
|
||||
}
|
||||
else if(rejectedInliers.size() >= Settings::getHomography_minimumInliers())
|
||||
else if(rejectedCode == DetectionInfo::kRejectedAllInliers)
|
||||
{
|
||||
label->setText(QString("Ignored, all inliers (%1 in %2 out)").arg(rejectedInliers.size()).arg(rejectedOutliers.size()));
|
||||
}
|
||||
else
|
||||
else if(rejectedCode == DetectionInfo::kRejectedNotValid)
|
||||
{
|
||||
label->setText(QString("Not valid homography (%1 in %2 out)").arg(rejectedInliers.size()).arg(rejectedOutliers.size()));
|
||||
}
|
||||
else if(rejectedCode == DetectionInfo::kRejectedLowInliers)
|
||||
{
|
||||
label->setText(QString("Too low inliers (%1 in %2 out)").arg(rejectedInliers.size()).arg(rejectedOutliers.size()));
|
||||
}
|
||||
|
||||
else if(rejectedCode == DetectionInfo::kRejectedCornersOutside)
|
||||
{
|
||||
label->setText(QString("Corners not visible (%1 in %2 out)").arg(rejectedInliers.size()).arg(rejectedOutliers.size()));
|
||||
}
|
||||
else if(rejectedCode == DetectionInfo::kRejectedByAngle)
|
||||
{
|
||||
label->setText(QString("Angle too small (%1 in %2 out)").arg(rejectedInliers.size()).arg(rejectedOutliers.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(camera_->isRunning() && Settings::getGeneral_autoPauseOnDetection() && info.objDetected_.size())
|
||||
{
|
||||
this->pauseProcessing();
|
||||
}
|
||||
|
||||
// Add homography rectangles when homographies are computed
|
||||
QMultiMap<int, QMultiMap<int,int> >::const_iterator inliersIter = info.objDetectedInliers_.constBegin();
|
||||
QMultiMap<int, QMultiMap<int,int> >::const_iterator outliersIter = info.objDetectedOutliers_.constBegin();
|
||||
|
||||
+29
-1
@@ -48,7 +48,35 @@ void RectItem::showDescription()
|
||||
placeHolder_->setBrush(QBrush(QColor ( 0, 0, 0, 170 ))); // Black transparent background
|
||||
QGraphicsTextItem * text = new QGraphicsTextItem(placeHolder_);
|
||||
text->setDefaultTextColor(this->pen().color().rgb());
|
||||
text->setPlainText(tr("Object=%1").arg(id_));
|
||||
QTransform t = this->transform();
|
||||
QPolygonF rectH = this->mapToScene(this->rect());
|
||||
float angle = 90.0f;
|
||||
for(int a=0; a<rectH.size(); ++a)
|
||||
{
|
||||
// Find the smaller angle
|
||||
QLineF ab(rectH.at(a).x(), rectH.at(a).y(), rectH.at((a+1)%4).x(), rectH.at((a+1)%4).y());
|
||||
QLineF cb(rectH.at((a+1)%4).x(), rectH.at((a+1)%4).y(), rectH.at((a+2)%4).x(), rectH.at((a+2)%4).y());
|
||||
float angleTmp = ab.angle(cb);
|
||||
if(angleTmp > 90.0f)
|
||||
{
|
||||
angleTmp = 180.0f - angleTmp;
|
||||
}
|
||||
if(angleTmp < angle)
|
||||
{
|
||||
angle = angleTmp;
|
||||
}
|
||||
}
|
||||
text->setPlainText(tr(
|
||||
"Object=%1\n"
|
||||
"Homography= [\n"
|
||||
" %2 %3 %4\n"
|
||||
" %5 %6 %7\n"
|
||||
" %8 %9 %10]\n"
|
||||
"Angle=%11").arg(id_)
|
||||
.arg(t.m11()).arg(t.m12()).arg(t.m13())
|
||||
.arg(t.m21()).arg(t.m22()).arg(t.m23())
|
||||
.arg(t.m31()).arg(t.m32()).arg(t.m33())
|
||||
.arg(angle));
|
||||
placeHolder_->setRect(text->boundingRect());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user