Added "save/load session" menu actions.

git-svn-id: http://find-object.googlecode.com/svn/trunk/find_object@418 620bd6b2-0a58-f614-fd9a-1bd335dccda9
This commit is contained in:
matlabbe 2015-01-09 22:44:16 +00:00
parent 84166e6ddc
commit 02e31c2e69
9 changed files with 310 additions and 27 deletions

View File

@ -65,6 +65,9 @@ public:
FindObject(QObject * parent = 0); FindObject(QObject * parent = 0);
virtual ~FindObject(); virtual ~FindObject();
bool loadSession(const QString & path);
bool saveSession(const QString & path) const;
int loadObjects(const QString & dirPath); // call updateObjects() int loadObjects(const QString & dirPath); // 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 & filename = QString()); const ObjSignature * addObject(const cv::Mat & image, int id=0, const QString & filename = QString());

View File

@ -81,6 +81,8 @@ public Q_SLOTS:
void update(const cv::Mat & image); void update(const cv::Mat & image);
private Q_SLOTS: private Q_SLOTS:
void loadSession();
void saveSession();
void loadSettings(); void loadSettings();
void saveSettings(); void saveSettings();
void loadObjects(); void loadObjects();
@ -110,7 +112,7 @@ Q_SIGNALS:
private: private:
bool loadSettings(const QString & path); bool loadSettings(const QString & path);
bool saveSettings(const QString & path); bool saveSettings(const QString & path) const;
int loadObjects(const QString & dirPath); int loadObjects(const QString & dirPath);
int saveObjects(const QString & dirPath); int saveObjects(const QString & dirPath);
void setupTCPServer(); void setupTCPServer();

View File

@ -358,7 +358,7 @@ void AddObjectDialog::setState(int state)
objSignature_ = 0; objSignature_ = 0;
} }
objSignature_ = new ObjSignature(0, imgRoi.clone(), ""); 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())); objWidget_ = new ObjWidget(0, keypoints, cvtCvMat2QImage(imgRoi.clone()));
this->accept(); this->accept();

View File

