Added wordID in shown keypoint info, improved fixed vocabulary behavior

This commit is contained in:
matlabbe 2015-06-24 18:07:52 -04:00
parent b4e97e450d
commit f1bba5b8fb
12 changed files with 288 additions and 148 deletions

View File

@ -79,8 +79,6 @@ int main(int argc, char * argv[])
// GUI stuff // GUI stuff
QApplication app(argc, argv); QApplication app(argc, argv);
ObjWidget objWidget;
ObjWidget sceneWidget;
time.start(); time.start();
//Load as grayscale //Load as grayscale
@ -199,8 +197,8 @@ int main(int argc, char * argv[])
// PROCESS NEAREST NEIGHBOR RESULTS // PROCESS NEAREST NEIGHBOR RESULTS
//////////////////////////// ////////////////////////////
// Set gui data // Set gui data
objWidget.setData(objectKeypoints, cvtCvMat2QImage(objectImg)); ObjWidget objWidget(0, objectKeypoints, QMultiMap<int,int>(), cvtCvMat2QImage(objectImg));
sceneWidget.setData(sceneKeypoints, cvtCvMat2QImage(sceneImg)); ObjWidget sceneWidget(0, sceneKeypoints, QMultiMap<int,int>(), cvtCvMat2QImage(sceneImg));
// Find correspondences by NNDR (Nearest Neighbor Distance Ratio) // Find correspondences by NNDR (Nearest Neighbor Distance Ratio)
float nndrRatio = 0.8f; float nndrRatio = 0.8f;

View File

@ -80,7 +80,7 @@ public:
void updateDetectorExtractor(); void updateDetectorExtractor();
void updateObjects(const QList<int> & ids = QList<int>()); void updateObjects(const QList<int> & ids = QList<int>());
void updateVocabulary(); void updateVocabulary(const QList<int> & ids = QList<int>());
const QMap<int, ObjSignature*> & objects() const {return objects_;} const QMap<int, ObjSignature*> & objects() const {return objects_;}
const Vocabulary * vocabulary() const {return vocabulary_;} const Vocabulary * vocabulary() const {return vocabulary_;}

View File

@ -104,7 +104,7 @@ private Q_SLOTS:
void showHideControls(); void showHideControls();
void showObjectsFeatures(); void showObjectsFeatures();
void hideObjectsFeatures(); void hideObjectsFeatures();
void updateObjects(); void updateObjects(const QList<int> & ids = QList<int>());
void notifyParametersChanged(const QStringList & param); void notifyParametersChanged(const QStringList & param);
void moveCameraFrame(int frame); void moveCameraFrame(int frame);
void rectHovered(int objId); void rectHovered(int objId);
@ -121,8 +121,7 @@ private:
int addObjectFromFile(const QString & filePath); int addObjectFromFile(const QString & filePath);
void showObject(find_object::ObjWidget * obj); void showObject(find_object::ObjWidget * obj);
void updateObjectSize(find_object::ObjWidget * obj); void updateObjectSize(find_object::ObjWidget * obj);
void updateVocabulary(); void updateVocabulary(const QList<int> & ids = QList<int>());
void updateObjects(const QList<int> & ids);
private: private:
Ui_mainWindow * ui_; Ui_mainWindow * ui_;

View File

@ -53,14 +53,18 @@ class FINDOBJECT_EXP ObjWidget : public QWidget
public: public:
ObjWidget(QWidget * parent = 0); ObjWidget(QWidget * parent = 0);
ObjWidget(int id, const std::vector<cv::KeyPoint> & keypoints, const QImage & image, QWidget * parent = 0); ObjWidget(int id, const std::vector<cv::KeyPoint> & keypoints, const QMultiMap<int,int> & words, const QImage & image, QWidget * parent = 0);
virtual ~ObjWidget(); virtual ~ObjWidget();
void setId(int id); void setId(int id);
void setData(const std::vector<cv::KeyPoint> & keypoints, const QImage & image, const QRect & rect = QRect()); void updateImage(const QImage & image);
void updateData(const std::vector<cv::KeyPoint> & keypoints, const QMultiMap<int, int> & words=QMultiMap<int, int>());
void updateWords(const QMultiMap<int,int> & words);
void setTextLabel(const QString & text); void setTextLabel(const QString & text);
void resetKptsColor(); void resetKptsColor();
void resetKptsWordID();
void setKptColor(int index, const QColor & color); void setKptColor(int index, const QColor & color);
void setKptWordID(int index, int wordId);
void setGraphicsViewMode(bool on); void setGraphicsViewMode(bool on);
void setAutoScale(bool autoScale); void setAutoScale(bool autoScale);
void setSizedFeatures(bool on); void setSizedFeatures(bool on);
@ -75,6 +79,7 @@ public:
int id() const {return id_;} int id() const {return id_;}
const QColor & color() const {return color_;} const QColor & color() const {return color_;}
const std::vector<cv::KeyPoint> keypoints() const {return keypoints_;} const std::vector<cv::KeyPoint> keypoints() const {return keypoints_;}
const QMap<int,int> & words() const {return words_;}
const QPixmap & pixmap() const {return pixmap_;} const QPixmap & pixmap() const {return pixmap_;}
QColor defaultColor() const; QColor defaultColor() const;
bool isImageShown() const; bool isImageShown() const;
@ -110,6 +115,7 @@ private:
private: private:
int id_; int id_;
std::vector<cv::KeyPoint> keypoints_; std::vector<cv::KeyPoint> keypoints_;
QMap<int,int> words_; //<keypoint, word>
QPixmap pixmap_; QPixmap pixmap_;
QRect rect_; QRect rect_;
QList<KeypointItem*> keypointItems_; QList<KeypointItem*> keypointItems_;

View File

