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())));
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 QMap<int, Signature>::const_iterator ster = signatures.find(
iter->first);
237 if(ster!= signatures.end())
239 cv::Mat image = ster.value().sensorData().imageRaw();
242 ster.value().sensorData().uncompressDataConst(&image, 0, 0, 0);
245 double maxLinearVel =
_ui->doubleSpinBox_linearSpeed->value();
246 double maxAngularVel =
_ui->doubleSpinBox_angularSpeed->value();
247 double laplacianThr =
_ui->doubleSpinBox_laplacianVariance->value();
248 bool blurryImage =
false;
249 const std::vector<float> &
velocity = ster.value().getVelocity();
250 if(maxLinearVel>0.0 || maxAngularVel>0.0)
256 if(maxLinearVel>0.0 && transVel > maxLinearVel)
258 UWARN(
"Fast motion detected for camera %d (speed=%f m/s > thr=%f m/s), camera is ignored for texturing.",
iter->first, transVel, maxLinearVel);
261 else if(maxAngularVel>0.0 && rotVel > maxAngularVel)
263 UWARN(
"Fast motion detected for camera %d (speed=%f rad/s > thr=%f rad/s), camera is ignored for texturing.",
iter->first, rotVel, maxAngularVel);
269 UWARN(
"Camera motion filtering is set, but velocity of camera %d is not available.",
iter->first);
273 if(!blurryImage && !image.empty() && laplacianThr>0.0)
275 cv::Mat imgLaplacian;
276 cv::Laplacian(image, imgLaplacian, CV_16S);
278 cv::meanStdDev(imgLaplacian,
m,
s);
279 double stddev_pxl =
s.at<
double>(0);
280 double var = stddev_pxl*stddev_pxl;
281 if(var < laplacianThr)
284 UWARN(
"Camera's image %d is detected as blurry (var=%f < thr=%f), camera is ignored for texturing.",
iter->first, var, laplacianThr);
289 cameras.insert(*
iter);
290 cameraIndexes.insert(std::make_pair(
iter->first, camIndex++));
291 QString
p = QString(
"images")+QDir::separator()+tr(
"%1.jpg").arg(
iter->first);
292 p =
path+QDir::separator()+
p;
293 if(cv::imwrite(
p.toStdString(), image))
295 UINFO(
"saved image %s",
p.toStdString().c_str());
299 UERROR(
"Failed to save image %s",
p.toStdString().c_str());
318 if(wordReferences.size())
320 std::list<FeatureBA> descriptors;
321 for(std::map<
int, std::map<int, FeatureBA> >::
iterator jter=wordReferences.begin(); jter!=wordReferences.end(); ++jter)
323 for(std::map<int, FeatureBA>::iterator kter=jter->second.begin(); kter!=jter->second.end(); ++kter)
325 if(kter->first ==
iter->first)
327 if(!kter->second.descriptor.empty())
329 descriptors.push_back(kter->second);
332 if(colors.find(jter->first) == colors.end())
335 kter->second.kpt.pt.x >= 0.0f && (
int)kter->second.kpt.pt.x < image.cols &&
336 kter->second.kpt.pt.y >= 0.0f && (
int)kter->second.kpt.pt.y < image.rows)
338 UASSERT(image.type() == CV_8UC3 || image.type() == CV_8UC1);
340 if(image.channels() == 3)
342 cv::Vec3b & pixel = image.at<cv::Vec3b>((
int)kter->second.kpt.pt.y, (
int)kter->second.kpt.pt.x);
343 c.setRgb(pixel[2], pixel[1], pixel[0]);
347 unsigned char & pixel = image.at<
unsigned char>((
int)kter->second.kpt.pt.y, (
int)kter->second.kpt.pt.x);
348 c.setRgb(pixel, pixel, pixel);
350 colors.insert(std::make_pair(jter->first,
c));
357 QString
p = QString(
"keys")+QDir::separator()+tr(
"%1.key").arg(
iter->first);
358 p =
path+QDir::separator()+
p;
360 if(fileKey.open(QIODevice::WriteOnly | QIODevice::Text))
362 if(descriptors.size())
364 QTextStream
key(&fileKey);
365 key << descriptors.size() <<
" " << descriptors.front().descriptor.cols <<
"\n";
366 for(std::list<FeatureBA>::iterator dter=descriptors.begin(); dter!=descriptors.end(); ++dter)
369 int octave = dter->kpt.octave & 255;
370 octave = octave < 128 ? octave : (-128 | octave);
371 float scale = octave >= 0 ? 1.f/(1 << octave) : (
float)(1 << -octave);
373 key << dter->kpt.pt.x <<
" " << dter->kpt.pt.y <<
" " <<
scale <<
" " << dter->kpt.angle <<
"\n";
374 for(
int i=0;
i<dter->descriptor.cols; ++
i)
376 if(dter->descriptor.type() == CV_8U)
378 key <<
" " << (
int)dter->descriptor.at<
unsigned char>(
i);
382 key <<
" " << (
int)dter->descriptor.at<
float>(
i);
384 if((
i+1)%20 == 0 &&
i+1 < dter->descriptor.cols)
394 UWARN(
"No descriptors saved for frame %d in file %s. "
395 "Descriptors may not have been saved in the nodes. "
396 "Verify that parameter %s was true during mapping.",
397 iter->first,
p.toStdString().c_str(),
398 Parameters::kMemRawDescriptorsKept().c_str());
407 UWARN(
"Could not find node data for pose %d",
iter->first);
412 0.0
f, -1.0
f, 0.0
f, 0.0
f,
413 0.0
f, 0.0
f, 1.0
f, 0.0
f,
414 -1.0
f, 0.0
f, 0.0
f, 0.0
f);
416 static const Transform optical_rotation_inv(
417 0.0
f, -1.0
f, 0.0
f, 0.0
f,
418 0.0
f, 0.0
f, -1.0
f, 0.0
f,
419 1.0
f, 0.0
f, 0.0
f, 0.0
f);
421 QTextStream
out(&fileOut);
422 QTextStream
list(&fileList);
423 out <<
"# Bundle file v0.3\n";
424 out << cameras.size() <<
" " << points3DMap.size() <<
"\n";
435 for(std::map<int, Transform>::iterator
iter=cameras.begin();
iter!=cameras.end(); ++
iter)
437 QString
p = QString(
"images")+QDir::separator()+tr(
"%1.jpg").arg(
iter->first);
441 QMap<int, Signature>::const_iterator ster = signatures.find(
iter->first);
442 UASSERT(ster!=signatures.end());
443 if(ster.value().sensorData().cameraModels().size())
445 out << ster.value().sensorData().cameraModels().at(0).fx() <<
" 0 0\n";
446 localTransform = ster.value().sensorData().cameraModels().at(0).localTransform();
448 else if(ster.value().sensorData().stereoCameraModels().size())
450 out << ster.value().sensorData().stereoCameraModels()[0].left().fx() <<
" 0 0\n";
451 localTransform = ster.value().sensorData().stereoCameraModels()[0].left().localTransform();
455 if(!localTransform.
isNull())
457 pose*=localTransform*optical_rotation_inv;
461 out << poseGL.
r11() <<
" " << poseGL.
r12() <<
" " << poseGL.
r13() <<
"\n";
462 out << poseGL.
r21() <<
" " << poseGL.
r22() <<
" " << poseGL.
r23() <<
"\n";
463 out << poseGL.
r31() <<
" " << poseGL.
r32() <<
" " << poseGL.
r33() <<
"\n";
464 out << poseGL.
x() <<
" " << poseGL.
y() <<
" " << poseGL.
z() <<
"\n";
466 if(wordReferences.size())
468 if(fileListKeys.open(QIODevice::WriteOnly | QIODevice::Text))
470 QTextStream listKeys(&fileListKeys);
471 for(std::map<int, Transform>::iterator
iter=cameras.begin();
iter!=cameras.end(); ++
iter)
473 QString
p = QString(
"keys")+QDir::separator()+tr(
"%1.key").arg(
iter->first);
474 listKeys <<
p <<
"\n";
476 fileListKeys.close();
500 std::map<int, int> descriptorIndexes;
501 for(std::map<int, cv::Point3f>::iterator
iter=points3DMap.begin();
iter!=points3DMap.end(); ++
iter)
503 std::map<int, std::map<int, FeatureBA> >
::iterator jter = wordReferences.find(
iter->first);
504 out <<
iter->second.x <<
" " <<
iter->second.y <<
" " <<
iter->second.z <<
"\n";
506 QColor &
c = colors.at(
iter->first);
507 out <<
c.red() <<
" " <<
c.green() <<
" " <<
c.blue() <<
"\n";
508 out << jter->second.size();
509 for(std::map<int, FeatureBA>::iterator kter = jter->second.begin(); kter!=jter->second.end(); ++kter)
512 int camId = kter->first;
513 UASSERT(signatures.contains(camId));
514 UASSERT(cameraIndexes.find(camId) != cameraIndexes.end());
517 if(signatures[camId].sensorData().cameraModels().
size())
519 pt.x = kter->second.kpt.pt.x -
s.sensorData().cameraModels().at(0).cx();
520 pt.y = kter->second.kpt.pt.y -
s.sensorData().cameraModels().at(0).cy();
522 else if(signatures[camId].sensorData().stereoCameraModels().
size())
524 pt.x = kter->second.kpt.pt.x -
s.sensorData().stereoCameraModels()[0].left().cx();
525 pt.y = kter->second.kpt.pt.y -
s.sensorData().stereoCameraModels()[0].left().cy();
527 descriptorIndexes.insert(std::make_pair(camId, 0));
528 out <<
" " << cameraIndexes.at(camId) <<
" " << descriptorIndexes.at(camId)++ <<
" " << pt.x <<
" " << -pt.y;
536 QMessageBox::information(
this,
537 tr(
"Exporting cameras in Bundler format..."),
538 tr(
"%1 cameras/images and %2 points exported to directory \"%3\".%4")
539 .
arg(newPoses.size())
540 .
arg(points3DMap.size())
542 .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()):
""));
547 QMessageBox::warning(
this, tr(
"Exporting cameras..."), tr(
"Failed opening file %1 for writing.").
arg(
path+QDir::separator()+
"list.txt"));
552 QMessageBox::warning(
this, tr(
"Exporting cameras..."), tr(
"Failed opening file %1 for writing.").
arg(
path+QDir::separator()+
"cameras.out"));