Added Load/Save vocabulary actions in File menu (in console mode, see "--vocabulary" option) (issue #2)

This commit is contained in:
matlabbe 2015-07-07 16:49:38 -04:00
parent a4a9b8ca5f
commit dd14e5e1ca
12 changed files with 341 additions and 58 deletions

View File

@ -99,15 +99,19 @@ void showUsage()
" --console Don't use the GUI (by default the camera will be\n" " --console Don't use the GUI (by default the camera will be\n"
" started automatically). Option --objects must also be\n" " started automatically). Option --objects must also be\n"
" used with valid objects.\n" " used with valid objects.\n"
" --session \"path\" Path to a session to load (*.bin).\n" " --session \"path\" Path to a session to load (*.bin). Use \"--session_new\" to\n"
" create a session instead (will be saved to \"path\" on exit, only\n"
" on console mode).\n"
" --object \"path\" Path to an object to detect.\n" " --object \"path\" Path to an object to detect.\n"
" --objects \"path\" Directory of the objects to detect (--object is ignored).\n" " --objects \"path\" Directory of the objects to detect (--object is ignored).\n"
" --config \"path\" Path to configuration file (default: %s).\n" " --config \"path\" Path to configuration file (default: %s).\n"
" If set to \"\", default parameters are used " " If set to \"\", default parameters are used\n"
" without saving modified parameters on closing.\n" " without saving modified parameters on closing.\n"
" --scene \"path\" Path to a scene image file.\n" " --scene \"path\" Path to a scene image file.\n"
" --vocabulary \"path\" Path to a vocabulary file (*.yaml *.xml). Parameters \"General/invertedSearch\"\n"
" and \"General/vocabularyFixed\" will be also enabled. Ignored if \"--session\" is set.\n"
" --images_not_saved Don't keep images in RAM after the features are extracted (only\n" " --images_not_saved Don't keep images in RAM after the features are extracted (only\n"
" in console mode). Images won't be saved if an output session is set.\n" " in console mode). Images won't be saved if an output session is set.\n"
" --debug Show debug log.\n" " --debug Show debug log.\n"
" --debug-time Show debug log with time.\n" " --debug-time Show debug log with time.\n"
" --params Show all parameters.\n" " --params Show all parameters.\n"
@ -133,10 +137,12 @@ int main(int argc, char* argv[])
////////////////////////// //////////////////////////
bool guiMode = true; bool guiMode = true;
QString sessionPath = ""; QString sessionPath = "";
bool sessionNew = false;
QString objectsPath = ""; QString objectsPath = "";
QString objectPath = ""; QString objectPath = "";
QString scenePath = ""; QString scenePath = "";
QString configPath = find_object::Settings::iniDefaultPath(); QString configPath = find_object::Settings::iniDefaultPath();
QString vocabularyPath = "";
QString jsonPath; QString jsonPath;
find_object::ParametersMap customParameters; find_object::ParametersMap customParameters;
bool imagesSaved = true; bool imagesSaved = true;
@ -176,8 +182,15 @@ int main(int argc, char* argv[])
continue; continue;
} }
if(strcmp(argv[i], "-session") == 0 || if(strcmp(argv[i], "-session") == 0 ||
strcmp(argv[i], "--session") == 0) strcmp(argv[i], "--session") == 0 ||
strcmp(argv[i], "-session_new") == 0 ||
strcmp(argv[i], "--session_new") == 0)
{ {
if(strcmp(argv[i], "-session_new") == 0 ||
strcmp(argv[i], "--session_new") == 0)
{
sessionNew = true;
}
++i; ++i;
if(i < argc) if(i < argc)
{ {
@ -186,9 +199,10 @@ int main(int argc, char* argv[])
{ {
sessionPath.replace('~', QDir::homePath()); sessionPath.replace('~', QDir::homePath());
} }
if(!QFile(sessionPath).exists())
if(!sessionNew && !QFile(sessionPath).exists())
{ {
UERROR("Session path not valid : %s", sessionPath.toStdString().c_str()); UERROR("Session path not valid : %s (if you want to create a new session, use \"--session_new\")", sessionPath.toStdString().c_str());
showUsage(); showUsage();
} }
} }
@ -244,6 +258,29 @@ int main(int argc, char* argv[])
} }
continue; continue;
} }
if(strcmp(argv[i], "-vocabulary") == 0 ||
strcmp(argv[i], "--vocabulary") == 0)
{
++i;
if(i < argc)
{
vocabularyPath = argv[i];
if(vocabularyPath.contains('~'))
{
vocabularyPath.replace('~', QDir::homePath());
}
if(!QFile(vocabularyPath).exists())
{
UERROR("Vocabulary path not valid : %s", vocabularyPath.toStdString().c_str());
showUsage();
}
}
else
{
showUsage();
}
continue;
}
if(strcmp(argv[i], "-config") == 0 || if(strcmp(argv[i], "-config") == 0 ||
strcmp(argv[i], "--config") == 0) strcmp(argv[i], "--config") == 0)
{ {
@ -365,7 +402,14 @@ int main(int argc, char* argv[])
UINFO(" GUI mode = %s", guiMode?"true":"false"); UINFO(" GUI mode = %s", guiMode?"true":"false");
if(!sessionPath.isEmpty()) if(!sessionPath.isEmpty())
{ {
UINFO(" Session path: \"%s\"", sessionPath.toStdString().c_str()); if(sessionNew)
{
UINFO(" Session path: \"%s\" [NEW]", sessionPath.toStdString().c_str());
}
else
{
UINFO(" Session path: \"%s\"", sessionPath.toStdString().c_str());
}
} }
else if(!objectsPath.isEmpty()) else if(!objectsPath.isEmpty())
{ {
@ -381,6 +425,21 @@ int main(int argc, char* argv[])
UINFO(" JSON path: \"%s\"", jsonPath.toStdString().c_str()); UINFO(" JSON path: \"%s\"", jsonPath.toStdString().c_str());
} }
UINFO(" Settings path: \"%s\"", configPath.toStdString().c_str()); UINFO(" Settings path: \"%s\"", configPath.toStdString().c_str());
UINFO(" Vocabulary path: \"%s\"", vocabularyPath.toStdString().c_str());
if(!vocabularyPath.isEmpty())
{
if(customParameters.contains(find_object::Settings::kGeneral_vocabularyFixed()))
{
UWARN("\"General/vocabularyFixed\" custom parameter overwritten as a fixed vocabulary is used.");
}
if(customParameters.contains(find_object::Settings::kGeneral_invertedSearch()))
{
UWARN("\"General/invertedSearch\" custom parameter overwritten as a fixed vocabulary is used.");
}
customParameters[find_object::Settings::kGeneral_vocabularyFixed()] = true;
customParameters[find_object::Settings::kGeneral_invertedSearch()] = true;
}
for(find_object::ParametersMap::iterator iter= customParameters.begin(); iter!=customParameters.end(); ++iter) for(find_object::ParametersMap::iterator iter= customParameters.begin(); iter!=customParameters.end(); ++iter)
{ {
@ -405,8 +464,14 @@ int main(int argc, char* argv[])
// Load objects if path is set // Load objects if path is set
int objectsLoaded = 0; int objectsLoaded = 0;
if(!sessionPath.isEmpty()) if(!sessionPath.isEmpty() && !sessionNew)
{ {
if(!vocabularyPath.isEmpty() && !findObject->loadVocabulary(vocabularyPath))
{
UWARN("Vocabulary \"%s\" is not loaded as a session \"%s\" is already loaded",
vocabularyPath.toStdString().c_str(),
sessionPath.toStdString().c_str());
}
if(!findObject->loadSession(sessionPath)) if(!findObject->loadSession(sessionPath))
{ {
UERROR("Could not load session \"%s\"", sessionPath.toStdString().c_str()); UERROR("Could not load session \"%s\"", sessionPath.toStdString().c_str());
@ -418,6 +483,10 @@ int main(int argc, char* argv[])
} }
else if(!objectsPath.isEmpty()) else if(!objectsPath.isEmpty())
{ {
if(!vocabularyPath.isEmpty() && !findObject->loadVocabulary(vocabularyPath))
{
UERROR("Failed to load vocabulary \"%s\"", vocabularyPath.toStdString().c_str());
}
objectsLoaded = findObject->loadObjects(objectsPath); objectsLoaded = findObject->loadObjects(objectsPath);
if(!objectsLoaded) if(!objectsLoaded)
{ {
@ -426,6 +495,11 @@ int main(int argc, char* argv[])
} }
else if(!objectPath.isEmpty()) else if(!objectPath.isEmpty())
{ {
if(!vocabularyPath.isEmpty() && !findObject->loadVocabulary(vocabularyPath))
{
UERROR("Failed to load vocabulary \"%s\"", vocabularyPath.toStdString().c_str());
}
const find_object::ObjSignature * obj = findObject->addObject(objectPath); const find_object::ObjSignature * obj = findObject->addObject(objectPath);
if(obj) if(obj)
{ {
@ -438,6 +512,11 @@ int main(int argc, char* argv[])
UWARN("No object loaded from \"%s\"", objectsPath.toStdString().c_str()); UWARN("No object loaded from \"%s\"", objectsPath.toStdString().c_str());
} }
} }
else if(!vocabularyPath.isEmpty() && !findObject->loadVocabulary(vocabularyPath))
{
UERROR("Failed to load vocabulary \"%s\"", vocabularyPath.toStdString().c_str());
}
cv::Mat scene; cv::Mat scene;
if(!scenePath.isEmpty()) if(!scenePath.isEmpty())
{ {
@ -468,13 +547,6 @@ int main(int argc, char* argv[])
} }
else else
{ {
if(objectsLoaded == 0)
{
UERROR("In console mode, at least one object must be loaded! See -console option.");
delete findObject;
showUsage();
}
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
if(!scene.empty()) if(!scene.empty())
@ -535,13 +607,20 @@ int main(int argc, char* argv[])
{ {
app.exec(); app.exec();
if(!sessionPath.isEmpty() && findObject->isSessionModified()) if(!sessionPath.isEmpty())
{ {
UINFO("The session has been modified, updating the session file..."); if(findObject->isSessionModified())
if(findObject->saveSession(sessionPath))
{ {
UINFO("Session \"%s\" successfully saved (%d objects)!", UINFO("The session has been modified, updating the session file...");
sessionPath.toStdString().c_str(), findObject->objects().size()); if(findObject->saveSession(sessionPath))
{
UINFO("Session \"%s\" successfully saved (%d objects)!",
sessionPath.toStdString().c_str(), findObject->objects().size());
}
}
else if(sessionNew)
{
UINFO("The session has not been modified, session file not created...");
} }
} }
} }

View File

@ -69,6 +69,9 @@ public:
bool saveSession(const QString & path); bool saveSession(const QString & path);
bool isSessionModified() const {return sessionModified_;} bool isSessionModified() const {return sessionModified_;}
bool saveVocabulary(const QString & filePath) const;
bool loadVocabulary(const QString & filePath);
int loadObjects(const QString & dirPath, bool recursive = false); // call updateObjects() int loadObjects(const QString & dirPath, bool recursive = false); // call updateObjects()
const ObjSignature * addObject(const QString & filePath); const ObjSignature * addObject(const QString & filePath);
const ObjSignature * addObject(const cv::Mat & image, int id=0, const QString & filePath = QString()); const ObjSignature * addObject(const cv::Mat & image, int id=0, const QString & filePath = QString());

View File

@ -87,6 +87,8 @@ private Q_SLOTS:
void saveSettings(); void saveSettings();
void loadObjects(); void loadObjects();
bool saveObjects(); bool saveObjects();
void loadVocabulary();
void saveVocabulary();
void addObjectFromScene(); void addObjectFromScene();
void addObjectsFromFiles(const QStringList & fileNames); void addObjectsFromFiles(const QStringList & fileNames);
void addObjectsFromFiles(); void addObjectsFromFiles();
@ -104,7 +106,7 @@ private Q_SLOTS:
void showHideControls(); void showHideControls();
void showObjectsFeatures(); void showObjectsFeatures();
void hideObjectsFeatures(); void hideObjectsFeatures();
void updateObjects(const QList<int> & ids = QList<int>()); void updateObjects();
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);
@ -122,6 +124,7 @@ private:
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(const QList<int> & ids = QList<int>()); void updateVocabulary(const QList<int> & ids = QList<int>());
void updateObjects(const QList<int> & ids);
private: private:
Ui_mainWindow * ui_; Ui_mainWindow * ui_;

View File

@ -81,7 +81,7 @@ public:
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 QMap<int,int> & words() const {return words_;}
const QPixmap & pixmap() const {return pixmap_;} const QPixmap & pixmap() const {return pixmap_;}
QColor defaultColor() const; QColor defaultColor(int id) const;
bool isImageShown() const; bool isImageShown() const;
bool isFeaturesShown() const; bool isFeaturesShown() const;
bool isSizedFeatures() const; bool isSizedFeatures() const;

View File

@ -267,7 +267,7 @@ class FINDOBJECT_EXP Settings
PARAMETER(General, nextObjID, uint, 1, "Next object ID to use."); PARAMETER(General, nextObjID, uint, 1, "Next object ID to use.");
PARAMETER(General, imageFormats, QString, "*.png *.jpg *.bmp *.tiff *.ppm *.pgm", "Image formats supported."); PARAMETER(General, imageFormats, QString, "*.png *.jpg *.bmp *.tiff *.ppm *.pgm", "Image formats supported.");
PARAMETER(General, videoFormats, QString, "*.avi *.m4v *.mp4", "Video formats supported."); PARAMETER(General, videoFormats, QString, "*.avi *.m4v *.mp4", "Video formats supported.");
PARAMETER(General, mirrorView, bool, true, "Flip the camera image horizontally (like all webcam applications)."); PARAMETER(General, mirrorView, bool, false, "Flip the camera image horizontally (like all webcam applications).");
PARAMETER(General, invertedSearch, bool, true, "Instead of matching 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, true, "Instead of matching 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.");

View File

@ -143,6 +143,29 @@ bool FindObject::saveSession(const QString & path)
return false; return false;
} }
bool FindObject::saveVocabulary(const QString & filePath) const
{
return vocabulary_->save(filePath);
}
bool FindObject::loadVocabulary(const QString & filePath)
{
if(!Settings::getGeneral_vocabularyFixed() || !Settings::getGeneral_invertedSearch())
{
UWARN("Doesn't make sense to load a vocabulary if \"General/vocabularyFixed\" and \"General/invertedSearch\" are not enabled! It will "
"be cleared at the time the objects are updated.");
}
if(vocabulary_->load(filePath))
{
if(objects_.size())
{
updateVocabulary();
}
return true;
}
return false;
}
int FindObject::loadObjects(const QString & dirPath, bool recursive) int FindObject::loadObjects(const QString & dirPath, bool recursive)
{ {
QString formats = Settings::getGeneral_imageFormats().remove('*').remove('.'); QString formats = Settings::getGeneral_imageFormats().remove('*').remove('.');
@ -192,7 +215,6 @@ int FindObject::loadObjects(const QString & dirPath, bool recursive)
const ObjSignature * FindObject::addObject(const QString & filePath) const ObjSignature * FindObject::addObject(const QString & filePath)
{ {
UINFO("Load file %s", filePath.toStdString().c_str());
if(!filePath.isNull()) if(!filePath.isNull())
{ {
cv::Mat img = cv::imread(filePath.toStdString().c_str(), cv::IMREAD_GRAYSCALE); cv::Mat img = cv::imread(filePath.toStdString().c_str(), cv::IMREAD_GRAYSCALE);
@ -218,8 +240,26 @@ const ObjSignature * FindObject::addObject(const QString & filePath)
id = 0; id = 0;
} }
} }
return this->addObject(img, id, filePath); else
{
UERROR("File name doesn't contain \".\" (\"%s\")", filePath.toStdString().c_str());
}
const ObjSignature * s = this->addObject(img, id, filePath);
if(s)
{
UINFO("Added object %d (%s)", s->id(), filePath.toStdString().c_str());
return s;
}
} }
else
{
UERROR("Could not read image \"%s\"", filePath.toStdString().c_str());
}
}
else
{
UERROR("File path is null!?");
} }
return 0; return 0;
} }

View File

@ -44,6 +44,7 @@ public:
void setColor(const QColor & color); void setColor(const QColor & color);
void setWordID(int id) {wordID_ = id;} void setWordID(int id) {wordID_ = id;}
int wordID() const {return wordID_;}
int id() const {return id_;} int id() const {return id_;}
protected: protected:

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(const QList<int> &))); connect(ui_->pushButton_updateObjects, SIGNAL(clicked()), this, SLOT(updateObjects()));
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);
@ -211,6 +211,8 @@ MainWindow::MainWindow(FindObject * findObject, Camera * camera, QWidget * paren
connect(ui_->actionLoad_settings, SIGNAL(triggered()), this, SLOT(loadSettings())); connect(ui_->actionLoad_settings, SIGNAL(triggered()), this, SLOT(loadSettings()));
connect(ui_->actionSave_session, SIGNAL(triggered()), this, SLOT(saveSession())); connect(ui_->actionSave_session, SIGNAL(triggered()), this, SLOT(saveSession()));
connect(ui_->actionLoad_session, SIGNAL(triggered()), this, SLOT(loadSession())); connect(ui_->actionLoad_session, SIGNAL(triggered()), this, SLOT(loadSession()));
connect(ui_->actionSave_vocabulary, SIGNAL(triggered()), this, SLOT(saveVocabulary()));
connect(ui_->actionLoad_vocabulary, SIGNAL(triggered()), this, SLOT(loadVocabulary()));
connect(ui_->actionShow_objects_features, SIGNAL(triggered()), this, SLOT(showObjectsFeatures())); connect(ui_->actionShow_objects_features, SIGNAL(triggered()), this, SLOT(showObjectsFeatures()));
connect(ui_->actionHide_objects_features, SIGNAL(triggered()), this, SLOT(hideObjectsFeatures())); connect(ui_->actionHide_objects_features, SIGNAL(triggered()), this, SLOT(hideObjectsFeatures()));
@ -251,6 +253,13 @@ MainWindow::MainWindow(FindObject * findObject, Camera * camera, QWidget * paren
objWidgets_.insert(obj->id(), obj); objWidgets_.insert(obj->id(), obj);
this->showObject(obj); this->showObject(obj);
} }
ui_->actionSave_objects->setEnabled(true);
ui_->actionSave_session->setEnabled(true);
}
if(findObject_->vocabulary()->size())
{
ui_->label_vocabularySize->setNum(findObject_->vocabulary()->size());
ui_->actionSave_session->setEnabled(true);
} }
@ -585,6 +594,66 @@ bool MainWindow::saveObjects()
return false; return false;
} }
void MainWindow::loadVocabulary()
{
if(!Settings::getGeneral_vocabularyFixed() ||
!Settings::getGeneral_invertedSearch())
{
QMessageBox::StandardButton b = QMessageBox::question(this, tr("Load vocabulary..."),
tr("Parameters \"General/vocabularyFixed\" and \"General/invertedSearch\" should be enabled to load a vocabulary. "
"Do you want to enable them now?"),
QMessageBox::Cancel | QMessageBox::Yes);
if(b == QMessageBox::Yes)
{
Settings::setGeneral_vocabularyFixed(true);
Settings::setGeneral_invertedSearch(true);
}
}
if(Settings::getGeneral_vocabularyFixed() &&
Settings::getGeneral_invertedSearch())
{
QString path = QFileDialog::getOpenFileName(this, tr("Load vocabulary..."), Settings::workingDirectory(), "Data (*.yaml *.xml)");
if(!path.isEmpty())
{
if(findObject_->loadVocabulary(path))
{
ui_->label_vocabularySize->setNum(findObject_->vocabulary()->size());
ui_->actionSave_session->setEnabled(findObject_->vocabulary()->size() || findObject_->objects().size());
QMessageBox::information(this, tr("Loading..."), tr("Vocabulary loaded from \"%1\" (%2 words).").arg(path).arg(findObject_->vocabulary()->size()));
}
else
{
QMessageBox::warning(this, tr("Loading..."), tr("Failed to load vocabulary \"%1\"!").arg(path));
}
}
}
}
void MainWindow::saveVocabulary()
{
if(findObject_->vocabulary()->size() == 0)
{
QMessageBox::warning(this, tr("Saving vocabulary..."), tr("Vocabulary is empty!"));
return;
}
QString path = QFileDialog::getSaveFileName(this, tr("Save vocabulary..."), Settings::workingDirectory(), "Data (*.yaml *.xml)");
if(!path.isEmpty())
{
if(QFileInfo(path).suffix().compare("yaml") != 0 && QFileInfo(path).suffix().compare("xml") != 0)
{
path.append(".yaml");
}
if(findObject_->saveVocabulary(path))
{
QMessageBox::information(this, tr("Saving..."), tr("Vocabulary saved to \"%1\" (%2 words).").arg(path).arg(findObject_->vocabulary()->size()));
}
else
{
QMessageBox::warning(this, tr("Saving..."), tr("Failed to save vocabulary \"%1\"!").arg(path));
}
}
}
void MainWindow::removeObject(find_object::ObjWidget * object) void MainWindow::removeObject(find_object::ObjWidget * object)
{ {
if(object) if(object)
@ -977,6 +1046,11 @@ void MainWindow::showObject(ObjWidget * obj)
} }
} }
void MainWindow::updateObjects()
{
updateObjects(QList<int>());
}
void MainWindow::updateObjects(const QList<int> & ids) void MainWindow::updateObjects(const QList<int> & ids)
{ {
if(objWidgets_.size()) if(objWidgets_.size())
@ -1045,6 +1119,7 @@ void MainWindow::updateVocabulary(const QList<int> & ids)
} }
lastObjectsUpdateParameters_ = Settings::getParameters(); lastObjectsUpdateParameters_ = Settings::getParameters();
this->statusBar()->clearMessage(); this->statusBar()->clearMessage();
ui_->dockWidget_objects->update();
} }
void MainWindow::startProcessing() void MainWindow::startProcessing()