@ -275,7 +275,8 @@ class FINDOBJECT_EXP Settings
PARAMETER(General, multiDetectionRadius, int, 30, "Ignore detection of the same object in X pixels radius of the previous detections."); PARAMETER(General, multiDetectionRadius, int, 30, "Ignore detection of the same object in X pixels radius of the previous detections.");
PARAMETER(General, port, int, 0, "Port on objects detected are published. If port=0, a port is chosen automatically.") PARAMETER(General, port, int, 0, "Port on objects detected are published. If port=0, a port is chosen automatically.")
PARAMETER(General, autoScroll, bool, true, "Auto scroll to detected object in Objects panel."); PARAMETER(General, autoScroll, bool, true, "Auto scroll to detected object in Objects panel.");
PARAMETER(General, vocabularyIncremental, bool, false, "The vocabulary is created incrementally. When new objects are added, their descriptors are compared to those already in vocabulary to find if the visual word already exist or not. \"NearestNeighbor/nndrRatio\" is used to compare descriptors."); PARAMETER(General, vocabularyFixed, bool, false, "If the vocabulary is fixed, no new words will be added to it when adding new objects.");
PARAMETER(General, vocabularyIncremental, bool, false, "The vocabulary is created incrementally. When new objects are added, their descriptors are compared to those already in vocabulary to find if the visual word already exist or not. \"NearestNeighbor/nndrRatio\" and \"NearestNeighbor/minDistance\" are used to compare descriptors.");
PARAMETER(General, vocabularyUpdateMinWords, int, 2000, "When the vocabulary is incremental (see \"General/vocabularyIncremental\"), after X words added to vocabulary, the internal index is updated with new words. This parameter lets avoiding to reconstruct the whole nearest neighbor index after each time descriptors of an object are added to vocabulary. 0 means no incremental update."); PARAMETER(General, vocabularyUpdateMinWords, int, 2000, "When the vocabulary is incremental (see \"General/vocabularyIncremental\"), after X words added to vocabulary, the internal index is updated with new words. This parameter lets avoiding to reconstruct the whole nearest neighbor index after each time descriptors of an object are added to vocabulary. 0 means no incremental update.");
PARAMETER(General, sendNoObjDetectedEvents, bool, true, "When there are no objects detected, send an empty object detection event."); PARAMETER(General, sendNoObjDetectedEvents, bool, true, "When there are no objects detected, send an empty object detection event.");
PARAMETER(General, autoPauseOnDetection, bool, false, "Auto pause the camera when an object is detected."); PARAMETER(General, autoPauseOnDetection, bool, false, "Auto pause the camera when an object is detected.");

View File

@ -316,7 +316,8 @@ void AddObjectDialog::setState(int state)
selectedKeypoints.clear(); selectedKeypoints.clear();
detector_->detect(imgRoi, selectedKeypoints); detector_->detect(imgRoi, selectedKeypoints);
} }
ui_->objectView->setData(selectedKeypoints, cvtCvMat2QImage(imgRoi.clone())); ui_->objectView->updateImage(cvtCvMat2QImage(imgRoi.clone()));
ui_->objectView->updateData(selectedKeypoints, QMultiMap<int,int>());
ui_->objectView->setMinimumSize(roi_.width, roi_.height); ui_->objectView->setMinimumSize(roi_.width, roi_.height);
ui_->objectView->update(); ui_->objectView->update();
ui_->pushButton_next->setEnabled(true); ui_->pushButton_next->setEnabled(true);
@ -359,7 +360,7 @@ void AddObjectDialog::setState(int state)
} }
objSignature_ = new ObjSignature(0, imgRoi.clone(), ""); objSignature_ = new ObjSignature(0, imgRoi.clone(), "");
objSignature_->setData(keypoints, descriptors); objSignature_->setData(keypoints, descriptors);
objWidget_ = new ObjWidget(0, keypoints, cvtCvMat2QImage(imgRoi.clone())); objWidget_ = new ObjWidget(0, keypoints, QMultiMap<int,int>(), cvtCvMat2QImage(imgRoi.clone()));
this->accept(); this->accept();
} }
@ -385,7 +386,8 @@ void AddObjectDialog::update(const cv::Mat & image)
std::vector<cv::KeyPoint> keypoints; std::vector<cv::KeyPoint> keypoints;
detector_->detect(cameraImage_, keypoints); detector_->detect(cameraImage_, keypoints);
ui_->cameraView->setData(keypoints, cvtCvMat2QImage(cameraImage_)); ui_->cameraView->updateImage(cvtCvMat2QImage(cameraImage_));
ui_->cameraView->updateData(keypoints, QMultiMap<int,int>());
ui_->cameraView->update(); ui_->cameraView->update();
} }
else else

View File

