29 #include "ui_exportBundlerDialog.h" 34 #include <QFileDialog> 35 #include <QPushButton> 36 #include <QMessageBox> 37 #include <QTextStream> 44 _ui =
new Ui_ExportBundlerDialog();
47 connect(
_ui->toolButton_path, SIGNAL(clicked()),
this, SLOT(
getPath()));
50 connect(
_ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()),
this, SLOT(
restoreDefaults()));
52 connect(
_ui->doubleSpinBox_laplacianVariance, SIGNAL(valueChanged(
double)),
this, SIGNAL(
configChanged()));
53 connect(
_ui->doubleSpinBox_linearSpeed, SIGNAL(valueChanged(
double)),
this, SIGNAL(
configChanged()));
54 connect(
_ui->doubleSpinBox_angularSpeed, SIGNAL(valueChanged(
double)),
this, SIGNAL(
configChanged()));
55 connect(
_ui->groupBox_export_points, SIGNAL(clicked(
bool)),
this, SIGNAL(
configChanged()));
56 connect(
_ui->sba_iterations, SIGNAL(valueChanged(
int)),
this, SIGNAL(
configChanged()));
57 connect(
_ui->comboBox_sbaType, SIGNAL(currentIndexChanged(
int)),
this, SIGNAL(
configChanged()));
58 connect(
_ui->comboBox_sbaType, SIGNAL(currentIndexChanged(
int)),
this, SLOT(
updateVisibility()));
59 connect(
_ui->sba_rematchFeatures, SIGNAL(stateChanged(
int)),
this, SIGNAL(
configChanged()));
63 _ui->groupBox_export_points->setEnabled(
false);
64 _ui->groupBox_export_points->setChecked(
false);
68 _ui->comboBox_sbaType->setItemData(1, 0, Qt::UserRole - 1);
69 _ui->comboBox_sbaType->setCurrentIndex(0);
73 _ui->comboBox_sbaType->setItemData(0, 0, Qt::UserRole - 1);
74 _ui->comboBox_sbaType->setCurrentIndex(1);
77 _ui->lineEdit_path->setText(QDir::currentPath());
91 settings.beginGroup(group);
93 settings.setValue(
"maxLinearSpeed",
_ui->doubleSpinBox_linearSpeed->value());
94 settings.setValue(
"maxAngularSpeed",
_ui->doubleSpinBox_angularSpeed->value());
95 settings.setValue(
"laplacianThr",
_ui->doubleSpinBox_laplacianVariance->value());
96 settings.setValue(
"exportPoints",
_ui->groupBox_export_points->isChecked());
97 settings.setValue(
"sba_iterations",
_ui->sba_iterations->value());
98 settings.setValue(
"sba_type",
_ui->comboBox_sbaType->currentIndex());
99 settings.setValue(
"sba_variance",
_ui->sba_variance->value());
100 settings.setValue(
"sba_rematch_features",
_ui->sba_rematchFeatures->isChecked());
111 settings.beginGroup(group);
113 _ui->doubleSpinBox_linearSpeed->setValue(settings.value(
"maxLinearSpeed",
_ui->doubleSpinBox_linearSpeed->value()).toDouble());
114 _ui->doubleSpinBox_angularSpeed->setValue(settings.value(
"maxAngularSpeed",
_ui->doubleSpinBox_angularSpeed->value()).toDouble());
115 _ui->doubleSpinBox_laplacianVariance->setValue(settings.value(
"laplacianThr",
_ui->doubleSpinBox_laplacianVariance->value()).toDouble());
116 _ui->groupBox_export_points->setChecked(settings.value(
"exportPoints",
_ui->groupBox_export_points->isChecked()).toBool());
117 _ui->sba_iterations->setValue(settings.value(
"sba_iterations",
_ui->sba_iterations->value()).toInt());
118 _ui->comboBox_sbaType->setCurrentIndex((
Optimizer::Type)settings.value(
"sba_type",
_ui->comboBox_sbaType->currentIndex()).toInt());
119 _ui->sba_variance->setValue(settings.value(
"sba_variance",
_ui->sba_variance->value()).toDouble());
120 _ui->sba_rematchFeatures->setChecked(settings.value(
"sba_rematch_features",
_ui->sba_rematchFeatures->isChecked()).toBool());
129 _ui->lineEdit_path->setText((path.isEmpty()?QDir::currentPath():path) +
"/bundler");
134 _ui->doubleSpinBox_linearSpeed->setValue(0);
135 _ui->doubleSpinBox_angularSpeed->setValue(0);
136 _ui->doubleSpinBox_laplacianVariance->setValue(0);
137 _ui->groupBox_export_points->setChecked(
false);
138 _ui->sba_iterations->setValue(20);
141 _ui->comboBox_sbaType->setCurrentIndex(0);
145 _ui->comboBox_sbaType->setCurrentIndex(1);
148 _ui->sba_variance->setValue(1.0);
149 _ui->sba_rematchFeatures->setChecked(
true);
154 _ui->sba_variance->setVisible(
_ui->comboBox_sbaType->currentIndex() == 0);
155 _ui->label_variance->setVisible(
_ui->comboBox_sbaType->currentIndex() == 0);
160 QString path = QFileDialog::getExistingDirectory(
this, tr(
"Exporting cameras in Bundler format..."),
_ui->lineEdit_path->text());
163 _ui->lineEdit_path->setText(path);
168 const std::map<int, Transform> & poses,
169 const std::multimap<int, Link> & links,
170 const QMap<int, Signature> & signatures,
173 if(this->exec() != QDialog::Accepted)
177 QString path =
_ui->lineEdit_path->text();
180 if(!QDir(path).mkpath(
"."))
182 QMessageBox::warning(
this, tr(
"Exporting cameras..."), tr(
"Failed creating directory %1.").arg(path));
186 std::map<int, cv::Point3f> points3DMap;
187 std::map<int, std::map<int, FeatureBA> > wordReferences;
188 std::map<int, Transform> newPoses = poses;
189 if(
_ui->groupBox_export_points->isEnabled() &&
_ui->groupBox_export_points->isChecked())
191 std::map<int, Transform> posesOut;
192 std::multimap<int, Link> linksOut;
196 uInsert(parametersSBA, std::make_pair(Parameters::kOptimizerIterations(),
uNumber2Str(
_ui->sba_iterations->value())));
197 uInsert(parametersSBA, std::make_pair(Parameters::kg2oPixelVariance(),
uNumber2Str(
_ui->sba_variance->value())));
199 sba->getConnectedGraph(poses.begin()->first, poses, links, posesOut, linksOut);
200 newPoses = sba->optimizeBA(
201 posesOut.begin()->first,
204 signatures.toStdMap(),
207 _ui->sba_rematchFeatures->isChecked());
212 QMessageBox::warning(
this, tr(
"Exporting cameras..."), tr(
"SBA optimization failed! Cannot export with 3D points.").arg(path));
218 QFile fileOut(path+QDir::separator()+
"cameras.out");
219 QFile fileList(path+QDir::separator()+
"list.txt");
220 QFile fileListKeys(path+QDir::separator()+
"list_keys.txt");
221 QDir(path).mkdir(
"images");
222 if(wordReferences.size())
224 QDir(path).mkdir(
"keys");
226 if(fileOut.open(QIODevice::WriteOnly | QIODevice::Text))
228 if(fileList.open(QIODevice::WriteOnly | QIODevice::Text))
230 std::map<int, Transform> cameras;
231 std::map<int, int> cameraIndexes;
233 std::map<int, QColor> colors;
234 for(std::map<int, Transform>::const_iterator iter=newPoses.begin(); iter!=newPoses.end(); ++iter)
236 if(signatures.find(iter->first) != signatures.end())
238 cv::Mat image = signatures[iter->first].sensorData().imageRaw();
241 signatures[iter->first].sensorData().uncompressDataConst(&image, 0, 0, 0);
244 double maxLinearVel =
_ui->doubleSpinBox_linearSpeed->value();
245 double maxAngularVel =
_ui->doubleSpinBox_angularSpeed->value();
246 double laplacianThr =
_ui->doubleSpinBox_laplacianVariance->value();
247 bool blurryImage =
false;
248 const std::vector<float> & velocity = signatures[iter->first].getVelocity();
249 if(maxLinearVel>0.0 || maxAngularVel>0.0)
251 if(velocity.size() == 6)
253 float transVel =
uMax3(fabs(velocity[0]), fabs(velocity[1]), fabs(velocity[2]));
254 float rotVel =
uMax3(fabs(velocity[3]), fabs(velocity[4]), fabs(velocity[5]));
255 if(maxLinearVel>0.0 && transVel > maxLinearVel)
257 UWARN(
"Fast motion detected for camera %d (speed=%f m/s > thr=%f m/s), camera is ignored for texturing.", iter->first, transVel, maxLinearVel);
260 else if(maxAngularVel>0.0 && rotVel > maxAngularVel)
262 UWARN(
"Fast motion detected for camera %d (speed=%f rad/s > thr=%f rad/s), camera is ignored for texturing.", iter->first, rotVel, maxAngularVel);
268 UWARN(
"Camera motion filtering is set, but velocity of camera %d is not available.", iter->first);
272 if(!blurryImage && !image.empty() && laplacianThr>0.0)
274 cv::Mat imgLaplacian;
275 cv::Laplacian(image, imgLaplacian, CV_16S);
277 cv::meanStdDev(imgLaplacian, m, s);
278 double stddev_pxl = s.at<
double>(0);
279 double var = stddev_pxl*stddev_pxl;
280 if(var < laplacianThr)
283 UWARN(
"Camera's image %d is detected as blurry (var=%f < thr=%f), camera is ignored for texturing.", iter->first, var, laplacianThr);
288 cameras.insert(*iter);
289 cameraIndexes.insert(std::make_pair(iter->first, camIndex++));
290 QString p = QString(
"images")+QDir::separator()+tr(
"%1.jpg").arg(iter->first);
291 p = path+QDir::separator()+p;
292 if(cv::imwrite(p.toStdString(), image))
294 UINFO(
"saved image %s", p.toStdString().c_str());
298 UERROR(
"Failed to save image %s", p.toStdString().c_str());
317 if(wordReferences.size())
319 std::list<FeatureBA> descriptors;
320 for(std::map<
int, std::map<int, FeatureBA> >::iterator jter=wordReferences.begin(); jter!=wordReferences.end(); ++jter)
322 for(std::map<int, FeatureBA>::iterator kter=jter->second.begin(); kter!=jter->second.end(); ++kter)
324 if(kter->first == iter->first)
326 if(!kter->second.descriptor.empty())
328 descriptors.push_back(kter->second);
331 if(colors.find(jter->first) == colors.end())
334 kter->second.kpt.pt.x >= 0.0f && (int)kter->second.kpt.pt.x < image.cols &&
335 kter->second.kpt.pt.y >= 0.0f && (
int)kter->second.kpt.pt.y < image.rows)
337 UASSERT(image.type() == CV_8UC3 || image.type() == CV_8UC1);
339 if(image.channels() == 3)
341 cv::Vec3b & pixel = image.at<cv::Vec3b>((int)kter->second.kpt.pt.y, (
int)kter->second.kpt.pt.x);
342 c.setRgb(pixel[2], pixel[1], pixel[0]);
346 unsigned char & pixel = image.at<
unsigned char>((int)kter->second.kpt.pt.y, (
int)kter->second.kpt.pt.x);
347 c.setRgb(pixel, pixel, pixel);
349 colors.insert(std::make_pair(jter->first, c));
356 QString p = QString(
"keys")+QDir::separator()+tr(
"%1.key").arg(iter->first);
357 p = path+QDir::separator()+p;
359 if(fileKey.open(QIODevice::WriteOnly | QIODevice::Text))
361 if(descriptors.size())
363 QTextStream key(&fileKey);
364 key << descriptors.size() <<
" " << descriptors.front().descriptor.cols <<
"\n";
365 for(std::list<FeatureBA>::iterator dter=descriptors.begin(); dter!=descriptors.end(); ++dter)
368 int octave = dter->kpt.octave & 255;
369 octave = octave < 128 ? octave : (-128 | octave);
370 float scale = octave >= 0 ? 1.f/(1 << octave) : (
float)(1 << -octave);
372 key << dter->kpt.pt.x <<
" " << dter->kpt.pt.y <<
" " << scale <<
" " << dter->kpt.angle <<
"\n";
373 for(
int i=0; i<dter->descriptor.cols; ++i)
375 if(dter->descriptor.type() == CV_8U)
377 key <<
" " << (int)dter->descriptor.at<
unsigned char>(i);
381 key <<
" " << (int)dter->descriptor.at<
float>(i);
383 if((i+1)%20 == 0 && i+1 < dter->descriptor.cols)
393 UWARN(
"No descriptors saved for frame %d in file %s. " 394 "Descriptors may not have been saved in the nodes. " 395 "Verify that parameter %s was true during mapping.",
396 iter->first, p.toStdString().c_str(),
397 Parameters::kMemRawDescriptorsKept().c_str());
406 UWARN(
"Could not find node data for pose %d", iter->first);
411 0.0
f, -1.0
f, 0.0
f, 0.0
f,
412 0.0
f, 0.0
f, 1.0
f, 0.0
f,
413 -1.0
f, 0.0
f, 0.0
f, 0.0
f);
415 static const Transform optical_rotation_inv(
416 0.0
f, -1.0
f, 0.0
f, 0.0
f,
417 0.0
f, 0.0
f, -1.0
f, 0.0
f,
418 1.0
f, 0.0
f, 0.0
f, 0.0
f);
420 QTextStream out(&fileOut);
421 QTextStream list(&fileList);
422 out <<
"# Bundle file v0.3\n";
423 out << cameras.size() <<
" " << points3DMap.size() <<
"\n";
434 for(std::map<int, Transform>::iterator iter=cameras.begin(); iter!=cameras.end(); ++iter)
436 QString p = QString(
"images")+QDir::separator()+tr(
"%1.jpg").arg(iter->first);
440 if(signatures[iter->first].sensorData().cameraModels().size())
442 out << signatures[iter->first].sensorData().cameraModels().at(0).fx() <<
" 0 0\n";
443 localTransform = signatures[iter->first].sensorData().cameraModels().at(0).localTransform();
447 out << signatures[iter->first].sensorData().stereoCameraModel().left().fx() <<
" 0 0\n";
448 localTransform = signatures[iter->first].sensorData().stereoCameraModel().left().localTransform();
452 if(!localTransform.
isNull())
454 pose*=localTransform*optical_rotation_inv;
458 out << poseGL.
r11() <<
" " << poseGL.
r12() <<
" " << poseGL.
r13() <<
"\n";
459 out << poseGL.
r21() <<
" " << poseGL.
r22() <<
" " << poseGL.
r23() <<
"\n";
460 out << poseGL.
r31() <<
" " << poseGL.
r32() <<
" " << poseGL.
r33() <<
"\n";
461 out << poseGL.
x() <<
" " << poseGL.
y() <<
" " << poseGL.
z() <<
"\n";
463 if(wordReferences.size())
465 if(fileListKeys.open(QIODevice::WriteOnly | QIODevice::Text))
467 QTextStream listKeys(&fileListKeys);
468 for(std::map<int, Transform>::iterator iter=cameras.begin(); iter!=cameras.end(); ++iter)
470 QString p = QString(
"keys")+QDir::separator()+tr(
"%1.key").arg(iter->first);
471 listKeys << p <<
"\n";
473 fileListKeys.close();
497 std::map<int, int> descriptorIndexes;
498 for(std::map<int, cv::Point3f>::iterator iter=points3DMap.begin(); iter!=points3DMap.end(); ++iter)
500 std::map<int, std::map<int, FeatureBA> >::iterator jter = wordReferences.find(iter->first);
501 out << iter->second.x <<
" " << iter->second.y <<
" " << iter->second.z <<
"\n";
502 UASSERT(colors.find(iter->first) != colors.end());
503 QColor & c = colors.at(iter->first);
504 out << c.red() <<
" " << c.green() <<
" " << c.blue() <<
"\n";
505 out << jter->second.size();
506 for(std::map<int, FeatureBA>::iterator kter = jter->second.begin(); kter!=jter->second.end(); ++kter)
509 int camId = kter->first;
510 UASSERT(signatures.contains(camId));
511 UASSERT(cameraIndexes.find(camId) != cameraIndexes.end());
514 if(signatures[camId].sensorData().cameraModels().size())
517 pt.y = kter->second.kpt.pt.y - s.sensorData().cameraModels().at(0).cy();
521 pt.x = kter->second.kpt.pt.x - s.sensorData().stereoCameraModel().left().cx();
522 pt.y = kter->second.kpt.pt.y - s.sensorData().stereoCameraModel().left().cy();
524 descriptorIndexes.insert(std::make_pair(camId, 0));
525 out <<
" " << cameraIndexes.at(camId) <<
" " << descriptorIndexes.at(camId)++ <<
" " << pt.x <<
" " << -pt.y;
533 QMessageBox::information(
this,
534 tr(
"Exporting cameras in Bundler format..."),
535 tr(
"%1 cameras/images and %2 points exported to directory \"%3\".%4")
536 .arg(newPoses.size())
537 .arg(points3DMap.size())
539 .arg(newPoses.size()>cameras.size()?tr(
" %1/%2 cameras ignored for too fast motion and/or blur level.").arg(newPoses.size()-cameras.size()).arg(newPoses.size()):
""));
544 QMessageBox::warning(
this, tr(
"Exporting cameras..."), tr(
"Failed opening file %1 for writing.").arg(path+QDir::separator()+
"list.txt"));
549 QMessageBox::warning(
this, tr(
"Exporting cameras..."), tr(
"Failed opening file %1 for writing.").arg(path+QDir::separator()+
"cameras.out"));
void exportBundler(const std::map< int, Transform > &poses, const std::multimap< int, Link > &links, const QMap< int, Signature > &signatures, const ParametersMap ¶meters)
ExportBundlerDialog(QWidget *parent=0)
virtual ~ExportBundlerDialog()
std::map< std::string, std::string > ParametersMap
Basic mathematics functions.
void setWorkingDirectory(const QString &path)
static bool isAvailable(Optimizer::Type type)
#define UASSERT(condition)
Wrappers of STL for convenient functions.
static const rtabmap::Transform opengl_world_T_rtabmap_world(0.0f,-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,-1.0f, 0.0f, 0.0f, 0.0f)
T uMax3(const T &a, const T &b, const T &c)
void saveSettings(QSettings &settings, const QString &group="") const
const std::vector< CameraModel > & cameraModels() const
Ui_ExportBundlerDialog * _ui
SensorData & sensorData()
void loadSettings(QSettings &settings, const QString &group="")
std::string UTILITE_EXP uNumber2Str(unsigned int number)
static Optimizer * create(const ParametersMap ¶meters)
void uInsert(std::map< K, V > &map, const std::pair< K, V > &pair)