-Added actions to show/hide all features of the objects -Auto adjust size slider to widest added object -Compare to both size of the scene and object to reject a too large homography -Removed descriptor/detector label on objects -Added Refresh GUI time statistic git-svn-id: http://find-object.googlecode.com/svn/trunk/find_object@416 620bd6b2-0a58-f614-fd9a-1bd335dccda9
1316 lines
44 KiB
C++
1316 lines
44 KiB
C++
/*
|
|
Copyright (c) 2011-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the Universite de Sherbrooke nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "find_object/MainWindow.h"
|
|
#include "find_object/Camera.h"
|
|
#include "find_object/Settings.h"
|
|
#include "find_object/TcpServer.h"
|
|
#include "find_object/FindObject.h"
|
|
#include "find_object/utilite/ULogger.h"
|
|
#include "find_object/ObjWidget.h"
|
|
#include "find_object/QtOpenCV.h"
|
|
|
|
#include "AddObjectDialog.h"
|
|
#include "ui_mainWindow.h"
|
|
#include "KeypointItem.h"
|
|
#include "RectItem.h"
|
|
#include "ParametersToolBox.h"
|
|
#include "AboutDialog.h"
|
|
#include "rtabmap/PdfPlot.h"
|
|
#include "Vocabulary.h"
|
|
#include "ObjSignature.h"
|
|
|
|
#include <iostream>
|
|
#include <stdio.h>
|
|
|
|
#include "opencv2/calib3d/calib3d.hpp"
|
|
#include "opencv2/imgproc/imgproc.hpp"
|
|
#include "opencv2/gpu/gpu.hpp"
|
|
|
|
#include <QtCore/QTextStream>
|
|
#include <QtCore/QFile>
|
|
#include <QtCore/QBuffer>
|
|
#include <QtCore/QThread>
|
|
#include <QtCore/QLineF>
|
|
|
|
#include <QtGui/QFileDialog>
|
|
#include <QtGui/QMessageBox>
|
|
#include <QtGui/QGraphicsScene>
|
|
#include <QtGui/QGraphicsRectItem>
|
|
#include <QtGui/QSpinBox>
|
|
#include <QtGui/QStatusBar>
|
|
#include <QtGui/QProgressDialog>
|
|
#include <QtGui/QCloseEvent>
|
|
#include <QtGui/QCheckBox>
|
|
#include <QtGui/QScrollBar>
|
|
#include <QtGui/QInputDialog>
|
|
|
|
#include "utilite/UDirectory.h"
|
|
|
|
namespace find_object {
|
|
|
|
// Camera ownership transferred
|
|
MainWindow::MainWindow(FindObject * findObject, Camera * camera, QWidget * parent) :
|
|
QMainWindow(parent),
|
|
camera_(camera),
|
|
findObject_(findObject),
|
|
likelihoodCurve_(0),
|
|
inliersCurve_(0),
|
|
lowestRefreshRate_(99),
|
|
objectsModified_(false),
|
|
tcpServer_(0)
|
|
{
|
|
Q_ASSERT(findObject_ != 0);
|
|
|
|
ui_ = new Ui_mainWindow();
|
|
ui_->setupUi(this);
|
|
aboutDialog_ = new AboutDialog(this);
|
|
this->setStatusBar(new QStatusBar());
|
|
|
|
likelihoodCurve_ = new rtabmap::PdfPlotCurve("Likelihood", &imagesMap_, this);
|
|
inliersCurve_ = new rtabmap::PdfPlotCurve("Inliers", &imagesMap_, this);
|
|
likelihoodCurve_->setPen(QPen(Qt::blue));
|
|
inliersCurve_->setPen(QPen(Qt::red));
|
|
ui_->likelihoodPlot->addCurve(likelihoodCurve_, false);
|
|
ui_->likelihoodPlot->addCurve(inliersCurve_, false);
|
|
ui_->likelihoodPlot->setGraphicsView(true);
|
|
|
|
ui_->dockWidget_statistics->setVisible(false);
|
|
ui_->dockWidget_parameters->setVisible(false);
|
|
ui_->dockWidget_plot->setVisible(false);
|
|
ui_->widget_controls->setVisible(false);
|
|
|
|
QByteArray geometry;
|
|
QByteArray state;
|
|
Settings::loadWindowSettings(geometry, state);
|
|
this->restoreGeometry(geometry);
|
|
this->restoreState(state);
|
|
lastObjectsUpdateParameters_ = Settings::getParameters();
|
|
|
|
ui_->toolBox->setupUi();
|
|
|
|
if(!camera_)
|
|
{
|
|
camera_ = new Camera(this);
|
|
}
|
|
else
|
|
{
|
|
camera_->setParent(this);
|
|
ui_->toolBox->getParameterWidget(Settings::kCamera_1deviceId())->setEnabled(false);
|
|
ui_->toolBox->getParameterWidget(Settings::kCamera_2imageWidth())->setEnabled(false);
|
|
ui_->toolBox->getParameterWidget(Settings::kCamera_3imageHeight())->setEnabled(false);
|
|
ui_->toolBox->getParameterWidget(Settings::kCamera_5mediaPath())->setEnabled(false);
|
|
ui_->toolBox->getParameterWidget(Settings::kCamera_6useTcpCamera())->setEnabled(false);
|
|
ui_->toolBox->getParameterWidget(Settings::kCamera_8port())->setEnabled(false);
|
|
ui_->toolBox->getParameterWidget(Settings::kCamera_9queueSize())->setEnabled(false);
|
|
ui_->actionCamera_from_video_file->setVisible(false);
|
|
ui_->actionCamera_from_TCP_IP->setVisible(false);
|
|
ui_->actionCamera_from_directory_of_images->setVisible(false);
|
|
ui_->actionLoad_scene_from_file->setVisible(false);
|
|
}
|
|
|
|
if(cv::gpu::getCudaEnabledDeviceCount() == 0)
|
|
{
|
|
#if FINDOBJECT_NONFREE == 1
|
|
ui_->toolBox->updateParameter(Settings::kFeature2D_SURF_gpu());
|
|
ui_->toolBox->getParameterWidget(Settings::kFeature2D_SURF_gpu())->setEnabled(false);
|
|
ui_->toolBox->getParameterWidget(Settings::kFeature2D_SURF_keypointsRatio())->setEnabled(false);
|
|
#endif
|
|
ui_->toolBox->updateParameter(Settings::kFeature2D_Fast_gpu());
|
|
ui_->toolBox->updateParameter(Settings::kFeature2D_ORB_gpu());
|
|
ui_->toolBox->updateParameter(Settings::kNearestNeighbor_BruteForce_gpu());
|
|
ui_->toolBox->getParameterWidget(Settings::kFeature2D_Fast_gpu())->setEnabled(false);
|
|
ui_->toolBox->getParameterWidget(Settings::kFeature2D_Fast_keypointsRatio())->setEnabled(false);
|
|
ui_->toolBox->getParameterWidget(Settings::kFeature2D_ORB_gpu())->setEnabled(false);
|
|
ui_->toolBox->getParameterWidget(Settings::kNearestNeighbor_BruteForce_gpu())->setEnabled(false);
|
|
}
|
|
|
|
connect((QDoubleSpinBox*)ui_->toolBox->getParameterWidget(Settings::kCamera_4imageRate()),
|
|
SIGNAL(editingFinished()),
|
|
camera_,
|
|
SLOT(updateImageRate()));
|
|
ui_->menuView->addAction(ui_->dockWidget_statistics->toggleViewAction());
|
|
ui_->menuView->addAction(ui_->dockWidget_parameters->toggleViewAction());
|
|
ui_->menuView->addAction(ui_->dockWidget_objects->toggleViewAction());
|
|
ui_->menuView->addAction(ui_->dockWidget_plot->toggleViewAction());
|
|
connect(ui_->toolBox, SIGNAL(parametersChanged(const QStringList &)), this, SLOT(notifyParametersChanged(const QStringList &)));
|
|
|
|
ui_->imageView_source->setGraphicsViewMode(true);
|
|
ui_->imageView_source->setTextLabel(tr("Press \"space\" to start the camera or drop an image here..."));
|
|
ui_->imageView_source->setMirrorView(Settings::getGeneral_mirrorView());
|
|
connect((QCheckBox*)ui_->toolBox->getParameterWidget(Settings::kGeneral_mirrorView()),
|
|
SIGNAL(stateChanged(int)),
|
|
this,
|
|
SLOT(updateMirrorView()));
|
|
|
|
ui_->widget_controls->setVisible(Settings::getGeneral_controlsShown());
|
|
connect((QCheckBox*)ui_->toolBox->getParameterWidget(Settings::kGeneral_controlsShown()),
|
|
SIGNAL(stateChanged(int)),
|
|
this,
|
|
SLOT(showHideControls()));
|
|
|
|
//buttons
|
|
connect(ui_->pushButton_restoreDefaults, SIGNAL(clicked()), ui_->toolBox, SLOT(resetCurrentPage()));
|
|
connect(ui_->pushButton_updateObjects, SIGNAL(clicked()), this, SLOT(updateObjects()));
|
|
connect(ui_->horizontalSlider_objectsSize, SIGNAL(valueChanged(int)), this, SLOT(updateObjectsSize()));
|
|
|
|
ui_->actionStop_camera->setEnabled(false);
|
|
ui_->actionPause_camera->setEnabled(false);
|
|
ui_->actionSave_objects->setEnabled(false);
|
|
|
|
// Actions
|
|
connect(ui_->actionAdd_object_from_scene, SIGNAL(triggered()), this, SLOT(addObjectFromScene()));
|
|
connect(ui_->actionAdd_objects_from_files, SIGNAL(triggered()), this, SLOT(addObjectsFromFiles()));
|
|
connect(ui_->actionLoad_scene_from_file, SIGNAL(triggered()), this, SLOT(loadSceneFromFile()));
|
|
connect(ui_->actionStart_camera, SIGNAL(triggered()), this, SLOT(startProcessing()));
|
|
connect(ui_->actionStop_camera, SIGNAL(triggered()), this, SLOT(stopProcessing()));
|
|
connect(ui_->actionPause_camera, SIGNAL(triggered()), this, SLOT(pauseProcessing()));
|
|
connect(ui_->actionExit, SIGNAL(triggered()), this, SLOT(close()));
|
|
connect(ui_->actionSave_objects, SIGNAL(triggered()), this, SLOT(saveObjects()));
|
|
connect(ui_->actionLoad_objects, SIGNAL(triggered()), this, SLOT(loadObjects()));
|
|
connect(ui_->actionCamera_from_video_file, SIGNAL(triggered()), this, SLOT(setupCameraFromVideoFile()));
|
|
connect(ui_->actionCamera_from_directory_of_images, SIGNAL(triggered()), this, SLOT(setupCameraFromImagesDirectory()));
|
|
connect(ui_->actionCamera_from_TCP_IP, SIGNAL(triggered()), this, SLOT(setupCameraFromTcpIp()));
|
|
connect(ui_->actionAbout, SIGNAL(triggered()), aboutDialog_ , SLOT(exec()));
|
|
connect(ui_->actionRestore_all_default_settings, SIGNAL(triggered()), ui_->toolBox, SLOT(resetAllPages()));
|
|
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_->actionShow_objects_features, SIGNAL(triggered()), this, SLOT(showObjectsFeatures()));
|
|
connect(ui_->actionHide_objects_features, SIGNAL(triggered()), this, SLOT(hideObjectsFeatures()));
|
|
|
|
connect(ui_->pushButton_play, SIGNAL(clicked()), this, SLOT(startProcessing()));
|
|
connect(ui_->pushButton_stop, SIGNAL(clicked()), this, SLOT(stopProcessing()));
|
|
connect(ui_->pushButton_pause, SIGNAL(clicked()), this, SLOT(pauseProcessing()));
|
|
connect(ui_->horizontalSlider_frames, SIGNAL(valueChanged(int)), this, SLOT(moveCameraFrame(int)));
|
|
connect(ui_->horizontalSlider_frames, SIGNAL(valueChanged(int)), ui_->label_frame, SLOT(setNum(int)));
|
|
ui_->pushButton_play->setVisible(true);
|
|
ui_->pushButton_pause->setVisible(false);
|
|
ui_->pushButton_stop->setEnabled(false);
|
|
ui_->horizontalSlider_frames->setEnabled(false);
|
|
ui_->label_frame->setVisible(false);
|
|
|
|
ui_->objects_area->addAction(ui_->actionAdd_object_from_scene);
|
|
ui_->objects_area->addAction(ui_->actionAdd_objects_from_files);
|
|
ui_->objects_area->setContextMenuPolicy(Qt::ActionsContextMenu);
|
|
|
|
ui_->actionStart_camera->setShortcut(Qt::Key_Space);
|
|
ui_->actionPause_camera->setShortcut(Qt::Key_Space);
|
|
|
|
ui_->actionCamera_from_video_file->setChecked(!Settings::getCamera_5mediaPath().isEmpty() && !UDirectory::exists(Settings::getCamera_5mediaPath().toStdString()));
|
|
ui_->actionCamera_from_directory_of_images->setChecked(!Settings::getCamera_5mediaPath().isEmpty() && UDirectory::exists(Settings::getCamera_5mediaPath().toStdString()));
|
|
ui_->actionCamera_from_TCP_IP->setChecked(Settings::getCamera_6useTcpCamera());
|
|
|
|
ui_->label_ipAddress->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
|
ui_->label_port->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
|
setupTCPServer();
|
|
|
|
if(findObject_->objects().size())
|
|
{
|
|
// show objects already loaded in FindObject
|
|
for(QMap<int, ObjSignature *>::const_iterator iter = findObject_->objects().constBegin();
|
|
iter!=findObject_->objects().constEnd();
|
|
++iter)
|
|
{
|
|
ObjWidget * obj = new ObjWidget(iter.key(), iter.value()->keypoints(), cvtCvMat2QImage(iter.value()->image()));
|
|
objWidgets_.insert(obj->id(), obj);
|
|
this->showObject(obj);
|
|
}
|
|
}
|
|
|
|
|
|
if(Settings::getGeneral_autoStartCamera())
|
|
{
|
|
// Set 1 msec to see state on the status bar.
|
|
QTimer::singleShot(1, this, SLOT(startProcessing()));
|
|
}
|
|
|
|
//Setup drag and drop images
|
|
connect(ui_->imageDrop_objects, SIGNAL(imagesReceived(const QStringList &)), this, SLOT(addObjectsFromFiles(const QStringList &)));
|
|
connect(ui_->imageDrop_scene, SIGNAL(imagesReceived(const QStringList &)), this, SLOT(loadSceneFromFile(const QStringList &)));
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
disconnect(camera_, SIGNAL(imageReceived(const cv::Mat &)), this, SLOT(update(const cv::Mat &)));
|
|
disconnect(camera_, SIGNAL(finished()), this, SLOT(stopProcessing()));
|
|
camera_->stop();
|
|
qDeleteAll(objWidgets_);
|
|
objWidgets_.clear();
|
|
delete ui_;
|
|
delete findObject_;
|
|
}
|
|
|
|
void MainWindow::closeEvent(QCloseEvent * event)
|
|
{
|
|
bool quit = true;
|
|
this->stopProcessing();
|
|
if(objectsModified_ && this->isVisible() && objWidgets_.size())
|
|
{
|
|
int ret = QMessageBox::question(this, tr("Save new objects"), tr("Do you want to save added objects?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
|
|
switch(ret)
|
|
{
|
|
case QMessageBox::Yes:
|
|
quit = this->saveObjects();
|
|
break;
|
|
case QMessageBox::Cancel:
|
|
quit = false;
|
|
break;
|
|
case QMessageBox::No:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if(quit)
|
|
{
|
|
Settings::saveWindowSettings(this->saveGeometry(), this->saveState());
|
|
event->accept();
|
|
}
|
|
else
|
|
{
|
|
event->ignore();
|
|
}
|
|
}
|
|
|
|
void MainWindow::setupTCPServer()
|
|
{
|
|
if(tcpServer_)
|
|
{
|
|
tcpServer_->close();
|
|
delete tcpServer_;
|
|
}
|
|
tcpServer_ = new TcpServer(Settings::getGeneral_port(), this);
|
|
connect(this, SIGNAL(objectsFound(find_object::DetectionInfo)), tcpServer_, SLOT(publishDetectionInfo(find_object::DetectionInfo)));
|
|
ui_->label_ipAddress->setText(tcpServer_->getHostAddress().toString());
|
|
ui_->label_port->setNum(tcpServer_->getPort());
|
|
UINFO("Detection sent on port: %d (IP=%s)", tcpServer_->getPort(), tcpServer_->getHostAddress().toString().toStdString().c_str());
|
|
}
|
|
|
|
void MainWindow::setSourceImageText(const QString & text)
|
|
{
|
|
ui_->imageView_source->setTextLabel(text);
|
|
}
|
|
|
|
void MainWindow::loadSettings()
|
|
{
|
|
QString path = QFileDialog::getOpenFileName(this, tr("Load settings..."), Settings::workingDirectory(), "*.ini");
|
|
if(!path.isEmpty())
|
|
{
|
|
if(QFileInfo(path).suffix().compare("ini") != 0)
|
|
{
|
|
path.append(".ini");
|
|
}
|
|
loadSettings(path);
|
|
}
|
|
}
|
|
void MainWindow::saveSettings()
|
|
{
|
|
QString path = QFileDialog::getSaveFileName(this, tr("Save settings..."), Settings::workingDirectory(), "*.ini");
|
|
if(!path.isEmpty())
|
|
{
|
|
if(QFileInfo(path).suffix().compare("ini") != 0)
|
|
{
|
|
path.append(".ini");
|
|
}
|
|
saveSettings(path);
|
|
}
|
|
}
|
|
|
|
bool MainWindow::loadSettings(const QString & path)
|
|
{
|
|
if(!path.isEmpty() && QFileInfo(path).suffix().compare("ini") == 0)
|
|
{
|
|
QByteArray geometry;
|
|
QByteArray state;
|
|
Settings::loadSettings(path);
|
|
Settings::loadWindowSettings(geometry, state, path);
|
|
this->restoreGeometry(geometry);
|
|
this->restoreState(state);
|
|
|
|
//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());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
UINFO("Path \"%s\" not valid (should be *.ini)", path.toStdString().c_str());
|
|
return false;
|
|
}
|
|
|
|
bool MainWindow::saveSettings(const QString & path)
|
|
{
|
|
if(!path.isEmpty() && QFileInfo(path).suffix().compare("ini") == 0)
|
|
{
|
|
Settings::saveSettings(path);
|
|
Settings::saveWindowSettings(this->saveGeometry(), this->saveState(), path);
|
|
return true;
|
|
}
|
|
UINFO("Path \"%s\" not valid (should be *.ini)", path.toStdString().c_str());
|
|
return false;
|
|
}
|
|
|
|
int MainWindow::loadObjects(const QString & dirPath)
|
|
{
|
|
int loadedObjects = 0;
|
|
QString formats = Settings::getGeneral_imageFormats().remove('*').remove('.');
|
|
UDirectory dir(dirPath.toStdString(), formats.toStdString());
|
|
if(dir.isValid())
|
|
{
|
|
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)
|
|
{
|
|
if(this->addObjectFromFile((dirPath.toStdString()+dir.separator()+*iter).c_str()))
|
|
{
|
|
++loadedObjects;
|
|
}
|
|
}
|
|
if(loadedObjects)
|
|
{
|
|
this->updateObjects();
|
|
}
|
|
}
|
|
return loadedObjects;
|
|
}
|
|
|
|
int MainWindow::saveObjects(const QString & dirPath)
|
|
{
|
|
int count = 0;
|
|
QDir dir(dirPath);
|
|
if(dir.exists())
|
|
{
|
|
for(QMap<int, ObjWidget*>::iterator iter=objWidgets_.begin(); iter!=objWidgets_.end(); ++iter)
|
|
{
|
|
if(iter.value()->pixmap().save(QString("%1/%2.png").arg(dirPath).arg(iter.key())))
|
|
{
|
|
++count;
|
|
}
|
|
else
|
|
{
|
|
UERROR("Failed to save object %d", iter.key());
|
|
}
|
|
}
|
|
objectsModified_ = false;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void MainWindow::loadObjects()
|
|
{
|
|
QString dirPath = QFileDialog::getExistingDirectory(this, tr("Loading objects... Select a directory."), Settings::workingDirectory());
|
|
if(!dirPath.isEmpty())
|
|
{
|
|
int count = loadObjects(dirPath);
|
|
if(count)
|
|
{
|
|
QMessageBox::information(this, tr("Loading..."), tr("%1 objects loaded from \"%2\".").arg(count).arg(dirPath));
|
|
}
|
|
else
|
|
{
|
|
QMessageBox::information(this, tr("Loading..."), tr("No objects loaded from \"%1\"!").arg(dirPath));
|
|
}
|
|
}
|
|
}
|
|
bool MainWindow::saveObjects()
|
|
{
|
|
QString dirPath = QFileDialog::getExistingDirectory(this, tr("Saving objects... Select a directory."), Settings::workingDirectory());
|
|
if(!dirPath.isEmpty())
|
|
{
|
|
int count = saveObjects(dirPath);
|
|
if(count)
|
|
{
|
|
QMessageBox::information(this, tr("Saving..."), tr("%1 objects saved to \"%2\".").arg(count).arg(dirPath));
|
|
}
|
|
else
|
|
{
|
|
QMessageBox::warning(this, tr("Saving..."), tr("No objects saved to %1!").arg(dirPath));
|
|
}
|
|
return count > 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MainWindow::removeObject(find_object::ObjWidget * object)
|
|
{
|
|
if(object)
|
|
{
|
|
objWidgets_.remove(object->id());
|
|
if(objWidgets_.size() == 0)
|
|
{
|
|
ui_->actionSave_objects->setEnabled(false);
|
|
}
|
|
findObject_->removeObject(object->id());
|
|
object->deleteLater();
|
|
if(Settings::getGeneral_autoUpdateObjects())
|
|
{
|
|
this->updateVocabulary();
|
|
}
|
|
if(!camera_->isRunning() && !sceneImage_.empty())
|
|
{
|
|
this->update(sceneImage_);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::removeAllObjects()
|
|
{
|
|
qDeleteAll(objWidgets_);
|
|
objWidgets_.clear();
|
|
ui_->actionSave_objects->setEnabled(false);
|
|
findObject_->removeAllObjects();
|
|
if(!camera_->isRunning() && !sceneImage_.empty())
|
|
{
|
|
this->update(sceneImage_);
|
|
}
|
|
}
|
|
|
|
void MainWindow::updateObjectsSize()
|
|
{
|
|
for(QMap<int, ObjWidget*>::iterator iter=objWidgets_.begin(); iter!=objWidgets_.end(); ++iter)
|
|
{
|
|
updateObjectSize(iter.value());
|
|
}
|
|
}
|
|
|
|
void MainWindow::updateObjectSize(ObjWidget * obj)
|
|
{
|
|
if(obj)
|
|
{
|
|
int value = ui_->horizontalSlider_objectsSize->value();
|
|
if((obj->pixmap().width()*value)/100 > 4 && (obj->pixmap().height()*value)/100 > 4)
|
|
{
|
|
obj->setVisible(true);
|
|
obj->setMinimumSize((obj->pixmap().width()*value)/100, (obj->pixmap().height())*value/100);
|
|
}
|
|
else
|
|
{
|
|
obj->setVisible(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::updateMirrorView()
|
|
{
|
|
bool mirrorView = Settings::getGeneral_mirrorView();
|
|
ui_->imageView_source->setMirrorView(mirrorView);
|
|
for(QMap<int, ObjWidget*>::iterator iter=objWidgets_.begin(); iter!=objWidgets_.end(); ++iter)
|
|
{
|
|
iter.value()->setMirrorView(mirrorView);
|
|
}
|
|
}
|
|
|
|
void MainWindow::showHideControls()
|
|
{
|
|
ui_->widget_controls->setVisible(Settings::getGeneral_controlsShown());
|
|
}
|
|
|
|
void MainWindow::showObjectsFeatures()
|
|
{
|
|
for(QMap<int, ObjWidget*>::iterator iter=objWidgets_.begin(); iter!=objWidgets_.end(); ++iter)
|
|
{
|
|
iter.value()->setFeaturesShown(true);
|
|
}
|
|
}
|
|
|
|
void MainWindow::hideObjectsFeatures()
|
|
{
|
|
for(QMap<int, ObjWidget*>::iterator iter=objWidgets_.begin(); iter!=objWidgets_.end(); ++iter)
|
|
{
|
|
iter.value()->setFeaturesShown(false);
|
|
}
|
|
}
|
|
|
|
void MainWindow::addObjectFromScene()
|
|
{
|
|
disconnect(camera_, SIGNAL(imageReceived(const cv::Mat &)), this, SLOT(update(const cv::Mat &)));
|
|
disconnect(camera_, SIGNAL(finished()), this, SLOT(stopProcessing()));
|
|
AddObjectDialog * dialog;
|
|
bool resumeCamera = camera_->isRunning();
|
|
if(camera_->isRunning() || sceneImage_.empty())
|
|
{
|
|
dialog = new AddObjectDialog(camera_, cv::Mat(), ui_->imageView_source->isMirrorView(), this);
|
|
}
|
|
else
|
|
{
|
|
dialog = new AddObjectDialog(0, sceneImage_, ui_->imageView_source->isMirrorView(), this);
|
|
}
|
|
if(dialog->exec() == QDialog::Accepted)
|
|
{
|
|
ObjWidget * obj = 0;
|
|
ObjSignature * signature = 0;
|
|
dialog->retrieveObject(&obj, &signature);
|
|
Q_ASSERT(obj!=0 && signature!=0);
|
|
findObject_->addObject(signature);
|
|
obj->setId(signature->id());
|
|
objWidgets_.insert(obj->id(), obj);
|
|
ui_->actionSave_objects->setEnabled(true);
|
|
showObject(obj);
|
|
updateVocabulary();
|
|
objectsModified_ = true;
|
|
}
|
|
if(resumeCamera || sceneImage_.empty())
|
|
{
|
|
this->startProcessing();
|
|
}
|
|
else
|
|
{
|
|
connect(camera_, SIGNAL(imageReceived(const cv::Mat &)), this, SLOT(update(const cv::Mat &)), Qt::UniqueConnection);
|
|
connect(camera_, SIGNAL(finished()), this, SLOT(stopProcessing()), Qt::UniqueConnection);
|
|
if(!sceneImage_.empty())
|
|
{
|
|
this->update(sceneImage_);
|
|
}
|
|
}
|
|
delete dialog;
|
|
}
|
|
|
|
void MainWindow::addObjectsFromFiles(const QStringList & fileNames)
|
|
{
|
|
if(fileNames.size())
|
|
{
|
|
for(int i=0; i<fileNames.size(); ++i)
|
|
{
|
|
this->addObjectFromFile(fileNames.at(i));
|
|
}
|
|
objectsModified_ = true;
|
|
updateObjects();
|
|
}
|
|
}
|
|
|
|
void MainWindow::addObjectsFromFiles()
|
|
{
|
|
addObjectsFromFiles(QFileDialog::getOpenFileNames(this, tr("Add objects..."), Settings::workingDirectory(), tr("Image Files (%1)").arg(Settings::getGeneral_imageFormats())));
|
|
}
|
|
|
|
bool MainWindow::addObjectFromFile(const QString & filePath)
|
|
{
|
|
const ObjSignature * s = findObject_->addObject(filePath);
|
|
if(s)
|
|
{
|
|
ObjWidget * obj = new ObjWidget(s->id(), std::vector<cv::KeyPoint>(), cvtCvMat2QImage(s->image()));
|
|
objWidgets_.insert(obj->id(), obj);
|
|
ui_->actionSave_objects->setEnabled(true);
|
|
this->showObject(obj);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
QMessageBox::critical(this, tr("Error adding object"), tr("Failed to add object from \"%1\"").arg(filePath));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void MainWindow::loadSceneFromFile(const QStringList & fileNames)
|
|
{
|
|
//take the first
|
|
if(fileNames.size())
|
|
{
|
|
cv::Mat img = cv::imread(fileNames.first().toStdString().c_str());
|
|
if(!img.empty())
|
|
{
|
|
this->update(img);
|
|
ui_->label_timeRefreshRate->setVisible(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::loadSceneFromFile()
|
|
{
|
|
QString fileName = QFileDialog::getOpenFileName(this, tr("Load scene..."), Settings::workingDirectory(), tr("Image Files (%1)").arg(Settings::getGeneral_imageFormats()));
|
|
if(!fileName.isEmpty())
|
|
{
|
|
cv::Mat img = cv::imread(fileName.toStdString().c_str());
|
|
if(!img.empty())
|
|
{
|
|
this->update(img);
|
|
ui_->label_timeRefreshRate->setVisible(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::setupCameraFromVideoFile()
|
|
{
|
|
if(!ui_->actionCamera_from_video_file->isChecked())
|
|
{
|
|
Settings::setCamera_5mediaPath("");
|
|
ui_->toolBox->updateParameter(Settings::kCamera_5mediaPath());
|
|
}
|
|
else
|
|
{
|
|
QString fileName = QFileDialog::getOpenFileName(this, tr("Setup camera from video file..."), Settings::workingDirectory(), tr("Video Files (%1)").arg(Settings::getGeneral_videoFormats()));
|
|
if(!fileName.isEmpty())
|
|
{
|
|
Settings::setCamera_6useTcpCamera(false);
|
|
ui_->toolBox->updateParameter(Settings::kCamera_6useTcpCamera());
|
|
|
|
Settings::setCamera_5mediaPath(fileName);
|
|
ui_->toolBox->updateParameter(Settings::kCamera_5mediaPath());
|
|
if(camera_->isRunning())
|
|
{
|
|
this->stopProcessing();
|
|
this->startProcessing();
|
|
}
|
|
Settings::setGeneral_controlsShown(true);
|
|
ui_->toolBox->updateParameter(Settings::kGeneral_controlsShown());
|
|
}
|
|
}
|
|
ui_->actionCamera_from_video_file->setChecked(!Settings::getCamera_5mediaPath().isEmpty());
|
|
ui_->actionCamera_from_directory_of_images->setChecked(false);
|
|
ui_->actionCamera_from_TCP_IP->setChecked(false);
|
|
}
|
|
|
|
void MainWindow::setupCameraFromImagesDirectory()
|
|
{
|
|
if(!ui_->actionCamera_from_directory_of_images->isChecked())
|
|
{
|
|
Settings::setCamera_5mediaPath("");
|
|
ui_->toolBox->updateParameter(Settings::kCamera_5mediaPath());
|
|
}
|
|
else
|
|
{
|
|
QString directory = QFileDialog::getExistingDirectory(this, tr("Setup camera from directory of images..."), Settings::workingDirectory());
|
|
if(!directory.isEmpty())
|
|
{
|
|
Settings::setCamera_6useTcpCamera(false);
|
|
ui_->toolBox->updateParameter(Settings::kCamera_6useTcpCamera());
|
|
|
|
Settings::setCamera_5mediaPath(directory);
|
|
ui_->toolBox->updateParameter(Settings::kCamera_5mediaPath());
|
|
if(camera_->isRunning())
|
|
{
|
|
this->stopProcessing();
|
|
this->startProcessing();
|
|
}
|
|
Settings::setGeneral_controlsShown(true);
|
|
ui_->toolBox->updateParameter(Settings::kGeneral_controlsShown());
|
|
}
|
|
}
|
|
ui_->actionCamera_from_directory_of_images->setChecked(!Settings::getCamera_5mediaPath().isEmpty());
|
|
ui_->actionCamera_from_video_file->setChecked(false);
|
|
ui_->actionCamera_from_TCP_IP->setChecked(false);
|
|
}
|
|
|
|
void MainWindow::setupCameraFromTcpIp()
|
|
{
|
|
if(!ui_->actionCamera_from_TCP_IP->isChecked())
|
|
{
|
|
Settings::setCamera_6useTcpCamera(false);
|
|
ui_->toolBox->updateParameter(Settings::kCamera_6useTcpCamera());
|
|
}
|
|
else
|
|
{
|
|
bool ok;
|
|
int port = QInputDialog::getInteger(this, tr("Server port..."), "Port: ", Settings::getCamera_8port(), 1, USHRT_MAX, 1, &ok);
|
|
|
|
if(ok)
|
|
{
|
|
int queue = QInputDialog::getInteger(this, tr("Queue size..."), "Images buffer size (0 means infinite): ", Settings::getCamera_9queueSize(), 0, 2147483647, 1, &ok);
|
|
if(ok)
|
|
{
|
|
Settings::setCamera_6useTcpCamera(true);
|
|
ui_->toolBox->updateParameter(Settings::kCamera_6useTcpCamera());
|
|
Settings::setCamera_8port(port);
|
|
ui_->toolBox->updateParameter(Settings::kCamera_8port());
|
|
Settings::setCamera_9queueSize(queue);
|
|
ui_->toolBox->updateParameter(Settings::kCamera_9queueSize());
|
|
if(camera_->isRunning())
|
|
{
|
|
this->stopProcessing();
|
|
}
|
|
this->startProcessing();
|
|
}
|
|
}
|
|
}
|
|
ui_->actionCamera_from_directory_of_images->setChecked(false);
|
|
ui_->actionCamera_from_video_file->setChecked(false);
|
|
ui_->actionCamera_from_TCP_IP->setChecked(Settings::getCamera_6useTcpCamera());
|
|
}
|
|
|
|
void MainWindow::showObject(ObjWidget * obj)
|
|
{
|
|
if(obj)
|
|
{
|
|
obj->setGraphicsViewMode(false);
|
|
obj->setMirrorView(ui_->imageView_source->isMirrorView());
|
|
QList<ObjWidget*> objs = ui_->objects_area->findChildren<ObjWidget*>();
|
|
QVBoxLayout * vLayout = new QVBoxLayout();
|
|
ui_->toolBox->updateParameter(Settings::kGeneral_nextObjID());
|
|
|
|
QLabel * title = new QLabel(QString("%1 (%2)").arg(obj->id()).arg(obj->keypoints().size()), this);
|
|
QLabel * detectedLabel = new QLabel(this);
|
|
title->setObjectName(QString("%1title").arg(obj->id()));
|
|
detectedLabel->setObjectName(QString("%1detection").arg(obj->id()));
|
|
QHBoxLayout * hLayout = new QHBoxLayout();
|
|
hLayout->addWidget(title);
|
|
hLayout->addStretch(1);
|
|
hLayout->addStretch(1);
|
|
hLayout->addWidget(detectedLabel);
|
|
vLayout->addLayout(hLayout);
|
|
vLayout->addWidget(obj);
|
|
obj->setDeletable(true);
|
|
connect(obj, SIGNAL(removalTriggered(find_object::ObjWidget*)), this, SLOT(removeObject(find_object::ObjWidget*)));
|
|
connect(obj, SIGNAL(destroyed(QObject *)), title, SLOT(deleteLater()));
|
|
connect(obj, SIGNAL(destroyed(QObject *)), detectedLabel, SLOT(deleteLater()));
|
|
connect(obj, SIGNAL(destroyed(QObject *)), vLayout, SLOT(deleteLater()));
|
|
ui_->verticalLayout_objects->insertLayout(ui_->verticalLayout_objects->count()-1, vLayout);
|
|
|
|
QByteArray ba;
|
|
QBuffer buffer(&ba);
|
|
buffer.open(QIODevice::WriteOnly);
|
|
obj->pixmap().scaledToWidth(128).save(&buffer, "JPEG"); // writes image into JPEG format
|
|
imagesMap_.insert(obj->id(), ba);
|
|
|
|
// update objects size slider
|
|
int objectsPanelWidth = ui_->dockWidget_objects->width();
|
|
if(objectsPanelWidth > 0 && (obj->pixmap().width()*ui_->horizontalSlider_objectsSize->value()) / 100 > objectsPanelWidth)
|
|
{
|
|
ui_->horizontalSlider_objectsSize->setValue((objectsPanelWidth * 100) / obj->pixmap().width());
|
|
}
|
|
else
|
|
{
|
|
updateObjectSize(obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::updateObjects()
|
|
{
|
|
this->statusBar()->showMessage(tr("Updating %1 objects...").arg(findObject_->objects().size()));
|
|
QApplication::processEvents();
|
|
|
|
findObject_->updateObjects();
|
|
|
|
updateVocabulary();
|
|
|
|
QList<ObjSignature*> signatures = findObject_->objects().values();
|
|
for(int i=0; i<signatures.size(); ++i)
|
|
{
|
|
objWidgets_.value(signatures[i]->id())->setData(signatures[i]->keypoints(), cvtCvMat2QImage(signatures[i]->image()));
|
|
|
|
//update object labels
|
|
QLabel * title = qFindChild<QLabel*>(this, QString("%1title").arg(signatures[i]->id()));
|
|
title->setText(QString("%1 (%2)").arg(signatures[i]->id()).arg(QString::number(signatures[i]->keypoints().size())));
|
|
}
|
|
|
|
if(!camera_->isRunning() && !sceneImage_.empty())
|
|
{
|
|
this->update(sceneImage_);
|
|
}
|
|
this->statusBar()->clearMessage();
|
|
}
|
|
|
|
void MainWindow::updateVocabulary()
|
|
{
|
|
this->statusBar()->showMessage(tr("Updating vocabulary..."));
|
|
QApplication::processEvents();
|
|
|
|
QTime time;
|
|
time.start();
|
|
findObject_->updateVocabulary();
|
|
|
|
if(findObject_->vocabulary()->size())
|
|
{
|
|
ui_->label_timeIndexing->setNum(time.elapsed());
|
|
ui_->label_vocabularySize->setNum(findObject_->vocabulary()->size());
|
|
}
|
|
lastObjectsUpdateParameters_ = Settings::getParameters();
|
|
this->statusBar()->clearMessage();
|
|
}
|
|
|
|
void MainWindow::startProcessing()
|
|
{
|
|
UINFO("Starting camera...");
|
|
bool updateStatusMessage = this->statusBar()->currentMessage().isEmpty();
|
|
if(updateStatusMessage)
|
|
{
|
|
this->statusBar()->showMessage(tr("Starting camera..."));
|
|
}
|
|
if(camera_->start())
|
|
{
|
|
connect(camera_, SIGNAL(imageReceived(const cv::Mat &)), this, SLOT(update(const cv::Mat &)), Qt::UniqueConnection);
|
|
connect(camera_, SIGNAL(finished()), this, SLOT(stopProcessing()), Qt::UniqueConnection);
|
|
ui_->actionStop_camera->setEnabled(true);
|
|
ui_->actionPause_camera->setEnabled(true);
|
|
ui_->actionStart_camera->setEnabled(false);
|
|
ui_->actionLoad_scene_from_file->setEnabled(false);
|
|
ui_->actionCamera_from_directory_of_images->setEnabled(false);
|
|
ui_->actionCamera_from_video_file->setEnabled(false);
|
|
ui_->actionCamera_from_TCP_IP->setEnabled(false);
|
|
ui_->label_timeRefreshRate->setVisible(true);
|
|
|
|
//update control bar
|
|
ui_->pushButton_play->setVisible(false);
|
|
ui_->pushButton_pause->setVisible(true);
|
|
ui_->pushButton_stop->setEnabled(true);
|
|
int totalFrames = camera_->getTotalFrames();
|
|
if(totalFrames>0)
|
|
{
|
|
ui_->label_frame->setVisible(true);
|
|
ui_->horizontalSlider_frames->setEnabled(true);
|
|
ui_->horizontalSlider_frames->setMaximum(totalFrames-1);
|
|
}
|
|
|
|
//update camera port if TCP is used
|
|
ui_->label_port_image->setText("-");
|
|
if(Settings::getCamera_6useTcpCamera() && camera_->getPort())
|
|
{
|
|
ui_->label_port_image->setNum(camera_->getPort());
|
|
}
|
|
|
|
if(updateStatusMessage)
|
|
{
|
|
this->statusBar()->showMessage(tr("Camera started."), 2000);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(updateStatusMessage)
|
|
{
|
|
this->statusBar()->clearMessage();
|
|
}
|
|
|
|
if(Settings::getCamera_6useTcpCamera())
|
|
{
|
|
QMessageBox::critical(this, tr("Camera error"), tr("Camera initialization failed! (with port %1)").arg(Settings::getCamera_8port()));
|
|
}
|
|
else
|
|
{
|
|
QMessageBox::critical(this, tr("Camera error"), tr("Camera initialization failed! (with device %1)").arg(Settings::getCamera_1deviceId()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::stopProcessing()
|
|
{
|
|
if(camera_)
|
|
{
|
|
disconnect(camera_, SIGNAL(imageReceived(const cv::Mat &)), this, SLOT(update(const cv::Mat &)));
|
|
disconnect(camera_, SIGNAL(finished()), this, SLOT(stopProcessing()));
|
|
camera_->stop();
|
|
}
|
|
ui_->actionStop_camera->setEnabled(false);
|
|
ui_->actionPause_camera->setEnabled(false);
|
|
ui_->actionStart_camera->setEnabled(true);
|
|
ui_->actionLoad_scene_from_file->setEnabled(true);
|
|
ui_->actionCamera_from_directory_of_images->setEnabled(true);
|
|
ui_->actionCamera_from_video_file->setEnabled(true);
|
|
ui_->actionCamera_from_TCP_IP->setEnabled(true);
|
|
ui_->pushButton_play->setVisible(true);
|
|
ui_->pushButton_pause->setVisible(false);
|
|
ui_->pushButton_stop->setEnabled(false);
|
|
ui_->horizontalSlider_frames->setEnabled(false);
|
|
ui_->horizontalSlider_frames->setValue(0);
|
|
ui_->label_frame->setVisible(false);
|
|
ui_->label_port_image->setText("-");
|
|
}
|
|
|
|
void MainWindow::pauseProcessing()
|
|
{
|
|
ui_->actionStop_camera->setEnabled(true);
|
|
ui_->actionPause_camera->setEnabled(true);
|
|
ui_->actionStart_camera->setEnabled(false);
|
|
if(camera_->isRunning())
|
|
{
|
|
ui_->pushButton_play->setVisible(true);
|
|
ui_->pushButton_pause->setVisible(false);
|
|
camera_->pause();
|
|
}
|
|
else
|
|
{
|
|
ui_->pushButton_play->setVisible(false);
|
|
ui_->pushButton_pause->setVisible(true);
|
|
camera_->start();
|
|
}
|
|
}
|
|
|
|
void MainWindow::moveCameraFrame(int frame)
|
|
{
|
|
if(ui_->horizontalSlider_frames->isEnabled())
|
|
{
|
|
camera_->moveToFrame(frame);
|
|
if(!camera_->isRunning())
|
|
{
|
|
camera_->takeImage();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::rectHovered(int objId)
|
|
{
|
|
if(objId>=0 && Settings::getGeneral_autoScroll())
|
|
{
|
|
QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1title").arg(objId));
|
|
if(label)
|
|
{
|
|
ui_->objects_area->verticalScrollBar()->setValue(label->pos().y());
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::update(const cv::Mat & image)
|
|
{
|
|
if(image.empty())
|
|
{
|
|
UWARN("The image received is empty...");
|
|
return;
|
|
}
|
|
sceneImage_ = image.clone();
|
|
|
|
// reset objects color
|
|
for(QMap<int, ObjWidget*>::iterator iter=objWidgets_.begin(); iter!=objWidgets_.end(); ++iter)
|
|
{
|
|
iter.value()->resetKptsColor();
|
|
}
|
|
|
|
QTime guiRefreshTime;
|
|
|
|
DetectionInfo info;
|
|
if(findObject_->detect(sceneImage_, info))
|
|
{
|
|
guiRefreshTime.start();
|
|
ui_->label_timeDetection->setNum(info.timeStamps_.value(DetectionInfo::kTimeKeypointDetection, 0));
|
|
ui_->label_timeSkewAffine->setNum(info.timeStamps_.value(DetectionInfo::kTimeSkewAffine, 0));
|
|
ui_->label_timeExtraction->setNum(info.timeStamps_.value(DetectionInfo::kTimeDescriptorExtraction, 0));
|
|
ui_->imageView_source->setData(info.sceneKeypoints_, cvtCvMat2QImage(sceneImage_));
|
|
if(!findObject_->vocabulary()->size())
|
|
{
|
|
ui_->label_timeIndexing->setNum(info.timeStamps_.value(DetectionInfo::kTimeIndexing, 0));
|
|
}
|
|
ui_->label_timeMatching->setNum(info.timeStamps_.value(DetectionInfo::kTimeMatching, 0));
|
|
ui_->label_timeHomographies->setNum(info.timeStamps_.value(DetectionInfo::kTimeHomography, 0));
|
|
|
|
ui_->label_vocabularySize->setNum(findObject_->vocabulary()->size());
|
|
|
|
// Colorize features matched
|
|
const QMap<int, QMultiMap<int, int> > & matches = info.matches_;
|
|
QMap<int, int> scores;
|
|
int maxScoreId = -1;
|
|
int maxScore = 0;
|
|
for(QMap<int, QMultiMap<int, int> >::const_iterator jter=matches.constBegin(); jter!=matches.end();++jter)
|
|
{
|
|
scores.insert(jter.key(), jter.value().size());
|
|
if(maxScoreId == -1 || maxScore < jter.value().size())
|
|
{
|
|
maxScoreId = jter.key();
|
|
maxScore = jter.value().size();
|
|
}
|
|
|
|
int id = jter.key();
|
|
QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1detection").arg(id));
|
|
if(!Settings::getHomography_homographyComputed())
|
|
{
|
|
label->setText(QString("%1 matches").arg(jter.value().size()));
|
|
|
|
ObjWidget * obj = objWidgets_.value(id);
|
|
Q_ASSERT(obj != 0);
|
|
|
|
for(QMultiMap<int, int>::const_iterator iter = jter.value().constBegin(); iter!= jter.value().constEnd(); ++iter)
|
|
{
|
|
obj->setKptColor(iter.key(), obj->color());
|
|
ui_->imageView_source->setKptColor(iter.value(), obj->color());
|
|
}
|
|
}
|
|
else if(!info.objDetected_.contains(id))
|
|
{
|
|
// Homography could not be computed...
|
|
QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1detection").arg(id));
|
|
QMultiMap<int, int> rejectedInliers = info.rejectedInliers_.value(id);
|
|
QMultiMap<int, int> rejectedOutliers = info.rejectedOutliers_.value(id);
|
|
int rejectedCode = info.rejectedCodes_.value(id);
|
|
if(rejectedCode == DetectionInfo::kRejectedLowMatches)
|
|
{
|
|
label->setText(QString("Too low matches (%1)").arg(jter.value().size()));
|
|
}
|
|
else if(rejectedCode == DetectionInfo::kRejectedAllInliers)
|
|
{
|
|
label->setText(QString("Ignored, all inliers (%1 in %2 out)").arg(rejectedInliers.size()).arg(rejectedOutliers.size()));
|
|
}
|
|
else if(rejectedCode == DetectionInfo::kRejectedNotValid)
|
|
{
|
|
label->setText(QString("Not valid homography (%1 in %2 out)").arg(rejectedInliers.size()).arg(rejectedOutliers.size()));
|
|
}
|
|
else if(rejectedCode == DetectionInfo::kRejectedLowInliers)
|
|
{
|
|
label->setText(QString("Too low inliers (%1 in %2 out)").arg(rejectedInliers.size()).arg(rejectedOutliers.size()));
|
|
}
|
|
else if(rejectedCode == DetectionInfo::kRejectedCornersOutside)
|
|
{
|
|
label->setText(QString("Corners not visible (%1 in %2 out)").arg(rejectedInliers.size()).arg(rejectedOutliers.size()));
|
|
}
|
|
else if(rejectedCode == DetectionInfo::kRejectedByAngle)
|
|
{
|
|
label->setText(QString("Angle too small (%1 in %2 out)").arg(rejectedInliers.size()).arg(rejectedOutliers.size()));
|
|
}
|
|
}
|
|
}
|
|
|
|
if(camera_->isRunning() && Settings::getGeneral_autoPauseOnDetection() && info.objDetected_.size())
|
|
{
|
|
this->pauseProcessing();
|
|
}
|
|
|
|
// Add homography rectangles when homographies are computed
|
|
QMultiMap<int, QMultiMap<int,int> >::const_iterator inliersIter = info.objDetectedInliers_.constBegin();
|
|
QMultiMap<int, QMultiMap<int,int> >::const_iterator outliersIter = info.objDetectedOutliers_.constBegin();
|
|
for(QMultiMap<int,QTransform>::iterator iter = info.objDetected_.begin();
|
|
iter!=info.objDetected_.end();
|
|
++iter, ++inliersIter, ++outliersIter)
|
|
{
|
|
int id = iter.key();
|
|
ObjWidget * obj = objWidgets_.value(id);
|
|
Q_ASSERT(obj != 0);
|
|
|
|
// COLORIZE (should be done in the GUI thread)
|
|
QTransform hTransform = iter.value();
|
|
|
|
QRect rect = obj->pixmap().rect();
|
|
// add rectangle
|
|
QPen rectPen(obj->color());
|
|
rectPen.setWidth(Settings::getHomography_rectBorderWidth());
|
|
RectItem * rectItemScene = new RectItem(id, rect);
|
|
connect(rectItemScene, SIGNAL(hovered(int)), this, SLOT(rectHovered(int)));
|
|
rectItemScene->setPen(rectPen);
|
|
rectItemScene->setTransform(hTransform);
|
|
ui_->imageView_source->addRect(rectItemScene);
|
|
|
|
QGraphicsRectItem * rectItemObj = new QGraphicsRectItem(rect);
|
|
rectItemObj->setPen(rectPen);
|
|
obj->addRect(rectItemObj);
|
|
|
|
for(QMultiMap<int, int>::const_iterator iter = inliersIter.value().constBegin(); iter!= inliersIter.value().constEnd(); ++iter)
|
|
{
|
|
obj->setKptColor(iter.key(), obj->color());
|
|
ui_->imageView_source->setKptColor(iter.value(), obj->color());
|
|
}
|
|
|
|
QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1detection").arg(id));
|
|
if(info.objDetected_.count(id) > 1)
|
|
{
|
|
// if a homography is already found, set the objects count
|
|
label->setText(QString("%1 objects found").arg(info.objDetected_.count(id)));
|
|
}
|
|
else
|
|
{
|
|
label->setText(QString("%1 in %2 out").arg(inliersIter.value().size()).arg(outliersIter.value().size()));
|
|
}
|
|
}
|
|
|
|
|
|
//update likelihood plot
|
|
likelihoodCurve_->setData(scores, QMap<int, int>());
|
|
QMap<int, int> inlierScores;
|
|
for(QMap<int, int>::iterator iter=scores.begin(); iter!=scores.end(); ++iter)
|
|
{
|
|
QList<QMultiMap<int, int> > values = info.objDetectedInliers_.values(iter.key());
|
|
int maxValue = 0;
|
|
if(values.size())
|
|
{
|
|
maxValue = values[0].size();
|
|
for(int i=1; i<values.size(); ++i)
|
|
{
|
|
if(maxValue < values[i].size())
|
|
{
|
|
maxValue = values[i].size();
|
|
}
|
|
}
|
|
}
|
|
inlierScores.insert(iter.key(), maxValue);
|
|
}
|
|
inliersCurve_->setData(inlierScores, QMap<int, int>());
|
|
if(ui_->likelihoodPlot->isVisible())
|
|
{
|
|
ui_->likelihoodPlot->update();
|
|
}
|
|
|
|
ui_->label_minMatchedDistance->setNum(info.minMatchedDistance_);
|
|
ui_->label_maxMatchedDistance->setNum(info.maxMatchedDistance_);
|
|
|
|
//Scroll objects slider to the best score
|
|
if(maxScoreId>=0 && Settings::getGeneral_autoScroll())
|
|
{
|
|
QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1title").arg(maxScoreId));
|
|
if(label)
|
|
{
|
|
ui_->objects_area->verticalScrollBar()->setValue(label->pos().y());
|
|
}
|
|
}
|
|
|
|
// Emit homographies
|
|
if(info.objDetected_.size() > 1)
|
|
{
|
|
UINFO("(%s) %d objects detected!",
|
|
QTime::currentTime().toString("HH:mm:ss.zzz").toStdString().c_str(),
|
|
(int)info.objDetected_.size());
|
|
}
|
|
else if(info.objDetected_.size() == 1)
|
|
{
|
|
UINFO("(%s) Object %d detected!",
|
|
QTime::currentTime().toString("HH:mm:ss.zzz").toStdString().c_str(),
|
|
(int)info.objDetected_.begin().key());
|
|
}
|
|
else if(Settings::getGeneral_sendNoObjDetectedEvents())
|
|
{
|
|
UINFO("(%s) No objects detected.",
|
|
QTime::currentTime().toString("HH:mm:ss.zzz").toStdString().c_str());
|
|
}
|
|
|
|
if(info.objDetected_.size() > 0 || Settings::getGeneral_sendNoObjDetectedEvents())
|
|
{
|
|
Q_EMIT objectsFound(info);
|
|
}
|
|
ui_->label_objectsDetected->setNum(info.objDetected_.size());
|
|
}
|
|
else
|
|
{
|
|
guiRefreshTime.start();
|
|
|
|
if(findObject_->vocabulary()->size())
|
|
{
|
|
this->statusBar()->showMessage(tr("Cannot search, objects must be updated!"));
|
|
}
|
|
ui_->label_timeDetection->setNum(info.timeStamps_.value(DetectionInfo::kTimeKeypointDetection, 0));
|
|
ui_->label_timeSkewAffine->setNum(info.timeStamps_.value(DetectionInfo::kTimeSkewAffine, 0));
|
|
ui_->label_timeExtraction->setNum(info.timeStamps_.value(DetectionInfo::kTimeDescriptorExtraction, 0));
|
|
ui_->imageView_source->setData(info.sceneKeypoints_, cvtCvMat2QImage(sceneImage_));
|
|
}
|
|
|
|
|
|
//Update object pictures
|
|
for(QMap<int, ObjWidget*>::iterator iter=objWidgets_.begin(); iter!=objWidgets_.end(); ++iter)
|
|
{
|
|
iter.value()->update();
|
|
}
|
|
|
|
ui_->label_nfeatures->setNum((int)info.sceneKeypoints_.size());
|
|
ui_->imageView_source->update();
|
|
|
|
ui_->label_detectorDescriptorType->setText(QString("%1/%2").arg(Settings::currentDetectorType()).arg(Settings::currentDescriptorType()));
|
|
|
|
//update slider
|
|
if(ui_->horizontalSlider_frames->isEnabled())
|
|
{
|
|
ui_->horizontalSlider_frames->blockSignals(true);
|
|
ui_->horizontalSlider_frames->setValue(camera_->getCurrentFrameIndex()-1);
|
|
ui_->label_frame->setNum(camera_->getCurrentFrameIndex()-1);
|
|
ui_->horizontalSlider_frames->blockSignals(false);
|
|
}
|
|
|
|
ui_->label_timeTotal->setNum(info.timeStamps_.value(DetectionInfo::kTimeTotal, 0));
|
|
int refreshRate = qRound(1000.0f/float(updateRate_.restart()));
|
|
if(refreshRate > 0 && refreshRate < lowestRefreshRate_)
|
|
{
|
|
lowestRefreshRate_ = refreshRate;
|
|
}
|
|
// Refresh the label only after each 1000 ms
|
|
if(refreshStartTime_.elapsed() > 1000)
|
|
{
|
|
if(Settings::getCamera_4imageRate()>0.0)
|
|
{
|
|
ui_->label_timeRefreshRate->setText(QString("(%1 Hz - %2 Hz)").arg(QString::number(Settings::getCamera_4imageRate())).arg(QString::number(lowestRefreshRate_)));
|
|
}
|
|
else
|
|
{
|
|
ui_->label_timeRefreshRate->setText(QString("(%2 Hz)").arg(QString::number(lowestRefreshRate_)));
|
|
}
|
|
lowestRefreshRate_ = 99;
|
|
refreshStartTime_.start();
|
|
}
|
|
|
|
ui_->label_timeRefreshGUI->setNum(guiRefreshTime.elapsed());
|
|
}
|
|
|
|
void MainWindow::notifyParametersChanged(const QStringList & paramChanged)
|
|
{
|
|
//Selective update (to not update all objects for a simple camera's parameter modification)
|
|
bool detectorDescriptorParamsChanged = false;
|
|
bool nearestNeighborParamsChanged = false;
|
|
bool parameterChanged = false;
|
|
for(QStringList::const_iterator iter = paramChanged.begin(); iter!=paramChanged.end(); ++iter)
|
|
{
|
|
if(lastObjectsUpdateParameters_.value(*iter) != Settings::getParameter(*iter))
|
|
{
|
|
lastObjectsUpdateParameters_[*iter] = Settings::getParameter(*iter);
|
|
parameterChanged = true;
|
|
UINFO("Parameter changed: %s -> \"%s\"", iter->toStdString().c_str(), Settings::getParameter(*iter).toString().toStdString().c_str());
|
|
|
|
if(iter->contains("Feature2D"))
|
|
{
|
|
detectorDescriptorParamsChanged = true;
|
|
}
|
|
else if( (iter->contains("NearestNeighbor") && Settings::getGeneral_invertedSearch()) ||
|
|
iter->compare(Settings::kGeneral_invertedSearch()) == 0 ||
|
|
(iter->compare(Settings::kGeneral_vocabularyIncremental()) == 0 && Settings::getGeneral_invertedSearch()) ||
|
|
(iter->compare(Settings::kGeneral_threads()) == 0 && !Settings::getGeneral_invertedSearch()) )
|
|
{
|
|
nearestNeighborParamsChanged = true;
|
|
}
|
|
|
|
if(iter->compare(Settings::kGeneral_port()) == 0 &&
|
|
Settings::getGeneral_port() != ui_->label_port->text().toInt() &&
|
|
Settings::getGeneral_port() != 0)
|
|
{
|
|
setupTCPServer();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(detectorDescriptorParamsChanged)
|
|
{
|
|
//Re-init detector and extractor
|
|
findObject_->updateDetectorExtractor();
|
|
}
|
|
|
|
if(Settings::getGeneral_autoUpdateObjects())
|
|
{
|
|
if(detectorDescriptorParamsChanged)
|
|
{
|
|
this->updateObjects();
|
|
}
|
|
else if(nearestNeighborParamsChanged)
|
|
{
|
|
this->updateVocabulary();
|
|
}
|
|
}
|
|
else if(objWidgets_.size() && (detectorDescriptorParamsChanged || nearestNeighborParamsChanged))
|
|
{
|
|
this->statusBar()->showMessage(tr("A parameter has changed... \"Update objects\" may be required."));
|
|
}
|
|
if(parameterChanged && !camera_->isRunning() && !sceneImage_.empty())
|
|
{
|
|
this->update(sceneImage_);
|
|
ui_->label_timeRefreshRate->setVisible(false);
|
|
}
|
|
|
|
ui_->actionCamera_from_video_file->setChecked(!Settings::getCamera_5mediaPath().isEmpty() && !UDirectory::exists(Settings::getCamera_5mediaPath().toStdString()) && !Settings::getCamera_6useTcpCamera());
|
|
ui_->actionCamera_from_directory_of_images->setChecked(!Settings::getCamera_5mediaPath().isEmpty() && UDirectory::exists(Settings::getCamera_5mediaPath().toStdString()) && !Settings::getCamera_6useTcpCamera());
|
|
ui_->actionCamera_from_TCP_IP->setChecked(Settings::getCamera_6useTcpCamera());
|
|
}
|
|
|
|
} // namespace find_object
|