View File

@ -71,7 +71,7 @@ ObjWidget::ObjWidget(int id, const std::vector<cv::KeyPoint> & keypoints, const
graphicsView_(0), graphicsView_(0),
graphicsViewInitialized_(false), graphicsViewInitialized_(false),
alpha_(100), alpha_(100),
color_(QColor((Qt::GlobalColor)((id % 11 + 7)==Qt::yellow?Qt::gray:(id % 11 + 7)))) color_(QColor((Qt::GlobalColor)((id % 10 + 7)==Qt::yellow?Qt::darkYellow:(id % 10 + 7))))
{ {
setupUi(); setupUi();
this->updateImage(image); this->updateImage(image);
@ -136,7 +136,7 @@ void ObjWidget::setupUi()
void ObjWidget::setId(int id) void ObjWidget::setId(int id)
{ {
color_ = QColor((Qt::GlobalColor)((id % 11 + 7)==Qt::yellow?Qt::gray:(id % 11 + 7))); color_ = QColor((Qt::GlobalColor)((id % 10 + 7)==Qt::yellow?Qt::darkYellow:(id % 10 + 7)));
id_=id; id_=id;
if(id_) if(id_)
{ {
@ -274,7 +274,7 @@ void ObjWidget::updateImage(const QImage & image)
void ObjWidget::updateData(const std::vector<cv::KeyPoint> & keypoints, const QMultiMap<int, int> & words) 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(0));
keypointItems_.clear(); keypointItems_.clear();
rectItems_.clear(); rectItems_.clear();
this->updateWords(words); this->updateWords(words);
@ -297,9 +297,14 @@ void ObjWidget::updateWords(const QMultiMap<int,int> & words)
{ {
words_.insert(iter.value(), iter.key()); words_.insert(iter.value(), iter.key());
} }
for(int i=0; i<keypointItems_.size(); ++i) for(int i=0; i<kptColors_.size(); ++i)
{ {
keypointItems_[i]->setWordID(words_.value(i,-1)); kptColors_[i] = defaultColor(words_.size()?words_.value(i,-1):0);
if(keypointItems_.size() == kptColors_.size())
{
keypointItems_[i]->setWordID(words_.value(i,-1));
keypointItems_[i]->setColor(defaultColor(words_.size()?keypointItems_[i]->wordID():0));
}
} }
} }
@ -307,10 +312,14 @@ void ObjWidget::resetKptsColor()
{ {
for(int i=0; i<kptColors_.size(); ++i) for(int i=0; i<kptColors_.size(); ++i)
{ {
kptColors_[i] = defaultColor(); if(keypointItems_.size() == kptColors_.size())
if(graphicsViewMode_->isChecked())
{ {
keypointItems_[i]->setColor(this->defaultColor()); kptColors_[i] = defaultColor(keypointItems_[i]->wordID());
keypointItems_[i]->setColor(this->defaultColor(keypointItems_[i]->wordID()));
}
else
{
kptColors_[i] = defaultColor(words_.value(i,-1));
} }
} }
qDeleteAll(rectItems_.begin(), rectItems_.end()); qDeleteAll(rectItems_.begin(), rectItems_.end());
@ -738,6 +747,8 @@ void ObjWidget::drawKeypoints(QPainter * painter)
item->setVisible(this->isFeaturesShown()); item->setVisible(this->isFeaturesShown());
item->setZValue(2); item->setZValue(2);
graphicsView_->scene()->addItem(item); graphicsView_->scene()->addItem(item);
item->setColor(defaultColor(item->wordID()));
kptColors_[i] = defaultColor(item->wordID());
keypointItems_.append(item); keypointItems_.append(item);
} }
@ -752,9 +763,9 @@ void ObjWidget::drawKeypoints(QPainter * painter)
} }
} }
QColor ObjWidget::defaultColor() const QColor ObjWidget::defaultColor(int id) const
{ {
QColor color(Qt::yellow); QColor color(id >= 0 ? Qt::yellow : Qt::white);
color.setAlpha(alpha_); color.setAlpha(alpha_);
return color; return color;
} }