@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "find_object/FindObject.h" #include "find_object/FindObject.h"
#include "find_object/Settings.h" #include "find_object/Settings.h"
#include "find_object/utilite/ULogger.h" #include "find_object/utilite/ULogger.h"
#include "utilite/UConversion.h"
#include "ObjSignature.h" #include "ObjSignature.h"
#include "utilite/UDirectory.h" #include "utilite/UDirectory.h"
@ -144,12 +145,12 @@ bool FindObject::saveSession(const QString & path)
int FindObject::loadObjects(const QString & dirPath, bool recursive) int FindObject::loadObjects(const QString & dirPath, bool recursive)
{ {
int loadedObjects = 0;
QString formats = Settings::getGeneral_imageFormats().remove('*').remove('.'); QString formats = Settings::getGeneral_imageFormats().remove('*').remove('.');
QStringList paths; QStringList paths;
paths.append(dirPath); paths.append(dirPath);
QList<int> idsLoaded;
while(paths.size()) while(paths.size())
{ {
QString currentDir = paths.front(); QString currentDir = paths.front();
@ -159,9 +160,10 @@ int FindObject::loadObjects(const QString & dirPath, bool recursive)
const std::list<std::string> & names = dir.getFileNames(); // sorted in natural order const std::list<std::string> & names = dir.getFileNames(); // sorted in natural order
for(std::list<std::string>::const_iterator iter=names.begin(); iter!=names.end(); ++iter) for(std::list<std::string>::const_iterator iter=names.begin(); iter!=names.end(); ++iter)
{ {
if(this->addObject((currentDir.toStdString()+dir.separator()+*iter).c_str())) const ObjSignature * s = this->addObject((currentDir.toStdString()+dir.separator()+*iter).c_str());
if(s)
{ {
++loadedObjects; idsLoaded.push_back(s->id());
} }
} }
} }
@ -179,13 +181,13 @@ int FindObject::loadObjects(const QString & dirPath, bool recursive)
} }
} }
if(loadedObjects) if(idsLoaded.size())
{ {
this->updateObjects(); this->updateObjects(idsLoaded);
this->updateVocabulary(); this->updateVocabulary(idsLoaded);
} }
return loadedObjects; return idsLoaded.size();
} }
const ObjSignature * FindObject::addObject(const QString & filePath) const ObjSignature * FindObject::addObject(const QString & filePath)
@ -279,7 +281,7 @@ void FindObject::addObjectAndUpdate(const cv::Mat & image, int id, const QString
QList<int> ids; QList<int> ids;
ids.push_back(s->id()); ids.push_back(s->id());
updateObjects(ids); updateObjects(ids);
updateVocabulary(); updateVocabulary(ids);
} }
} }
@ -739,14 +741,38 @@ void FindObject::clearVocabulary()
vocabulary_->clear(); vocabulary_->clear();
} }
void FindObject::updateVocabulary() void FindObject::updateVocabulary(const QList<int> & ids)
{ {
clearVocabulary();
int count = 0; int count = 0;
int dim = -1; int dim = -1;
int type = -1; int type = -1;
QList<ObjSignature*> objectsList;
if(ids.size())
{
for(int i=0; i<ids.size(); ++i)
{
if(objects_.contains(ids[i]))
{
objectsList.push_back(objects_[ids[i]]);
}
else
{
UERROR("Not found object %d!", ids[i]);
}
}
if(vocabulary_->size())
{
dim = vocabulary_->dim();
type = vocabulary_->type();
}
}
else
{
clearVocabulary();
objectsList = objects_.values();
}
// Get the total size and verify descriptors // Get the total size and verify descriptors
QList<ObjSignature*> objectsList = objects_.values();
for(int i=0; i<objectsList.size(); ++i) for(int i=0; i<objectsList.size(); ++i)
{ {
if(!objectsList.at(i)->descriptors().empty()) if(!objectsList.at(i)->descriptors().empty())
@ -774,94 +800,119 @@ void FindObject::updateVocabulary()
{ {
UINFO("Updating global descriptors matrix: Objects=%d, total descriptors=%d, dim=%d, type=%d", UINFO("Updating global descriptors matrix: Objects=%d, total descriptors=%d, dim=%d, type=%d",
(int)objects_.size(), count, dim, type); (int)objects_.size(), count, dim, type);
if(Settings::getGeneral_invertedSearch() || Settings::getGeneral_threads() == 1) if(!Settings::getGeneral_invertedSearch())
{ {
// If only one thread, put all descriptors in the same cv::Mat if(Settings::getGeneral_threads() == 1)
objectsDescriptors_.insert(0, cv::Mat(count, dim, type));
int row = 0;
for(int i=0; i<objectsList.size(); ++i)
{ {
if(objectsList.at(i)->descriptors().rows) // If only one thread, put all descriptors in the same cv::Mat
int row = 0;
bool vocabularyEmpty = objectsDescriptors_.size() == 0;
if(vocabularyEmpty)
{ {
cv::Mat dest(objectsDescriptors_.begin().value(), cv::Range(row, row+objectsList.at(i)->descriptors().rows)); UASSERT(objectsDescriptors_.size() == 0);
objectsList.at(i)->descriptors().copyTo(dest); objectsDescriptors_.insert(0, cv::Mat(count, dim, type));
row += objectsList.at(i)->descriptors().rows; }
// dataRange contains the upper_bound for each else
// object (the last descriptors position in the {
// global object descriptors matrix) row = objectsDescriptors_.begin().value().rows;
}
for(int i=0; i<objectsList.size(); ++i)
{
objectsList[i]->setWords(QMultiMap<int,int>());
if(objectsList.at(i)->descriptors().rows) if(objectsList.at(i)->descriptors().rows)
{ {
dataRange_.insert(row-1, objectsList.at(i)->id()); if(vocabularyEmpty)
{
cv::Mat dest(objectsDescriptors_.begin().value(), cv::Range(row, row+objectsList.at(i)->descriptors().rows));
objectsList.at(i)->descriptors().copyTo(dest);
}
else
{
UASSERT_MSG(objectsDescriptors_.begin().value().cols == objectsList.at(i)->descriptors().cols,
uFormat("%d vs %d", objectsDescriptors_.begin().value().cols, objectsList.at(i)->descriptors().cols).c_str());
UASSERT(objectsDescriptors_.begin().value().type() == objectsList.at(i)->descriptors().type());
objectsDescriptors_.begin().value().push_back(objectsList.at(i)->descriptors());
}
row += objectsList.at(i)->descriptors().rows;
// dataRange contains the upper_bound for each
// object (the last descriptors position in the
// global object descriptors matrix)
if(objectsList.at(i)->descriptors().rows)
{
dataRange_.insert(row-1, objectsList.at(i)->id());
}
} }
} }
} }
else
if(Settings::getGeneral_invertedSearch())
{ {
sessionModified_ = true;
QTime time;
time.start();
bool incremental = Settings::getGeneral_vocabularyIncremental() && !Settings::getGeneral_vocabularyFixed();
if(incremental)
{
UINFO("Creating incremental vocabulary...");
}
else if(Settings::getGeneral_vocabularyFixed())
{
UINFO("Updating vocabulary correspondences only (vocabulary is fixed)...");
}
else
{
UINFO("Creating vocabulary...");
}
QTime localTime;
localTime.start();
int updateVocabularyMinWords = Settings::getGeneral_vocabularyUpdateMinWords();
int addedWords = 0;
for(int i=0; i<objectsList.size(); ++i) for(int i=0; i<objectsList.size(); ++i)
{ {
QMultiMap<int, int> words = vocabulary_->addWords(objectsList[i]->descriptors(), objectsList.at(i)->id()); objectsList[i]->setWords(QMultiMap<int,int>());
objectsList[i]->setWords(words); objectsDescriptors_.insert(objectsList.at(i)->id(), objectsList.at(i)->descriptors());
addedWords += words.uniqueKeys().size();
bool updated = false;
if(incremental && addedWords && addedWords >= updateVocabularyMinWords)
{
vocabulary_->update();
addedWords = 0;
updated = true;
}
UINFO("Object %d, %d words from %d descriptors (%d words, %d ms) %s",
objectsList[i]->id(),
words.uniqueKeys().size(),
objectsList[i]->descriptors().rows,
vocabulary_->size(),
localTime.restart(),
updated?"updated":"");
}
if(addedWords && !Settings::getGeneral_vocabularyFixed())
{
vocabulary_->update();
}
if(incremental)
{
UINFO("Creating incremental vocabulary... done! size=%d (%d ms)", vocabulary_->size(), time.elapsed());
}
else if(Settings::getGeneral_vocabularyFixed())
{
UINFO("Updating vocabulary correspondences only (vocabulary is fixed)... done! size=%d (%d ms)", time.elapsed());
}
else
{
UINFO("Creating vocabulary... done! size=%d (%d ms)", vocabulary_->size(), time.elapsed());
} }
} }
} }
else else
{ {
// Inverted index on (vocabulary)
sessionModified_ = true;
QTime time;
time.start();
bool incremental = Settings::getGeneral_vocabularyIncremental() && !Settings::getGeneral_vocabularyFixed();
if(incremental)
{
UINFO("Creating incremental vocabulary...");
}
else if(Settings::getGeneral_vocabularyFixed())
{
UINFO("Updating vocabulary correspondences only (vocabulary is fixed)...");
}
else
{
UINFO("Creating vocabulary...");
}
QTime localTime;
localTime.start();
int updateVocabularyMinWords = Settings::getGeneral_vocabularyUpdateMinWords();
int addedWords = 0;
for(int i=0; i<objectsList.size(); ++i) for(int i=0; i<objectsList.size(); ++i)
{ {
objectsDescriptors_.insert(objectsList.at(i)->id(), objectsList.at(i)->descriptors()); QMultiMap<int, int> words = vocabulary_->addWords(objectsList[i]->descriptors(), objectsList.at(i)->id());
objectsList[i]->setWords(words);
addedWords += words.uniqueKeys().size();
bool updated = false;
if(incremental && addedWords && addedWords >= updateVocabularyMinWords)
{
vocabulary_->update();
addedWords = 0;
updated = true;
}
UINFO("Object %d, %d words from %d descriptors (%d words, %d ms) %s",
objectsList[i]->id(),
words.uniqueKeys().size(),
objectsList[i]->descriptors().rows,
vocabulary_->size(),
localTime.restart(),
updated?"updated":"");
}
if(addedWords && !Settings::getGeneral_vocabularyFixed())
{
vocabulary_->update();
}
if(incremental)
{
UINFO("Creating incremental vocabulary... done! size=%d (%d ms)", vocabulary_->size(), time.elapsed());
}
else if(Settings::getGeneral_vocabularyFixed())
{
UINFO("Updating vocabulary correspondences only (vocabulary is fixed)... done! size=%d (%d ms)", vocabulary_->size(), time.elapsed());
}
else
{
UINFO("Creating vocabulary... done! size=%d (%d ms)", vocabulary_->size(), time.elapsed());
} }
} }
} }
@ -1202,6 +1253,7 @@ bool FindObject::detect(const cv::Mat & image, find_object::DetectionInfo & info
words = vocabulary_->addWords(info.sceneDescriptors_, -1); words = vocabulary_->addWords(info.sceneDescriptors_, -1);
vocabulary_->update(); vocabulary_->update();
info.timeStamps_.insert(DetectionInfo::kTimeIndexing, time.restart()); info.timeStamps_.insert(DetectionInfo::kTimeIndexing, time.restart());
info.sceneWords_ = words;
} }
for(QMap<int, ObjSignature*>::iterator iter=objects_.begin(); iter!=objects_.end(); ++iter) for(QMap<int, ObjSignature*>::iterator iter=objects_.begin(); iter!=objects_.end(); ++iter)
@ -1274,9 +1326,10 @@ bool FindObject::detect(const cv::Mat & image, find_object::DetectionInfo & info
if(matched) if(matched)
{ {
int wordId = results.at<int>(i,0);
if(Settings::getGeneral_invertedSearch()) if(Settings::getGeneral_invertedSearch())
{ {
int wordId = results.at<int>(i,0); info.sceneWords_.insertMulti(wordId, i);
QList<int> objIds = vocabulary_->wordToObjects().values(wordId); QList<int> objIds = vocabulary_->wordToObjects().values(wordId);
for(int j=0; j<objIds.size(); ++j) for(int j=0; j<objIds.size(); ++j)
{ {
@ -1294,7 +1347,6 @@ bool FindObject::detect(const cv::Mat & image, find_object::DetectionInfo & info
int fisrtObjectDescriptorIndex = (iter == dataRange_.begin())?0:(--iter).key()+1; int fisrtObjectDescriptorIndex = (iter == dataRange_.begin())?0:(--iter).key()+1;
int objectDescriptorIndex = i - fisrtObjectDescriptorIndex; int objectDescriptorIndex = i - fisrtObjectDescriptorIndex;
int wordId = results.at<int>(i,0);
if(words.count(wordId) == 1) if(words.count(wordId) == 1)
{ {
info.matches_.find(objectId).value().insert(objectDescriptorIndex, words.value(wordId)); info.matches_.find(objectId).value().insert(objectDescriptorIndex, words.value(wordId));

View File

@ -33,11 +33,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace find_object { namespace find_object {
KeypointItem::KeypointItem(int id, qreal x, qreal y, int r, const QString & info, const QColor & color, QGraphicsItem * parent) : KeypointItem::KeypointItem(int id, qreal x, qreal y, int r, const cv::KeyPoint & kpt, int wordID, const QColor & color, QGraphicsItem * parent) :
QGraphicsEllipseItem(x, y, r, r, parent), QGraphicsEllipseItem(x, y, r, r, parent),
info_(info),
placeHolder_(0), placeHolder_(0),
id_(id) id_(id),
kpt_(kpt),
wordID_(wordID)
{ {
this->setPen(QPen(color)); this->setPen(QPen(color));
this->setBrush(QBrush(color)); this->setBrush(QBrush(color));
@ -70,13 +71,21 @@ void KeypointItem::showDescription()
{ {
if(!placeHolder_) if(!placeHolder_)
{ {
QString info = QString( "Keypoint = %1\n"
"Word = %2\n"
"Response = %3\n"
"Angle = %4\n"
"X = %5\n"
"Y = %6\n"
"Size = %7").arg(id_).arg(wordID_).arg(kpt_.response).arg(kpt_.angle).arg(kpt_.pt.x).arg(kpt_.pt.y).arg(kpt_.size);
placeHolder_ = new QGraphicsRectItem(); placeHolder_ = new QGraphicsRectItem();
placeHolder_->setVisible(false); placeHolder_->setVisible(false);
this->scene()->addItem(placeHolder_); this->scene()->addItem(placeHolder_);
placeHolder_->setBrush(QBrush(QColor ( 0, 0, 0, 170 ))); // Black transparent background placeHolder_->setBrush(QBrush(QColor ( 0, 0, 0, 170 ))); // Black transparent background
QGraphicsTextItem * text = new QGraphicsTextItem(placeHolder_); QGraphicsTextItem * text = new QGraphicsTextItem(placeHolder_);
text->setDefaultTextColor(this->pen().color().rgb()); text->setDefaultTextColor(this->pen().color().rgb());
text->setPlainText(info_); text->setPlainText(info);
placeHolder_->setRect(text->boundingRect()); placeHolder_->setRect(text->boundingRect());
} }

View File

@ -32,16 +32,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtGui/QGraphicsTextItem> #include <QtGui/QGraphicsTextItem>
#include <QtGui/QPen> #include <QtGui/QPen>
#include <QtGui/QBrush> #include <QtGui/QBrush>
#include <opencv2/features2d/features2d.hpp>
namespace find_object { namespace find_object {
class KeypointItem : public QGraphicsEllipseItem class KeypointItem : public QGraphicsEllipseItem
{ {
public: public:
KeypointItem(int id, qreal x, qreal y, int r, const QString & info, const QColor & color = Qt::green, QGraphicsItem * parent = 0); KeypointItem(int id, qreal x, qreal y, int r, const cv::KeyPoint & kpt, int wordID = -1, const QColor & color = Qt::green, QGraphicsItem * parent = 0);
virtual ~KeypointItem(); virtual ~KeypointItem();
void setColor(const QColor & color); void setColor(const QColor & color);
void setWordID(int id) {wordID_ = id;}
int id() const {return id_;} int id() const {return id_;}
protected: protected:
@ -55,9 +57,10 @@ private:
void hideDescription(); void hideDescription();
private: private:
QString info_;
QGraphicsRectItem * placeHolder_; QGraphicsRectItem * placeHolder_;
int id_; int id_;
cv::KeyPoint kpt_;
int wordID_;
}; };
} // namespace find_object } // namespace find_object

View File

@ -183,7 +183,7 @@ MainWindow::MainWindow(FindObject * findObject, Camera * camera, QWidget * paren
//buttons //buttons
connect(ui_->pushButton_restoreDefaults, SIGNAL(clicked()), ui_->toolBox, SLOT(resetCurrentPage())); connect(ui_->pushButton_restoreDefaults, SIGNAL(clicked()), ui_->toolBox, SLOT(resetCurrentPage()));
connect(ui_->pushButton_updateObjects, SIGNAL(clicked()), this, SLOT(updateObjects())); connect(ui_->pushButton_updateObjects, SIGNAL(clicked()), this, SLOT(updateObjects(const QList<int> &)));
connect(ui_->horizontalSlider_objectsSize, SIGNAL(valueChanged(int)), this, SLOT(updateObjectsSize())); connect(ui_->horizontalSlider_objectsSize, SIGNAL(valueChanged(int)), this, SLOT(updateObjectsSize()));
ui_->actionStop_camera->setEnabled(false); ui_->actionStop_camera->setEnabled(false);
@ -247,7 +247,7 @@ MainWindow::MainWindow(FindObject * findObject, Camera * camera, QWidget * paren
iter!=findObject_->objects().constEnd(); iter!=findObject_->objects().constEnd();
++iter) ++iter)
{ {
ObjWidget * obj = new ObjWidget(iter.key(), iter.value()->keypoints(), cvtCvMat2QImage(iter.value()->image())); ObjWidget * obj = new ObjWidget(iter.key(), iter.value()->keypoints(), iter.value()->words(), cvtCvMat2QImage(iter.value()->image()));
objWidgets_.insert(obj->id(), obj); objWidgets_.insert(obj->id(), obj);
this->showObject(obj); this->showObject(obj);
} }
@ -366,7 +366,7 @@ void MainWindow::loadSession()
{ {
if(iter.value()) if(iter.value())
{ {
ObjWidget * obj = new ObjWidget(iter.key(), iter.value()->keypoints(), cvtCvMat2QImage(iter.value()->image())); ObjWidget * obj = new ObjWidget(iter.key(), iter.value()->keypoints(), iter.value()->words(), cvtCvMat2QImage(iter.value()->image()));
objWidgets_.insert(obj->id(), obj); objWidgets_.insert(obj->id(), obj);
ui_->actionSave_objects->setEnabled(true); ui_->actionSave_objects->setEnabled(true);
ui_->actionSave_session->setEnabled(true); ui_->actionSave_session->setEnabled(true);
@ -714,7 +714,9 @@ void MainWindow::addObjectFromScene()
ui_->actionSave_objects->setEnabled(true); ui_->actionSave_objects->setEnabled(true);
ui_->actionSave_session->setEnabled(true); ui_->actionSave_session->setEnabled(true);
showObject(obj); showObject(obj);
updateVocabulary(); QList<int> ids;
ids.push_back(obj->id());
updateVocabulary(ids);
objectsModified_ = true; objectsModified_ = true;
} }
if(resumeCamera || sceneImage_.empty()) if(resumeCamera || sceneImage_.empty())
@ -764,7 +766,7 @@ int MainWindow::addObjectFromFile(const QString & filePath)
const ObjSignature * s = findObject_->addObject(filePath); const ObjSignature * s = findObject_->addObject(filePath);
if(s) if(s)
{ {
ObjWidget * obj = new ObjWidget(s->id(), std::vector<cv::KeyPoint>(), cvtCvMat2QImage(s->image())); ObjWidget * obj = new ObjWidget(s->id(), std::vector<cv::KeyPoint>(), QMultiMap<int,int>(), cvtCvMat2QImage(s->image()));
objWidgets_.insert(obj->id(), obj); objWidgets_.insert(obj->id(), obj);
ui_->actionSave_objects->setEnabled(true); ui_->actionSave_objects->setEnabled(true);
ui_->actionSave_session->setEnabled(true); ui_->actionSave_session->setEnabled(true);
@ -787,7 +789,7 @@ void MainWindow::addObjectFromTcp(const cv::Mat & image, int id, const QString &
const ObjSignature * s = findObject_->addObject(image, id, filePath); const ObjSignature * s = findObject_->addObject(image, id, filePath);
if(s) if(s)
{ {
ObjWidget * obj = new ObjWidget(s->id(), std::vector<cv::KeyPoint>(), cvtCvMat2QImage(s->image())); ObjWidget * obj = new ObjWidget(s->id(), std::vector<cv::KeyPoint>(), QMultiMap<int,int>(), cvtCvMat2QImage(s->image()));
objWidgets_.insert(obj->id(), obj); objWidgets_.insert(obj->id(), obj);
ui_->actionSave_objects->setEnabled(true); ui_->actionSave_objects->setEnabled(true);
ui_->actionSave_session->setEnabled(true); ui_->actionSave_session->setEnabled(true);
@ -977,21 +979,24 @@ void MainWindow::showObject(ObjWidget * obj)
void MainWindow::updateObjects(const QList<int> & ids) void MainWindow::updateObjects(const QList<int> & ids)
{ {
if(ids.size()) if(objWidgets_.size())
{ {
this->statusBar()->showMessage(tr("Updating %1 objects...").arg(ids.size())); this->statusBar()->showMessage(tr("Updating %1 objects...").arg(ids.size()==0?objWidgets_.size():ids.size()));
findObject_->updateObjects(ids); findObject_->updateObjects(ids);
updateVocabulary(); QList<int> idsTmp = ids;
if(idsTmp.size() == 0)
{
idsTmp = objWidgets_.keys();
}
QList<ObjSignature*> signatures = findObject_->objects().values(); QList<ObjSignature*> signatures = findObject_->objects().values();
for(int i=0; i<signatures.size(); ++i) for(int i=0; i<signatures.size(); ++i)
{ {
if(ids.contains(signatures[i]->id())) if(idsTmp.contains(signatures[i]->id()))
{ {
QImage qtImage = cvtCvMat2QImage(signatures[i]->image()); objWidgets_.value(signatures[i]->id())->updateData(signatures[i]->keypoints());
objWidgets_.value(signatures[i]->id())->setData(signatures[i]->keypoints(), qtImage, signatures[i]->rect());
//update object labels //update object labels
QLabel * title = qFindChild<QLabel*>(this, QString("%1title").arg(signatures[i]->id())); QLabel * title = qFindChild<QLabel*>(this, QString("%1title").arg(signatures[i]->id()));
@ -999,6 +1004,8 @@ void MainWindow::updateObjects(const QList<int> & ids)
} }
} }
updateVocabulary(ids);
if(!camera_->isRunning() && !sceneImage_.empty()) if(!camera_->isRunning() && !sceneImage_.empty())
{ {
this->update(sceneImage_); this->update(sceneImage_);
@ -1007,23 +1014,34 @@ void MainWindow::updateObjects(const QList<int> & ids)
} }
} }
void MainWindow::updateObjects() void MainWindow::updateVocabulary(const QList<int> & ids)
{
updateObjects(objWidgets_.keys());
}
void MainWindow::updateVocabulary()
{ {
this->statusBar()->showMessage(tr("Updating vocabulary...")); this->statusBar()->showMessage(tr("Updating vocabulary..."));
QTime time; QTime time;
time.start(); time.start();
findObject_->updateVocabulary(); findObject_->updateVocabulary(ids);
if(findObject_->vocabulary()->size()) QList<int> idsTmp = ids;
if(idsTmp.size() == 0)
{ {
ui_->label_timeIndexing->setNum(time.elapsed()); idsTmp = objWidgets_.keys();
ui_->label_vocabularySize->setNum(findObject_->vocabulary()->size()); }
QList<ObjSignature*> signatures = findObject_->objects().values();
for(int i=0; i<signatures.size(); ++i)
{
if(idsTmp.contains(signatures[i]->id()))
{
objWidgets_.value(signatures[i]->id())->updateWords(signatures[i]->words());
}
}
ui_->label_timeIndexing->setNum(time.elapsed());
ui_->label_vocabularySize->setNum(findObject_->vocabulary()->size());
if(ids.size() && findObject_->vocabulary()->size() == 0 && Settings::getGeneral_vocabularyFixed() && Settings::getGeneral_invertedSearch())
{
QMessageBox::warning(this, tr("Vocabulary update"), tr("\"General/VocabularyFixed=true\" and the "
"vocabulary is empty. New features cannot be matched to any words in the vocabulary."));
} }
lastObjectsUpdateParameters_ = Settings::getParameters(); lastObjectsUpdateParameters_ = Settings::getParameters();
this->statusBar()->clearMessage(); this->statusBar()->clearMessage();
@ -1172,6 +1190,10 @@ void MainWindow::update(const cv::Mat & image)
for(QMap<int, ObjWidget*>::iterator iter=objWidgets_.begin(); iter!=objWidgets_.end(); ++iter) for(QMap<int, ObjWidget*>::iterator iter=objWidgets_.begin(); iter!=objWidgets_.end(); ++iter)
{ {
iter.value()->resetKptsColor(); iter.value()->resetKptsColor();
if(!Settings::getGeneral_invertedSearch())
{
iter.value()->resetKptsWordID();
}
} }
QTime guiRefreshTime; QTime guiRefreshTime;
@ -1184,7 +1206,8 @@ void MainWindow::update(const cv::Mat & image)
ui_->label_timeSkewAffine->setNum(info.timeStamps_.value(DetectionInfo::kTimeSkewAffine, 0)); ui_->label_timeSkewAffine->setNum(info.timeStamps_.value(DetectionInfo::kTimeSkewAffine, 0));
ui_->label_timeExtraction->setNum(info.timeStamps_.value(DetectionInfo::kTimeDescriptorExtraction, 0)); ui_->label_timeExtraction->setNum(info.timeStamps_.value(DetectionInfo::kTimeDescriptorExtraction, 0));
ui_->label_timeSubPix->setNum(info.timeStamps_.value(DetectionInfo::kTimeSubPixelRefining, 0)); ui_->label_timeSubPix->setNum(info.timeStamps_.value(DetectionInfo::kTimeSubPixelRefining, 0));
ui_->imageView_source->setData(info.sceneKeypoints_, cvtCvMat2QImage(sceneImage_)); ui_->imageView_source->updateImage(cvtCvMat2QImage(sceneImage_));
ui_->imageView_source->updateData(info.sceneKeypoints_, info.sceneWords_);
if(!findObject_->vocabulary()->size()) if(!findObject_->vocabulary()->size())
{ {
ui_->label_timeIndexing->setNum(info.timeStamps_.value(DetectionInfo::kTimeIndexing, 0)); ui_->label_timeIndexing->setNum(info.timeStamps_.value(DetectionInfo::kTimeIndexing, 0));
@ -1221,6 +1244,10 @@ void MainWindow::update(const cv::Mat & image)
{ {
obj->setKptColor(iter.key(), obj->color()); obj->setKptColor(iter.key(), obj->color());
ui_->imageView_source->setKptColor(iter.value(), obj->color()); ui_->imageView_source->setKptColor(iter.value(), obj->color());
if(!Settings::getGeneral_invertedSearch())
{
obj->setKptWordID(iter.key(), ui_->imageView_source->words().value(iter.value(), -1));
}
} }
} }
else if(!info.objDetected_.contains(id)) else if(!info.objDetected_.contains(id))
@ -1263,6 +1290,8 @@ void MainWindow::update(const cv::Mat & image)
} }
// Add homography rectangles when homographies are computed // Add homography rectangles when homographies are computed
int maxHomographyScoreId = -1;
int maxHomographyScore = 0;
QMultiMap<int, QMultiMap<int,int> >::const_iterator inliersIter = info.objDetectedInliers_.constBegin(); QMultiMap<int, QMultiMap<int,int> >::const_iterator inliersIter = info.objDetectedInliers_.constBegin();
QMultiMap<int, QMultiMap<int,int> >::const_iterator outliersIter = info.objDetectedOutliers_.constBegin(); QMultiMap<int, QMultiMap<int,int> >::const_iterator outliersIter = info.objDetectedOutliers_.constBegin();
for(QMultiMap<int,QTransform>::iterator iter = info.objDetected_.begin(); for(QMultiMap<int,QTransform>::iterator iter = info.objDetected_.begin();
@ -1270,6 +1299,13 @@ void MainWindow::update(const cv::Mat & image)
++iter, ++inliersIter, ++outliersIter) ++iter, ++inliersIter, ++outliersIter)
{ {
int id = iter.key(); int id = iter.key();
if(maxHomographyScoreId == -1 || maxHomographyScore < inliersIter.value().size())
{
maxHomographyScoreId = id;
maxHomographyScore = inliersIter.value().size();
}
ObjWidget * obj = objWidgets_.value(id); ObjWidget * obj = objWidgets_.value(id);
UASSERT(obj != 0); UASSERT(obj != 0);
@ -1294,6 +1330,10 @@ void MainWindow::update(const cv::Mat & image)
{ {
obj->setKptColor(iter.key(), obj->color()); obj->setKptColor(iter.key(), obj->color());
ui_->imageView_source->setKptColor(iter.value(), obj->color()); ui_->imageView_source->setKptColor(iter.value(), obj->color());
if(!Settings::getGeneral_invertedSearch())
{
obj->setKptWordID(iter.key(), ui_->imageView_source->words().value(iter.value(), -1));
}
} }
QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1detection").arg(id)); QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1detection").arg(id));
@ -1339,9 +1379,9 @@ void MainWindow::update(const cv::Mat & image)
ui_->label_maxMatchedDistance->setNum(info.maxMatchedDistance_); ui_->label_maxMatchedDistance->setNum(info.maxMatchedDistance_);
//Scroll objects slider to the best score //Scroll objects slider to the best score
if(maxScoreId>=0 && Settings::getGeneral_autoScroll()) if((maxScoreId>=0 || maxHomographyScoreId>=0) && Settings::getGeneral_autoScroll())
{ {
QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1title").arg(maxScoreId)); QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1title").arg(maxHomographyScoreId>=0?maxHomographyScoreId:maxScoreId));
if(label) if(label)
{ {
ui_->objects_area->verticalScrollBar()->setValue(label->pos().y()); ui_->objects_area->verticalScrollBar()->setValue(label->pos().y());
@ -1385,7 +1425,8 @@ void MainWindow::update(const cv::Mat & image)
ui_->label_timeSkewAffine->setNum(info.timeStamps_.value(DetectionInfo::kTimeSkewAffine, 0)); ui_->label_timeSkewAffine->setNum(info.timeStamps_.value(DetectionInfo::kTimeSkewAffine, 0));
ui_->label_timeExtraction->setNum(info.timeStamps_.value(DetectionInfo::kTimeDescriptorExtraction, 0)); ui_->label_timeExtraction->setNum(info.timeStamps_.value(DetectionInfo::kTimeDescriptorExtraction, 0));
ui_->label_timeSubPix->setNum(info.timeStamps_.value(DetectionInfo::kTimeSubPixelRefining, 0)); ui_->label_timeSubPix->setNum(info.timeStamps_.value(DetectionInfo::kTimeSubPixelRefining, 0));
ui_->imageView_source->setData(info.sceneKeypoints_, cvtCvMat2QImage(sceneImage_)); ui_->imageView_source->updateImage(cvtCvMat2QImage(sceneImage_));
ui_->imageView_source->updateData(info.sceneKeypoints_, info.sceneWords_);
} }
@ -1454,6 +1495,7 @@ void MainWindow::notifyParametersChanged(const QStringList & paramChanged)
else if( (iter->contains("NearestNeighbor") && Settings::getGeneral_invertedSearch()) || else if( (iter->contains("NearestNeighbor") && Settings::getGeneral_invertedSearch()) ||
iter->compare(Settings::kGeneral_invertedSearch()) == 0 || iter->compare(Settings::kGeneral_invertedSearch()) == 0 ||
(iter->compare(Settings::kGeneral_vocabularyIncremental()) == 0 && Settings::getGeneral_invertedSearch()) || (iter->compare(Settings::kGeneral_vocabularyIncremental()) == 0 && Settings::getGeneral_invertedSearch()) ||
(iter->compare(Settings::kGeneral_vocabularyFixed()) == 0 && Settings::getGeneral_invertedSearch()) ||
(iter->compare(Settings::kGeneral_threads()) == 0 && !Settings::getGeneral_invertedSearch()) ) (iter->compare(Settings::kGeneral_threads()) == 0 && !Settings::getGeneral_invertedSearch()) )
{ {
nearestNeighborParamsChanged = true; nearestNeighborParamsChanged = true;
@ -1478,7 +1520,7 @@ void MainWindow::notifyParametersChanged(const QStringList & paramChanged)
{ {
if(detectorDescriptorParamsChanged) if(detectorDescriptorParamsChanged)
{ {
this->updateObjects(objWidgets_.keys()); this->updateObjects();
} }
else if(nearestNeighborParamsChanged) else if(nearestNeighborParamsChanged)
{ {

View File

@ -65,7 +65,7 @@ ObjWidget::ObjWidget(QWidget * parent) :
{ {
setupUi(); setupUi();
} }
ObjWidget::ObjWidget(int id, const std::vector<cv::KeyPoint> & keypoints, const QImage & image, QWidget * parent) : ObjWidget::ObjWidget(int id, const std::vector<cv::KeyPoint> & keypoints, const QMultiMap<int,int> & words, const QImage & image, QWidget * parent) :
QWidget(parent), QWidget(parent),
id_(id), id_(id),
graphicsView_(0), graphicsView_(0),
@ -74,7 +74,8 @@ ObjWidget::ObjWidget(int id, const std::vector<cv::KeyPoint> & keypoints, const
color_(QColor((Qt::GlobalColor)((id % 11 + 7)==Qt::yellow?Qt::gray:(id % 11 + 7)))) color_(QColor((Qt::GlobalColor)((id % 11 + 7)==Qt::yellow?Qt::gray:(id % 11 + 7))))
{ {
setupUi(); setupUi();
this->setData(keypoints, image, image.rect()); this->updateImage(image);
this->updateData(keypoints, words);
} }
ObjWidget::~ObjWidget() ObjWidget::~ObjWidget()
{ {
@ -264,29 +265,42 @@ void ObjWidget::setTextLabel(const QString & text)
label_->setText(text); label_->setText(text);
} }
void ObjWidget::setData(const std::vector<cv::KeyPoint> & keypoints, const QImage & image, const QRect & rect) void ObjWidget::updateImage(const QImage & image)
{
pixmap_ = QPixmap::fromImage(image);
rect_ = pixmap_.rect();
label_->setVisible(image.isNull());
}
void ObjWidget::updateData(const std::vector<cv::KeyPoint> & keypoints, const QMultiMap<int, int> & words)
{ {
keypoints_ = keypoints; keypoints_ = keypoints;
kptColors_ = QVector<QColor>((int)keypoints.size(), defaultColor()); kptColors_ = QVector<QColor>((int)keypoints.size(), defaultColor());
keypointItems_.clear(); keypointItems_.clear();
rectItems_.clear(); rectItems_.clear();
this->updateWords(words);
graphicsView_->scene()->clear(); graphicsView_->scene()->clear();
graphicsViewInitialized_ = false; graphicsViewInitialized_ = false;
mouseCurrentPos_ = mousePressedPos_; // this will reset roi selection mouseCurrentPos_ = mousePressedPos_; // this will reset roi selection
pixmap_ = QPixmap::fromImage(image);
rect_ = rect;
if(rect_.isNull())
{
rect_ = pixmap_.rect();
}
//this->setMinimumSize(image_.size()); //this->setMinimumSize(image_.size());
if(graphicsViewMode_->isChecked()) if(graphicsViewMode_->isChecked())
{ {
this->setupGraphicsView(); this->setupGraphicsView();
} }
label_->setVisible(image.isNull()); }
void ObjWidget::updateWords(const QMultiMap<int,int> & words)
{
words_.clear();
for(QMultiMap<int,int>::const_iterator iter=words.begin(); iter!=words.end(); ++iter)
{
words_.insert(iter.value(), iter.key());
}
for(int i=0; i<keypointItems_.size(); ++i)
{
keypointItems_[i]->setWordID(words_.value(i,-1));
}
} }
void ObjWidget::resetKptsColor() void ObjWidget::resetKptsColor()
@ -303,6 +317,15 @@ void ObjWidget::resetKptsColor()
rectItems_.clear(); rectItems_.clear();
} }
void ObjWidget::resetKptsWordID()
{
words_.clear();
for(int i=0; i<keypointItems_.size(); ++i)
{
keypointItems_[i]->setWordID(-1);
}
}
void ObjWidget::setKptColor(int index, const QColor & color) void ObjWidget::setKptColor(int index, const QColor & color)
{ {
if(index < kptColors_.size()) if(index < kptColors_.size())
@ -325,6 +348,15 @@ void ObjWidget::setKptColor(int index, const QColor & color)
} }
} }
void ObjWidget::setKptWordID(int index, int wordID)
{
words_.insert(index, wordID);
if(index < keypointItems_.size())
{
keypointItems_.at(index)->setWordID(wordID);
}
}
void ObjWidget::addRect(QGraphicsRectItem * rect) void ObjWidget::addRect(QGraphicsRectItem * rect)
{ {
if(graphicsViewInitialized_) if(graphicsViewInitialized_)
@ -701,14 +733,8 @@ void ObjWidget::drawKeypoints(QPainter * painter)
QColor color(kptColors_.at(i).red(), kptColors_.at(i).green(), kptColors_.at(i).blue(), alpha_); QColor color(kptColors_.at(i).red(), kptColors_.at(i).green(), kptColors_.at(i).blue(), alpha_);
if(graphicsViewMode_->isChecked()) if(graphicsViewMode_->isChecked())
{ {
QString info = QString( "ID = %1\n"
"Response = %2\n"
"Angle = %3\n"
"X = %4\n"
"Y = %5\n"
"Size = %6").arg(i+1).arg(r.response).arg(r.angle).arg(r.pt.x).arg(r.pt.y).arg(r.size);
// YELLOW = NEW and multiple times // YELLOW = NEW and multiple times
item = new KeypointItem(i+1, r.pt.x-radius, r.pt.y-radius, radius*2, info, color); item = new KeypointItem(i, r.pt.x-radius, r.pt.y-radius, radius*2, r, words_.value(i, -1), color);
item->setVisible(this->isFeaturesShown()); item->setVisible(this->isFeaturesShown());
item->setZValue(2); item->setZValue(2);
graphicsView_->scene()->addItem(item); graphicsView_->scene()->addItem(item);
@ -743,7 +769,7 @@ std::vector<cv::KeyPoint> ObjWidget::selectedKeypoints() const
{ {
if(qgraphicsitem_cast<KeypointItem*>(items.at(i))) if(qgraphicsitem_cast<KeypointItem*>(items.at(i)))
{ {
selected.push_back(keypoints_.at(((KeypointItem*)items.at(i))->id()-1)); // ids start at 1 selected.push_back(keypoints_.at(((KeypointItem*)items.at(i))->id()));
} }
} }
} }

View File

@ -44,6 +44,8 @@ public:
void update(); void update();
void search(const cv::Mat & descriptors, cv::Mat & results, cv::Mat & dists, int k); void search(const cv::Mat & descriptors, cv::Mat & results, cv::Mat & dists, int k);
int size() const {return indexedDescriptors_.rows + notIndexedDescriptors_.rows;} int size() const {return indexedDescriptors_.rows + notIndexedDescriptors_.rows;}
int dim() const {return !indexedDescriptors_.empty()?indexedDescriptors_.cols:notIndexedDescriptors_.cols;}
int type() const {return !indexedDescriptors_.empty()?indexedDescriptors_.type():notIndexedDescriptors_.type();}
const QMultiMap<int, int> & wordToObjects() const {return wordToObjects_;} const QMultiMap<int, int> & wordToObjects() const {return wordToObjects_;}
const cv::Mat & indexedDescriptors() const {return indexedDescriptors_;} const cv::Mat & indexedDescriptors() const {return indexedDescriptors_;}