mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-13 06:50:19 +08:00
Reprocess: added --skip option (to skip frames when reprocessing). Rtabmap: always save Loop/Map_id/ stat. UPlot: export NaN insted of NA for invalid values (compatible with octave). GraphViewer and ImageView: added support for PDF output format, also changed default directory to User's Pictures standard folder.
This commit is contained in:
@@ -3180,7 +3180,9 @@ bool Rtabmap::process(
|
||||
statistics_.setProximityDetectionId(lastProximitySpaceClosureId);
|
||||
statistics_.setProximityDetectionMapId(_memory->getMapId(lastProximitySpaceClosureId));
|
||||
|
||||
statistics_.addStatistic(Statistics::kLoopId(), _loopClosureHypothesis.first>0?_loopClosureHypothesis.first:lastProximitySpaceClosureId);
|
||||
int loopId = _loopClosureHypothesis.first>0?_loopClosureHypothesis.first:lastProximitySpaceClosureId;
|
||||
statistics_.addStatistic(Statistics::kLoopId(), loopId);
|
||||
statistics_.addStatistic(Statistics::kLoopMap_id(), (loopId>0 && sLoop)?sLoop->mapId():-1);
|
||||
|
||||
float x,y,z,roll,pitch,yaw;
|
||||
if(_loopClosureHypothesis.first || lastProximitySpaceClosureId)
|
||||
@@ -3192,7 +3194,6 @@ bool Rtabmap::process(
|
||||
UINFO("Set loop closure transform = %s", loopIter->second.transform().prettyPrint().c_str());
|
||||
statistics_.setLoopClosureTransform(loopIter->second.transform());
|
||||
|
||||
statistics_.addStatistic(Statistics::kLoopMap_id(), sLoop->mapId());
|
||||
statistics_.addStatistic(Statistics::kLoopVisual_words(), sLoop->getWords().size());
|
||||
statistics_.addStatistic(Statistics::kLoopDistance_since_last_loc(), _distanceTravelledSinceLastLocalization);
|
||||
_distanceTravelledSinceLastLocalization = 0.0f;
|
||||
|
||||
+85
-64
@@ -37,6 +37,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <QtGui/QDesktopServices>
|
||||
#include <QtGui/QContextMenuEvent>
|
||||
#include <QColorDialog>
|
||||
#include <QPrinter>
|
||||
#include <QFileDialog>
|
||||
#ifdef QT_SVG_LIB
|
||||
#include <QtSvg/QSvgGenerator>
|
||||
#endif
|
||||
@@ -54,6 +56,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <rtabmap/utilite/ULogger.h>
|
||||
#include <rtabmap/utilite/UTimer.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QStandardPaths>
|
||||
#endif
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
class NodeItem: public QGraphicsEllipseItem
|
||||
@@ -1540,11 +1547,7 @@ QIcon createIcon(const QColor & color)
|
||||
void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
||||
{
|
||||
QMenu menu;
|
||||
QAction * aScreenShotPNG = menu.addAction(tr("Take a screenshot (PNG)"));
|
||||
QAction * aScreenShotSVG = menu.addAction(tr("Take a screenshot (SVG)"));
|
||||
#ifndef QT_SVG_LIB
|
||||
aScreenShotSVG->setEnabled(false);
|
||||
#endif
|
||||
QAction * aScreenShot = menu.addAction(tr("Take a screenshot..."));
|
||||
menu.addSeparator();
|
||||
|
||||
QAction * aChangeNodeColor = menu.addAction(createIcon(_nodeColor), tr("Set node color..."));
|
||||
@@ -1710,85 +1713,103 @@ void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
|
||||
QAction * aRestoreDefaults = menu.addAction(tr("Restore defaults"));
|
||||
|
||||
QAction * r = menu.exec(event->globalPos());
|
||||
if(r == aScreenShotPNG || r == aScreenShotSVG)
|
||||
if(r == aScreenShot)
|
||||
{
|
||||
if(_root)
|
||||
{
|
||||
QString targetDir = _workingDirectory + "/ScreensCaptured";
|
||||
QString filePath;
|
||||
#if QT_VERSION >= 0x050000
|
||||
filePath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
|
||||
#endif
|
||||
QDir dir;
|
||||
if(!dir.exists(targetDir))
|
||||
if(!dir.exists(filePath))
|
||||
{
|
||||
dir.mkdir(targetDir);
|
||||
filePath = QDir::homePath();
|
||||
}
|
||||
targetDir += "/";
|
||||
targetDir += "Graph_view";
|
||||
if(!dir.exists(targetDir))
|
||||
{
|
||||
dir.mkdir(targetDir);
|
||||
}
|
||||
targetDir += "/";
|
||||
bool isPNG = r == aScreenShotPNG;
|
||||
QString name = (QDateTime::currentDateTime().toString("yyMMddhhmmsszzz") + (isPNG?".png":".svg"));
|
||||
filePath += "/graph.png";
|
||||
|
||||
if(_gridCellSize)
|
||||
#ifdef QT_SVG_LIB
|
||||
filePath = QFileDialog::getSaveFileName(this, tr("Save figure to ..."), filePath, "*.png *.xpm *.jpg *.pdf *.svg");
|
||||
#else
|
||||
filePath = QFileDialog::getSaveFileName(this, tr("Save figure to ..."), filePath, "*.png *.xpm *.jpg *.pdf");
|
||||
#endif
|
||||
if(!filePath.isEmpty())
|
||||
{
|
||||
_root->setScale(1.0f/(_gridCellSize*100.0f)); // grid map precision (for 5cm grid cell, x20 to have 1pix/5cm)
|
||||
}
|
||||
else
|
||||
{
|
||||
_root->setScale(this->transform().m11()); // current view
|
||||
}
|
||||
|
||||
this->scene()->clearSelection(); // Selections would also render to the file
|
||||
this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
|
||||
QSize sceneSize = this->scene()->sceneRect().size().toSize();
|
||||
|
||||
if(isPNG)
|
||||
{
|
||||
QImage image(sceneSize, QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
|
||||
image.fill(Qt::transparent); // Start all pixels transparent
|
||||
QPainter painter(&image);
|
||||
|
||||
this->scene()->render(&painter);
|
||||
if(!image.isNull())
|
||||
if(QFileInfo(filePath).suffix() == "")
|
||||
{
|
||||
image.save(targetDir + name);
|
||||
//use png by default
|
||||
filePath += ".png";
|
||||
}
|
||||
|
||||
if(_gridCellSize)
|
||||
{
|
||||
_root->setScale(1.0f/(_gridCellSize*100.0f)); // grid map precision (for 5cm grid cell, x20 to have 1pix/5cm)
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(this,
|
||||
tr("Save PNG"),
|
||||
tr("Could not export in PNG (the scene may be too large %1x%2), try saving in SVG.").arg(sceneSize.width()).arg(sceneSize.height()));
|
||||
_root->setScale(this->transform().m11()); // current view
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
this->scene()->clearSelection(); // Selections would also render to the file
|
||||
this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
|
||||
QSize sceneSize = this->scene()->sceneRect().size().toSize();
|
||||
|
||||
if(QFileInfo(filePath).suffix().compare("pdf") == 0)
|
||||
{
|
||||
QPrinter printer(QPrinter::HighResolution);
|
||||
printer.setOrientation(QPrinter::Portrait);
|
||||
printer.setOutputFileName( filePath );
|
||||
QPainter p(&printer);
|
||||
scene()->render(&p);
|
||||
p.end();
|
||||
}
|
||||
else if(QFileInfo(filePath).suffix().compare("svg") == 0)
|
||||
{
|
||||
#ifdef QT_SVG_LIB
|
||||
QSvgGenerator svgGen;
|
||||
QSvgGenerator svgGen;
|
||||
|
||||
svgGen.setFileName( targetDir + name );
|
||||
svgGen.setSize(sceneSize);
|
||||
// add 1% border to make sure values are not cropped
|
||||
int borderH = sceneSize.width()/100;
|
||||
int borderV = sceneSize.height()/100;
|
||||
svgGen.setViewBox(QRect(-borderH, -borderV, sceneSize.width()+borderH*2, sceneSize.height()+borderV*2));
|
||||
svgGen.setTitle(tr("RTAB-Map graph"));
|
||||
svgGen.setDescription(tr("RTAB-Map map and graph"));
|
||||
svgGen.setFileName( filePath );
|
||||
svgGen.setSize(sceneSize);
|
||||
// add 1% border to make sure values are not cropped
|
||||
int borderH = sceneSize.width()/100;
|
||||
int borderV = sceneSize.height()/100;
|
||||
svgGen.setViewBox(QRect(-borderH, -borderV, sceneSize.width()+borderH*2, sceneSize.height()+borderV*2));
|
||||
svgGen.setTitle(tr("RTAB-Map graph"));
|
||||
svgGen.setDescription(tr("RTAB-Map map and graph"));
|
||||
|
||||
QPainter painter( &svgGen );
|
||||
QPainter painter( &svgGen );
|
||||
|
||||
this->scene()->render(&painter);
|
||||
this->scene()->render(&painter);
|
||||
#else
|
||||
UERROR("RTAB-MAp is not built with Qt's SVG library, cannot save picture in svg format.");
|
||||
UERROR("RTAB-MAp is not built with Qt's SVG library, cannot save picture in svg format.");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
QImage image(sceneSize, QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
|
||||
image.fill(Qt::transparent); // Start all pixels transparent
|
||||
QPainter painter(&image);
|
||||
|
||||
this->scene()->render(&painter);
|
||||
if(!image.isNull())
|
||||
{
|
||||
image.save(filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(this,
|
||||
tr("Save PNG"),
|
||||
tr("Could not export in PNG (the scene may be too large %1x%2), try saving in SVG.").arg(sceneSize.width()).arg(sceneSize.height()));
|
||||
}
|
||||
}
|
||||
|
||||
//reset scale
|
||||
_root->setScale(1.0f);
|
||||
this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
|
||||
|
||||
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
|
||||
}
|
||||
|
||||
//reset scale
|
||||
_root->setScale(1.0f);
|
||||
this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
|
||||
|
||||
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(targetDir + name));
|
||||
}
|
||||
return; // without emitting configChanged
|
||||
}
|
||||
|
||||
@@ -37,11 +37,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <QInputDialog>
|
||||
#include <QVBoxLayout>
|
||||
#include <QColorDialog>
|
||||
#include <QPrinter>
|
||||
#include <QGraphicsRectItem>
|
||||
#include "rtabmap/utilite/ULogger.h"
|
||||
#include "rtabmap/gui/KeypointItem.h"
|
||||
#include "rtabmap/core/util2d.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#if QT_VERSION >= 0x050000
|
||||
#include <QStandardPaths>
|
||||
#endif
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
//LineItem
|
||||
@@ -158,7 +164,6 @@ QIcon ImageView::createIcon(const QColor & color)
|
||||
|
||||
ImageView::ImageView(QWidget * parent) :
|
||||
QWidget(parent),
|
||||
_savedFileName((QDir::homePath()+ "/") + "picture" + ".png"),
|
||||
_alpha(100),
|
||||
_featuresSize(0.0f),
|
||||
_defaultBgColor(Qt::black),
|
||||
@@ -168,6 +173,19 @@ ImageView::ImageView(QWidget * parent) :
|
||||
_imageItem(0),
|
||||
_imageDepthItem(0)
|
||||
{
|
||||
#if QT_VERSION >= 0x050000
|
||||
_savedFileName = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
|
||||
#endif
|
||||
QDir dir;
|
||||
if(!dir.exists(_savedFileName))
|
||||
{
|
||||
_savedFileName = QDir::homePath()+ "/picture.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
_savedFileName += "/picture.png";
|
||||
}
|
||||
|
||||
_graphicsView = new QGraphicsView(this);
|
||||
_graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
|
||||
_graphicsView->setScene(new QGraphicsScene(this));
|
||||
@@ -806,11 +824,16 @@ void ImageView::contextMenuEvent(QContextMenuEvent * e)
|
||||
if(!_graphicsView->scene()->sceneRect().isNull())
|
||||
{
|
||||
QString text;
|
||||
QString extensions = "*.png *.xpm *.jpg";
|
||||
if(_graphicsView->isVisible())
|
||||
{
|
||||
extensions += " *.pdf";
|
||||
}
|
||||
#ifdef QT_SVG_LIB
|
||||
text = QFileDialog::getSaveFileName(this, tr("Save figure to ..."), _savedFileName, "*.png *.xpm *.jpg *.pdf *.svg");
|
||||
#else
|
||||
text = QFileDialog::getSaveFileName(this, tr("Save figure to ..."), _savedFileName, "*.png *.xpm *.jpg *.pdf");
|
||||
extensions += " *.svg";
|
||||
#endif
|
||||
text = QFileDialog::getSaveFileName(this, tr("Save figure to ..."), _savedFileName, extensions);
|
||||
|
||||
if(!text.isEmpty())
|
||||
{
|
||||
if(QFileInfo(text).suffix() == "")
|
||||
@@ -820,17 +843,34 @@ void ImageView::contextMenuEvent(QContextMenuEvent * e)
|
||||
}
|
||||
|
||||
_savedFileName = text;
|
||||
QImage img(_graphicsView->sceneRect().width(), _graphicsView->sceneRect().height(), QImage::Format_ARGB32_Premultiplied);
|
||||
QPainter p(&img);
|
||||
if(_graphicsView->isVisible())
|
||||
if(QFileInfo(text).suffix().compare("pdf") == 0)
|
||||
{
|
||||
QPrinter printer(QPrinter::HighResolution);
|
||||
printer.setOrientation(QPrinter::Portrait);
|
||||
printer.setOutputFileName( text );
|
||||
QPainter p(&printer);
|
||||
p.begin(&printer);
|
||||
double xscale = printer.pageRect().width()/double(_graphicsView->sceneRect().width());
|
||||
double yscale = printer.pageRect().height()/double(_graphicsView->sceneRect().height());
|
||||
double scale = qMin(xscale, yscale);
|
||||
p.scale(scale, scale);
|
||||
_graphicsView->scene()->render(&p, _graphicsView->sceneRect(), _graphicsView->sceneRect());
|
||||
p.end();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->render(&p, QPoint(), _graphicsView->sceneRect().toRect());
|
||||
QImage img(_graphicsView->sceneRect().width(), _graphicsView->sceneRect().height(), QImage::Format_ARGB32_Premultiplied);
|
||||
QPainter p(&img);
|
||||
if(_graphicsView->isVisible())
|
||||
{
|
||||
_graphicsView->scene()->render(&p, _graphicsView->sceneRect(), _graphicsView->sceneRect());
|
||||
}
|
||||
else
|
||||
{
|
||||
this->render(&p, QPoint(), _graphicsView->sceneRect().toRect());
|
||||
}
|
||||
img.save(text);
|
||||
}
|
||||
img.save(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1743,7 +1743,7 @@ void UPlotLegend::contextMenuEvent(QContextMenuEvent * event)
|
||||
{
|
||||
if(uIsNan(axes[j][i]))
|
||||
{
|
||||
text.append("NA");
|
||||
text.append("NaN"); // NaN is interpreted by default as NaN in MatLab/Octave
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -58,6 +58,7 @@ void showUsage()
|
||||
" To see warnings when loop closures are rejected, add \"--uwarn\" argument.\n"
|
||||
" Options:\n"
|
||||
" -r Use database stamps as input rate.\n"
|
||||
" -skip # Skip # frames after each processed frame (default 0=don't skip any frames).\n"
|
||||
" -c \"path.ini\" Configuration file, overwriting parameters read \n"
|
||||
" from the database. If custom parameters are also set as \n"
|
||||
" arguments, they overwrite those in config file and the database.\n"
|
||||
@@ -213,6 +214,7 @@ int main(int argc, char * argv[])
|
||||
bool useDatabaseRate = false;
|
||||
int startId = 0;
|
||||
int stopId = 0;
|
||||
int framesToSkip = 0;
|
||||
bool scanFromDepth = false;
|
||||
int scanDecimation = 1;
|
||||
float scanRangeMin = 0.0f;
|
||||
@@ -257,7 +259,7 @@ int main(int argc, char * argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("-start option require a value\n");
|
||||
printf("-start option requires a value\n");
|
||||
showUsage();
|
||||
}
|
||||
}
|
||||
@@ -271,7 +273,21 @@ int main(int argc, char * argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("-stop option require a value\n");
|
||||
printf("-stop option requires a value\n");
|
||||
showUsage();
|
||||
}
|
||||
}
|
||||
else if (strcmp(argv[i], "-skip") == 0 || strcmp(argv[i], "--skip") == 0)
|
||||
{
|
||||
++i;
|
||||
if(i < argc - 2)
|
||||
{
|
||||
framesToSkip = atoi(argv[i]);
|
||||
printf("Will skip %d frames.\n", framesToSkip);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("-skip option requires a value\n");
|
||||
showUsage();
|
||||
}
|
||||
}
|
||||
@@ -516,6 +532,11 @@ int main(int argc, char * argv[])
|
||||
delete dbDriver;
|
||||
dbDriver = 0;
|
||||
|
||||
if(framesToSkip)
|
||||
{
|
||||
totalIds/=framesToSkip+1;
|
||||
}
|
||||
|
||||
std::string workingDirectory = UDirectory::getDir(outputDatabasePath);
|
||||
printf("Set working directory to \"%s\".\n", workingDirectory.c_str());
|
||||
uInsert(parameters, ParametersPair(Parameters::kRtabmapWorkingDirectory(), workingDirectory));
|
||||
@@ -742,6 +763,26 @@ int main(int argc, char * argv[])
|
||||
}
|
||||
|
||||
Transform odomPose = info.odomPose;
|
||||
|
||||
if(framesToSkip>0)
|
||||
{
|
||||
int skippedFrames = framesToSkip;
|
||||
while(skippedFrames-- > 0)
|
||||
{
|
||||
data = dbReader->takeImage(&info);
|
||||
if(!odometryIgnored && !info.odomCovariance.empty() && info.odomCovariance.at<double>(0,0)>=9999)
|
||||
{
|
||||
printf("High variance detected, triggering a new map...\n");
|
||||
if(!incrementalMemory && processed>0)
|
||||
{
|
||||
showLocalizationStats(outputDatabasePath);
|
||||
lastLocalizationOdomPose = info.odomPose;
|
||||
}
|
||||
rtabmap.triggerNewMap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data = dbReader->takeImage(&info);
|
||||
if(scanFromDepth)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user