ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
BundlerFilter.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "BundlerFilter.h"
9 
10 // Local
11 #include "BinFilter.h"
12 #include "BundlerImportDlg.h"
13 
14 // CV_DB_LIB
15 #include <CVLog.h>
16 #include <ecvCameraSensor.h>
17 #include <ecvGLMatrix.h>
18 #include <ecvHObjectCaster.h>
19 #include <ecvImage.h>
20 #include <ecvPointCloud.h>
21 #include <ecvProgressDialog.h>
22 
23 // Qt
24 #include <QApplication>
25 #include <QDir>
26 #include <QFile>
27 #include <QFileDialog>
28 #include <QFileInfo>
29 #include <QInputDialog>
30 #include <QString>
31 
32 // Qt5/Qt6 Compatibility
33 #include <QtCompat.h>
34 
35 // cloudViewer (for DTM generation)
36 #include <MeshSamplingTools.h>
37 #include <PointProjectionTools.h>
38 #include <ecvMesh.h>
39 
40 // System
41 #include <string>
42 
44 struct BundlerCamera {
46  BundlerCamera() : f_pix(0), k1(0), k2(0), trans(), isValid(true) {}
47 
49  float f_pix;
51  float k1;
53  float k2;
57  bool isValid;
58 };
59 
61  : FileIOFilter({"_Snavely Bundler Filter",
62  DEFAULT_PRIORITY, // priority
63  QStringList{"out"}, "out",
64  QStringList{"Snavely's Bundler output (*.out)"},
65  QStringList(), Import}) {}
66 
68  ccHObject& container,
69  LoadParameters& parameters) {
70  return loadFileExtended(filename, container, parameters);
71 }
72 
73 // ortho-rectified image related information
74 struct ORImageInfo {
75  QString name; // image name
76  unsigned w, h; // image dimensions
77  double minC[2], maxC[2]; // local bounding box
78 };
79 
81  const QString& filename,
82  ccHObject& container,
83  LoadParameters& parameters,
84  const QString& _altKeypointsFilename /*=QString()*/,
85  bool _undistortImages /*=false*/,
86  bool _generateColoredDTM /*=false*/,
87  unsigned _coloredDTMVerticesCount /*=1000000*/,
88  float _scaleFactor /*=1.0f*/) {
89  // opening file (ASCII)
90  QFile f(filename);
91  if (!f.open(QIODevice::ReadOnly)) return CC_FERR_READING;
92 
93  QTextStream stream(&f);
94 
95  // read header (should be "# Bundle file vX.Y")
96  QString currentLine = stream.readLine();
97  if (!currentLine.startsWith("# Bundle file", Qt::CaseInsensitive)) {
98  CVLog::Error("File should start by '# Bundle file vX.Y'!");
100  }
101  unsigned majorVer = 0, minorVer = 0;
102  sscanf(qPrintable(currentLine), "# Bundle file v%u.%u", &majorVer,
103  &minorVer);
104  if (majorVer != 0 || (minorVer != 3 && minorVer != 4)) {
105  CVLog::Error(
106  "Only version 0.3 and 0.4 of Bundler files are supported!");
108  }
109 
110  // second header line (should be <num_cameras> <num_points>)
111  currentLine = stream.readLine();
112  QStringList list =
113  qtCompatSplitRegex(currentLine, "\\s+", QtCompat::SkipEmptyParts);
114  if (list.size() != 2) {
115  CVLog::Error(
116  "[Bundler] Second line should be <num_cameras> <num_points>!");
117  return CC_FERR_MALFORMED_FILE;
118  }
119  unsigned camCount = list[0].toInt();
120  if (camCount == 0)
121  CVLog::Warning("[Bundler] No camera defined in Bundler file!");
122 
123  unsigned ptsCount = list[1].toInt();
124  if (ptsCount == 0)
125  CVLog::Warning("[Bundler] No keypoints defined in Bundler file!");
126 
127  // parameters
128  bool importKeypoints = false;
129  bool useAltKeypoints = false;
130  bool importImages = false;
131  bool undistortImages = false;
132  bool orthoRectifyImagesAsClouds = false;
133  bool orthoRectifyImagesAsImages = false;
134  bool orthoRectifyImages = false;
135  bool generateColoredDTM = false;
136  unsigned coloredDTMVerticesCount = 1000000;
137  float scaleFactor = 1.0f;
138  bool keepImagesInMemory = false;
139  bool applyOptMatrix = false;
140  ccGLMatrix orthoOptMatrix;
141  orthoOptMatrix.toIdentity();
142  BundlerImportDlg::OrthoRectMethod orthoRectMethod =
144 
145  // default paths
146  QString imageListFilename = QFileInfo(f).dir().absoluteFilePath("list.txt");
147  QString altKeypointsFilename =
148  QFileInfo(f).dir().absoluteFilePath("pmvs.ply");
149 
150  if (parameters.alwaysDisplayLoadDialog) {
151  // open dialog
152  BundlerImportDlg biDlg;
153  biDlg.setKeypointsCount(ptsCount);
154  biDlg.setCamerasCount(camCount);
155  biDlg.setVer(majorVer, minorVer);
156  biDlg.setImageListFilename(imageListFilename);
157  biDlg.setAltKeypointsFilename(altKeypointsFilename);
158 
159  if (!biDlg.exec()) return CC_FERR_CANCELED_BY_USER;
160 
161  importKeypoints = biDlg.importKeypoints();
162  useAltKeypoints = biDlg.useAlternativeKeypoints();
163  if (useAltKeypoints)
164  altKeypointsFilename = biDlg.getAltKeypointsFilename();
165  importImages = biDlg.importImages();
166  undistortImages = biDlg.undistortImages();
167  orthoRectifyImagesAsClouds = biDlg.orthoRectifyImagesAsClouds();
168  orthoRectifyImagesAsImages = biDlg.orthoRectifyImagesAsImages();
169  generateColoredDTM = biDlg.generateColoredDTM();
170  coloredDTMVerticesCount = biDlg.getDTMVerticesCount();
171  scaleFactor = static_cast<float>(biDlg.getScaleFactor());
172  keepImagesInMemory = biDlg.keepImagesInMemory();
173  imageListFilename = biDlg.getImageListFilename();
174  applyOptMatrix = biDlg.getOptionalTransfoMatrix(orthoOptMatrix);
175  orthoRectMethod = biDlg.getOrthorectificationMethod();
176  } else {
177  importImages = true;
178  orthoRectifyImagesAsImages = true;
179  useAltKeypoints = !_altKeypointsFilename.isEmpty();
180  if (useAltKeypoints) altKeypointsFilename = _altKeypointsFilename;
181  undistortImages = _undistortImages;
182  generateColoredDTM = _generateColoredDTM;
183  if (generateColoredDTM)
184  coloredDTMVerticesCount = _coloredDTMVerticesCount;
185  scaleFactor = _scaleFactor;
186  }
187 
188  if (!importKeypoints && !importImages) return CC_FERR_NO_LOAD;
189 
190  orthoRectifyImages =
191  orthoRectifyImagesAsClouds || orthoRectifyImagesAsImages;
192 
193  // data
194  std::vector<BundlerCamera> cameras;
195  ccPointCloud* keypointsCloud = 0;
196  ccHObject* altEntity = 0;
197  typedef std::pair<unsigned, ccCameraSensor::KeyPoint> KeypointAndCamIndex;
198  std::vector<KeypointAndCamIndex> keypointsDescriptors;
199 
200  // Read Bundler '.out' file
201  {
202  // progress dialog
203  QScopedPointer<ecvProgressDialog> pDlg(0);
204  if (parameters.parentWidget) {
205  pDlg.reset(new ecvProgressDialog(
206  true, parameters.parentWidget)); // cancel available
207  pDlg->setMethodTitle(QObject::tr("Open Bundler file"));
208  pDlg->setInfo(QObject::tr("Cameras: %1\nPoints: %2")
209  .arg(camCount)
210  .arg(ptsCount));
211  pDlg->start();
212  }
214  pDlg.data(),
215  camCount + (importKeypoints || orthoRectifyImages ||
216  generateColoredDTM
217  ? ptsCount
218  : 0));
219 
220  // read cameras info (whatever the case!)
221  cameras.resize(camCount);
222  unsigned camIndex = 0;
223  for (std::vector<BundlerCamera>::iterator it = cameras.begin();
224  it != cameras.end(); ++it, ++camIndex) {
225  // f, k1 and k2
226  currentLine = stream.readLine();
227  if (currentLine.isEmpty()) return CC_FERR_READING;
228  if (importImages) {
229  QStringList tokens = qtCompatSplitRegex(
230  currentLine, "\\s+", QtCompat::SkipEmptyParts);
231  if (tokens.size() < 3) return CC_FERR_MALFORMED_FILE;
232  bool ok[3] = {true, true, true};
233  it->f_pix = tokens[0].toFloat(ok);
234  it->k1 = tokens[1].toFloat(ok + 1);
235  it->k2 = tokens[2].toFloat(ok + 2);
236  if (!ok[0] || !ok[1] || !ok[2]) return CC_FERR_MALFORMED_FILE;
237  }
238  // Rotation matrix
239  double* mat = (importImages ? it->trans.data() : 0);
240  double sum = 0;
241  for (unsigned l = 0; l < 3; ++l) {
242  currentLine = stream.readLine();
243  if (currentLine.isEmpty()) return CC_FERR_READING;
244  if (importImages) {
245  QStringList tokens =
246  currentLine.split("\\s+", QtCompat::SkipEmptyParts);
247  if (tokens.size() < 3) return CC_FERR_MALFORMED_FILE;
248  bool ok[3] = {true, true, true};
249  mat[l] = tokens[0].toDouble(ok);
250  mat[4 + l] = tokens[1].toDouble(ok + 1);
251  mat[8 + l] = tokens[2].toDouble(ok + 2);
252  if (!ok[0] || !ok[1] || !ok[2])
253  return CC_FERR_MALFORMED_FILE;
254  sum += fabs(mat[l]) + fabs(mat[4 + l]) + fabs(mat[8 + l]);
255  }
256  }
257  if (importImages && cloudViewer::LessThanEpsilon(sum)) {
258  CVLog::Warning("[Bundler] Camera #%i is invalid!",
259  camIndex + 1);
260  it->isValid = false;
261  }
262 
263  // Translation
264  currentLine = stream.readLine();
265  if (currentLine.isEmpty()) return CC_FERR_READING;
266  if (importImages) {
267  QStringList tokens = qtCompatSplitRegex(
268  currentLine, "\\s+", QtCompat::SkipEmptyParts);
269  if (tokens.size() < 3) return CC_FERR_MALFORMED_FILE;
270  bool ok[3] = {true, true, true};
271  mat[12] = tokens[0].toDouble(ok);
272  mat[13] = tokens[1].toDouble(ok + 1);
273  mat[14] = tokens[2].toDouble(ok + 2);
274  if (!ok[0] || !ok[1] || !ok[2]) return CC_FERR_MALFORMED_FILE;
275  }
276 
277  if (pDlg && !nprogress.oneStep()) // cancel requested?
278  {
280  }
281  }
282 
283  // read points
284  if (!useAltKeypoints &&
285  (importKeypoints || orthoRectifyImages || generateColoredDTM)) {
286  keypointsCloud = new ccPointCloud("Keypoints");
287  if (!keypointsCloud->reserve(ptsCount)) {
288  delete keypointsCloud;
290  }
291 
292  bool hasColors = false;
293  if (importKeypoints) {
294  hasColors = keypointsCloud->reserveTheRGBTable();
295  if (!hasColors)
297  "[Bundler] Not enough memory to load colors!");
298  else
299  keypointsCloud->showColors(true);
300  }
301 
302  bool storeKeypoints = orthoRectifyImages /* && !useAltKeypoints*/;
303  // we'll check if all cameras are used or not!
304  std::vector<bool> camUsage;
305  // if (!useAltKeypoints)
306  {
307  try {
308  camUsage.resize(cameras.size(), false);
309  } catch (const std::bad_alloc&) {
310  // nothing serious here
311  }
312  }
313 
314  CCVector3d Pshift(0, 0, 0);
315  for (unsigned i = 0; i < ptsCount; ++i) {
316  // Point (X,Y,Z)
317  currentLine = stream.readLine();
318  if (currentLine.startsWith(
319  "--")) // skip lines starting with '--' (yes it
320  // happens in some weird version of
321  // Bundler?!)
322  currentLine = stream.readLine();
323  if (currentLine.isEmpty()) {
324  delete keypointsCloud;
325  return CC_FERR_READING;
326  }
327 
328  // read point coordinates (as strings)
329  CCVector3d Pd(0, 0, 0);
330  {
331  QStringList tokens =
332  currentLine.split("\\s+", QtCompat::SkipEmptyParts);
333  if (tokens.size() < 3) {
334  delete keypointsCloud;
335  return CC_FERR_MALFORMED_FILE;
336  }
337  // decode coordinates
338  bool ok[3] = {true, true, true};
339  Pd.x = tokens[0].toDouble(ok);
340  Pd.y = tokens[1].toDouble(ok + 1);
341  Pd.z = tokens[2].toDouble(ok + 2);
342  if (!ok[0] || !ok[1] || !ok[2]) {
343  delete keypointsCloud;
344  return CC_FERR_MALFORMED_FILE;
345  }
346  }
347 
348  // first point: check for 'big' coordinates
349  if (i == 0) {
350  bool preserveCoordinateShift = true;
351  if (HandleGlobalShift(Pd, Pshift, preserveCoordinateShift,
352  parameters)) {
353  if (preserveCoordinateShift) {
354  keypointsCloud->setGlobalShift(Pshift);
355  // we must apply the shift to the cameras as well!!!
356  for (size_t j = 0; j < cameras.size(); ++j) {
357  ccGLMatrixd& trans = cameras[j].trans;
358  trans.invert();
359  trans.setTranslation(
360  trans.getTranslationAsVec3D() + Pshift);
361  trans.invert();
362  }
363  }
365  "[Bundler] Cloud has been recentered! "
366  "Translation: (%.2f ; %.2f ; %.2f)",
367  Pshift.x, Pshift.y, Pshift.z);
368  }
369  }
370  keypointsCloud->addPoint(CCVector3::fromArray((Pd + Pshift).u));
371 
372  // RGB
373  currentLine = stream.readLine();
374  if (currentLine.isEmpty()) {
375  delete keypointsCloud;
376  return CC_FERR_READING;
377  }
378  QStringList colorParts =
379  currentLine.split(" ", QtCompat::SkipEmptyParts);
380  if (colorParts.size() == 3) {
381  if (hasColors) {
382  QStringList tokens = currentLine.split(
383  "\\s+", QtCompat::SkipEmptyParts);
384  if (tokens.size() < 3) {
385  delete keypointsCloud;
386  return CC_FERR_MALFORMED_FILE;
387  }
388  int R = tokens[0].toInt();
389  int G = tokens[1].toInt();
390  int B = tokens[2].toInt();
391  keypointsCloud->addRGBColor(
392  static_cast<ColorCompType>(
393  std::min<int>(R, ecvColor::MAX)),
394  static_cast<ColorCompType>(
395  std::min<int>(G, ecvColor::MAX)),
396  static_cast<ColorCompType>(
397  std::min<int>(B, ecvColor::MAX)));
398  }
399 
400  currentLine = stream.readLine();
401  } else if (colorParts.size() > 3) {
402  // sometimes, it appears that keypoints has no associated
403  // color! so we skip the line and assume it's in fact the
404  // keypoint description...
406  "[Bundler] Keypoint #%i has no associated color!",
407  i);
408  if (hasColors)
409  keypointsCloud->addRGBColor(0, 0,
410  0); // black by default
411  } else {
412  delete keypointsCloud;
413  return CC_FERR_MALFORMED_FILE;
414  }
415 
416  // view list (currentLine should already be read, see above)
417  if (currentLine.isEmpty()) {
418  delete keypointsCloud;
419  return CC_FERR_READING;
420  }
421 
422  if (storeKeypoints || !camUsage.empty()) {
423  QStringList parts =
424  currentLine.split(" ", QtCompat::SkipEmptyParts);
425  if (!parts.isEmpty()) {
426  bool ok = false;
427  unsigned nviews = parts[0].toInt(&ok);
428  if (!ok || nviews * 4 + 1 > static_cast<unsigned>(
429  parts.size())) {
431  "[Bundler] View list for point #%i is "
432  "invalid!",
433  i);
434  } else {
435  unsigned pos = 1;
436  for (unsigned n = 0; n < nviews; ++n) {
437  int cam = parts[pos++].toInt(); // camera index
438  ++pos; // int key = parts[pos++].toInt();
439  // //index of the SIFT keypoint where
440  // the point was detected in that camera
441  // (not used)
442  if (cam < 0 ||
443  static_cast<unsigned>(cam) >= camCount) {
444  pos += 2;
445  continue;
446  }
447  if (!camUsage.empty()) {
448  camUsage[cam] = true;
449  }
450  if (storeKeypoints) {
451  float x =
452  parts[pos++]
453  .toFloat(); // detected
454  // positions of
455  // that
456  // keypoint (x)
457  float y =
458  parts[pos++]
459  .toFloat(); // detected
460  // positions of
461  // that
462  // keypoint (y)
463  // add key point
464  KeypointAndCamIndex lastKeyPoint;
465  lastKeyPoint.first =
466  static_cast<unsigned>(cam);
467  lastKeyPoint.second.index = i;
468  lastKeyPoint.second.x =
469  x *
470  scaleFactor; // the origin is the
471  // center of the
472  // image, the x-axis
473  // increases to the
474  // right
475  lastKeyPoint.second.y =
476  -y *
477  scaleFactor; // and the y-axis
478  // increases towards
479  // the top of the
480  // image
481  try {
482  keypointsDescriptors.push_back(
483  lastKeyPoint);
484  } catch (const std::bad_alloc&) {
486  "[Bundler] Not enough memory "
487  "to store keypoints!");
488  keypointsDescriptors.clear();
489  orthoRectifyImages = false;
490  storeKeypoints = false;
491  }
492  }
493  }
494  }
495  }
496  }
497 
498  if (pDlg && !nprogress.oneStep()) // cancel requested?
499  {
500  delete keypointsCloud;
502  }
503  }
504 
505  if (!camUsage.empty()) {
506  for (size_t i = 0; i < camUsage.size(); ++i) {
507  if (!camUsage[i])
508  CVLog::Warning(QString("[Bundler] Camera #%1 has no "
509  "associated keypoints!")
510  .arg(i + 1));
511  }
512  }
513 
514  // apply optional matrix (if any)
515  if (applyOptMatrix) {
516  keypointsCloud->applyGLTransformation_recursive(
517  &orthoOptMatrix);
518  CVLog::Print(
519  "[Bundler] Keypoints cloud has been transformed with "
520  "input matrix!");
521  // this transformation is of no interest for the user
522  // keypointsCloud->resetGLTransformationHistory_recursive();
523  }
524 
525  if (importKeypoints) container.addChild(keypointsCloud);
526  }
527 
528  if (pDlg) {
529  pDlg->stop();
530  QApplication::processEvents();
531  }
532  }
533 
534  // use alternative cloud/mesh as keypoints
535  if (useAltKeypoints) {
536  FileIOFilter::LoadParameters altKeypointsParams;
538  ccHObject* altKeypointsContainer = FileIOFilter::LoadFromFile(
539  altKeypointsFilename, altKeypointsParams, result);
540  if (!altKeypointsContainer ||
541  altKeypointsContainer->getChildrenNumber() != 1 ||
542  (!altKeypointsContainer->getChild(0)->isKindOf(
544  !altKeypointsContainer->getChild(0)->isKindOf(CV_TYPES::MESH))) {
545  if (!altKeypointsContainer)
546  CVLog::Error(QString("[Bundler] Failed to load alternative "
547  "keypoints file:\n'%1'")
548  .arg(altKeypointsFilename));
549  else
550  CVLog::Error(
551  "[Bundler] Can't use this kind of entities as "
552  "keypoints (need one and only one cloud or mesh)");
553 
555  } else {
556  altEntity = altKeypointsContainer->getChild(0);
557  if (importKeypoints) container.addChild(altEntity);
558  }
559  }
560 
561  if (!importImages) return CC_FERR_NO_ERROR;
562 
563  assert(camCount > 0);
564 
565  // load images
566  QDir imageDir =
567  QFileInfo(f)
568  .dir(); // by default we look in the Bundler file folder
569 
570  // let's try to open the images list file (if necessary)
571  QStringList imageFilenames;
572  {
573  imageFilenames.clear();
574  QFile imageListFile(imageListFilename);
575  if (!imageListFile.exists() ||
576  !imageListFile.open(QIODevice::ReadOnly)) {
577  CVLog::Error(
578  QString("[Bundler] Failed to open image list file! (%1)")
579  .arg(imageListFilename));
580  if (!importKeypoints && keypointsCloud) delete keypointsCloud;
581  if (!importKeypoints && altEntity) delete altEntity;
582  return CC_FERR_UNKNOWN_FILE;
583  }
584 
585  // we look for images in same directory
586  imageDir = QFileInfo(imageListFile).dir();
587  QTextStream imageListStream(&imageListFile);
588 
589  for (unsigned lineIndex = 0; lineIndex < camCount; ++lineIndex) {
590  QString nextLine = imageListStream.readLine();
591  if (nextLine.isEmpty()) break;
592 
593  QStringList parts = qtCompatSplitRegex(nextLine, "\\s+",
595  if (parts.size() > 0)
596  imageFilenames << parts[0];
597  else {
598  CVLog::Error(QString("[Bundler] Couldn't extract image name "
599  "from line %1 in file '%2'!")
600  .arg(lineIndex)
601  .arg(imageListFilename));
602  break;
603  }
604  }
605  }
606 
607  if (imageFilenames.size() <
608  static_cast<int>(camCount)) // not enough images!
609  {
610  if (imageFilenames.isEmpty())
611  CVLog::Error(QString("[Bundler] No filename could be extracted "
612  "from file '%1'!")
613  .arg(imageListFilename));
614  else
615  CVLog::Error(QString("[Bundler] Only %1 filenames (out of %2) "
616  "could be extracted\nfrom file '%3'!")
617  .arg(imageFilenames.size())
618  .arg(camCount)
619  .arg(imageListFilename));
620  if (!importKeypoints && keypointsCloud) delete keypointsCloud;
621  if (!importKeypoints && altEntity) delete altEntity;
622  return CC_FERR_MALFORMED_FILE;
623  }
624 
625  // let's try to open the image corresponding to each camera
626  QScopedPointer<ecvProgressDialog> ipDlg(0);
627  if (parameters.parentWidget) {
628  ipDlg.reset(new ecvProgressDialog(
629  true, parameters.parentWidget)); // cancel available
630  ipDlg->setMethodTitle(QObject::tr("Open & process images"));
631  ipDlg->setInfo(QObject::tr("Images: %1").arg(camCount));
632  ipDlg->start();
633  QApplication::processEvents();
634  }
635  cloudViewer::NormalizedProgress inprogress(ipDlg.data(), camCount);
636 
637  assert(imageFilenames.size() >= static_cast<int>(camCount));
638 
639  /*** pre-processing steps (colored MNT computation, etc.) ***/
640 
641  // for colored DTM generation
642  int* mntColors = nullptr;
643  cloudViewer::PointCloud* mntSamples = nullptr;
644  if (generateColoredDTM) {
645  QScopedPointer<ecvProgressDialog> toDlg(0);
646  if (parameters.parentWidget) {
647  toDlg.reset(new ecvProgressDialog(
648  true, parameters.parentWidget)); // cancel available
649  toDlg->setMethodTitle(QObject::tr("Preparing colored DTM"));
650  toDlg->start();
651  QApplication::processEvents();
652  }
653 
654  // 1st step: triangulate keypoints (or use existing one)
655  ccGenericMesh* baseDTMMesh =
656  (altEntity ? ccHObjectCaster::ToGenericMesh(altEntity) : 0);
657  cloudViewer::GenericIndexedMesh* dummyMesh = baseDTMMesh;
658  if (!baseDTMMesh) {
659  // alternative keypoints?
660  ccGenericPointCloud* altKeypoints =
661  (altEntity ? ccHObjectCaster::ToGenericPointCloud(altEntity)
662  : 0);
663  std::string errorStr;
665  altKeypoints ? altKeypoints : keypointsCloud,
667  if (!dummyMesh) {
668  CVLog::Warning(QString("[Bundler] Failed to generate DTM! (%1)")
669  .arg(QString::fromStdString(errorStr)));
670  }
671  }
672 
673  if (dummyMesh) {
674  // 2nd step: samples points on resulting mesh
676  (cloudViewer::GenericMesh*)dummyMesh,
677  coloredDTMVerticesCount);
678  if (!baseDTMMesh) delete dummyMesh;
679  dummyMesh = 0;
680 
681  if (mntSamples) {
682  // 3rd step: project each point in all images and get average
683  // color
684  unsigned count = mntSamples->size();
685  mntColors = new int[4 * count]; // R + G + B + accum count
686  if (!mntColors) {
687  // not enough memory
688  CVLog::Error(
689  "Not enough memory to store DTM colors! DTM "
690  "generation cancelled");
691  delete mntSamples;
692  mntSamples = 0;
693  generateColoredDTM = false;
694  } else {
695  memset(mntColors, 0, sizeof(int) * 4 * count);
696  }
697  }
698  }
699  }
700 
701  std::vector<ORImageInfo> OR_infos;
702  double OR_pixelSize = -1.0; // auto for first image
703  double OR_globalCorners[4] = {0, 0, 0, 0}; // corners for the global set
704 
705  // alternative keypoints? (for ortho-rectification only)
706  ccGenericPointCloud *altKeypoints = 0, *_keypointsCloud = 0;
707  if (orthoRectifyImages) {
708  altKeypoints =
709  (altEntity ? ccHObjectCaster::ToGenericPointCloud(altEntity)
710  : 0);
711  _keypointsCloud = (altKeypoints ? altKeypoints : keypointsCloud);
712  }
713 
714  /*** process each image ***/
715 
716  bool cancelledByUser = false;
717  for (unsigned i = 0; i < camCount; ++i) {
718  const BundlerCamera& cam = cameras[i];
719  if (!cam.isValid) continue;
720 
721  ccImage* image = new ccImage();
722  QString errorStr;
723  if (!image->load(imageDir.absoluteFilePath(imageFilenames[i]),
724  errorStr)) {
725  CVLog::Error(QString("[Bundler] %1 (image '%2')")
726  .arg(errorStr, imageFilenames[i]));
727  delete image;
728  image = 0;
729  break;
730  }
731 
732  image->setName(imageFilenames[i]);
733  image->setEnabled(false);
734  image->setAlpha(0.75f); // semi transparent by default
735 
736  // associate image with calibration information
737  ccCameraSensor* sensor = 0;
738  {
740  params.arrayWidth = static_cast<int>(image->getW());
741  params.arrayHeight = static_cast<int>(image->getH());
742  // we define an arbitrary principal point
743  params.principal_point[0] = params.arrayWidth / 2.0f;
744  params.principal_point[1] = params.arrayHeight / 2.0f;
745  // we use an arbitrary 'pixel size'
746  params.pixelSize_mm[0] = params.pixelSize_mm[1] =
747  1.0f / std::max(params.arrayWidth, params.arrayHeight);
748  params.vertFocal_pix = cam.f_pix * scaleFactor;
750  cam.f_pix, params.arrayHeight);
751 
752  // camera position/orientation
753  ccGLMatrix transf(cameras[i].trans.inverse().data());
754 
755  // dist to cloud
757  keypointsCloud ? (transf.getTranslationAsVec3D() -
758  keypointsCloud->getOwnBB().getCenter())
759  .norm()
760  : PC_ONE;
761  params.zFar_mm = dist;
762  params.zNear_mm = 0.001f;
763 
764  sensor = new ccCameraSensor(params);
765  sensor->setName(QString("Camera #%1").arg(i + 1));
766  sensor->setEnabled(true);
767  sensor->setVisible(true /*false*/);
768  sensor->setGraphicScale(
769  keypointsCloud
770  ? keypointsCloud->getOwnBB().getDiagNorm() / 10
771  : PC_ONE);
772  sensor->setRigidTransformation(transf);
773 
774  // distortion parameters
775  if (cameras[i].k1 != 0 || cameras[i].k2 != 0) {
778  distParams->k1 = cameras[i].k1;
779  distParams->k2 = cameras[i].k2;
780  sensor->setDistortionParameters(
782  distParams));
783  }
784 
785  // apply optional matrix (if any)
786  if (applyOptMatrix) {
787  sensor->applyGLTransformation_recursive(&orthoOptMatrix);
788  // CVLog::Print("[Bundler] Camera cloud has been transformed
789  // with input matrix!"); this transformation is of no interest
790  // for the user
791  // sensor->resetGLTransformationHistory_recursive();
792  }
793  }
794  // the image is a child of the sensor!
795  image->setAssociatedSensor(sensor);
796  sensor->addChild(image);
797 
798  // ortho-rectification
799  if (orthoRectifyImages) {
800  assert(sensor && _keypointsCloud);
801 
802  // select image keypoints
803  std::vector<ccCameraSensor::KeyPoint> keypointsImage;
804  ccBBox keypointsImageBB;
805  if (_keypointsCloud ==
806  keypointsCloud) // keypoints from Bundler file
807  {
808  for (std::vector<KeypointAndCamIndex>::const_iterator key =
809  keypointsDescriptors.begin();
810  key != keypointsDescriptors.end(); ++key) {
811  if (key->first == i) {
812  keypointsImage.push_back(key->second);
813  keypointsImageBB.add(
814  CCVector3(key->second.x, key->second.y, 0));
815  }
816  }
817  } else {
818  // project alternative cloud in image!
819  _keypointsCloud->placeIteratorAtBeginning();
820  int half_w = (image->getW() >> 1);
821  int half_h = (image->getH() >> 1);
823  unsigned keyptsCount = _keypointsCloud->size();
824  for (unsigned k = 0; k < keyptsCount; ++k) {
825  CCVector3 P(*_keypointsCloud->getPointPersistentPtr(k));
826  // apply bundler equation
827  cam.trans.apply(P);
828  // convert to keypoint
829  kp.x = -cam.f_pix * static_cast<float>(P.x / P.z);
830  kp.y = cam.f_pix * static_cast<float>(P.y / P.z);
831  if (static_cast<int>(kp.x) > -half_w &&
832  static_cast<int>(kp.x < half_w) &&
833  static_cast<int>(kp.y) > -half_h &&
834  static_cast<int>(kp.y < half_h)) {
835  kp.index = k;
836  keypointsImage.push_back(kp);
837  keypointsImageBB.add(CCVector3(kp.x, kp.y, 0));
838  }
839  }
840  }
841 
842  if (keypointsImage.size() < 4) {
843  CVLog::Warning(QString("[Bundler] Not enough keypoints "
844  "descriptors for image '%1'!")
845  .arg(image->getName()));
846  } else if (!keypointsImageBB.isValid() ||
847  keypointsImageBB.getDiagNorm() < PC_ONE) {
849  QString("[Bundler] Keypoints descriptors for image "
850  "'%1' are invalid (= all the same)")
851  .arg(image->getName()));
852  } else {
853  if (orthoRectifyImagesAsImages) {
854  // for ortho-rectification log
855  ORImageInfo info;
856  double corners[8];
857  ccImage* orthoImage = 0;
858 
859  //"standard" ortho-rectification method
860  if (orthoRectMethod == BundlerImportDlg::OPTIMIZED) {
861  orthoImage = sensor->orthoRectifyAsImage(
862  image, _keypointsCloud, keypointsImage,
863  OR_pixelSize, info.minC, info.maxC, corners);
864  }
865  //"direct" ortho-rectification method
866  else {
867  assert(orthoRectMethod == BundlerImportDlg::DIRECT ||
868  orthoRectMethod ==
870 
871  // we take the keypoints 'middle altitude' by default
872  CCVector3 bbMin, bbMax;
873  _keypointsCloud->getBoundingBox(bbMin, bbMax);
874  PointCoordinateType Z0 = (bbMin.z + bbMax.z) / 2;
875 
876  orthoImage = sensor->orthoRectifyAsImageDirect(
877  image, Z0, OR_pixelSize,
878  orthoRectMethod ==
880  info.minC, info.maxC, corners);
881  }
882 
883  if (orthoImage) {
884  assert(!orthoImage->data().isNull());
885  info.name = QString("ortho_%1.png")
886  .arg(QFileInfo(imageFilenames[i])
887  .baseName());
888  info.w = orthoImage->getW();
889  info.h = orthoImage->getH();
890  orthoImage->data().save(
891  imageDir.absoluteFilePath(info.name));
892  CVLog::Print(
893  QString("[Bundler] Ortho-rectified version of "
894  "image '%1' (%2 x %3) saved to '%4'")
895  .arg(imageFilenames[i])
896  .arg(info.w)
897  .arg(info.h)
898  .arg(imageDir.absoluteFilePath(
899  info.name)));
900 
901 #ifdef TEST_TEXTURED_BUNDLER_IMPORT
902 
903  // we tile the original image to avoid any OpenGL
904  // limitation on texture size
905 #define TBI_DEFAULT_TILE_POW 10 // 2^10 = 1024
906  const unsigned tileDim = (1 << TBI_DEFAULT_TILE_POW);
907  unsigned horiTile = (info.w >> TBI_DEFAULT_TILE_POW);
908  if (info.w - horiTile * tileDim != 0) ++horiTile;
909  unsigned vertTile = (info.h >> TBI_DEFAULT_TILE_POW);
910  if (info.h - vertTile * tileDim != 0) ++vertTile;
911  unsigned tiles = horiTile * vertTile;
912  unsigned verts = (horiTile + 1) * (vertTile + 1);
913 
914  // vertices
915  ccPointCloud* rectVertices =
916  new ccPointCloud("vertices");
917  rectVertices->reserve(verts);
918 
919  // mesh
920  ccMesh* rectMesh = new ccMesh(rectVertices);
921  rectMesh->reserve(2 * tiles);
922  rectMesh->addChild(rectVertices);
923 
924  // materials (=textures)
925  ccMaterialSet* matSet = new ccMaterialSet("Texture");
926 
927  // texture coordinates table
928  TextureCoordsContainer* texCoords =
930  // texCoords->reserve(verts);
931  texCoords->reserve(4);
932  {
933  // float minu = 1.0f/(float)(2*tileDim);
934  // float maxu = 1.0-1.0f/(float)(2*tileDim);
935  float minu = 0.0f;
936  float maxu = 1.0f;
937  float TA[2] = {minu, minu};
938  float TB[2] = {maxu, minu};
939  float TC[2] = {maxu, maxu};
940  float TD[2] = {minu, maxu};
941  texCoords->addElement(TA);
942  texCoords->addElement(TB);
943  texCoords->addElement(TC);
944  texCoords->addElement(TD);
945  }
946 
947  // per triangle material indexes
948  rectMesh->reservePerTriangleMtlIndexes();
949  // per triangle texture coordinates indexes
951 
952  double dcx = info.maxC[0] - info.minC[0];
953  double dcy = info.maxC[1] - info.minC[1];
954 
955  // process all tiles
956  for (unsigned ti = 0; ti <= horiTile; ++ti) {
957  unsigned x = std::min(ti * tileDim, info.w);
958  double xRel = static_cast<double>(x) / info.w;
959  for (unsigned tj = 0; tj <= vertTile; ++tj) {
960  unsigned y = std::min(tj * tileDim, info.h);
961  double yRel = static_cast<double>(y) / info.h;
962 
963  // add vertices
964  CCVector3 P(info.minC[0] + dcx * xRel,
965  info.maxC[1] - dcy * yRel, 0);
966  rectVertices->addPoint(P);
967 
968  // add texture coordinates
969  // float T0[2]={xRel,1.0-yRel};
970  // texCoords->addElement(T0);
971 
972  if (ti < horiTile && tj < vertTile) {
973  unsigned w = std::min(info.w - x, tileDim);
974  unsigned h = std::min(info.h - y, tileDim);
975 
976  // create corresponding texture
977  unsigned tileIndex = matSet->size();
978  matSet->push_back(ccMaterial(info.name));
979  matSet->back().texture =
980  orthoImage->data().copy(x, y, w, h);
981  // matSet->back().texture.save(imageDir.absoluteFilePath(QString("tile_%1_").arg(tileIndex)+info.name));
982 
983  unsigned iA = ti * (vertTile + 1) + tj;
984  unsigned iB =
985  (ti + 1) * (vertTile + 1) + tj;
986  unsigned iC = iB + 1;
987  unsigned iD = iA + 1;
988 
989  rectMesh->addTriangle(iA, iB, iD);
990  // rectMesh->addTriangleTexCoordIndexes(iA,iB,iD);
991  rectMesh->addTriangleTexCoordIndexes(0, 1,
992  3);
993  rectMesh->addTriangleMtlIndex(tileIndex);
994  rectMesh->addTriangle(iB, iC, iD);
995  // rectMesh->addTriangleTexCoordIndexes(iB,iC,iD);
996  rectMesh->addTriangleTexCoordIndexes(1, 2,
997  3);
998  rectMesh->addTriangleMtlIndex(tileIndex);
999  }
1000  }
1001  }
1002 
1003  rectMesh->showMaterials(true);
1004  rectMesh->setName(info.name);
1005 
1006  // associate texture coordinates table
1007  rectMesh->setTexCoordinatesTable(texCoords);
1008  // associate material set
1009  rectMesh->setMaterialSet(matSet);
1010  container.addChild(rectMesh);
1011 #endif
1012 
1013  delete orthoImage;
1014  orthoImage = 0;
1015 
1016  OR_infos.push_back(info);
1017 
1018  // update global boundaries
1019  if (OR_globalCorners[0] > info.minC[0])
1020  OR_globalCorners[0] = info.minC[0];
1021  if (OR_globalCorners[1] > info.minC[1])
1022  OR_globalCorners[1] = info.minC[1];
1023  if (OR_globalCorners[2] < info.maxC[0])
1024  OR_globalCorners[2] = info.maxC[0];
1025  if (OR_globalCorners[3] < info.maxC[1])
1026  OR_globalCorners[3] = info.maxC[1];
1027  } else {
1028  CVLog::Warning(QString("[Bundler] Failed to "
1029  "ortho-rectify image '%1'!")
1030  .arg(image->getName()));
1031  }
1032  }
1033 
1034  if (orthoRectifyImagesAsClouds) {
1035  ccPointCloud* orthoCloud = sensor->orthoRectifyAsCloud(
1036  image, _keypointsCloud, keypointsImage);
1037  if (orthoCloud) {
1038  orthoCloud->setGlobalScale(
1039  _keypointsCloud->getGlobalScale());
1040  orthoCloud->setGlobalShift(
1041  _keypointsCloud->getGlobalShift());
1042  container.addChild(orthoCloud);
1043  } else {
1045  QString("[Bundler] Failed to ortho-rectify "
1046  "image '%1' as a cloud!")
1047  .arg(image->getName()));
1048  }
1049  }
1050  }
1051  }
1052 
1053  // undistortion
1054  if (sensor && undistortImages)
1055  if (!sensor->undistort(image, true))
1057  QString("[Bundler] Failed to undistort image '%1'!")
1058  .arg(image->getName()));
1059 
1060  // DTM color 'blending'
1061  if (sensor && generateColoredDTM) {
1062  assert(mntSamples && mntColors);
1063  unsigned sampleCount = mntSamples->size();
1064  const QRgb blackValue = qRgb(0, 0, 0);
1065 
1066  ccGLMatrix sensorMatrix =
1067  sensor->getRigidTransformation().inverse();
1068 
1069  // back project each MNT samples in this image to get color
1070  for (unsigned k = 0; k < sampleCount; ++k) {
1071  CCVector3 P = *mntSamples->getPointPersistentPtr(k);
1072 
1073  // apply bundler equation
1074  sensorMatrix.apply(P);
1076  CCVector3 p(-P.x / P.z, -P.y / P.z, 0.0);
1077  // float norm_p2 = p.norm2();
1078  // float rp = 1.0+norm_p2*(cam.k1+cam.k2*norm_p2); //images
1079  // are already undistorted
1080  float rp = 1.0f;
1081  CCVector3 pprime = cam.f_pix * rp * p;
1082 
1083  int px = static_cast<int>(image->getW() / 2.0f + pprime.x);
1084  if (px >= 0 && px < static_cast<int>(image->getW())) {
1085  int py = static_cast<int>(image->getH() / 2.0f -
1086  pprime.y);
1087  if (py >= 0 && py < static_cast<int>(image->getH())) {
1088  QRgb rgb = image->data().pixel(px, py);
1089  if (qAlpha(rgb) != 0 &&
1090  rgb != blackValue) // black pixels are ignored
1091  {
1092  int* col = mntColors + 4 * k;
1093  col[0] += qRed(rgb);
1094  col[1] += qGreen(rgb);
1095  col[2] += qBlue(rgb);
1096  col[3]++; // accum
1097  }
1098  }
1099  }
1100  }
1101  }
1102  }
1103 
1104  if (keepImagesInMemory) {
1105  container.addChild(sensor);
1106  } else {
1107  delete sensor;
1108  sensor = 0;
1109  }
1110 
1111  QApplication::processEvents();
1112 
1113  if (ipDlg && !inprogress.oneStep()) {
1114  cancelledByUser = true;
1115  break;
1116  }
1117  }
1118 
1119  if (!importKeypoints && keypointsCloud) delete keypointsCloud;
1120  keypointsCloud = 0;
1121 
1122  if (!importKeypoints && altEntity) delete altEntity;
1123  altEntity = 0;
1124 
1125  /*** post-processing steps ***/
1126 
1127  if (orthoRectifyImages && OR_pixelSize > 0) {
1128  //'close' log
1129  QFile f(imageDir.absoluteFilePath("ortho_rectification_log.txt"));
1130  if (f.open(QIODevice::WriteOnly | QIODevice::Text)) {
1131  QTextStream stream(&f);
1132  stream.setRealNumberNotation(QTextStream::FixedNotation);
1133  stream.setRealNumberPrecision(12);
1134  stream << "PixelSize" << ' ' << OR_pixelSize << QtCompat::endl;
1135  stream << "Global3DBBox" << ' ' << OR_globalCorners[0] << ' '
1136  << OR_globalCorners[1] << ' ' << OR_globalCorners[2] << ' '
1137  << OR_globalCorners[3] << QtCompat::endl;
1138  int globalWidth = static_cast<int>(
1139  (OR_globalCorners[2] - OR_globalCorners[0]) / OR_pixelSize);
1140  int globalHeight = static_cast<int>(
1141  (OR_globalCorners[3] - OR_globalCorners[1]) / OR_pixelSize);
1142  stream << "Global2DBBox" << ' ' << 0 << ' ' << 0 << ' '
1143  << globalWidth - 1 << ' ' << globalHeight - 1
1144  << QtCompat::endl;
1145 
1146  for (unsigned i = 0; i < OR_infos.size(); ++i) {
1147  stream << "Image" << ' ' << OR_infos[i].name << ' ';
1148  stream << "Local3DBBox" << ' ' << OR_infos[i].minC[0] << ' '
1149  << OR_infos[i].minC[1] << ' ' << OR_infos[i].maxC[0]
1150  << ' ' << OR_infos[i].maxC[1] << ' ';
1151  int xShiftGlobal = static_cast<int>(
1152  (OR_infos[i].minC[0] - OR_globalCorners[0]) /
1153  OR_pixelSize);
1154  int yShiftGlobal = static_cast<int>(
1155  (OR_globalCorners[3] - OR_infos[i].maxC[1]) /
1156  OR_pixelSize);
1157  stream << "Local2DBBox" << ' ' << xShiftGlobal << ' '
1158  << yShiftGlobal << ' '
1159  << xShiftGlobal + (static_cast<int>(OR_infos[i].w) - 1)
1160  << ' '
1161  << yShiftGlobal + (static_cast<int>(OR_infos[i].h) - 1)
1162  << QtCompat::endl;
1163  }
1164  } else {
1166  "Failed to save orthorectification log file! "
1167  "(ortho_rectification_log.txt)");
1168  }
1169  }
1170 
1171  if (generateColoredDTM) {
1172  assert(mntSamples && mntColors);
1173 
1174  if (!cancelledByUser) {
1175  // 3rd step: project each point in all images and get average color
1176  unsigned sampleCount = mntSamples->size();
1177 
1178  ccPointCloud* mntCloud = new ccPointCloud("colored DTM");
1179  if (mntCloud->reserve(sampleCount) &&
1180  mntCloud->reserveTheRGBTable()) {
1181  // for each point
1182  unsigned realCount = 0;
1183  const int* col = mntColors;
1184  for (unsigned i = 0; i < sampleCount; ++i, col += 4) {
1185  if (col[3] > 0) // accum
1186  {
1187  const CCVector3* X =
1188  mntSamples->getPointPersistentPtr(i);
1189  ecvColor::Rgb avgCol(
1190  static_cast<ColorCompType>(col[0] / col[3]),
1191  static_cast<ColorCompType>(col[1] / col[3]),
1192  static_cast<ColorCompType>(col[2] / col[3]));
1193  mntCloud->addPoint(*X);
1194  mntCloud->addRGBColor(avgCol);
1195  ++realCount;
1196  }
1197  }
1198 
1199  if (realCount != 0) {
1200  if (realCount < sampleCount) mntCloud->resize(realCount);
1201 
1202  mntCloud->showColors(true);
1203  container.addChild(mntCloud);
1205  "[Bundler] DTM vertices successfully generated: "
1206  "clean it if necessary then use 'Edit > Mesh > "
1207  "Compute Delaunay 2D (Best LS plane)' then "
1208  "'Smooth' to get a proper mesh");
1209 
1210  if (!parameters.alwaysDisplayLoadDialog) {
1211  // auto save DTM vertices
1212  BinFilter bf;
1213  QString outputFile = imageDir.absoluteFilePath(
1214  "colored_dtm_vertices.bin");
1215  BinFilter::SaveParameters parameters;
1216  { parameters.alwaysDisplaySaveDialog = false; }
1217  if (bf.saveToFile(mntCloud, outputFile, parameters) ==
1219  CVLog::Print(QString("[Bundler] Color DTM vertices "
1220  "automatically saved to '%2'")
1221  .arg(outputFile));
1222  else
1223  CVLog::Warning(QString("[Bundler] Failed to save "
1224  "DTM vertices to '%2'")
1225  .arg(outputFile));
1226  }
1227  } else {
1229  "[Bundler] Failed to generate DTM! (no point "
1230  "viewed in images?)");
1231  }
1232  } else {
1234  "[Bundler] Failed to generate DTM vertices cloud! (not "
1235  "enough memory?)");
1236  delete mntCloud;
1237  mntCloud = 0;
1238  }
1239  }
1240 
1241  delete mntSamples;
1242  mntSamples = 0;
1243  delete[] mntColors;
1244  mntColors = 0;
1245  }
1246 
1247  return cancelledByUser ? CC_FERR_CANCELED_BY_USER : CC_FERR_NO_ERROR;
1248 }
constexpr PointCoordinateType PC_ONE
'1' as a PointCoordinateType value
Definition: CVConst.h:67
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
std::string filename
std::shared_ptr< core::Tensor > image
int count
CC_FILE_ERROR
Typical I/O filter errors.
Definition: FileIOFilter.h:20
@ CC_FERR_CANCELED_BY_USER
Definition: FileIOFilter.h:30
@ CC_FERR_NO_LOAD
Definition: FileIOFilter.h:28
@ CC_FERR_MALFORMED_FILE
Definition: FileIOFilter.h:32
@ CC_FERR_UNKNOWN_FILE
Definition: FileIOFilter.h:23
@ CC_FERR_NO_ERROR
Definition: FileIOFilter.h:21
@ CC_FERR_READING
Definition: FileIOFilter.h:26
@ CC_FERR_NOT_ENOUGH_MEMORY
Definition: FileIOFilter.h:31
@ CC_FERR_WRONG_FILE_TYPE
Definition: FileIOFilter.h:24
QStringList qtCompatSplitRegex(const QString &str, const QString &pattern, Qt::SplitBehavior behavior=Qt::KeepEmptyParts)
Definition: QtCompat.h:308
void * X
Definition: SmallVector.cpp:45
cmdLineReadable * params[]
core::Tensor result
Definition: VtkUtils.cpp:76
CLOUDVIEWER dedicated binary point cloud I/O filter.
Definition: BinFilter.h:15
virtual CC_FILE_ERROR saveToFile(ccHObject *entity, const QString &filename, const SaveParameters &parameters) override
Saves an entity (or a group of) to a file.
CC_FILE_ERROR loadFileExtended(const QString &filename, ccHObject &container, LoadParameters &parameters, const QString &altKeypointsFilename=QString(), bool undistortImages=false, bool generateColoredDTM=false, unsigned coloredDTMVerticesCount=1000000, float scaleFactor=1.0f)
Specific load method.
virtual CC_FILE_ERROR loadFile(const QString &filename, ccHObject &container, LoadParameters &parameters) override
Loads one or more entities from a file.
Dialog for importation of Snavely's Bundler files.
bool importImages() const
Returns whether images should be imported.
bool orthoRectifyImagesAsImages() const
Returns whether images should be ortho-rectified as images.
bool getOptionalTransfoMatrix(ccGLMatrix &mat)
Returns the optional transformation matrix (if defined)
bool importKeypoints() const
Returns whether keypoints should be imported.
unsigned getDTMVerticesCount() const
Returns desired number of vertices for DTM.
bool keepImagesInMemory() const
Returns images should be kept in memory or not.
QString getAltKeypointsFilename() const
Gets alternative keypoints filename (full path)
void setImageListFilename(const QString &filename)
Sets default image list filename (full path)
bool orthoRectifyImagesAsClouds() const
Returns whether images should be ortho-rectified as clouds.
void setVer(unsigned majorVer, unsigned minorVer)
Sets file version on initialization.
OrthoRectMethod getOrthorectificationMethod() const
Returns the ortho-rectification method (for images)
bool useAlternativeKeypoints() const
Returns whether alternative keypoints should be used.
void setCamerasCount(unsigned count)
Sets cameras count on initialization.
double getScaleFactor() const
Returns scale factor.
bool undistortImages() const
Returns whether images should be undistorted.
bool generateColoredDTM() const
Returns whether colored pseudo-DTM should be generated.
void setAltKeypointsFilename(const QString &filename)
Sets default alternative keypoints filename (full path)
void setKeypointsCount(unsigned count)
Sets keypoints count on initialization.
OrthoRectMethod
Image ortho-rectification methods.
QString getImageListFilename() const
Gets image list filename (full path)
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
Generic file I/O filter.
Definition: FileIOFilter.h:46
static constexpr float DEFAULT_PRIORITY
Definition: FileIOFilter.h:313
static ccHObject * LoadFromFile(const QString &filename, LoadParameters &parameters, Shared filter, CC_FILE_ERROR &result)
Loads one or more entities from a file with a known filter.
static bool HandleGlobalShift(const CCVector3d &P, CCVector3d &Pshift, bool &preserveCoordinateShift, LoadParameters &loadParameters, bool useInputCoordinatesShiftIfPossible=false)
Shortcut to the ecvGlobalShiftManager mechanism specific for files.
Array of 2D texture coordinates.
Type y
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
void addElement(const Type &value)
Definition: ecvArray.h:105
Bounding box structure.
Definition: ecvBBox.h:25
Camera (projective) sensor.
ccImage * orthoRectifyAsImage(const ccImage *image, cloudViewer::GenericIndexedCloud *keypoints3D, std::vector< KeyPoint > &keypointsImage, double &pixelSize, double *minCorner=nullptr, double *maxCorner=nullptr, double *realCorners=nullptr) const
Projective ortho-rectification of an image (as image)
ccPointCloud * orthoRectifyAsCloud(const ccImage *image, cloudViewer::GenericIndexedCloud *keypoints3D, std::vector< KeyPoint > &keypointsImage) const
Projective ortho-rectification of an image (as cloud)
ccImage * orthoRectifyAsImageDirect(const ccImage *image, PointCoordinateType altitude, double &pixelSize, bool undistortImages=true, double *minCorner=nullptr, double *maxCorner=nullptr, double *realCorners=nullptr) const
Direct ortho-rectification of an image (as image)
static float ComputeFovRadFromFocalPix(float focal_pix, int imageSize_pix)
Helper: deduces camera f.o.v. (in radians) from focal (in pixels)
QImage undistort(const QImage &image) const
Undistorts an image based on the sensor distortion parameters.
void setDistortionParameters(LensDistortionParameters::Shared params)
Sets uncertainty parameters.
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showColors(bool state)
Sets colors visibility.
Vector3Tpl< T > getTranslationAsVec3D() const
Returns a copy of the translation as a CCVector3.
void invert()
Inverts transformation.
ccGLMatrixTpl< T > inverse() const
Returns inverse transformation.
void apply(float vec[3]) const
Applies transformation to a 3D vector (in place) - float version.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
virtual void toIdentity()
Sets matrix to identity.
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
Double version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:56
Generic mesh interface.
virtual void showMaterials(bool state)
Sets whether textures should be displayed or not.
A 3D cloud interface with associated features (color, normals, octree, etc.)
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
static ccGenericPointCloud * ToGenericPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccGenericPointCloud.
static ccGenericMesh * ToGenericMesh(ccHObject *obj)
Converts current object to ccGenericMesh (if possible)
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
Definition: ecvHObject.h:325
Generic image.
Definition: ecvImage.h:19
unsigned getW() const
Returns image width.
Definition: ecvImage.h:51
unsigned getH() const
Returns image height.
Definition: ecvImage.h:54
QImage & data()
Returns image data.
Definition: ecvImage.h:43
Mesh (triangle) material.
Mesh (triangle) material.
Definition: ecvMaterial.h:28
Triangular mesh.
Definition: ecvMesh.h:35
bool reservePerTriangleMtlIndexes()
Reserves memory to store per-triangle material index.
void addTriangleMtlIndex(int mtlIndex)
Adds triangle material index for next triangle.
void setMaterialSet(ccMaterialSet *materialSet, bool autoReleaseOldMaterialSet=true)
Sets associated material set (may be shared)
bool reserve(std::size_t n)
Reserves the memory to store the vertex indexes (3 per triangle)
void addTriangleTexCoordIndexes(int i1, int i2, int i3)
Adds a triplet of tex coords indexes for next triangle.
void addTriangle(unsigned i1, unsigned i2, unsigned i3)
Adds a triangle to the mesh.
void setTexCoordinatesTable(TextureCoordsContainer *texCoordsTable, bool autoReleaseOldTable=true)
Sets per-triangle texture coordinates array (may be shared)
bool reservePerTriangleTexCoordIndexes()
Reserves memory to store per-triangle triplets of tex coords indexes.
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
bool isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
bool resize(unsigned numberOfPoints) override
Resizes all the active features arrays.
void addRGBColor(const ecvColor::Rgb &C)
Pushes an RGB color on stack.
virtual ccGLMatrix & getRigidTransformation()
Definition: ecvSensor.h:105
virtual void setRigidTransformation(const ccGLMatrix &mat)
Definition: ecvSensor.h:99
void setGraphicScale(PointCoordinateType scale)
Sets the sensor graphic representation scale.
Definition: ecvSensor.h:125
virtual void setGlobalScale(double scale)
virtual void setGlobalShift(double x, double y, double z)
Sets shift applied to original coordinates (information storage only)
Vector3Tpl< T > getCenter() const
Returns center.
Definition: BoundingBox.h:164
T getDiagNorm() const
Returns diagonal length.
Definition: BoundingBox.h:172
bool isValid() const
Returns whether bounding box is valid or not.
Definition: BoundingBox.h:203
void add(const Vector3Tpl< T > &P)
'Enlarges' the bounding box with a point
Definition: BoundingBox.h:131
A generic mesh with index-based vertex access.
static PointCloud * samplePointsOnMesh(GenericMesh *mesh, double samplingDensity, GenericProgressCallback *progressCb=nullptr, std::vector< unsigned > *triIndices=nullptr)
Samples points on a mesh.
bool oneStep()
Increments total progress value of a single unit.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
const CCVector3 * getPointPersistentPtr(unsigned index) override
unsigned size() const override
Definition: PointCloudTpl.h:38
static GenericIndexedMesh * computeTriangulation(GenericIndexedCloudPersist *cloud, TRIANGULATION_TYPES type, PointCoordinateType maxEdgeLength, unsigned char dim, std::string &outputErrorStr)
Applys a geometrical transformation to a single point.
RGB color structure.
Definition: ecvColorTypes.h:49
Graphical progress indicator (thread-safe)
__host__ __device__ float2 fabs(float2 v)
Definition: cutil_math.h:1254
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
unsigned char ColorCompType
Default color components type (R,G and B)
Definition: ecvColorTypes.h:29
static double dist(double x1, double y1, double x2, double y2)
Definition: lsd.c:207
@ MESH
Definition: CVTypes.h:105
@ POINT_CLOUD
Definition: CVTypes.h:104
constexpr QRegularExpression::PatternOption CaseInsensitive
Definition: QtCompat.h:174
constexpr Qt::SplitBehavior SkipEmptyParts
Definition: QtCompat.h:302
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
bool GreaterThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
Definition: CVMath.h:37
bool LessThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
Definition: CVMath.h:23
constexpr ColorCompType MAX
Max value of a single color component (default type)
Definition: ecvColorTypes.h:34
static const double TD
Bundler camera.
float k2
Second radial distortion coef.
float f_pix
focal (in pixels)
float k1
First radial distortion coef.
ccGLMatrixd trans
Rotation + translation.
bool isValid
Validity.
BundlerCamera()
Default constructor.
Generic loading parameters.
Definition: FileIOFilter.h:51
QWidget * parentWidget
Parent widget (if any)
Definition: FileIOFilter.h:78
Generic saving parameters.
Definition: FileIOFilter.h:84
double minC[2]
double maxC[2]
Intrinsic parameters of the camera sensor.
unsigned index
Index in associated point cloud.
float y
2D 'y' coordinate (in pixels)
float x
2D 'x' coordinate (in pixels)
QSharedPointer< LensDistortionParameters > Shared
Shared pointer type.
Simple radial distortion model.
float k2
2nd radial distortion coefficient
float k1
1st radial distortion coefficient