View File

@ -60,6 +60,8 @@ void Vocabulary::clear()
if(Settings::getGeneral_vocabularyFixed() && Settings::getGeneral_invertedSearch()) if(Settings::getGeneral_vocabularyFixed() && Settings::getGeneral_invertedSearch())
{ {
this->update(); // if vocabulary structure has changed
// If the dictionary is fixed, don't clear indexed descriptors // If the dictionary is fixed, don't clear indexed descriptors
return; return;
} }
@ -67,41 +69,81 @@ void Vocabulary::clear()
indexedDescriptors_ = cv::Mat(); indexedDescriptors_ = cv::Mat();
} }
void Vocabulary::save(QDataStream & streamPtr) const void Vocabulary::save(QDataStream & streamSessionPtr) const
{ {
if(!indexedDescriptors_.empty() && !wordToObjects_.empty()) // save index
{ streamSessionPtr << wordToObjects_;
UASSERT(notIndexedDescriptors_.empty() && notIndexedWordIds_.empty());
// save index // save words
streamPtr << wordToObjects_; qint64 dataSize = indexedDescriptors_.elemSize()*indexedDescriptors_.cols*indexedDescriptors_.rows;
streamSessionPtr << indexedDescriptors_.rows <<
// save words indexedDescriptors_.cols <<
qint64 dataSize = indexedDescriptors_.elemSize()*indexedDescriptors_.cols*indexedDescriptors_.rows; indexedDescriptors_.type() <<
streamPtr << indexedDescriptors_.rows << dataSize;
indexedDescriptors_.cols << streamSessionPtr << QByteArray((char*)indexedDescriptors_.data, dataSize);
indexedDescriptors_.type() <<
dataSize;
streamPtr << QByteArray((char*)indexedDescriptors_.data, dataSize);
}
} }
void Vocabulary::load(QDataStream & streamPtr) void Vocabulary::load(QDataStream & streamSessionPtr)
{ {
// load index // load index
streamPtr >> wordToObjects_; streamSessionPtr >> wordToObjects_;
// load words // load words
int rows,cols,type; int rows,cols,type;
qint64 dataSize; qint64 dataSize;
streamPtr >> rows >> cols >> type >> dataSize; streamSessionPtr >> rows >> cols >> type >> dataSize;
QByteArray data; QByteArray data;
streamPtr >> data; streamSessionPtr >> data;
indexedDescriptors_ = cv::Mat(rows, cols, type, data.data()).clone(); indexedDescriptors_ = cv::Mat(rows, cols, type, data.data()).clone();
update(); update();
} }
bool Vocabulary::save(const QString & filename) const
{
// save descriptors
cv::FileStorage fs(filename.toStdString(), cv::FileStorage::WRITE);
if(fs.isOpened())
{
fs << "Descriptors" << indexedDescriptors_;
return true;
}
else
{
UERROR("Failed to open vocabulary file \"%s\"", filename.toStdString().c_str());
}
return false;
}
bool Vocabulary::load(const QString & filename)
{
// save descriptors
cv::FileStorage fs(filename.toStdString(), cv::FileStorage::READ);
if(fs.isOpened())
{
cv::Mat tmp;
fs["Descriptors"] >> tmp;
if(!tmp.empty())
{
// clear index
wordToObjects_.clear();
indexedDescriptors_ = tmp;
update();
return true;
}
else
{
UERROR("Failed to read \"Descriptors\" matrix field (doesn't exist or is empty) from vocabulary file \"%s\"", filename.toStdString().c_str());
}
}
else
{
UERROR("Failed to open vocabulary file \"%s\"", filename.toStdString().c_str());
}
return false;
}
QMultiMap<int, int> Vocabulary::addWords(const cv::Mat & descriptors, int objectId) QMultiMap<int, int> Vocabulary::addWords(const cv::Mat & descriptors, int objectId)
{ {
QMultiMap<int, int> words; QMultiMap<int, int> words;
@ -119,7 +161,21 @@ QMultiMap<int, int> Vocabulary::addWords(const cv::Mat & descriptors, int object
bool globalSearch = false; bool globalSearch = false;
if(!indexedDescriptors_.empty() && indexedDescriptors_.rows >= (int)k) if(!indexedDescriptors_.empty() && indexedDescriptors_.rows >= (int)k)
{ {
UASSERT(indexedDescriptors_.type() == descriptors.type() && indexedDescriptors_.cols == descriptors.cols); if(indexedDescriptors_.type() != descriptors.type() || indexedDescriptors_.cols != descriptors.cols)
{
if(Settings::getGeneral_vocabularyFixed())
{
UERROR("Descriptors (type=%d size=%d) to search in vocabulary are not the same type/size as those in the vocabulary (type=%d size=%d)! Empty words returned.",
descriptors.type(), descriptors.cols, indexedDescriptors_.type(), indexedDescriptors_.cols);
return words;
}
else
{
UFATAL("Descriptors (type=%d size=%d) to search in vocabulary are not the same type/size as those in the vocabulary (type=%d size=%d)!",
descriptors.type(), descriptors.cols, indexedDescriptors_.type(), indexedDescriptors_.cols);
}
}
this->search(descriptors, results, dists, k); this->search(descriptors, results, dists, k);
if( dists.type() == CV_32S ) if( dists.type() == CV_32S )

View File

@ -49,8 +49,10 @@ public:
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_;}
void save(QDataStream & streamPtr) const; void save(QDataStream & streamSessionPtr) const;
void load(QDataStream & streamPtr); void load(QDataStream & streamSessionPtr);
bool save(const QString & filename) const;
bool load(const QString & filename);
private: private:
cv::flann::Index flannIndex_; cv::flann::Index flannIndex_;

View File

@ -225,6 +225,9 @@
<addaction name="actionLoad_objects"/> <addaction name="actionLoad_objects"/>
<addaction name="actionSave_objects"/> <addaction name="actionSave_objects"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionLoad_vocabulary"/>
<addaction name="actionSave_vocabulary"/>
<addaction name="separator"/>
<addaction name="actionLoad_settings"/> <addaction name="actionLoad_settings"/>
<addaction name="actionSave_settings"/> <addaction name="actionSave_settings"/>
<addaction name="separator"/> <addaction name="separator"/>
@ -894,6 +897,16 @@
<string>Show objects features</string> <string>Show objects features</string>
</property> </property>
</action> </action>
<action name="actionLoad_vocabulary">
<property name="text">
<string>Load vocabulary...</string>
</property>
</action>
<action name="actionSave_vocabulary">
<property name="text">
<string>Save vocabulary...</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>