Added Load/Save vocabulary actions in File menu (in console mode, see "--vocabulary" option) (issue #2)
This commit is contained in:
+42
-2
@@ -143,6 +143,29 @@ bool FindObject::saveSession(const QString & path)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
UINFO("Load file %s", filePath.toStdString().c_str());
|
||||
if(!filePath.isNull())
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
|
||||
void setColor(const QColor & color);
|
||||
void setWordID(int id) {wordID_ = id;}
|
||||
int wordID() const {return wordID_;}
|
||||
int id() const {return id_;}
|
||||
|
||||
protected:
|
||||
|
||||
+76
-1
@@ -183,7 +183,7 @@ MainWindow::MainWindow(FindObject * findObject, Camera * camera, QWidget * paren
|
||||
|
||||
//buttons
|
||||
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()));
|
||||
|
||||
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_->actionSave_session, SIGNAL(triggered()), this, SLOT(saveSession()));
|
||||
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_->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);
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if(object)
|
||||
@@ -977,6 +1046,11 @@ void MainWindow::showObject(ObjWidget * obj)
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updateObjects()
|
||||
{
|
||||
updateObjects(QList<int>());
|
||||
}
|
||||
|
||||
void MainWindow::updateObjects(const QList<int> & ids)
|
||||
{
|
||||
if(objWidgets_.size())
|
||||
@@ -1045,6 +1119,7 @@ void MainWindow::updateVocabulary(const QList<int> & ids)
|
||||
}
|
||||
lastObjectsUpdateParameters_ = Settings::getParameters();
|
||||
this->statusBar()->clearMessage();
|
||||
ui_->dockWidget_objects->update();
|
||||
}
|
||||
|
||||
void MainWindow::startProcessing()
|
||||
|
||||
+21
-10
@@ -71,7 +71,7 @@ ObjWidget::ObjWidget(int id, const std::vector<cv::KeyPoint> & keypoints, const
|
||||
graphicsView_(0),
|
||||
graphicsViewInitialized_(false),
|
||||
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();
|
||||
this->updateImage(image);
|
||||
@@ -136,7 +136,7 @@ void ObjWidget::setupUi()
|
||||
|
||||
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;
|
||||
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)
|
||||
{
|
||||
keypoints_ = keypoints;
|
||||
kptColors_ = QVector<QColor>((int)keypoints.size(), defaultColor());
|
||||
kptColors_ = QVector<QColor>((int)keypoints.size(), defaultColor(0));
|
||||
keypointItems_.clear();
|
||||
rectItems_.clear();
|
||||
this->updateWords(words);
|
||||
@@ -297,9 +297,14 @@ void ObjWidget::updateWords(const QMultiMap<int,int> & words)
|
||||
{
|
||||
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)
|
||||
{
|
||||
kptColors_[i] = defaultColor();
|
||||
if(graphicsViewMode_->isChecked())
|
||||
if(keypointItems_.size() == kptColors_.size())
|
||||
{
|
||||
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());
|
||||
@@ -738,6 +747,8 @@ void ObjWidget::drawKeypoints(QPainter * painter)
|
||||
item->setVisible(this->isFeaturesShown());
|
||||
item->setZValue(2);
|
||||
graphicsView_->scene()->addItem(item);
|
||||
item->setColor(defaultColor(item->wordID()));
|
||||
kptColors_[i] = defaultColor(item->wordID());
|
||||
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_);
|
||||
return color;
|
||||
}
|
||||
|
||||
+76
-20
@@ -60,6 +60,8 @@ void Vocabulary::clear()
|
||||
|
||||
if(Settings::getGeneral_vocabularyFixed() && Settings::getGeneral_invertedSearch())
|
||||
{
|
||||
this->update(); // if vocabulary structure has changed
|
||||
|
||||
// If the dictionary is fixed, don't clear indexed descriptors
|
||||
return;
|
||||
}
|
||||
@@ -67,41 +69,81 @@ void Vocabulary::clear()
|
||||
indexedDescriptors_ = cv::Mat();
|
||||
}
|
||||
|
||||
void Vocabulary::save(QDataStream & streamPtr) const
|
||||
void Vocabulary::save(QDataStream & streamSessionPtr) const
|
||||
{
|
||||
if(!indexedDescriptors_.empty() && !wordToObjects_.empty())
|
||||
{
|
||||
UASSERT(notIndexedDescriptors_.empty() && notIndexedWordIds_.empty());
|
||||
// save index
|
||||
streamSessionPtr << wordToObjects_;
|
||||
|
||||
// save index
|
||||
streamPtr << wordToObjects_;
|
||||
|
||||
// save words
|
||||
qint64 dataSize = indexedDescriptors_.elemSize()*indexedDescriptors_.cols*indexedDescriptors_.rows;
|
||||
streamPtr << indexedDescriptors_.rows <<
|
||||
indexedDescriptors_.cols <<
|
||||
indexedDescriptors_.type() <<
|
||||
dataSize;
|
||||
streamPtr << QByteArray((char*)indexedDescriptors_.data, dataSize);
|
||||
}
|
||||
// save words
|
||||
qint64 dataSize = indexedDescriptors_.elemSize()*indexedDescriptors_.cols*indexedDescriptors_.rows;
|
||||
streamSessionPtr << indexedDescriptors_.rows <<
|
||||
indexedDescriptors_.cols <<
|
||||
indexedDescriptors_.type() <<
|
||||
dataSize;
|
||||
streamSessionPtr << QByteArray((char*)indexedDescriptors_.data, dataSize);
|
||||
}
|
||||
|
||||
void Vocabulary::load(QDataStream & streamPtr)
|
||||
void Vocabulary::load(QDataStream & streamSessionPtr)
|
||||
{
|
||||
// load index
|
||||
streamPtr >> wordToObjects_;
|
||||
streamSessionPtr >> wordToObjects_;
|
||||
|
||||
// load words
|
||||
int rows,cols,type;
|
||||
qint64 dataSize;
|
||||
streamPtr >> rows >> cols >> type >> dataSize;
|
||||
streamSessionPtr >> rows >> cols >> type >> dataSize;
|
||||
QByteArray data;
|
||||
streamPtr >> data;
|
||||
streamSessionPtr >> data;
|
||||
indexedDescriptors_ = cv::Mat(rows, cols, type, data.data()).clone();
|
||||
|
||||
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> words;
|
||||
@@ -119,7 +161,21 @@ QMultiMap<int, int> Vocabulary::addWords(const cv::Mat & descriptors, int object
|
||||
bool globalSearch = false;
|
||||
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);
|
||||
|
||||
if( dists.type() == CV_32S )
|
||||
|
||||
+4
-2
@@ -49,8 +49,10 @@ public:
|
||||
const QMultiMap<int, int> & wordToObjects() const {return wordToObjects_;}
|
||||
const cv::Mat & indexedDescriptors() const {return indexedDescriptors_;}
|
||||
|
||||
void save(QDataStream & streamPtr) const;
|
||||
void load(QDataStream & streamPtr);
|
||||
void save(QDataStream & streamSessionPtr) const;
|
||||
void load(QDataStream & streamSessionPtr);
|
||||
bool save(const QString & filename) const;
|
||||
bool load(const QString & filename);
|
||||
|
||||
private:
|
||||
cv::flann::Index flannIndex_;
|
||||
|
||||
@@ -225,6 +225,9 @@
|
||||
<addaction name="actionLoad_objects"/>
|
||||
<addaction name="actionSave_objects"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionLoad_vocabulary"/>
|
||||
<addaction name="actionSave_vocabulary"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionLoad_settings"/>
|
||||
<addaction name="actionSave_settings"/>
|
||||
<addaction name="separator"/>
|
||||
@@ -894,6 +897,16 @@
|
||||
<string>Show objects features</string>
|
||||
</property>
|
||||
</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>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
||||
Reference in New Issue
Block a user