From 02e31c2e6938c6f5b54ec6a7a463d4482d5649be Mon Sep 17 00:00:00 2001 From: matlabbe Date: Fri, 9 Jan 2015 22:44:16 +0000 Subject: [PATCH] Added "save/load session" menu actions. git-svn-id: http://find-object.googlecode.com/svn/trunk/find_object@418 620bd6b2-0a58-f614-fd9a-1bd335dccda9 --- include/find_object/FindObject.h | 3 + include/find_object/MainWindow.h | 4 +- src/AddObjectDialog.cpp | 2 +- src/FindObject.cpp | 105 +++++++++++++++++++++++++++---- src/MainWindow.cpp | 92 +++++++++++++++++++++++++-- src/ObjSignature.h | 79 ++++++++++++++++++++--- src/Vocabulary.cpp | 35 +++++++++++ src/Vocabulary.h | 4 ++ src/ui/mainWindow.ui | 13 ++++ 9 files changed, 310 insertions(+), 27 deletions(-) diff --git a/include/find_object/FindObject.h b/include/find_object/FindObject.h index e4dbfac9..14f7e4c8 100644 --- a/include/find_object/FindObject.h +++ b/include/find_object/FindObject.h @@ -65,6 +65,9 @@ public: FindObject(QObject * parent = 0); virtual ~FindObject(); + bool loadSession(const QString & path); + bool saveSession(const QString & path) const; + int loadObjects(const QString & dirPath); // call updateObjects() const ObjSignature * addObject(const QString & filePath); const ObjSignature * addObject(const cv::Mat & image, int id=0, const QString & filename = QString()); diff --git a/include/find_object/MainWindow.h b/include/find_object/MainWindow.h index ce94d391..e20a8b9f 100644 --- a/include/find_object/MainWindow.h +++ b/include/find_object/MainWindow.h @@ -81,6 +81,8 @@ public Q_SLOTS: void update(const cv::Mat & image); private Q_SLOTS: + void loadSession(); + void saveSession(); void loadSettings(); void saveSettings(); void loadObjects(); @@ -110,7 +112,7 @@ Q_SIGNALS: private: bool loadSettings(const QString & path); - bool saveSettings(const QString & path); + bool saveSettings(const QString & path) const; int loadObjects(const QString & dirPath); int saveObjects(const QString & dirPath); void setupTCPServer(); diff --git a/src/AddObjectDialog.cpp b/src/AddObjectDialog.cpp index 1e134829..be5647d7 100644 --- a/src/AddObjectDialog.cpp +++ b/src/AddObjectDialog.cpp @@ -358,7 +358,7 @@ void AddObjectDialog::setState(int state) objSignature_ = 0; } objSignature_ = new ObjSignature(0, imgRoi.clone(), ""); - objSignature_->setData(keypoints, descriptors, Settings::currentDetectorType(), Settings::currentDescriptorType()); + objSignature_->setData(keypoints, descriptors); objWidget_ = new ObjWidget(0, keypoints, cvtCvMat2QImage(imgRoi.clone())); this->accept(); diff --git a/src/FindObject.cpp b/src/FindObject.cpp index f72ce7f9..e17d9ca0 100644 --- a/src/FindObject.cpp +++ b/src/FindObject.cpp @@ -59,6 +59,85 @@ FindObject::~FindObject() { objectsDescriptors_.clear(); } +bool FindObject::loadSession(const QString & path) +{ + if(QFile::exists(path) && !path.isEmpty() && QFileInfo(path).suffix().compare("bin") == 0) + { + QFile file(path); + file.open(QIODevice::ReadOnly); + QDataStream in(&file); + + ParametersMap parameters; + + // load parameters + in >> parameters; + for(QMap::iterator iter=parameters.begin(); iter!=parameters.end(); ++iter) + { + Settings::setParameter(iter.key(), iter.value()); + } + + // save vocabulary + vocabulary_->load(in); + + // load objects + while(!in.atEnd()) + { + ObjSignature * obj = new ObjSignature(); + obj->load(in); + if(obj->id() >= 0) + { + objects_.insert(obj->id(), obj); + } + else + { + UERROR("Failed to load and object!"); + delete obj; + } + } + file.close(); + + if(!Settings::getGeneral_invertedSearch()) + { + // this will fill objectsDescriptors_ matrix + updateVocabulary(); + } + + return true; + } + else + { + UERROR("Invalid session file (should be *.bin): \"%s\"", path.toStdString().c_str()); + } + return false; +} + +bool FindObject::saveSession(const QString & path) const +{ + if(!path.isEmpty() && QFileInfo(path).suffix().compare("bin") == 0) + { + QFile file(path); + file.open(QIODevice::WriteOnly); + QDataStream out(&file); + + // save parameters + out << Settings::getParameters(); + + // save vocabulary + vocabulary_->save(out); + + // save objects + for(QMultiMap::const_iterator iter=objects_.constBegin(); iter!=objects_.constEnd(); ++iter) + { + iter.value()->save(out); + } + + file.close(); + return true; + } + UERROR("Path \"%s\" not valid (should be *.bin)", path.toStdString().c_str()); + return false; +} + int FindObject::loadObjects(const QString & dirPath) { int loadedObjects = 0; @@ -143,7 +222,6 @@ bool FindObject::addObject(ObjSignature * obj) Settings::setGeneral_nextObjID(obj->id()+1); objects_.insert(obj->id(), obj); - clearVocabulary(); return true; } @@ -512,11 +590,7 @@ void FindObject::updateObjects() int id = threads[j]->objectId(); - objects_.value(id)->setData( - threads[j]->keypoints(), - threads[j]->descriptors(), - Settings::currentDetectorType(), - Settings::currentDescriptorType()); + objects_.value(id)->setData(threads[j]->keypoints(), threads[j]->descriptors()); } } UINFO("Features extraction from %d objects... done! (%d ms)", objects_.size(), time.elapsed()); @@ -915,13 +989,22 @@ bool FindObject::detect(const cv::Mat & image, find_object::DetectionInfo & info bool consistentNNData = (vocabulary_->size()!=0 && vocabulary_->wordToObjects().begin().value()!=-1 && Settings::getGeneral_invertedSearch()) || ((vocabulary_->size()==0 || vocabulary_->wordToObjects().begin().value()==-1) && !Settings::getGeneral_invertedSearch()); + bool descriptorsValid = !Settings::getGeneral_invertedSearch() && + !objectsDescriptors_.empty() && + objectsDescriptors_.begin().value().cols == info.sceneDescriptors_.cols && + objectsDescriptors_.begin().value().type() == info.sceneDescriptors_.type(); + + bool vocabularyValid = Settings::getGeneral_invertedSearch() && + vocabulary_->size() && + !vocabulary_->indexedDescriptors().empty() && + vocabulary_->indexedDescriptors().cols == info.sceneDescriptors_.cols && + vocabulary_->indexedDescriptors().type() == info.sceneDescriptors_.type(); + // COMPARE UDEBUG("COMPARE"); - if(!objectsDescriptors_.empty() && + if((descriptorsValid || vocabularyValid) && info.sceneKeypoints_.size() && - consistentNNData && - objectsDescriptors_.begin().value().cols == info.sceneDescriptors_.cols && - objectsDescriptors_.begin().value().type() == info.sceneDescriptors_.type()) // binary descriptor issue, if the dataTree is not yet updated with modified settings + consistentNNData) { success = true; QTime time; @@ -1243,7 +1326,7 @@ bool FindObject::detect(const cv::Mat & image, find_object::DetectionInfo & info info.timeStamps_.insert(DetectionInfo::kTimeHomography, time.restart()); } } - else if(!objectsDescriptors_.empty() && info.sceneKeypoints_.size()) + else if((descriptorsValid || vocabularyValid) && info.sceneKeypoints_.size()) { UWARN("Cannot search, objects must be updated"); } diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 04ace05b..9ccdd3cc 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -181,6 +181,7 @@ MainWindow::MainWindow(FindObject * findObject, Camera * camera, QWidget * paren ui_->actionStop_camera->setEnabled(false); ui_->actionPause_camera->setEnabled(false); ui_->actionSave_objects->setEnabled(false); + ui_->actionSave_session->setEnabled(false); // Actions connect(ui_->actionAdd_object_from_scene, SIGNAL(triggered()), this, SLOT(addObjectFromScene())); @@ -200,6 +201,8 @@ MainWindow::MainWindow(FindObject * findObject, Camera * camera, QWidget * paren connect(ui_->actionRemove_all_objects, SIGNAL(triggered()), this, SLOT(removeAllObjects())); connect(ui_->actionSave_settings, SIGNAL(triggered()), this, SLOT(saveSettings())); 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_->actionShow_objects_features, SIGNAL(triggered()), this, SLOT(showObjectsFeatures())); connect(ui_->actionHide_objects_features, SIGNAL(triggered()), this, SLOT(hideObjectsFeatures())); @@ -315,6 +318,84 @@ void MainWindow::setSourceImageText(const QString & text) ui_->imageView_source->setTextLabel(text); } +void MainWindow::loadSession() +{ + if(objWidgets_.size()) + { + QMessageBox::StandardButton b = QMessageBox::question(this, tr("Loading session..."), + tr("There are some objects (%1) already loaded, they will be " + "deleted when loading the session. Do you want to continue?").arg(objWidgets_.size()), + QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton); + if(b != QMessageBox::Yes) + { + return; + } + } + + QString path = QFileDialog::getOpenFileName(this, tr("Load session..."), Settings::workingDirectory(), "*.bin"); + if(!path.isEmpty()) + { + qDeleteAll(objWidgets_); + objWidgets_.clear(); + ui_->actionSave_objects->setEnabled(false); + findObject_->removeAllObjects(); + + if(findObject_->loadSession(path)) + { + //update parameters tool box + const ParametersMap & parameters = Settings::getParameters(); + for(ParametersMap::const_iterator iter = parameters.begin(); iter!= parameters.constEnd(); ++iter) + { + ui_->toolBox->updateParameter(iter.key()); + } + + //update object widgets + for(QMap::const_iterator iter=findObject_->objects().constBegin(); iter!=findObject_->objects().constEnd();++iter) + { + if(iter.value()) + { + ObjWidget * obj = new ObjWidget(iter.key(), iter.value()->keypoints(), cvtCvMat2QImage(iter.value()->image())); + objWidgets_.insert(obj->id(), obj); + ui_->actionSave_objects->setEnabled(true); + ui_->actionSave_session->setEnabled(true); + this->showObject(obj); + + //update object labels + QLabel * title = qFindChild(this, QString("%1title").arg(iter.value()->id())); + title->setText(QString("%1 (%2)").arg(iter.value()->id()).arg(QString::number(iter.value()->keypoints().size()))); + } + + } + + QMessageBox::information(this, tr("Session loaded!"), tr("Session \"%1\" successfully loaded (%2 objects)!").arg(path).arg(objWidgets_.size())); + + if(!camera_->isRunning() && !sceneImage_.empty()) + { + this->update(sceneImage_); + } + } + } +} +void MainWindow::saveSession() +{ + if(objWidgets_.size()) + { + QString path = QFileDialog::getSaveFileName(this, tr("Save session..."), Settings::workingDirectory(), "*.bin"); + if(!path.isEmpty()) + { + if(QFileInfo(path).suffix().compare("bin") != 0) + { + path.append(".bin"); + } + + if(findObject_->saveSession(path)) + { + QMessageBox::information(this, tr("Session saved!"), tr("Session \"%1\" successfully saved (%2 objects)!").arg(path).arg(objWidgets_.size())); + } + } + } +} + void MainWindow::loadSettings() { QString path = QFileDialog::getOpenFileName(this, tr("Load settings..."), Settings::workingDirectory(), "*.ini"); @@ -360,11 +441,11 @@ bool MainWindow::loadSettings(const QString & path) return true; } - UINFO("Path \"%s\" not valid (should be *.ini)", path.toStdString().c_str()); + UERROR("Path \"%s\" not valid (should be *.ini)", path.toStdString().c_str()); return false; } -bool MainWindow::saveSettings(const QString & path) +bool MainWindow::saveSettings(const QString & path) const { if(!path.isEmpty() && QFileInfo(path).suffix().compare("ini") == 0) { @@ -372,7 +453,7 @@ bool MainWindow::saveSettings(const QString & path) Settings::saveWindowSettings(this->saveGeometry(), this->saveState(), path); return true; } - UINFO("Path \"%s\" not valid (should be *.ini)", path.toStdString().c_str()); + UERROR("Path \"%s\" not valid (should be *.ini)", path.toStdString().c_str()); return false; } @@ -405,7 +486,7 @@ int MainWindow::saveObjects(const QString & dirPath) QDir dir(dirPath); if(dir.exists()) { - for(QMap::iterator iter=objWidgets_.begin(); iter!=objWidgets_.end(); ++iter) + for(QMap::const_iterator iter=objWidgets_.constBegin(); iter!=objWidgets_.constEnd(); ++iter) { if(iter.value()->pixmap().save(QString("%1/%2.png").arg(dirPath).arg(iter.key()))) { @@ -464,6 +545,7 @@ void MainWindow::removeObject(find_object::ObjWidget * object) if(objWidgets_.size() == 0) { ui_->actionSave_objects->setEnabled(false); + ui_->actionSave_session->setEnabled(false); } findObject_->removeObject(object->id()); object->deleteLater(); @@ -570,6 +652,7 @@ void MainWindow::addObjectFromScene() obj->setId(signature->id()); objWidgets_.insert(obj->id(), obj); ui_->actionSave_objects->setEnabled(true); + ui_->actionSave_session->setEnabled(true); showObject(obj); updateVocabulary(); objectsModified_ = true; @@ -616,6 +699,7 @@ bool MainWindow::addObjectFromFile(const QString & filePath) ObjWidget * obj = new ObjWidget(s->id(), std::vector(), cvtCvMat2QImage(s->image())); objWidgets_.insert(obj->id(), obj); ui_->actionSave_objects->setEnabled(true); + ui_->actionSave_session->setEnabled(true); this->showObject(obj); return true; } diff --git a/src/ObjSignature.h b/src/ObjSignature.h index 961e3224..80bcdb9c 100644 --- a/src/ObjSignature.h +++ b/src/ObjSignature.h @@ -32,11 +32,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include +#include namespace find_object { class ObjSignature { public: + ObjSignature() : + id_(-1) + {} ObjSignature(int id, const cv::Mat & image, const QString & filename) : id_(id), image_(image), @@ -44,15 +49,10 @@ public: {} virtual ~ObjSignature() {} - void setData(const std::vector & keypoints, - const cv::Mat & descriptors, - const QString & detectorType, - const QString & descriptorType) + void setData(const std::vector & keypoints, const cv::Mat & descriptors) { keypoints_ = keypoints; descriptors_ = descriptors; - detectorType_ = detectorType; - descriptorType_ = descriptorType; } void setWords(const QMultiMap & words) {words_ = words;} void setId(int id) {id_ = id;} @@ -65,8 +65,69 @@ public: const std::vector & keypoints() const {return keypoints_;} const cv::Mat & descriptors() const {return descriptors_;} const QMultiMap & words() const {return words_;} - const QString & detectorType() const {return detectorType_;} - const QString & descriptorType() const {return descriptorType_;} + + void save(QDataStream & streamPtr) const + { + streamPtr << id_; + streamPtr << filename_; + streamPtr << (int)keypoints_.size(); + for(unsigned int j=0; j bytes; + cv::imencode(".png", image_, bytes); + streamPtr << QByteArray((char*)bytes.data(), bytes.size()); + } + + void load(QDataStream & streamPtr) + { + int nKpts; + streamPtr >> id_ >> filename_ >> nKpts; + keypoints_.resize(nKpts); + for(int i=0;i> + keypoints_[i].angle >> + keypoints_[i].class_id >> + keypoints_[i].octave >> + keypoints_[i].pt.x >> + keypoints_[i].pt.y >> + keypoints_[i].response >> + keypoints_[i].size; + } + + int rows,cols,type; + qint64 dataSize; + streamPtr >> rows >> cols >> type >> dataSize; + QByteArray data; + streamPtr >> data; + descriptors_ = cv::Mat(rows, cols, type, data.data()).clone(); + + streamPtr >> words_; + + QByteArray image; + streamPtr >> image; + std::vector bytes(image.size()); + memcpy(bytes.data(), image.data(), image.size()); + image_ = cv::imdecode(bytes, cv::IMREAD_UNCHANGED); + } private: int id_; @@ -75,8 +136,6 @@ private: std::vector keypoints_; cv::Mat descriptors_; QMultiMap words_; // - QString detectorType_; - QString descriptorType_; }; } // namespace find_object diff --git a/src/Vocabulary.cpp b/src/Vocabulary.cpp index d7ec44c1..d69cce0f 100644 --- a/src/Vocabulary.cpp +++ b/src/Vocabulary.cpp @@ -51,6 +51,41 @@ void Vocabulary::clear() notIndexedWordIds_.clear(); } +void Vocabulary::save(QDataStream & streamPtr) const +{ + if(!indexedDescriptors_.empty() && !wordToObjects_.empty()) + { + UASSERT(notIndexedDescriptors_.empty() && notIndexedWordIds_.empty()); + + // 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); + } +} + +void Vocabulary::load(QDataStream & streamPtr) +{ + // load index + streamPtr >> wordToObjects_; + + // load words + int rows,cols,type; + qint64 dataSize; + streamPtr >> rows >> cols >> type >> dataSize; + QByteArray data; + streamPtr >> data; + indexedDescriptors_ = cv::Mat(rows, cols, type, data.data()).clone(); + + update(); +} + QMultiMap Vocabulary::addWords(const cv::Mat & descriptors, int objectId, bool incremental) { QMultiMap words; diff --git a/src/Vocabulary.h b/src/Vocabulary.h index b99fd2d7..427d93cb 100644 --- a/src/Vocabulary.h +++ b/src/Vocabulary.h @@ -45,6 +45,10 @@ public: void search(const cv::Mat & descriptors, cv::Mat & results, cv::Mat & dists, int k); int size() const {return indexedDescriptors_.rows + notIndexedDescriptors_.rows;} const QMultiMap & wordToObjects() const {return wordToObjects_;} + const cv::Mat & indexedDescriptors() const {return indexedDescriptors_;} + + void save(QDataStream & streamPtr) const; + void load(QDataStream & streamPtr); private: cv::flann::Index flannIndex_; diff --git a/src/ui/mainWindow.ui b/src/ui/mainWindow.ui index 5e3161fa..7b6f6298 100644 --- a/src/ui/mainWindow.ui +++ b/src/ui/mainWindow.ui @@ -219,6 +219,9 @@ File + + + @@ -850,6 +853,16 @@ Camera from TCP/IP... + + + Load session... + + + + + Save session... + + Hide objects features