Modified parameters' name to reduce the number of pages in the parameters toolbox.

Added a way to sort (with a prefix number) parameters in toolbox. 
Can now switch between nearest neighbor strategies (kd-trees, k-means, linear).
Added parameters for all FLANN nearest neighbor approaches.
Added likelihood plot (including UPlot from UtiLite library and PdfPlot from RTAB-Map library).
Added general parameter "MirrorView".
Added general parameter "InvertedSearch" to create the  vocabulary from objects descriptor instead of the scene descriptors. We can then compute likelihood of the current scene with all other objects (or another scenes). 
Fixed a crash (in AddObjectDialog) when selecting outside the image.
Added OpenCV version used in AboutDialog.

git-svn-id: http://find-object.googlecode.com/svn/trunk/find_object@109 620bd6b2-0a58-f614-fd9a-1bd335dccda9
This commit is contained in:
matlabbe 2012-08-28 13:44:57 +00:00
parent f6eff0e206
commit e3b15a7106
17 changed files with 4453 additions and 478 deletions

View File

@ -8,6 +8,8 @@ SET(headers_ui
../src/Camera.h
../src/ParametersToolBox.h
../src/AboutDialog.h
../src/utilite/UPlot.h
../src/rtabmap/PdfPlot.h
)
SET(uis
@ -42,6 +44,8 @@ SET(SRC_FILES
../src/Settings.cpp
../src/ObjWidget.cpp
../src/AboutDialog.cpp
../src/utilite/UPlot.cpp
../src/rtabmap/PdfPlot.cpp
${moc_srcs}
${moc_uis}
${srcs_qrc}

View File

@ -4,6 +4,7 @@
#include "AboutDialog.h"
#include "ui_aboutDialog.h"
#include <opencv2/core/version.hpp>
AboutDialog::AboutDialog(QWidget * parent) :
QDialog(parent)
@ -11,6 +12,7 @@ AboutDialog::AboutDialog(QWidget * parent) :
ui_ = new Ui_aboutDialog();
ui_->setupUi(this);
ui_->label_version->setText(PROJECT_VERSION);
ui_->label_version_opencv->setText(CV_VERSION);
}
AboutDialog::~AboutDialog()

View File

@ -91,6 +91,37 @@ void AddObjectDialog::updateNextButton()
void AddObjectDialog::updateNextButton(const QRect & rect)
{
roi_ = rect;
if(roi_.isValid() && ui_->cameraView->cvImage().cols)
{
//clip roi
if( roi_.x() >= ui_->cameraView->cvImage().cols ||
roi_.x()+roi_.width() <= 0 ||
roi_.y() >= ui_->cameraView->cvImage().rows ||
roi_.y()+roi_.height() <= 0)
{
//Not valid...
roi_ = QRect();
}
else
{
if(roi_.x() < 0)
{
roi_.setX(0);
}
if(roi_.x() + roi_.width() > ui_->cameraView->cvImage().cols)
{
roi_.setWidth(ui_->cameraView->cvImage().cols - roi_.x());
}
if(roi_.y() < 0)
{
roi_.setY(0);
}
if(roi_.y() + roi_.height() > ui_->cameraView->cvImage().rows)
{
roi_.setHeight(ui_->cameraView->cvImage().rows - roi_.y());
}
}
}
if(state_ == kSelectFeatures)
{
if(ui_->comboBox_selection->currentIndex() == 1)

View File

@ -44,13 +44,13 @@ void Camera::takeImage()
else
{
//resize
if( Settings::getCamera_imageWidth() &&
Settings::getCamera_imageHeight() &&
Settings::getCamera_imageWidth() != img.cols &&
Settings::getCamera_imageHeight() != img.rows)
if( Settings::getCamera_2imageWidth() &&
Settings::getCamera_3imageHeight() &&
Settings::getCamera_2imageWidth() != img.cols &&
Settings::getCamera_3imageHeight() != img.rows)
{
cv::Mat resampled;
cv::resize(img, resampled, cv::Size(Settings::getCamera_imageWidth(), Settings::getCamera_imageHeight()));
cv::resize(img, resampled, cv::Size(Settings::getCamera_2imageWidth(), Settings::getCamera_3imageHeight()));
emit imageReceived(resampled);
}
else
@ -65,7 +65,7 @@ bool Camera::start()
{
if(!capture_.isOpened())
{
QString videoFile = Settings::getCamera_videoFilePath();
QString videoFile = Settings::getCamera_5videoFilePath();
if(!videoFile.isEmpty())
{
capture_.open(videoFile.toStdString().c_str());
@ -77,11 +77,11 @@ bool Camera::start()
if(!capture_.isOpened())
{
//set camera device
capture_.open(Settings::getCamera_deviceId());
if(Settings::getCamera_imageWidth() && Settings::getCamera_imageHeight())
capture_.open(Settings::getCamera_1deviceId());
if(Settings::getCamera_2imageWidth() && Settings::getCamera_3imageHeight())
{
capture_.set(CV_CAP_PROP_FRAME_WIDTH, double(Settings::getCamera_imageWidth()));
capture_.set(CV_CAP_PROP_FRAME_HEIGHT, double(Settings::getCamera_imageHeight()));
capture_.set(CV_CAP_PROP_FRAME_WIDTH, double(Settings::getCamera_2imageWidth()));
capture_.set(CV_CAP_PROP_FRAME_HEIGHT, double(Settings::getCamera_3imageHeight()));
}
}
}
@ -108,9 +108,9 @@ void Camera::stopTimer()
void Camera::updateImageRate()
{
if(Settings::getCamera_imageRate())
if(Settings::getCamera_4imageRate())
{
cameraTimer_.setInterval(1000/Settings::getCamera_imageRate());
cameraTimer_.setInterval(1000/Settings::getCamera_4imageRate());
}
else
{

View File

@ -12,6 +12,7 @@
#include "Settings.h"
#include "ParametersToolBox.h"
#include "AboutDialog.h"
#include "rtabmap/PdfPlot.h"
#include <iostream>
#include <stdio.h>
@ -20,6 +21,7 @@
#include <QtCore/QTextStream>
#include <QtCore/QFile>
#include <QtCore/QBuffer>
#include <QtGui/QFileDialog>
#include <QtGui/QMessageBox>
@ -29,11 +31,13 @@
#include <QtGui/QStatusBar>
#include <QtGui/QProgressDialog>
#include <QtGui/QCloseEvent>
#include <QtGui/QCheckBox>
// Camera ownership transferred
MainWindow::MainWindow(Camera * camera, QWidget * parent) :
QMainWindow(parent),
camera_(camera),
likelihoodCurve_(0),
lowestRefreshRate_(99),
objectsModified_(false)
{
@ -42,6 +46,10 @@ MainWindow::MainWindow(Camera * camera, QWidget * parent) :
aboutDialog_ = new AboutDialog(this);
this->setStatusBar(new QStatusBar());
likelihoodCurve_ = new rtabmap::PdfPlotCurve("Likelihood", &imagesMap_);
ui_->likelihoodPlot->addCurve(likelihoodCurve_); // ownership transfered
ui_->likelihoodPlot->setGraphicsView(true);
if(!camera_)
{
camera_ = new Camera(this);
@ -51,27 +59,37 @@ MainWindow::MainWindow(Camera * camera, QWidget * parent) :
camera_->setParent(this);
}
ui_->dockWidget_parameters->setVisible(false);
ui_->dockWidget_plot->setVisible(false);
QByteArray geometry;
Settings::loadSettings(Settings::iniDefaultPath(), &geometry);
QByteArray state;
Settings::loadSettings(Settings::iniDefaultPath(), &geometry, &state);
this->restoreGeometry(geometry);
this->restoreState(state);
ui_->toolBox->setupUi();
connect((QSpinBox*)ui_->toolBox->getParameterWidget(Settings::kCamera_imageRate()),
connect((QSpinBox*)ui_->toolBox->getParameterWidget(Settings::kCamera_4imageRate()),
SIGNAL(editingFinished()),
camera_,
SLOT(updateImageRate()));
ui_->dockWidget_parameters->hide();
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()), this, SLOT(notifyParametersChanged()));
ui_->imageView_source->setGraphicsViewMode(false);
ui_->imageView_source->setTextLabel(tr("Press \"space\" to start camera..."));
ui_->imageView_source->setMirrorView(Settings::getGeneral_mirrorView());
connect((QCheckBox*)ui_->toolBox->getParameterWidget(Settings::kGeneral_mirrorView()),
SIGNAL(stateChanged(int)),
this,
SLOT(updateMirrorView()));
//reset button
//buttons
connect(ui_->pushButton_restoreDefaults, SIGNAL(clicked()), ui_->toolBox, SLOT(resetCurrentPage()));
// udpate objects button
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);
@ -100,7 +118,7 @@ MainWindow::MainWindow(Camera * camera, QWidget * parent) :
ui_->actionPause_camera->setShortcut(Qt::Key_Space);
ui_->actionCamera_from_video_file->setCheckable(true);
ui_->actionCamera_from_video_file->setChecked(!Settings::getCamera_videoFilePath().isEmpty());
ui_->actionCamera_from_video_file->setChecked(!Settings::getCamera_5videoFilePath().isEmpty());
if(Settings::getGeneral_autoStartCamera())
{
@ -113,7 +131,7 @@ MainWindow::~MainWindow()
{
disconnect(camera_, SIGNAL(imageReceived(const cv::Mat &)), this, SLOT(update(const cv::Mat &)));
camera_->stop();
dataTree_ = cv::Mat();
objectsDescriptors_ = cv::Mat();
qDeleteAll(objects_.begin(), objects_.end());
objects_.clear();
delete ui_;
@ -141,7 +159,7 @@ void MainWindow::closeEvent(QCloseEvent * event)
}
if(quit)
{
Settings::saveSettings(Settings::iniDefaultPath(), this->saveGeometry());
Settings::saveSettings(Settings::iniDefaultPath(), this->saveGeometry(), this->saveState());
event->accept();
}
else
@ -240,6 +258,42 @@ void MainWindow::removeAllObjects()
}
}
void MainWindow::updateObjectsSize()
{
for(int i=0; i<objects_.size(); ++i)
{
updateObjectSize(objects_[i]);
}
}
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);
}
obj->setFeaturesShown(value<=50?false:true);
}
}
void MainWindow::updateMirrorView()
{
bool mirrorView = Settings::getGeneral_mirrorView();
ui_->imageView_source->setMirrorView(mirrorView);
for(int i=0; i<objects_.size(); ++i)
{
objects_[i]->setMirrorView(mirrorView);
}
}
void MainWindow::addObjectFromScene()
{
disconnect(camera_, SIGNAL(imageReceived(const cv::Mat &)), this, SLOT(update(const cv::Mat &)));
@ -353,16 +407,16 @@ void MainWindow::setupCameraFromVideoFile()
{
if(!ui_->actionCamera_from_video_file->isChecked())
{
Settings::setCamera_videoFilePath("");
ui_->toolBox->updateParameter(Settings::kCamera_videoFilePath());
Settings::setCamera_5videoFilePath("");
ui_->toolBox->updateParameter(Settings::kCamera_5videoFilePath());
}
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_videoFilePath(fileName);
ui_->toolBox->updateParameter(Settings::kCamera_videoFilePath());
Settings::setCamera_5videoFilePath(fileName);
ui_->toolBox->updateParameter(Settings::kCamera_5videoFilePath());
if(camera_->isRunning())
{
this->stopProcessing();
@ -370,7 +424,7 @@ void MainWindow::setupCameraFromVideoFile()
}
}
}
ui_->actionCamera_from_video_file->setChecked(!Settings::getCamera_videoFilePath().isEmpty());
ui_->actionCamera_from_video_file->setChecked(!Settings::getCamera_5videoFilePath().isEmpty());
}
void MainWindow::showObject(ObjWidget * obj)
@ -381,7 +435,6 @@ void MainWindow::showObject(ObjWidget * obj)
obj->setMirrorView(ui_->imageView_source->isMirrorView());
QList<ObjWidget*> objs = ui_->objects_area->findChildren<ObjWidget*>();
QVBoxLayout * vLayout = new QVBoxLayout();
obj->setMinimumSize(obj->pixmap().width(), obj->pixmap().height());
int id = Settings::getGeneral_nextObjID();
if(obj->id() == 0)
{
@ -403,9 +456,10 @@ void MainWindow::showObject(ObjWidget * obj)
detectedLabel->setObjectName(QString("%1detection").arg(obj->id()));
QHBoxLayout * hLayout = new QHBoxLayout();
hLayout->addWidget(title);
hLayout->addWidget(detectorDescriptorType);
hLayout->addWidget(detectedLabel);
hLayout->addStretch(1);
hLayout->addWidget(detectorDescriptorType);
hLayout->addStretch(1);
hLayout->addWidget(detectedLabel);
vLayout->addLayout(hLayout);
vLayout->addWidget(obj);
objects_.last()->setDeletable(true);
@ -415,6 +469,14 @@ void MainWindow::showObject(ObjWidget * obj)
connect(obj, SIGNAL(destroyed(QObject *)), detectorDescriptorType, 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);
updateObjectSize(obj);
}
}
@ -473,7 +535,7 @@ void MainWindow::updateData()
ui_->actionSave_objects->setEnabled(false);
}
dataTree_ = cv::Mat();
objectsDescriptors_ = cv::Mat();
dataRange_.clear();
int count = 0;
int dim = -1;
@ -489,7 +551,7 @@ void MainWindow::updateData()
}
else
{
printf("ERROR: Descriptors of the objects are not all the same size! Objects opened must have all the same size (and from the same descriptor extractor).");
printf("ERROR: Descriptors of the objects are not all the same size! Objects opened must have all the same size (and from the same descriptor extractor).\n");
}
return;
}
@ -502,7 +564,7 @@ void MainWindow::updateData()
}
else
{
printf("ERROR: Descriptors of the objects are not all the same type! Objects opened must have been processed by the same descriptor extractor.");
printf("ERROR: Descriptors of the objects are not all the same type! Objects opened must have been processed by the same descriptor extractor.\n");
}
return;
}
@ -513,17 +575,32 @@ void MainWindow::updateData()
// Copy data
if(count)
{
cv::Mat data(count, dim, type);
objectsDescriptors_ = cv::Mat(count, dim, type);
printf("Total descriptors=%d, dim=%d, type=%d\n",count, dim, type);
int row = 0;
for(int i=0; i<objects_.size(); ++i)
{
cv::Mat dest(data, cv::Range(row, row+objects_.at(i)->descriptors().rows));
cv::Mat dest(objectsDescriptors_, cv::Range(row, row+objects_.at(i)->descriptors().rows));
objects_.at(i)->descriptors().copyTo(dest);
row += objects_.at(i)->descriptors().rows;
dataRange_.append(row);
// dataRange contains the upper_bound for each
// object (the last descriptors position in the
// global object descriptors matrix)
if(objects_.at(i)->descriptors().rows)
{
dataRange_.insert(row-1, i);
}
}
if(Settings::getGeneral_invertedSearch())
{
// CREATE INDEX
QTime time;
time.start();
cv::flann::IndexParams * params = Settings::createFlannIndexParams();
flannIndex_.build(objectsDescriptors_, *params, Settings::getFlannDistanceType());
delete params;
ui_->label_timeIndexing->setNum(time.restart());
}
data.convertTo(dataTree_, CV_32F);
}
}
@ -555,11 +632,11 @@ void MainWindow::startProcessing()
}
if(this->isVisible())
{
QMessageBox::critical(this, tr("Camera error"), tr("Camera initialization failed! (with device %1)").arg(Settings::getCamera_deviceId()));
QMessageBox::critical(this, tr("Camera error"), tr("Camera initialization failed! (with device %1)").arg(Settings::getCamera_1deviceId()));
}
else
{
printf("ERROR: Camera initialization failed! (with device %d)", Settings::getCamera_deviceId());
printf("ERROR: Camera initialization failed! (with device %d)", Settings::getCamera_1deviceId());
}
}
}
@ -594,6 +671,7 @@ void MainWindow::pauseProcessing()
void MainWindow::update(const cv::Mat & image)
{
updateRate_.start();
// reset objects color
for(int i=0; i<objects_.size(); ++i)
{
@ -629,7 +707,7 @@ void MainWindow::update(const cv::Mat & image)
std::vector<cv::KeyPoint> keypoints;
detector->detect(img, keypoints);
delete detector;
ui_->label_timeDetection->setText(QString::number(time.restart()));
ui_->label_timeDetection->setNum(time.restart());
cv::Mat descriptors;
if(keypoints.size())
@ -646,62 +724,77 @@ void MainWindow::update(const cv::Mat & image)
{
cvReleaseImage(&imageGrayScale);
}
ui_->label_timeExtraction->setText(QString::number(time.restart()));
ui_->label_timeExtraction->setNum(time.restart());
}
else
{
printf("WARNING: no features detected !?!\n");
ui_->label_timeExtraction->setText(QString::number(0));
ui_->label_timeExtraction->setNum(0);
}
// COMPARE
if(!dataTree_.empty() && keypoints.size() && (Settings::getNearestNeighbor_nndrRatioUsed() || Settings::getNearestNeighbor_minDistanceUsed()))
if(!objectsDescriptors_.empty() &&
keypoints.size() &&
(Settings::getNearestNeighbor_3nndrRatioUsed() || Settings::getNearestNeighbor_5minDistanceUsed()) &&
objectsDescriptors_.type() == descriptors.type()) // binary descriptor issue, if the dataTree is not yet updated with modified settings
{
// CREATE INDEX
cv::Mat environment(descriptors.rows, descriptors.cols, CV_32F);
descriptors.convertTo(environment, CV_32F);
cv::flann::Index treeFlannIndex(environment, cv::flann::KDTreeIndexParams());
ui_->label_timeIndexing->setText(QString::number(time.restart()));
if(!Settings::getGeneral_invertedSearch())
{
// CREATE INDEX
cv::flann::IndexParams * params = Settings::createFlannIndexParams();
flannIndex_.build(descriptors, *params, Settings::getFlannDistanceType());
delete params;
ui_->label_timeIndexing->setNum(time.restart());
}
// DO NEAREST NEIGHBOR
int k = 1;
if(Settings::getNearestNeighbor_nndrRatioUsed())
if(Settings::getNearestNeighbor_3nndrRatioUsed())
{
k = 2;
}
int emax = 64;
cv::Mat results(dataTree_.rows, k, CV_32SC1); // results index
cv::Mat dists(dataTree_.rows, k, CV_32FC1); // Distance results are CV_32FC1
treeFlannIndex.knnSearch(dataTree_, results, dists, k, cv::flann::SearchParams(emax) ); // maximum number of leafs checked
ui_->label_timeMatching->setText(QString::number(time.restart()));
cv::Mat results;
cv::Mat dists;
if(!Settings::getGeneral_invertedSearch())
{
results = cv::Mat(objectsDescriptors_.rows, k, CV_32SC1); // results index
dists = cv::Mat(objectsDescriptors_.rows, k, CV_32FC1); // Distance results are CV_32FC1
flannIndex_.knnSearch(objectsDescriptors_, results, dists, k, Settings::getFlannSearchParams() ); // maximum number of leafs checked
}
else
{
results = cv::Mat(descriptors.rows, k, CV_32SC1); // results index
dists = cv::Mat(descriptors.rows, k, CV_32FC1); // Distance results are CV_32FC1
flannIndex_.knnSearch(descriptors, results, dists, k, Settings::getFlannSearchParams() ); // maximum number of leafs checked
}
ui_->label_timeMatching->setNum(time.restart());
// PROCESS RESULTS
if(this->isVisible())
{
ui_->imageView_source->setData(keypoints, cv::Mat(), &iplImage, Settings::currentDetectorType(), Settings::currentDescriptorType());
}
int j=0;
std::vector<cv::Point2f> mpts_1, mpts_2;
std::vector<int> indexes_1, indexes_2;
std::vector<uchar> outlier_mask;
QVector<QMultiMap<int, int> > matches(objects_.size()); // Map< ObjectDescriptorIndex, SceneDescriptorIndex >
QMap<int, QPair<QRect, QTransform> > objectsDetected;
float minMatchedDistance = -1.0f;
float maxMatchedDistance = -1.0f;
for(int i=0; i<dataTree_.rows; ++i)
// Get all matches for each object
for(int i=0; i<dists.rows; ++i)
{
int nColor = j % 11 + 7;
QColor color((Qt::GlobalColor)(nColor==Qt::yellow?Qt::gray:nColor));
bool matched = false;
// Check if this descriptor matches with those of the objects
if(Settings::getNearestNeighbor_nndrRatioUsed() &&
dists.at<float>(i,0) <= Settings::getNearestNeighbor_nndrRatio() * dists.at<float>(i,1))
bool matched = false;
if(Settings::getNearestNeighbor_3nndrRatioUsed() &&
dists.at<float>(i,0) <= Settings::getNearestNeighbor_4nndrRatio() * dists.at<float>(i,1))
{
matched = true;
}
if((matched || !Settings::getNearestNeighbor_nndrRatioUsed()) &&
Settings::getNearestNeighbor_minDistanceUsed())
if((matched || !Settings::getNearestNeighbor_3nndrRatioUsed()) &&
Settings::getNearestNeighbor_5minDistanceUsed())
{
if(dists.at<float>(i,0) <= Settings::getNearestNeighbor_minDistance())
if(dists.at<float>(i,0) <= Settings::getNearestNeighbor_6minDistance())
{
matched = true;
}
@ -710,128 +803,165 @@ void MainWindow::update(const cv::Mat & image)
matched = false;
}
}
if(minMatchedDistance == -1 || minMatchedDistance>dists.at<float>(i,0))
if(minMatchedDistance == -1 || minMatchedDistance > dists.at<float>(i,0))
{
minMatchedDistance = dists.at<float>(i,0);
}
if(maxMatchedDistance == -1 || maxMatchedDistance<dists.at<float>(i,0))
if(maxMatchedDistance == -1 || maxMatchedDistance < dists.at<float>(i,0))
{
maxMatchedDistance = dists.at<float>(i,0);
}
if(matched)
{
if(j>0)
if(Settings::getGeneral_invertedSearch())
{
mpts_1.push_back(objects_.at(j)->keypoints().at(i-dataRange_.at(j-1)).pt);
indexes_1.push_back(i-dataRange_.at(j-1));
QMap<int, int>::iterator iter = dataRange_.lowerBound(results.at<int>(i,0));
int objectIndex = iter.value();
int previousDescriptorIndex = (iter == dataRange_.begin())?0:(--iter).key()+1;
int objectDescriptorIndex = results.at<int>(i,0) - previousDescriptorIndex;
matches[objectIndex].insert(objectDescriptorIndex, i);
}
else
{
mpts_1.push_back(objects_.at(j)->keypoints().at(i).pt);
indexes_1.push_back(i);
}
mpts_2.push_back(keypoints.at(results.at<int>(i,0)).pt);
indexes_2.push_back(results.at<int>(i,0));
// colorize all matched if homography is not computed
if(!Settings::getHomography_homographyComputed())
{
objects_.at(j)->setKptColor(indexes_1.back(), color);
ui_->imageView_source->setKptColor(results.at<int>(i,0), color);
QMap<int, int>::iterator iter = dataRange_.lowerBound(i);
int objectIndex = iter.value();
int fisrtObjectDescriptorIndex = (iter == dataRange_.begin())?0:(--iter).key()+1;
int objectDescriptorIndex = i - fisrtObjectDescriptorIndex;
matches[objectIndex].insert(objectDescriptorIndex, results.at<int>(i,0));
}
}
}
if(i+1 >= dataRange_.at(j))
QMap<int, float> scores;
// For each object
for(int i=0; i<matches.size(); ++i)
{
if(Settings::getHomography_homographyComputed())
{
QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1detection").arg(objects_.at(j)->id()));
if(Settings::getHomography_homographyComputed())
// HOMOGRAPHY
std::vector<cv::Point2f> mpts_1(matches[i].size()), mpts_2(matches[i].size());
std::vector<int> indexes_1(matches[i].size()), indexes_2(matches[i].size());
std::vector<uchar> outlier_mask;
int nColor = i % 11 + 7;
QColor color((Qt::GlobalColor)(nColor==Qt::yellow?Qt::gray:nColor));
int j=0;
for(QMultiMap<int, int>::iterator iter = matches[i].begin(); iter!=matches[i].end(); ++iter)
{
if(mpts_1.size() >= Settings::getHomography_minimumInliers())
mpts_1[j] = objects_.at(i)->keypoints().at(iter.key()).pt;
indexes_1[j] = iter.key();
mpts_2[j] = keypoints.at(iter.value()).pt;
indexes_2[j] = iter.value();
++j;
}
QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1detection").arg(objects_.at(i)->id()));
if(mpts_1.size() >= Settings::getHomography_minimumInliers())
{
cv::Mat H = findHomography(mpts_1,
mpts_2,
cv::RANSAC,
Settings::getHomography_ransacReprojThr(),
outlier_mask);
uint inliers=0, outliers=0;
for(unsigned int k=0; k<mpts_1.size();++k)
{
cv::Mat H = findHomography(mpts_1,
mpts_2,
cv::RANSAC,
Settings::getHomography_ransacReprojThr(),
outlier_mask);
uint inliers=0, outliers=0;
for(unsigned int k=0; k<mpts_1.size();++k)
if(outlier_mask.at(k))
{
if(outlier_mask.at(k))
{
++inliers;
}
else
{
++outliers;
}
}
// COLORIZE
if(inliers >= Settings::getHomography_minimumInliers())
{
if(this->isVisible())
{
for(unsigned int k=0; k<mpts_1.size();++k)
{
if(outlier_mask.at(k))
{
objects_.at(j)->setKptColor(indexes_1.at(k), color);
ui_->imageView_source->setKptColor(indexes_2.at(k), color);
}
else
{
objects_.at(j)->setKptColor(indexes_1.at(k), QColor(0,0,0));
}
}
}
QTransform hTransform(
H.at<double>(0,0), H.at<double>(1,0), H.at<double>(2,0),
H.at<double>(0,1), H.at<double>(1,1), H.at<double>(2,1),
H.at<double>(0,2), H.at<double>(1,2), H.at<double>(2,2));
// find center of object
QRect rect = objects_.at(j)->pixmap().rect();
objectsDetected.insert(objects_.at(j)->id(), QPair<QRect, QTransform>(rect, hTransform));
// Example getting the center of the object in the scene using the homography
//QPoint pos(rect.width()/2, rect.height()/2);
//hTransform.map(pos)
// add rectangle
if(this->isVisible())
{
label->setText(QString("%1 in %2 out").arg(inliers).arg(outliers));
QPen rectPen(color);
rectPen.setWidth(4);
QGraphicsRectItem * rectItem = new QGraphicsRectItem(rect);
rectItem->setPen(rectPen);
rectItem->setTransform(hTransform);
ui_->imageView_source->addRect(rectItem);
}
++inliers;
}
else
{
label->setText(QString("Too low inliers (%1)").arg(inliers));
++outliers;
}
}
// COLORIZE
if(inliers >= Settings::getHomography_minimumInliers())
{
if(this->isVisible())
{
for(unsigned int k=0; k<mpts_1.size();++k)
{
if(outlier_mask.at(k))
{
objects_.at(i)->setKptColor(indexes_1.at(k), color);
ui_->imageView_source->setKptColor(indexes_2.at(k), color);
}
else
{
objects_.at(i)->setKptColor(indexes_1.at(k), QColor(0,0,0));
}
}
}
QTransform hTransform(
H.at<double>(0,0), H.at<double>(1,0), H.at<double>(2,0),
H.at<double>(0,1), H.at<double>(1,1), H.at<double>(2,1),
H.at<double>(0,2), H.at<double>(1,2), H.at<double>(2,2));
// find center of object
QRect rect = objects_.at(i)->pixmap().rect();
objectsDetected.insert(objects_.at(i)->id(), QPair<QRect, QTransform>(rect, hTransform));
// Example getting the center of the object in the scene using the homography
//QPoint pos(rect.width()/2, rect.height()/2);
//hTransform.map(pos)
// add rectangle
if(this->isVisible())
{
label->setText(QString("%1 in %2 out").arg(inliers).arg(outliers));
QPen rectPen(color);
rectPen.setWidth(4);
QGraphicsRectItem * rectItem = new QGraphicsRectItem(rect);
rectItem->setPen(rectPen);
rectItem->setTransform(hTransform);
ui_->imageView_source->addRect(rectItem);
}
}
else
{
label->setText(QString("Too low matches (%1)").arg(mpts_1.size()));
label->setText(QString("Too low inliers (%1 in %2 out)").arg(inliers).arg(outliers));
}
}
else
{
label->setText(QString("%1 matches").arg(mpts_1.size()));
label->setText(QString("Too low matches (%1)").arg(mpts_1.size()));
}
mpts_1.clear();
mpts_2.clear();
indexes_1.clear();
indexes_2.clear();
outlier_mask.clear();
++j;
}
else
{
// colorize all matches if homography is not computed
int nColor = i % 11 + 7;
QColor color((Qt::GlobalColor)(nColor==Qt::yellow?Qt::gray:nColor));
for(QMultiMap<int, int>::iterator iter = matches[i].begin(); iter!=matches[i].end(); ++iter)
{
printf("iter.key()=%d, iter.value()=%d\n", iter.key(), iter.value());
printf("objects_[%d].keypoints=%d\n", i, (int)objects_[i]->keypoints().size());
objects_[i]->setKptColor(iter.key(), color);
ui_->imageView_source->setKptColor(iter.value(), color);
}
QLabel * label = ui_->dockWidget_objects->findChild<QLabel*>(QString("%1detection").arg(objects_.at(i)->id()));
label->setText(QString("%1 matches").arg(matches[i].size()));
}
scores.insert(objects_.at(i)->id(), matches[i].size());
}
//update likelihood plot
likelihoodCurve_->setData(scores, QMap<int, int>());
if(ui_->likelihoodPlot->isVisible())
{
ui_->likelihoodPlot->update();
}
ui_->label_minMatchedDistance->setNum(minMatchedDistance);
ui_->label_maxMatchedDistance->setNum(maxMatchedDistance);
@ -854,13 +984,15 @@ void MainWindow::update(const cv::Mat & image)
}
}
ui_->label_nfeatures->setText(QString::number(keypoints.size()));
ui_->label_nfeatures->setNum((int)keypoints.size());
ui_->imageView_source->update();
ui_->label_timeGui->setText(QString::number(time.restart()));
ui_->label_timeGui->setNum(time.restart());
}
ui_->label_detectorDescriptorType->setText(QString("%1/%2").arg(Settings::currentDetectorType()).arg(Settings::currentDescriptorType()));
int refreshRate = qRound(1000.0f/float(updateRate_.restart()));
int ms = updateRate_.restart();
ui_->label_timeTotal->setNum(ms);
int refreshRate = qRound(1000.0f/float(ms));
if(refreshRate > 0 && refreshRate < lowestRefreshRate_)
{
lowestRefreshRate_ = refreshRate;
@ -868,9 +1000,9 @@ void MainWindow::update(const cv::Mat & image)
// Refresh the label only after each 1000 ms
if(refreshStartTime_.elapsed() > 1000)
{
if(Settings::getCamera_imageRate()>0)
if(Settings::getCamera_4imageRate()>0)
{
ui_->label_timeRefreshRate->setText(QString("(%1 Hz - %2 Hz)").arg(QString::number(Settings::getCamera_imageRate())).arg(QString::number(lowestRefreshRate_)));
ui_->label_timeRefreshRate->setText(QString("(%1 Hz - %2 Hz)").arg(QString::number(Settings::getCamera_4imageRate())).arg(QString::number(lowestRefreshRate_)));
}
else
{
@ -898,5 +1030,5 @@ void MainWindow::notifyParametersChanged()
ui_->label_timeRefreshRate->setVisible(false);
}
ui_->actionCamera_from_video_file->setChecked(!Settings::getCamera_videoFilePath().isEmpty());
ui_->actionCamera_from_video_file->setChecked(!Settings::getCamera_5videoFilePath().isEmpty());
}

View File

@ -9,6 +9,8 @@
#include <QtCore/QSet>
#include <QtCore/QTimer>
#include <QtCore/QTime>
#include <QtCore/QMap>
#include <QtCore/QByteArray>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
@ -21,6 +23,11 @@ class ParametersToolBox;
class QLabel;
class AboutDialog;
namespace rtabmap
{
class PdfPlotCurve;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
@ -52,6 +59,8 @@ private slots:
void setupCameraFromVideoFile();
void removeObject(ObjWidget * object);
void removeAllObjects();
void updateObjectsSize();
void updateMirrorView();
void update(const cv::Mat & image);
void updateObjects();
void notifyParametersChanged();
@ -63,18 +72,22 @@ private:
void addObjectFromFile(const QString & filePath);
void showObject(ObjWidget * obj);
void updateData();
void updateObjectSize(ObjWidget * obj);
private:
Ui_mainWindow * ui_;
Camera * camera_;
rtabmap::PdfPlotCurve * likelihoodCurve_;
AboutDialog * aboutDialog_;
QList<ObjWidget*> objects_;
cv::Mat dataTree_;
QList<int> dataRange_;
cv::Mat objectsDescriptors_;
cv::flann::Index flannIndex_;
QMap<int, int> dataRange_; // <last id of object's descriptor, id>
QTime updateRate_;
QTime refreshStartTime_;
int lowestRefreshRate_;
bool objectsModified_;
QMap<int, QByteArray> imagesMap_;
};
#endif /* MainWindow_H_ */

View File

@ -287,6 +287,10 @@ void ObjWidget::setKptColor(int index, const QColor & color)
{
kptColors_[index] = color;
}
else
{
printf("PROBLEM index =%d > size=%d\n", index, kptColors_.size());
}
if(graphicsViewMode_->isChecked())
{
@ -344,6 +348,32 @@ void ObjWidget::setDeletable(bool deletable)
delete_->setEnabled(deletable);
}
void ObjWidget::setImageShown(bool shown)
{
showImage_->setChecked(shown);
if(graphicsViewMode_->isChecked())
{
this->updateItemsShown();
}
else
{
this->update();
}
}
void ObjWidget::setFeaturesShown(bool shown)
{
showFeatures_->setChecked(shown);
if(graphicsViewMode_->isChecked())
{
this->updateItemsShown();
}
else
{
this->update();
}
}
void ObjWidget::save(QDataStream & streamPtr) const
{
streamPtr << id_ << detectorType_ << descriptorType_;

View File

@ -48,6 +48,8 @@ public:
void setMirrorView(bool on);
void setAlpha(int alpha);
void setDeletable(bool deletable);
void setImageShown(bool shown);
void setFeaturesShown(bool shown);
void addRect(QGraphicsRectItem * rect);
void clearRoiSelection() {mousePressedPos_ = mouseCurrentPos_ = QPoint();update();}

View File

@ -11,6 +11,7 @@
#include <QtGui/QGroupBox>
#include <QtGui/QCheckBox>
#include <QtGui/QVBoxLayout>
#include <QtGui/QMessageBox>
#include <stdio.h>
ParametersToolBox::ParametersToolBox(QWidget *parent) :
@ -86,6 +87,7 @@ void ParametersToolBox::resetAllPages()
void ParametersToolBox::setupUi()
{
this->removeItem(0); // remove dummy page used in .ui
QWidget * currentItem = 0;
const ParametersMap & parameters = Settings::getParameters();
for(ParametersMap::const_iterator iter=parameters.constBegin();
@ -119,7 +121,17 @@ void ParametersToolBox::updateParameter(const QString & key)
QString type = Settings::getParametersType().value(key);
if(type.compare("QString") == 0)
{
((QLineEdit*)widget)->setText(Settings::getParameter(key).toString());
QString value = Settings::getParameter(key).toString();
if(value.contains(';'))
{
// It's a list, just change the index
QStringList splitted = value.split(':');
((QComboBox*)widget)->setCurrentIndex(splitted.first().toInt());
}
else
{
((QLineEdit*)widget)->setText(value);
}
}
else if(type.compare("int") == 0)
{
@ -272,7 +284,12 @@ void ParametersToolBox::addParameter(QVBoxLayout * layout, const QString & name,
{
QHBoxLayout * hLayout = new QHBoxLayout();
layout->insertLayout(layout->count()-1, hLayout);
hLayout->addWidget(new QLabel(name, this));
QString tmp = name;
if(tmp.at(0).isDigit())
{
tmp.remove(0,1);
}
hLayout->addWidget(new QLabel(tmp, this));
hLayout->addWidget(widget);
}
@ -314,6 +331,50 @@ void ParametersToolBox::changeParameter(const int & value)
QCheckBox * checkBox = qobject_cast<QCheckBox*>(sender());
if(comboBox)
{
if(comboBox->objectName().compare(Settings::kDetector_Descriptor_2Descriptor()) == 0 ||
comboBox->objectName().compare(Settings::kNearestNeighbor_1Strategy()) == 0)
{
//verify binary issue
QComboBox * descriptorBox = (QComboBox*)this->getParameterWidget(Settings::kDetector_Descriptor_2Descriptor());
QComboBox * nnBox = (QComboBox*)this->getParameterWidget(Settings::kNearestNeighbor_1Strategy());
bool isBinaryDescriptor = descriptorBox->currentText().compare("ORB") == 0 || descriptorBox->currentText().compare("Brief") == 0;
if(isBinaryDescriptor && nnBox->currentText().compare("Lsh") != 0)
{
QMessageBox::warning(this,
tr("Error"),
tr("Current selected descriptor type (\"%1\") is binary while nearest neighbor strategy is not (\"%2\").\n"
"Falling back to \"Lsh\" nearest neighbor strategy (by default).")
.arg(descriptorBox->currentText())
.arg(nnBox->currentText()));
QString tmp = Settings::getNearestNeighbor_1Strategy();
*tmp.begin() = '5'; // set index
Settings::setNearestNeighbor_1Strategy(tmp);
this->updateParameter(Settings::kNearestNeighbor_1Strategy());
if(sender() == nnBox)
{
return;
}
}
else if(!isBinaryDescriptor && nnBox->currentText().compare("Lsh") == 0)
{
QMessageBox::warning(this,
tr("Error"),
tr("Current selected descriptor type (\"%1\") is not binary while nearest neighbor strategy is (\"%2\").\n"
"Falling back to \"KDTree\" nearest neighbor strategy (by default).")
.arg(descriptorBox->currentText())
.arg(nnBox->currentText()));
QString tmp = Settings::getNearestNeighbor_1Strategy();
*tmp.begin() = '1'; // set index
Settings::setNearestNeighbor_1Strategy(tmp);
this->updateParameter(Settings::kNearestNeighbor_1Strategy());
if(sender() == nnBox)
{
return;
}
}
}
QStringList items;
for(int i=0; i<comboBox->count(); ++i)
{

View File

@ -10,6 +10,8 @@
#include <stdio.h>
#include <opencv2/nonfree/features2d.hpp>
#define VERBOSE 0
ParametersMap Settings::defaultParameters_;
ParametersMap Settings::parameters_;
ParametersType Settings::parametersType_;
@ -33,7 +35,7 @@ QString Settings::iniDefaultPath()
#endif
}
void Settings::loadSettings(const QString & fileName, QByteArray * windowGeometry)
void Settings::loadSettings(const QString & fileName, QByteArray * windowGeometry, QByteArray * windowState)
{
QString path = fileName;
if(fileName.isEmpty())
@ -59,11 +61,19 @@ void Settings::loadSettings(const QString & fileName, QByteArray * windowGeometr
*windowGeometry = value.toByteArray();
}
}
if(windowState)
{
QVariant value = ini.value("windowState", QVariant());
if(value.isValid())
{
*windowState = value.toByteArray();
}
}
printf("Settings loaded from %s\n", path.toStdString().c_str());
}
void Settings::saveSettings(const QString & fileName, const QByteArray & windowGeometry)
void Settings::saveSettings(const QString & fileName, const QByteArray & windowGeometry, const QByteArray & windowState)
{
QString path = fileName;
if(fileName.isEmpty())
@ -87,13 +97,17 @@ void Settings::saveSettings(const QString & fileName, const QByteArray & windowG
{
ini.setValue("windowGeometry", windowGeometry);
}
if(!windowState.isEmpty())
{
ini.setValue("windowState", windowState);
}
printf("Settings saved to %s\n", path.toStdString().c_str());
}
cv::FeatureDetector * Settings::createFeaturesDetector()
{
cv::FeatureDetector * detector = 0;
QString str = getDetector_Type();
QString str = getDetector_Descriptor_1Detector();
QStringList split = str.split(':');
if(split.size()==2)
{
@ -110,95 +124,103 @@ cv::FeatureDetector * Settings::createFeaturesDetector()
if(strategies.at(index).compare("Dense") == 0)
{
detector = new cv::DenseFeatureDetector(
getDense_initFeatureScale(),
getDense_featureScaleLevels(),
getDense_featureScaleMul(),
getDense_initXyStep(),
getDense_initImgBound(),
getDense_varyXyStepWithScale(),
getDense_varyImgBoundWithScale());
getDetector_Descriptor_Dense_initFeatureScale(),
getDetector_Descriptor_Dense_featureScaleLevels(),
getDetector_Descriptor_Dense_featureScaleMul(),
getDetector_Descriptor_Dense_initXyStep(),
getDetector_Descriptor_Dense_initImgBound(),
getDetector_Descriptor_Dense_varyXyStepWithScale(),
getDetector_Descriptor_Dense_varyImgBoundWithScale());
if(VERBOSE)printf("Settings::createFeaturesDetector() type=%s\n", "Dense");
}
break;
case 1:
if(strategies.at(index).compare("Fast") == 0)
{
detector = new cv::FastFeatureDetector(
getFast_threshold(),
getFast_nonmaxSuppression());
getDetector_Descriptor_Fast_threshold(),
getDetector_Descriptor_Fast_nonmaxSuppression());
if(VERBOSE)printf("Settings::createFeaturesDetector() type=%s\n", "Fast");
}
break;
case 2:
if(strategies.at(index).compare("GFTT") == 0)
{
detector = new cv::GFTTDetector(
getGFTT_maxCorners(),
getGFTT_qualityLevel(),
getGFTT_minDistance(),
getGFTT_blockSize(),
getGFTT_useHarrisDetector(),
getGFTT_k());
getDetector_Descriptor_GFTT_maxCorners(),
getDetector_Descriptor_GFTT_qualityLevel(),
getDetector_Descriptor_GFTT_minDistance(),
getDetector_Descriptor_GFTT_blockSize(),
getDetector_Descriptor_GFTT_useHarrisDetector(),
getDetector_Descriptor_GFTT_k());
if(VERBOSE)printf("Settings::createFeaturesDetector() type=%s\n", "GFTT");
}
break;
case 3:
if(strategies.at(index).compare("MSER") == 0)
{
detector = new cv::MSER(
getMSER_delta(),
getMSER_minArea(),
getMSER_maxArea(),
getMSER_maxVariation(),
getMSER_minDiversity(),
getMSER_maxEvolution(),
getMSER_areaThreshold(),
getMSER_minMargin(),
getMSER_edgeBlurSize());
getDetector_Descriptor_MSER_delta(),
getDetector_Descriptor_MSER_minArea(),
getDetector_Descriptor_MSER_maxArea(),
getDetector_Descriptor_MSER_maxVariation(),
getDetector_Descriptor_MSER_minDiversity(),
getDetector_Descriptor_MSER_maxEvolution(),
getDetector_Descriptor_MSER_areaThreshold(),
getDetector_Descriptor_MSER_minMargin(),
getDetector_Descriptor_MSER_edgeBlurSize());
if(VERBOSE)printf("Settings::createFeaturesDetector() type=%s\n", "MSER");
}
break;
case 4:
if(strategies.at(index).compare("ORB") == 0)
{
detector = new cv::ORB(
getORB_nFeatures(),
getORB_scaleFactor(),
getORB_nLevels(),
getORB_edgeThreshold(),
getORB_firstLevel(),
getORB_WTA_K(),
getORB_scoreType(),
getORB_patchSize());
getDetector_Descriptor_ORB_nFeatures(),
getDetector_Descriptor_ORB_scaleFactor(),
getDetector_Descriptor_ORB_nLevels(),
getDetector_Descriptor_ORB_edgeThreshold(),
getDetector_Descriptor_ORB_firstLevel(),
getDetector_Descriptor_ORB_WTA_K(),
getDetector_Descriptor_ORB_scoreType(),
getDetector_Descriptor_ORB_patchSize());
if(VERBOSE)printf("Settings::createFeaturesDetector() type=%s\n", "ORB");
}
break;
case 5:
if(strategies.at(index).compare("SIFT") == 0)
{
detector = new cv::SIFT(
getSIFT_nfeatures(),
getSIFT_nOctaveLayers(),
getSIFT_contrastThreshold(),
getSIFT_edgeThreshold(),
getSIFT_sigma());
getDetector_Descriptor_SIFT_nfeatures(),
getDetector_Descriptor_SIFT_nOctaveLayers(),
getDetector_Descriptor_SIFT_contrastThreshold(),
getDetector_Descriptor_SIFT_edgeThreshold(),
getDetector_Descriptor_SIFT_sigma());
if(VERBOSE)printf("Settings::createFeaturesDetector() type=%s\n", "SIFT");
}
break;
case 6:
if(strategies.at(index).compare("Star") == 0)
{
detector = new cv::StarFeatureDetector(
getStar_maxSize(),
getStar_responseThreshold(),
getStar_lineThresholdProjected(),
getStar_lineThresholdBinarized(),
getStar_suppressNonmaxSize());
getDetector_Descriptor_Star_maxSize(),
getDetector_Descriptor_Star_responseThreshold(),
getDetector_Descriptor_Star_lineThresholdProjected(),
getDetector_Descriptor_Star_lineThresholdBinarized(),
getDetector_Descriptor_Star_suppressNonmaxSize());
if(VERBOSE)printf("Settings::createFeaturesDetector() type=%s\n", "Star");
}
break;
case 7:
if(strategies.at(index).compare("SURF") == 0)
{
detector = new cv::SURF(
getSURF_hessianThreshold(),
getSURF_nOctaves(),
getSURF_nOctaveLayers(),
getSURF_extended(),
getSURF_upright());
getDetector_Descriptor_SURF_hessianThreshold(),
getDetector_Descriptor_SURF_nOctaves(),
getDetector_Descriptor_SURF_nOctaveLayers(),
getDetector_Descriptor_SURF_extended(),
getDetector_Descriptor_SURF_upright());
if(VERBOSE)printf("Settings::createFeaturesDetector() type=%s\n", "SURF");
}
break;
default:
@ -207,13 +229,18 @@ cv::FeatureDetector * Settings::createFeaturesDetector()
}
}
}
if(!detector)
{
printf("ERROR: detector strategy not found !? Using default SURF...\n");
detector = new cv::SURF();
}
return detector;
}
cv::DescriptorExtractor * Settings::createDescriptorsExtractor()
{
cv::DescriptorExtractor * extractor = 0;
QString str = getDescriptor_Type();
QString str = getDetector_Descriptor_2Descriptor();
QStringList split = str.split(':');
if(split.size()==2)
{
@ -230,43 +257,47 @@ cv::DescriptorExtractor * Settings::createDescriptorsExtractor()
if(strategies.at(index).compare("Brief") == 0)
{
extractor = new cv::BriefDescriptorExtractor(
getBrief_bytes());
getDetector_Descriptor_Brief_bytes());
if(VERBOSE)printf("Settings::createDescriptorsExtractor() type=%s\n", "Brief");
}
break;
case 1:
if(strategies.at(index).compare("ORB") == 0)
{
extractor = new cv::ORB(
getORB_nFeatures(),
getORB_scaleFactor(),
getORB_nLevels(),
getORB_edgeThreshold(),
getORB_firstLevel(),
getORB_WTA_K(),
getORB_scoreType(),
getORB_patchSize());
getDetector_Descriptor_ORB_nFeatures(),
getDetector_Descriptor_ORB_scaleFactor(),
getDetector_Descriptor_ORB_nLevels(),
getDetector_Descriptor_ORB_edgeThreshold(),
getDetector_Descriptor_ORB_firstLevel(),
getDetector_Descriptor_ORB_WTA_K(),
getDetector_Descriptor_ORB_scoreType(),
getDetector_Descriptor_ORB_patchSize());
if(VERBOSE)printf("Settings::createDescriptorsExtractor() type=%s\n", "ORB");
}
break;
case 2:
if(strategies.at(index).compare("SIFT") == 0)
{
extractor = new cv::SIFT(
getSIFT_nfeatures(),
getSIFT_nOctaveLayers(),
getSIFT_contrastThreshold(),
getSIFT_edgeThreshold(),
getSIFT_sigma());
getDetector_Descriptor_SIFT_nfeatures(),
getDetector_Descriptor_SIFT_nOctaveLayers(),
getDetector_Descriptor_SIFT_contrastThreshold(),
getDetector_Descriptor_SIFT_edgeThreshold(),
getDetector_Descriptor_SIFT_sigma());
if(VERBOSE)printf("Settings::createDescriptorsExtractor() type=%s\n", "SIFT");
}
break;
case 3:
if(strategies.at(index).compare("SURF") == 0)
{
extractor = new cv::SURF(
getSURF_hessianThreshold(),
getSURF_nOctaves(),
getSURF_nOctaveLayers(),
getSURF_extended(),
getSURF_upright());
getDetector_Descriptor_SURF_hessianThreshold(),
getDetector_Descriptor_SURF_nOctaves(),
getDetector_Descriptor_SURF_nOctaveLayers(),
getDetector_Descriptor_SURF_extended(),
getDetector_Descriptor_SURF_upright());
if(VERBOSE)printf("Settings::createDescriptorsExtractor() type=%s\n", "SURF");
}
break;
default:
@ -275,19 +306,170 @@ cv::DescriptorExtractor * Settings::createDescriptorsExtractor()
}
}
}
if(!extractor)
{
printf("ERROR: descriptor strategy not found !? Using default SURF...\n");
extractor = new cv::SURF();
}
return extractor;
}
QString Settings::currentDetectorType()
{
int index = Settings::getDetector_Type().split(':').first().toInt();
return getDetector_Type().split(':').last().split(';').at(index);
int index = getDetector_Descriptor_1Detector().split(':').first().toInt();
return getDetector_Descriptor_1Detector().split(':').last().split(';').at(index);
}
QString Settings::currentDescriptorType()
{
int index = Settings::getDescriptor_Type().split(':').first().toInt();
return getDescriptor_Type().split(':').last().split(';').at(index);
int index = getDetector_Descriptor_2Descriptor().split(':').first().toInt();
return getDetector_Descriptor_2Descriptor().split(':').last().split(';').at(index);
}
QString Settings::currentNearestNeighborType()
{
int index = getNearestNeighbor_1Strategy().split(':').first().toInt();
return getNearestNeighbor_1Strategy().split(':').last().split(';').at(index);
}
cv::flann::IndexParams * Settings::createFlannIndexParams()
{
cv::flann::IndexParams * params = 0;
QString str = getNearestNeighbor_1Strategy();
QStringList split = str.split(':');
if(split.size()==2)
{
bool ok = false;
int index = split.first().toInt(&ok);
if(ok)
{
QStringList strategies = split.last().split(';');
if(strategies.size() == 6 && index>=0 && index<6)
{
switch(index)
{
case 0:
if(strategies.at(index).compare("Linear") == 0)
{
if(VERBOSE)printf("Settings::getFlannIndexParams() type=%s\n", "Linear");
params = new cv::flann::LinearIndexParams();
}
break;
case 1:
if(strategies.at(index).compare("KDTree") == 0)
{
if(VERBOSE)printf("Settings::getFlannIndexParams() type=%s\n", "KDTree");
params = new cv::flann::KDTreeIndexParams(
getNearestNeighbor_KDTree_trees());
}
break;
case 2:
if(strategies.at(index).compare("KMeans") == 0)
{
cvflann::flann_centers_init_t centers_init = cvflann::FLANN_CENTERS_RANDOM;
QString str = getNearestNeighbor_KMeans_centers_init();
QStringList split = str.split(':');
if(split.size()==2)
{
bool ok = false;
int index = split.first().toInt(&ok);
if(ok)
{
centers_init = (cvflann::flann_centers_init_t)index;
}
}
if(VERBOSE)printf("Settings::getFlannIndexParams() type=%s\n", "KMeans");
params = new cv::flann::KMeansIndexParams(
getNearestNeighbor_KMeans_branching(),
getNearestNeighbor_KMeans_iterations(),
centers_init,
getNearestNeighbor_KMeans_cb_index());
}
break;
case 3:
if(strategies.at(index).compare("Composite") == 0)
{
cvflann::flann_centers_init_t centers_init = cvflann::FLANN_CENTERS_RANDOM;
QString str = getNearestNeighbor_Composite_centers_init();
QStringList split = str.split(':');
if(split.size()==2)
{
bool ok = false;
int index = split.first().toInt(&ok);
if(ok)
{
centers_init = (cvflann::flann_centers_init_t)index;
}
}
if(VERBOSE)printf("Settings::getFlannIndexParams() type=%s\n", "Composite");
params = new cv::flann::CompositeIndexParams(
getNearestNeighbor_Composite_trees(),
getNearestNeighbor_Composite_branching(),
getNearestNeighbor_Composite_iterations(),
centers_init,
getNearestNeighbor_Composite_cb_index());
}
break;
case 4:
if(strategies.at(index).compare("Autotuned") == 0)
{
if(VERBOSE)printf("Settings::getFlannIndexParams() type=%s\n", "Autotuned");
params = new cv::flann::AutotunedIndexParams(
getNearestNeighbor_Autotuned_target_precision(),
getNearestNeighbor_Autotuned_build_weight(),
getNearestNeighbor_Autotuned_memory_weight(),
getNearestNeighbor_Autotuned_sample_fraction());
}
break;
case 5:
if(strategies.at(index).compare("Lsh") == 0)
{
if(VERBOSE)printf("Settings::getFlannIndexParams() type=%s\n", "Lsh");
params = new cv::flann::LshIndexParams(
getNearestNeighbor_Lsh_table_number(),
getNearestNeighbor_Lsh_key_size(),
getNearestNeighbor_Lsh_multi_probe_level());
}
break;
default:
break;
}
}
}
}
if(!params)
{
printf("ERROR: NN strategy not found !? Using default KDTRee...\n");
params = new cv::flann::KDTreeIndexParams();
}
return params ;
}
cvflann::flann_distance_t Settings::getFlannDistanceType()
{
cvflann::flann_distance_t distance = cvflann::FLANN_DIST_L2;
QString str = getNearestNeighbor_2Distance_type();
QStringList split = str.split(':');
if(split.size()==2)
{
bool ok = false;
int index = split.first().toInt(&ok);
if(ok)
{
QStringList strategies = split.last().split(';');
if(strategies.size() == 8 && index>=0 && index<8)
{
distance = (cvflann::flann_distance_t)(index+1);
}
}
}
if(VERBOSE)printf("Settings::getFlannDistanceType() distance=%d\n", distance);
return distance;
}
cv::flann::SearchParams Settings::getFlannSearchParams()
{
return cv::flann::SearchParams();
}