@ -59,6 +59,85 @@ FindObject::~FindObject() {
objectsDescriptors_.clear(); 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<QString, QVariant>::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<int, ObjSignature*>::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 FindObject::loadObjects(const QString & dirPath)
{ {
int loadedObjects = 0; int loadedObjects = 0;
@ -143,7 +222,6 @@ bool FindObject::addObject(ObjSignature * obj)
Settings::setGeneral_nextObjID(obj->id()+1); Settings::setGeneral_nextObjID(obj->id()+1);
objects_.insert(obj->id(), obj); objects_.insert(obj->id(), obj);
clearVocabulary();
return true; return true;
} }
@ -512,11 +590,7 @@ void FindObject::updateObjects()
int id = threads[j]->objectId(); int id = threads[j]->objectId();
objects_.value(id)->setData( objects_.value(id)->setData(threads[j]->keypoints(), threads[j]->descriptors());
threads[j]->keypoints(),
threads[j]->descriptors(),
Settings::currentDetectorType(),
Settings::currentDescriptorType());
} }
} }
UINFO("Features extraction from %d objects... done! (%d ms)", objects_.size(), time.elapsed()); 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()) || bool consistentNNData = (vocabulary_->size()!=0 && vocabulary_->wordToObjects().begin().value()!=-1 && Settings::getGeneral_invertedSearch()) ||
((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 // COMPARE
UDEBUG("COMPARE"); UDEBUG("COMPARE");
if(!objectsDescriptors_.empty() && if((descriptorsValid || vocabularyValid) &&
info.sceneKeypoints_.size() && info.sceneKeypoints_.size() &&
consistentNNData && 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
{ {
success = true; success = true;
QTime time; QTime time;
@ -1243,7 +1326,7 @@ bool FindObject::detect(const cv::Mat & image, find_object::DetectionInfo & info
info.timeStamps_.insert(DetectionInfo::kTimeHomography, time.restart()); 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"); UWARN("Cannot search, objects must be updated");
} }

View File

@ -181,6 +181,7 @@ MainWindow::MainWindow(FindObject * findObject, Camera * camera, QWidget * paren
ui_->actionStop_camera->setEnabled(false); ui_->actionStop_camera->setEnabled(false);
ui_->actionPause_camera->setEnabled(false); ui_->actionPause_camera->setEnabled(false);
ui_->actionSave_objects->setEnabled(false); ui_->actionSave_objects->setEnabled(false);
ui_->actionSave_session->setEnabled(false);
// Actions // Actions
connect(ui_->actionAdd_object_from_scene, SIGNAL(triggered()), this, SLOT(addObjectFromScene())); 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_->actionRemove_all_objects, SIGNAL(triggered()), this, SLOT(removeAllObjects()));
connect(ui_->actionSave_settings, SIGNAL(triggered()), this, SLOT(saveSettings())); connect(ui_->actionSave_settings, SIGNAL(triggered()), this, SLOT(saveSettings()));
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_->actionLoad_session, SIGNAL(triggered()), this, SLOT(loadSession()));
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()));
@ -315,6 +318,84 @@ void MainWindow::setSourceImageText(const QString & text)
ui_->imageView_source->setTextLabel(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<int, ObjSignature *>::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<QLabel*>(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() void MainWindow::loadSettings()
{ {
QString path = QFileDialog::getOpenFileName(this, tr("Load settings..."), Settings::workingDirectory(), "*.ini"); QString path = QFileDialog::getOpenFileName(this, tr("Load settings..."), Settings::workingDirectory(), "*.ini");
@ -360,11 +441,11 @@ bool MainWindow::loadSettings(const QString & path)
return true; 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; return false;
} }
bool MainWindow::saveSettings(const QString & path) bool MainWindow::saveSettings(const QString & path) const
{ {
if(!path.isEmpty() && QFileInfo(path).suffix().compare("ini") == 0) 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); Settings::saveWindowSettings(this->saveGeometry(), this->saveState(), path);
return true; 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; return false;
} }
@ -405,7 +486,7 @@ int MainWindow::saveObjects(const QString & dirPath)
QDir dir(dirPath); QDir dir(dirPath);
if(dir.exists()) if(dir.exists())
{ {
for(QMap<int, ObjWidget*>::iterator iter=objWidgets_.begin(); iter!=objWidgets_.end(); ++iter) for(QMap<int, ObjWidget*>::const_iterator iter=objWidgets_.constBegin(); iter!=objWidgets_.constEnd(); ++iter)
{ {
if(iter.value()->pixmap().save(QString("%1/%2.png").arg(dirPath).arg(iter.key()))) 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) if(objWidgets_.size() == 0)
{ {
ui_->actionSave_objects->setEnabled(false); ui_->actionSave_objects->setEnabled(false);
ui_->actionSave_session->setEnabled(false);
} }
findObject_->removeObject(object->id()); findObject_->removeObject(object->id());
object->deleteLater(); object->deleteLater();
@ -570,6 +652,7 @@ void MainWindow::addObjectFromScene()
obj->setId(signature->id()); obj->setId(signature->id());
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);
showObject(obj); showObject(obj);
updateVocabulary(); updateVocabulary();
objectsModified_ = true; objectsModified_ = true;
@ -616,6 +699,7 @@ bool MainWindow::addObjectFromFile(const QString & filePath)
ObjWidget * obj = new ObjWidget(s->id(), std::vector<cv::KeyPoint>(), cvtCvMat2QImage(s->image())); ObjWidget * obj = new ObjWidget(s->id(), std::vector<cv::KeyPoint>(), 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);
this->showObject(obj); this->showObject(obj);
return true; return true;
} }

View File

@ -32,11 +32,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QMultiMap> #include <QtCore/QMultiMap>
#include <QtCore/QRect> #include <QtCore/QRect>
#include <QtCore/QDataStream>
#include <QtCore/QByteArray>
namespace find_object { namespace find_object {
class ObjSignature { class ObjSignature {
public: public:
ObjSignature() :
id_(-1)
{}
ObjSignature(int id, const cv::Mat & image, const QString & filename) : ObjSignature(int id, const cv::Mat & image, const QString & filename) :
id_(id), id_(id),
image_(image), image_(image),
@ -44,15 +49,10 @@ public:
{} {}
virtual ~ObjSignature() {} virtual ~ObjSignature() {}
void setData(const std::vector<cv::KeyPoint> & keypoints, void setData(const std::vector<cv::KeyPoint> & keypoints, const cv::Mat & descriptors)
const cv::Mat & descriptors,
const QString & detectorType,
const QString & descriptorType)
{ {
keypoints_ = keypoints; keypoints_ = keypoints;
descriptors_ = descriptors; descriptors_ = descriptors;
detectorType_ = detectorType;
descriptorType_ = descriptorType;
} }
void setWords(const QMultiMap<int, int> & words) {words_ = words;} void setWords(const QMultiMap<int, int> & words) {words_ = words;}
void setId(int id) {id_ = id;} void setId(int id) {id_ = id;}
@ -65,8 +65,69 @@ public:
const std::vector<cv::KeyPoint> & keypoints() const {return keypoints_;} const std::vector<cv::KeyPoint> & keypoints() const {return keypoints_;}
const cv::Mat & descriptors() const {return descriptors_;} const cv::Mat & descriptors() const {return descriptors_;}
const QMultiMap<int, int> & words() const {return words_;} const QMultiMap<int, int> & 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<keypoints_.size(); ++j)
{
streamPtr << keypoints_.at(j).angle <<
keypoints_.at(j).class_id <<
keypoints_.at(j).octave <<
keypoints_.at(j).pt.x <<
keypoints_.at(j).pt.y <<
keypoints_.at(j).response <<
keypoints_.at(j).size;
}
qint64 dataSize = descriptors_.elemSize()*descriptors_.cols*descriptors_.rows;
streamPtr << descriptors_.rows <<
descriptors_.cols <<
descriptors_.type() <<
dataSize;
streamPtr << QByteArray((char*)descriptors_.data, dataSize);
streamPtr << words_;
std::vector<unsigned char> 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<nKpts;++i)
{
streamPtr >>
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<unsigned char> bytes(image.size());
memcpy(bytes.data(), image.data(), image.size());
image_ = cv::imdecode(bytes, cv::IMREAD_UNCHANGED);
}
private: private:
int id_; int id_;
@ -75,8 +136,6 @@ private:
std::vector<cv::KeyPoint> keypoints_; std::vector<cv::KeyPoint> keypoints_;
cv::Mat descriptors_; cv::Mat descriptors_;
QMultiMap<int, int> words_; // <word id, keypoint indexes> QMultiMap<int, int> words_; // <word id, keypoint indexes>
QString detectorType_;
QString descriptorType_;
}; };
} // namespace find_object } // namespace find_object

View File

@ -51,6 +51,41 @@ void Vocabulary::clear()
notIndexedWordIds_.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<int, int> Vocabulary::addWords(const cv::Mat & descriptors, int objectId, bool incremental) QMultiMap<int, int> Vocabulary::addWords(const cv::Mat & descriptors, int objectId, bool incremental)
{ {
QMultiMap<int, int> words; QMultiMap<int, int> words;

View File

@ -45,6 +45,10 @@ public:
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;}
const QMultiMap<int, int> & wordToObjects() const {return wordToObjects_;} const QMultiMap<int, int> & wordToObjects() const {return wordToObjects_;}
const cv::Mat & indexedDescriptors() const {return indexedDescriptors_;}
void save(QDataStream & streamPtr) const;
void load(QDataStream & streamPtr);
private: private:
cv::flann::Index flannIndex_; cv::flann::Index flannIndex_;

View File

@ -219,6 +219,9 @@
<property name="title"> <property name="title">
<string>File</string> <string>File</string>
</property> </property>
<addaction name="actionLoad_session"/>
<addaction name="actionSave_session"/>
<addaction name="separator"/>
<addaction name="actionLoad_objects"/> <addaction name="actionLoad_objects"/>
<addaction name="actionSave_objects"/> <addaction name="actionSave_objects"/>
<addaction name="separator"/> <addaction name="separator"/>
@ -850,6 +853,16 @@
<string>Camera from TCP/IP...</string> <string>Camera from TCP/IP...</string>
</property> </property>
</action> </action>
<action name="actionLoad_session">
<property name="text">
<string>Load session...</string>
</property>
</action>
<action name="actionSave_session">
<property name="text">
<string>Save session...</string>
</property>
</action>
<action name="actionHide_objects_features"> <action name="actionHide_objects_features">
<property name="text"> <property name="text">
<string>Hide objects features</string> <string>Hide objects features</string>