View File

@ -52,83 +52,109 @@ typedef unsigned int uint;
class Settings
{
PARAMETER(Camera, deviceId, int, 0);
PARAMETER(Camera, imageWidth, int, 640);
PARAMETER(Camera, imageHeight, int, 480);
PARAMETER(Camera, imageRate, int, 2); // Hz
PARAMETER(Camera, videoFilePath, QString, "");
PARAMETER(Camera, 1deviceId, int, 0);
PARAMETER(Camera, 2imageWidth, int, 640);
PARAMETER(Camera, 3imageHeight, int, 480);
PARAMETER(Camera, 4imageRate, int, 2); // Hz
PARAMETER(Camera, 5videoFilePath, QString, "");
//List format : [Index:item0;item1;item3;...]
PARAMETER(Detector, Type, QString, "7:Dense;Fast;GFTT;MSER;ORB;SIFT;Star;SURF");
PARAMETER(Descriptor, Type, QString, "3:Brief;ORB;SIFT;SURF");
PARAMETER(Detector_Descriptor, 1Detector, QString, "7:Dense;Fast;GFTT;MSER;ORB;SIFT;Star;SURF");
PARAMETER(Detector_Descriptor, 2Descriptor, QString, "3:Brief;ORB;SIFT;SURF");
PARAMETER(Brief, bytes, int, 32);
PARAMETER(Detector_Descriptor, Brief_bytes, int, 32);
PARAMETER(Dense, initFeatureScale, float, 1.f);
PARAMETER(Dense, featureScaleLevels, int, 1);
PARAMETER(Dense, featureScaleMul, float, 0.1f);
PARAMETER(Dense, initXyStep, int, 6);
PARAMETER(Dense, initImgBound, int, 0);
PARAMETER(Dense, varyXyStepWithScale, bool, true);
PARAMETER(Dense, varyImgBoundWithScale, bool, false);
PARAMETER(Detector_Descriptor, Dense_initFeatureScale, float, 1.f);
PARAMETER(Detector_Descriptor, Dense_featureScaleLevels, int, 1);
PARAMETER(Detector_Descriptor, Dense_featureScaleMul, float, 0.1f);
PARAMETER(Detector_Descriptor, Dense_initXyStep, int, 6);
PARAMETER(Detector_Descriptor, Dense_initImgBound, int, 0);
PARAMETER(Detector_Descriptor, Dense_varyXyStepWithScale, bool, true);
PARAMETER(Detector_Descriptor, Dense_varyImgBoundWithScale, bool, false);
PARAMETER(Fast, threshold, int, 10);
PARAMETER(Fast, nonmaxSuppression, bool, true);
PARAMETER(Detector_Descriptor, Fast_threshold, int, 10);
PARAMETER(Detector_Descriptor, Fast_nonmaxSuppression, bool, true);
PARAMETER(GFTT, maxCorners, int, 1000);
PARAMETER(GFTT, qualityLevel, double, 0.01);
PARAMETER(GFTT, minDistance, double, 1);
PARAMETER(GFTT, blockSize, int, 3);
PARAMETER(GFTT, useHarrisDetector, bool, false);
PARAMETER(GFTT, k, double, 0.04);
PARAMETER(Detector_Descriptor, GFTT_maxCorners, int, 1000);
PARAMETER(Detector_Descriptor, GFTT_qualityLevel, double, 0.01);
PARAMETER(Detector_Descriptor, GFTT_minDistance, double, 1);
PARAMETER(Detector_Descriptor, GFTT_blockSize, int, 3);
PARAMETER(Detector_Descriptor, GFTT_useHarrisDetector, bool, false);
PARAMETER(Detector_Descriptor, GFTT_k, double, 0.04);
PARAMETER(ORB, nFeatures, int, 500);
PARAMETER(ORB, scaleFactor, float, 1.2f);
PARAMETER(ORB, nLevels, int, 8);
PARAMETER(ORB, edgeThreshold, int, 31);
PARAMETER(ORB, firstLevel, int, 0);
PARAMETER(ORB, WTA_K, int, 2);
PARAMETER(ORB, scoreType, int, 0);
PARAMETER(ORB, patchSize, int, 31);
PARAMETER(Detector_Descriptor, ORB_nFeatures, int, 500);
PARAMETER(Detector_Descriptor, ORB_scaleFactor, float, 1.2f);
PARAMETER(Detector_Descriptor, ORB_nLevels, int, 8);
PARAMETER(Detector_Descriptor, ORB_edgeThreshold, int, 31);
PARAMETER(Detector_Descriptor, ORB_firstLevel, int, 0);
PARAMETER(Detector_Descriptor, ORB_WTA_K, int, 2);
PARAMETER(Detector_Descriptor, ORB_scoreType, int, 0);
PARAMETER(Detector_Descriptor, ORB_patchSize, int, 31);
PARAMETER(MSER, delta, int, 5);
PARAMETER(MSER, minArea, int, 60);
PARAMETER(MSER, maxArea, int, 14400);
PARAMETER(MSER, maxVariation, double, 0.25);
PARAMETER(MSER, minDiversity, double, 0.2);
PARAMETER(MSER, maxEvolution, int, 200);
PARAMETER(MSER, areaThreshold, double, 1.01);
PARAMETER(MSER, minMargin, double, 0.003);
PARAMETER(MSER, edgeBlurSize, int, 5);
PARAMETER(Detector_Descriptor, MSER_delta, int, 5);
PARAMETER(Detector_Descriptor, MSER_minArea, int, 60);
PARAMETER(Detector_Descriptor, MSER_maxArea, int, 14400);
PARAMETER(Detector_Descriptor, MSER_maxVariation, double, 0.25);
PARAMETER(Detector_Descriptor, MSER_minDiversity, double, 0.2);
PARAMETER(Detector_Descriptor, MSER_maxEvolution, int, 200);
PARAMETER(Detector_Descriptor, MSER_areaThreshold, double, 1.01);
PARAMETER(Detector_Descriptor, MSER_minMargin, double, 0.003);
PARAMETER(Detector_Descriptor, MSER_edgeBlurSize, int, 5);
PARAMETER(SIFT, nfeatures, int, 0);
PARAMETER(SIFT, nOctaveLayers, int, 3);
PARAMETER(SIFT, contrastThreshold, double, 0.04);
PARAMETER(SIFT, edgeThreshold, double, 10);
PARAMETER(SIFT, sigma, double, 1.6);
PARAMETER(Detector_Descriptor, SIFT_nfeatures, int, 0);
PARAMETER(Detector_Descriptor, SIFT_nOctaveLayers, int, 3);
PARAMETER(Detector_Descriptor, SIFT_contrastThreshold, double, 0.04);
PARAMETER(Detector_Descriptor, SIFT_edgeThreshold, double, 10);
PARAMETER(Detector_Descriptor, SIFT_sigma, double, 1.6);
PARAMETER(Star, maxSize, int, 45);
PARAMETER(Star, responseThreshold, int, 30);
PARAMETER(Star, lineThresholdProjected, int, 10);
PARAMETER(Star, lineThresholdBinarized, int, 8);
PARAMETER(Star, suppressNonmaxSize, int, 5);
PARAMETER(Detector_Descriptor, Star_maxSize, int, 45);
PARAMETER(Detector_Descriptor, Star_responseThreshold, int, 30);
PARAMETER(Detector_Descriptor, Star_lineThresholdProjected, int, 10);
PARAMETER(Detector_Descriptor, Star_lineThresholdBinarized, int, 8);
PARAMETER(Detector_Descriptor, Star_suppressNonmaxSize, int, 5);
PARAMETER(SURF, hessianThreshold, double, 600.0);
PARAMETER(SURF, nOctaves, int, 4);
PARAMETER(SURF, nOctaveLayers, int, 2);
PARAMETER(SURF, extended, bool, true);
PARAMETER(SURF, upright, bool, false);
PARAMETER(Detector_Descriptor, SURF_hessianThreshold, double, 600.0);
PARAMETER(Detector_Descriptor, SURF_nOctaves, int, 4);
PARAMETER(Detector_Descriptor, SURF_nOctaveLayers, int, 2);
PARAMETER(Detector_Descriptor, SURF_extended, bool, true);
PARAMETER(Detector_Descriptor, SURF_upright, bool, false);
PARAMETER(NearestNeighbor, nndrRatioUsed, bool, true);
PARAMETER(NearestNeighbor, nndrRatio, float, 0.8f);
PARAMETER(NearestNeighbor, minDistanceUsed, bool, false);
PARAMETER(NearestNeighbor, minDistance, float, 1.6f);
PARAMETER(NearestNeighbor, 1Strategy, QString, "1:Linear;KDTree;KMeans;Composite;Autotuned;Lsh");
PARAMETER(NearestNeighbor, 2Distance_type, QString, "0:EUCLIDEAN_L2;MANHATTAN_L1;MINKOWSKI;MAX;HIST_INTERSECT;HELLINGER;CHI_SQUARE_CS;KULLBACK_LEIBLER_KL");
PARAMETER(NearestNeighbor, 3nndrRatioUsed, bool, true);
PARAMETER(NearestNeighbor, 4nndrRatio, float, 0.8f);
PARAMETER(NearestNeighbor, 5minDistanceUsed, bool, false);
PARAMETER(NearestNeighbor, 6minDistance, float, 1.6f);
PARAMETER(NearestNeighbor, KDTree_trees, int, 4);
PARAMETER(NearestNeighbor, Composite_trees, int, 4);
PARAMETER(NearestNeighbor, Composite_branching, int, 32);
PARAMETER(NearestNeighbor, Composite_iterations, int, 11);
PARAMETER(NearestNeighbor, Composite_centers_init, QString, "0:RANDOM;GONZALES;KMEANSPP");
PARAMETER(NearestNeighbor, Composite_cb_index, double, 0.2);
PARAMETER(NearestNeighbor, Autotuned_target_precision, double, 0.8);
PARAMETER(NearestNeighbor, Autotuned_build_weight, double, 0.01);
PARAMETER(NearestNeighbor, Autotuned_memory_weight, double, 0);
PARAMETER(NearestNeighbor, Autotuned_sample_fraction, double, 0.1);
PARAMETER(NearestNeighbor, KMeans_branching, int, 32);
PARAMETER(NearestNeighbor, KMeans_iterations, int, 11);
PARAMETER(NearestNeighbor, KMeans_centers_init, QString, "0:RANDOM;GONZALES;KMEANSPP");
PARAMETER(NearestNeighbor, KMeans_cb_index, double, 0.2);
PARAMETER(NearestNeighbor, Lsh_table_number, int, 20);
PARAMETER(NearestNeighbor, Lsh_key_size, int, 10);
PARAMETER(NearestNeighbor, Lsh_multi_probe_level, int, 2);
PARAMETER(General, autoStartCamera, bool, false);
PARAMETER(General, autoUpdateObjects, bool, true);
PARAMETER(General, nextObjID, uint, 1);
PARAMETER(General, imageFormats, QString, "*.png *.jpg *.bmp *.tiff")
PARAMETER(General, videoFormats, QString, "*.avi *.m4v *.mp4")
PARAMETER(General, imageFormats, QString, "*.png *.jpg *.bmp *.tiff *.ppm");
PARAMETER(General, videoFormats, QString, "*.avi *.m4v *.mp4");
PARAMETER(General, mirrorView, bool, true);
PARAMETER(General, invertedSearch, bool, false);
PARAMETER(Homography, homographyComputed, bool, true);
PARAMETER(Homography, ransacReprojThr, double, 1.0);
@ -141,8 +167,8 @@ public:
static QString iniDefaultPath();
static QString iniDefaultFileName() {return "config.ini";}
static void loadSettings(const QString & fileName = QString(), QByteArray * windowGeometry = 0);
static void saveSettings(const QString & fileName = QString(), const QByteArray & windowGeometry = QByteArray());
static void loadSettings(const QString & fileName = QString(), QByteArray * windowGeometry = 0, QByteArray * windowState = 0);
static void saveSettings(const QString & fileName = QString(), const QByteArray & windowGeometry = QByteArray(), const QByteArray & windowState = QByteArray());
static const ParametersMap & getDefaultParameters() {return defaultParameters_;}
static const ParametersMap & getParameters() {return parameters_;}
@ -156,6 +182,11 @@ public:
static QString currentDescriptorType();
static QString currentDetectorType();
static QString currentNearestNeighborType();
static cv::flann::IndexParams * createFlannIndexParams();
static cvflann::flann_distance_t getFlannDistanceType();
static cv::flann::SearchParams getFlannSearchParams();
private:
Settings(){}

163
src/rtabmap/PdfPlot.cpp Normal file
View File

@ -0,0 +1,163 @@
// Taken from RTAB-Map library r605 [www.rtabmap.googlecode.com]
/*
* Copyright (C) 2010-2011, Mathieu Labbe and IntRoLab - Universite de Sherbrooke
*
* This file is part of RTAB-Map.
*
* RTAB-Map is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RTAB-Map is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RTAB-Map. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PdfPlot.h"
#define ULOGGER_DEBUG(A, ...)
namespace rtabmap {
PdfPlotItem::PdfPlotItem(float dataX, float dataY, float width, int childCount) :
UPlotItem(dataX, dataY, width),
_img(0),
_imagesRef(0)
{
setLikelihood(dataX, dataY, childCount);
_text = new QGraphicsTextItem(this);
_text->setVisible(false);
}
PdfPlotItem::~PdfPlotItem()
{
}
void PdfPlotItem::setLikelihood(int id, float value, int childCount)
{
if(_img && id != this->data().x())
{
delete _img;
_img = 0;
}
this->setData(QPointF(id, value));
_childCount = childCount;
}
void PdfPlotItem::showDescription(bool shown)
{
if(shown)
{
if(!_img && _imagesRef)
{
QImage img;
QMap<int, QByteArray>::const_iterator iter = _imagesRef->find(int(this->data().x()));
if(iter != _imagesRef->constEnd())
{
if(img.loadFromData(iter.value(), "JPEG"))
{
QPixmap scaled = QPixmap::fromImage(img).scaledToWidth(128);
_img = new QGraphicsPixmapItem(scaled, this);
_img->setVisible(false);
}
}
}
if(_img)
_text->setPos(this->mapFromScene(4+150,0));
else
_text->setPos(this->mapFromScene(4,0));
if(_childCount >= 0)
{
_text->setPlainText(QString("ID = %1\nValue = %2\nWeight = %3").arg(this->data().x()).arg(this->data().y()).arg(_childCount));
}
else
{
_text->setPlainText(QString("ID = %1\nValue = %2").arg(this->data().x()).arg(this->data().y()));
}
_text->setVisible(true);
if(_img)
{
_img->setPos(this->mapFromScene(4,0));
_img->setVisible(true);
}
}
else
{
_text->setVisible(false);
if(_img)
_img->setVisible(false);
}
UPlotItem::showDescription(shown);
}
PdfPlotCurve::PdfPlotCurve(const QString & name, const QMap<int, QByteArray> * imagesMapRef = 0, QObject * parent) :
UPlotCurve(name, parent),
_imagesMapRef(imagesMapRef)
{
}
PdfPlotCurve::~PdfPlotCurve()
{
}
void PdfPlotCurve::clear()
{
UPlotCurve::clear();
}
void PdfPlotCurve::setData(const QMap<int, float> & dataMap, const QMap<int, int> & weightsMap)
{
ULOGGER_DEBUG("dataMap=%d, weightsMap=%d", dataMap.size(), weightsMap.size());
if(dataMap.size() > 0)
{
//match the size of the current data
int margin = int((_items.size()+1)/2) - dataMap.size();
while(margin < 0)
{
PdfPlotItem * newItem = new PdfPlotItem(0, 0, 2, 0);
newItem->setImagesRef(_imagesMapRef);
this->_addValue(newItem);
++margin;
}
while(margin > 0)
{
this->removeItem(0);
--margin;
}
ULOGGER_DEBUG("itemsize=%d", _items.size());
// update values
QList<QGraphicsItem*>::iterator iter = _items.begin();
QMap<int, int>::const_iterator j=weightsMap.begin();
for(QMap<int, float>::const_iterator i=dataMap.begin(); i!=dataMap.end(); ++i, ++j)
{
((PdfPlotItem*)*iter)->setLikelihood(i.key(), i.value(), j!=weightsMap.end()?j.value():-1);
//2 times...
++iter;
++iter;
}
//reset minMax, this will force the plot to update the axes
this->updateMinMax();
emit dataChanged(this);
}
}
}

69
src/rtabmap/PdfPlot.h Normal file
View File

@ -0,0 +1,69 @@
// Taken from RTAB-Map library r605 [www.rtabmap.googlecode.com]
/*
* Copyright (C) 2010-2011, Mathieu Labbe and IntRoLab - Universite de Sherbrooke
*
* This file is part of RTAB-Map.
*
* RTAB-Map is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RTAB-Map is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RTAB-Map. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PDFPLOT_H_
#define PDFPLOT_H_
#include "utilite/UPlot.h"
namespace rtabmap {
class PdfPlotItem : public UPlotItem
{
public:
PdfPlotItem(float dataX, float dataY, float width, int childCount = -1);
virtual ~PdfPlotItem();
void setLikelihood(int id, float value, int childCount);
void setImagesRef(const QMap<int, QByteArray> * imagesRef) {_imagesRef = imagesRef;}
float value() const {return this->data().y();}
int id() const {return this->data().x();}
protected:
virtual void showDescription(bool shown);
private:
QGraphicsTextItem * _text;
QGraphicsPixmapItem * _img;
int _childCount;
const QMap<int, QByteArray> * _imagesRef;
};
class PdfPlotCurve : public UPlotCurve
{
Q_OBJECT
public:
PdfPlotCurve(const QString & name, const QMap<int, QByteArray> * imagesMapRef, QObject * parent = 0);
virtual ~PdfPlotCurve();
virtual void clear();
void setData(const QMap<int, float> & dataMap, const QMap<int, int> & weightsMap);
private:
const QMap<int, QByteArray> * _imagesMapRef;
};
}
#endif /* PDFPLOT_H_ */

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>404</width>
<height>223</height>
<width>527</width>
<height>289</height>
</rect>
</property>
<property name="sizePolicy">
@ -137,6 +137,23 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>OpenCV version :</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_version_opencv">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1104</width>
<height>497</height>
<width>1180</width>
<height>684</height>
</rect>
</property>
<property name="windowTitle">
@ -134,8 +134,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1104</width>
<height>25</height>
<width>1180</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -185,8 +185,8 @@
<widget class="QDockWidget" name="dockWidget_parameters">
<property name="minimumSize">
<size>
<width>300</width>
<height>194</height>
<width>360</width>
<height>376</height>
</size>
</property>
<property name="floating">
@ -200,6 +200,188 @@
</attribute>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Statistics</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0">
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="5" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Detect outliers and GUI</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_timeGui">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Descriptors extraction</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>ms</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_timeExtraction">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>ms</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Features detection</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_timeDetection">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Descriptors matching</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Descriptors indexing</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_timeIndexing">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_timeMatching">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_9">
<property name="text">
<string>ms</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="label_10">
<property name="text">
<string>ms</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="label_12">
<property name="text">
<string>ms</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_minMatchedDistance">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Min matched distance</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Max matched distance</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_maxMatchedDistance">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Total</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_timeTotal">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_16">
<property name="text">
<string>ms</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="ParametersToolBox" name="toolBox">
<property name="currentIndex">
@ -210,171 +392,14 @@
<rect>
<x>0</x>
<y>0</y>
<width>282</width>
<height>365</height>
<width>348</width>
<height>373</height>
</rect>
</property>
<attribute name="label">
<string>Statistics</string>
<string>Dummy</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0">
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="0" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>ms</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_timeExtraction">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>ms</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Descriptors extraction</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Features detection</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_timeDetection">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Descriptors matching</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Descriptors indexing</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_timeIndexing">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_timeMatching">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_9">
<property name="text">
<string>ms</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_10">
<property name="text">
<string>ms</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Detect outliers and GUI</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_timeGui">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="label_12">
<property name="text">
<string>ms</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_minMatchedDistance">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Min matched distance</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Max matched distance</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_maxMatchedDistance">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>174</height>
</size>
</property>
</spacer>
</item>
</layout>
<layout class="QVBoxLayout" name="verticalLayout_5"/>
</widget>
</widget>
</item>
@ -389,12 +414,6 @@
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_objects">
<property name="minimumSize">
<size>
<width>300</width>
<height>196</height>
</size>
</property>
<property name="windowTitle">
<string>Objects</string>
</property>
@ -403,6 +422,12 @@
</attribute>
<widget class="QWidget" name="dockWidgetContents_2">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="objects_area">
<property name="minimumSize">
@ -419,11 +444,14 @@
<rect>
<x>0</x>
<y>0</y>
<width>280</width>
<height>394</height>
<width>265</width>
<height>557</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_objects">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
@ -445,11 +473,58 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_updateObjects">
<property name="text">
<string>Update objects</string>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>12</number>
</property>
</widget>
<item>
<widget class="QPushButton" name="pushButton_updateObjects">
<property name="text">
<string>Update objects</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="horizontalSlider_objectsSize">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<widget class="QDockWidget" name="dockWidget_plot">
<property name="windowTitle">
<string>Likelihood</string>
</property>
<attribute name="dockWidgetArea">
<number>8</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents_3">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="UPlot" name="likelihoodPlot" native="true"/>
</item>
</layout>
</widget>
@ -533,6 +608,12 @@
<header>ParametersToolBox.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>UPlot</class>
<extends>QWidget</extends>
<header>utilite/UPlot.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../resources.qrc"/>

2559
src/utilite/UPlot.cpp Normal file

File diff suppressed because it is too large Load Diff

598
src/utilite/UPlot.h Normal file
View File

@ -0,0 +1,598 @@
// Taken from UtiLite library r185 [www.utilite.googlecode.com]
/*
* utilite is a cross-platform library with
* useful utilities for fast and small developing.
* Copyright (C) 2010 Mathieu Labbe
*
* utilite is free library: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* utilite is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UPLOT_H_
#define UPLOT_H_
//#include "utilite/UtiLiteExp.h" // DLL export/import defines
#include <QtGui/QFrame>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtGui/QPen>
#include <QtGui/QBrush>
#include <QtGui/QGraphicsEllipseItem>
#include <QtCore/QMutex>
#include <QtGui/QLabel>
#include <QtGui/QPushButton>
#include <QtCore/QTime>
class QGraphicsView;
class QGraphicsScene;
class QGraphicsItem;
class QFormLayout;
/**
* UPlotItem is a QGraphicsEllipseItem and can be inherited to do custom behaviors
* on an hoverEnterEvent() for example.
*/
class UPlotItem : public QGraphicsEllipseItem
{
public:
/**
* Constructor 1.
*/
UPlotItem(qreal dataX, qreal dataY, qreal width=2);
/**
* Constructor 2.
*/
UPlotItem(const QPointF & data, qreal width=2);
virtual ~UPlotItem();
public:
void setNextItem(UPlotItem * nextItem);
void setPreviousItem(UPlotItem * previousItem);
void setData(const QPointF & data);
UPlotItem * nextItem() const {return _nextItem;}
UPlotItem * previousItem() const {return _previousItem;};
const QPointF & data() const {return _data;}
protected:
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent * event);
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent * event);
virtual void focusInEvent(QFocusEvent * event);
virtual void focusOutEvent(QFocusEvent * event);
virtual void keyReleaseEvent(QKeyEvent * keyEvent);
virtual void showDescription(bool shown);
private:
QPointF _data;
QGraphicsTextItem * _text;
UPlotItem * _previousItem;
UPlotItem * _nextItem;
};
class UPlot;
/**
* UPlotCurve is a curve used to hold data shown in a UPlot.
*/
class UPlotCurve : public QObject
{
Q_OBJECT
public:
/**
* Constructor 1
*/
UPlotCurve(const QString & name, QObject * parent = 0);
/**
* Constructor 2
*/
UPlotCurve(const QString & name, const QVector<UPlotItem *> data, QObject * parent = 0);
/**
* Constructor 3
*/
UPlotCurve(const QString & name, const QVector<float> & x, const QVector<float> & y, QObject * parent = 0);
virtual ~UPlotCurve();
/**
* Get pen.
*/
const QPen & pen() const {return _pen;}
/**
* Get brush.
*/
const QBrush & brush() const {return _brush;}
/**
* Set pen.
*/
void setPen(const QPen & pen);
/**
* Set brush.
*/
void setBrush(const QBrush & brush);
/**
* Get name.
*/
QString name() const {return _name;}
/**
* Get the number of items in the curve (dot + line items).
*/
int itemsSize() const;
QPointF getItemData(int index);
bool isVisible() const {return _visible;}
void setData(QVector<UPlotItem*> & data); // take the ownership
void setData(const QVector<float> & x, const QVector<float> & y);
void setData(const std::vector<float> & x, const std::vector<float> & y);
void setData(const QVector<float> & y);
void setData(const std::vector<float> & y);
void getData(QVector<float> & x, QVector<float> & y) const; // only call in Qt MainThread
void draw(QPainter * painter);
const QVector<float> & getMinMax() const {return _minMax;}
public slots:
/**
*
* Clear curve's values.
*/
virtual void clear();
/**
*
* Show or hide the curve.
*/
void setVisible(bool visible);
/**
*
* Set increment of the x values (when auto-increment is used).
*/
void setXIncrement(float increment);
/**
*
* Set starting x value (when auto-increment is used).
*/
void setXStart(float val);
/**
*
* Add a single value, using a custom UPlotItem.
*/
void addValue(UPlotItem * data); // take the ownership
/**
*
* Add a single value y, x is auto-incremented by the increment set with setXIncrement().
* @see setXStart()
*/
void addValue(float y);
/**
*
* Add a single value y at x.
*/
void addValue(float x, float y);
/**
*
* For convenience...
* Add a single value y, x is auto-incremented by the increment set with setXIncrement().
* @see setXStart()
*/
void addValue(const QString & y);
/**
*
* For convenience...
* Add multiple values, using custom UPlotItem.
*/
void addValues(QVector<UPlotItem *> & data); // take the ownership
/**
*
* Add multiple values y at x. Vectors must have the same size.
*/
void addValues(const QVector<float> & xs, const QVector<float> & ys);
/**
*
* Add multiple values y, x is auto-incremented by the increment set with setXIncrement().
* @see setXStart()
*/
void addValues(const QVector<float> & ys);
void addValues(const QVector<int> & ys); // for convenience
/**
*
* Add multiple values y, x is auto-incremented by the increment set with setXIncrement().
* @see setXStart()
*/
void addValues(const std::vector<float> & ys); // for convenience
void addValues(const std::vector<int> & ys); // for convenience
signals:
/**
*
* emitted when data is changed.
*/
void dataChanged(const UPlotCurve *);
protected:
friend class UPlot;
void attach(UPlot * plot);
void detach(UPlot * plot);
void updateMinMax();
int removeItem(int index);
void _addValue(UPlotItem * data);;
virtual bool isMinMaxValid() const {return _minMax.size();}
virtual void update(float scaleX, float scaleY, float offsetX, float offsetY, float xDir, float yDir, bool allDataKept);
QList<QGraphicsItem *> _items;
UPlot * _plot;
private:
void removeItem(UPlotItem * item);
private:
QString _name;
QPen _pen;
QBrush _brush;
float _xIncrement;
float _xStart;
bool _visible;
bool _valuesShown;
QVector<float> _minMax; // minX, maxX, minY, maxY
};
/**
* A special UPlotCurve that shows as a line at the specified value, spanning all the UPlot.
*/
class UPlotCurveThreshold : public UPlotCurve
{
Q_OBJECT
public:
/**
* Constructor.
*/
UPlotCurveThreshold(const QString & name, float thesholdValue, Qt::Orientation orientation = Qt::Horizontal, QObject * parent = 0);
virtual ~UPlotCurveThreshold();
public slots:
/**
* Set threshold value.
*/
void setThreshold(float threshold);
/**
* Set orientation (Qt::Horizontal or Qt::Vertical).
*/
void setOrientation(Qt::Orientation orientation);
protected:
friend class UPlot;
virtual void update(float scaleX, float scaleY, float offsetX, float offsetY, float xDir, float yDir, bool allDataKept);
virtual bool isMinMaxValid() const {return false;}
private:
Qt::Orientation _orientation;
};
/**
* The UPlot axis object.
*/
class UPlotAxis : public QWidget
{
public:
/**
* Constructor.
*/
UPlotAxis(Qt::Orientation orientation = Qt::Horizontal, float min=0, float max=1, QWidget * parent = 0);
virtual ~UPlotAxis();
public:
/**
* Set axis minimum and maximum values, compute the resulting
* intervals depending on the size of the axis.
*/
void setAxis(float & min, float & max);
/**
* Size of the border between the first line and the beginning of the widget.
*/
int border() const {return _border;}
/**
* Interval step value.
*/
int step() const {return _step;}
/**
* Number of intervals.
*/
int count() const {return _count;}
/**
* Reverse the axis (for vertical :bottom->up, for horizontal :right->left)
*/
void setReversed(bool reversed); // Vertical :bottom->up, horizontal :right->left
protected:
virtual void paintEvent(QPaintEvent * event);
private:
Qt::Orientation _orientation;
float _min;
float _max;
int _count;
int _step;
bool _reversed;
int _gradMaxDigits;
int _border;
};
/**
* The UPlot legend item. Used internally by UPlot.
*/
class UPlotLegendItem : public QPushButton
{
Q_OBJECT
public:
/**
* Constructor.
*/
UPlotLegendItem(const UPlotCurve * curve, QWidget * parent = 0);
virtual ~UPlotLegendItem();
const UPlotCurve * curve() const {return _curve;}
signals:
void legendItemRemoved(const UPlotCurve *);
protected:
virtual void contextMenuEvent(QContextMenuEvent * event);
private:
const UPlotCurve * _curve;
QMenu * _menu;
QAction * _aChangeText;
QAction * _aResetText;
QAction * _aRemoveCurve;
QAction * _aCopyToClipboard;
};
/**
* The UPlot legend. Used internally by UPlot.
*/
class UPlotLegend : public QWidget
{
Q_OBJECT
public:
/**
* Constructor.
*/
UPlotLegend(QWidget * parent = 0);
virtual ~UPlotLegend();
void setFlat(bool on);
bool isFlat() const {return _flat;}
void addItem(const UPlotCurve * curve);
QPixmap createSymbol(const QPen & pen, const QBrush & brush);
bool remove(const UPlotCurve * curve);
public slots:
void removeLegendItem(const UPlotCurve * curve);
signals:
void legendItemRemoved(const UPlotCurve * curve);
void legendItemToggled(const UPlotCurve * curve, bool toggled);
protected:
virtual void contextMenuEvent(QContextMenuEvent * event);
private slots:
void redirectToggled(bool);
private:
bool _flat;
QMenu * _menu;
QAction * _aUseFlatButtons;
};
/**
* Orientable QLabel. Inherit QLabel and let you to specify the orientation.
*/
class UOrientableLabel : public QLabel
{
Q_OBJECT
public:
/**
* Constructor.
*/
UOrientableLabel(const QString & text, Qt::Orientation orientation = Qt::Horizontal, QWidget * parent = 0);
virtual ~UOrientableLabel();
/**
* Get orientation.
*/
Qt::Orientation orientation() const {return _orientation;}
/**
* Set orientation (Qt::Vertical or Qt::Horizontal).
*/
void setOrientation(Qt::Orientation orientation);
QSize sizeHint() const;
QSize minimumSizeHint() const;
protected:
virtual void paintEvent(QPaintEvent* event);
private:
Qt::Orientation _orientation;
};
/**
* UPlot is a QWidget to create a plot like MATLAB, and
* incrementally add new values like a scope using Qt signals/slots.
* Many customizations can be done at runtime with the right-click menu.
* @image html UPlot.gif
* @image html UPlotMenu.png
*
* Example:
* @code
* #include "utilite/UPlot.h"
* #include <QApplication>
*
* int main(int argc, char * argv[])
* {
* QApplication app(argc, argv);
* UPlot plot;
* UPlotCurve * curve = plot.addCurve("My curve");
* float y[10] = {0, 1, 2, 3, -3, -2, -1, 0, 1, 2};
* curve->addValues(std::vector<float>(y, y+10));
* plot.showGrid(true);
* plot.setGraphicsView(true);
* plot.show();
* app.exec();
* return 0;
* }
* @endcode
* @image html SimplePlot.tiff
*
*
*/
class UPlot : public QWidget
{
Q_OBJECT
public:
/**
* Constructor.
*/
UPlot(QWidget * parent = 0);
virtual ~UPlot();
/**
* Add a curve. The returned curve doesn't need to be deallocated (UPlot keeps the ownership).
*/
UPlotCurve * addCurve(const QString & curveName, const QColor & color = QColor());
/**
* Add a curve. Ownership is transferred to UPlot.
* If you add the curve to more than one UPlot, the ownership is transferred
* to the last UPlot.
*/
bool addCurve(UPlotCurve * curve);
/**
* Get all curve names.
*/
QStringList curveNames();
bool contains(const QString & curveName);
void removeCurves();
/**
* Add a threshold to the plot.
*/
UPlotCurveThreshold * addThreshold(const QString & name, float value, Qt::Orientation orientation = Qt::Horizontal);
QString title() const {return this->objectName();}
QPen getRandomPenColored();
void showLegend(bool shown);
void showGrid(bool shown);
void showRefreshRate(bool shown);
void keepAllData(bool kept);
void showXAxis(bool shown) {_horizontalAxis->setVisible(shown);}
void showYAxis(bool shown) {_verticalAxis->setVisible(shown);}
void setVariableXAxis() {_fixedAxis[0] = false;}
void setVariableYAxis() {_fixedAxis[1] = false;}
void setFixedXAxis(float x1, float x2);
void setFixedYAxis(float y1, float y2);
void setMaxVisibleItems(int maxVisibleItems);
void setTitle(const QString & text);
void setXLabel(const QString & text);
void setYLabel(const QString & text, Qt::Orientation orientation = Qt::Vertical);
void setWorkingDirectory(const QString & workingDirectory);
void setGraphicsView(bool on);
QRectF sceneRect() const;
public slots:
/**
*
* Remove a curve. If UPlot is the parent of the curve, the curve is deleted.
*/
void removeCurve(const UPlotCurve * curve);
void showCurve(const UPlotCurve * curve, bool shown);
void updateAxis(); //reset axis and recompute it with all curves minMax
/**
*
* Clear all curves' data.
*/
void clearData();
private slots:
void captureScreen();
void updateAxis(const UPlotCurve * curve);
protected:
virtual void contextMenuEvent(QContextMenuEvent * event);
virtual void paintEvent(QPaintEvent * event);
virtual void resizeEvent(QResizeEvent * event);
private:
friend class UPlotCurve;
void addItem(QGraphicsItem * item);
private:
void replot(QPainter * painter);
bool updateAxis(float x, float y);
bool updateAxis(float x1, float x2, float y1, float y2);
void setupUi();
void createActions();
void createMenus();
void selectScreenCaptureFormat();
private:
UPlotLegend * _legend;
QGraphicsView * _view;
QGraphicsItem * _sceneRoot;
QWidget * _graphicsViewHolder;
float _axisMaximums[4]; // {x1->x2, y1->y2}
bool _axisMaximumsSet[4]; // {x1->x2, y1->y2}
bool _fixedAxis[2];
UPlotAxis * _verticalAxis;
UPlotAxis * _horizontalAxis;
int _penStyleCount;
int _maxVisibleItems;
QList<QGraphicsLineItem *> hGridLines;
QList<QGraphicsLineItem *> vGridLines;
QList<UPlotCurve*> _curves;
QLabel * _title;
QLabel * _xLabel;
UOrientableLabel * _yLabel;
QLabel * _refreshRate;
QString _workingDirectory;
QTime _refreshIntervalTime;
int _lowestRefreshRate;
QTime _refreshStartTime;
QString _autoScreenCaptureFormat;
QMenu * _menu;
QAction * _aShowLegend;
QAction * _aShowGrid;
QAction * _aKeepAllData;
QAction * _aLimit0;
QAction * _aLimit10;
QAction * _aLimit50;
QAction * _aLimit100;
QAction * _aLimit500;
QAction * _aLimit1000;
QAction * _aLimitCustom;
QAction * _aAddVerticalLine;
QAction * _aAddHorizontalLine;
QAction * _aChangeTitle;
QAction * _aChangeXLabel;
QAction * _aChangeYLabel;
QAction * _aYLabelVertical;
QAction * _aShowRefreshRate;
QAction * _aSaveFigure;
QAction * _aAutoScreenCapture;
QAction * _aClearData;
QAction * _aGraphicsView;
};
#endif /* UPLOT_H_ */