ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
PhotoScanFilter.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 "PhotoScanFilter.h"
9 
10 // Qt
11 #include <CVLog.h>
12 
13 #include <QDir>
14 #include <QFileInfo>
15 #include <QXmlStreamReader>
16 
17 // Qt5/Qt6 Compatibility
18 #include <QtCompat.h>
19 
20 // qCC_db
21 #include <ecvCameraSensor.h>
22 #include <ecvHObject.h>
23 #include <ecvImage.h>
24 #include <ecvMesh.h>
25 #include <ecvPointCloud.h>
26 #include <ecvProgressDialog.h>
27 
28 // qCC_io
29 #include <PlyFilter.h>
30 
31 // quazip
32 #include <quazip.h>
33 #include <quazipfile.h>
34 
35 // System
36 #include <assert.h>
37 #include <string.h>
38 
39 struct CameraDesc {
40  CameraDesc() : id(-1), sensorId(-1) {}
41 
43  QString imageFilename;
44  int id, sensorId;
45 };
46 
47 struct CloudDesc {
48  QString filename;
49  QString type;
50 };
51 
52 struct MeshDesc {
53  QString filename;
54  QString texture;
55 };
56 
57 enum Sections {
65  TRANSFORM
66 };
67 
68 QString ToName(Sections section) {
69  switch (section) {
70  case DOCUMENT:
71  return QStringLiteral("DOCUMENT");
72  case CHUNKS:
73  return QStringLiteral("CHUNKS");
74  case CHUNK:
75  return QStringLiteral("CHUNK");
76  case SENSORS:
77  return QStringLiteral("SENSORS");
78  case CAMERAS:
79  return QStringLiteral("CAMERAS");
80  case FRAMES:
81  return QStringLiteral("FRAMES");
82  case FRAME:
83  return QStringLiteral("FRAME");
84  case TRANSFORM:
85  return QStringLiteral("TRANSFORM");
86  default:
87  assert(false);
88  }
89 
90  return QString();
91 }
92 
93 template <typename T>
94 bool DecodeRotation(const QString& rotationValues, ccGLMatrixTpl<T>& output) {
95  QStringList tokens = rotationValues.split(" ", QtCompat::SkipEmptyParts);
96  if (tokens.size() != 9) {
97  return false;
98  }
99 
100  T* m = output.data();
101  for (int i = 0; i < 9; ++i) {
102  int col = (i / 3);
103  int row = (i % 3);
104  bool ok = true;
105  m[col * 4 + row] = static_cast<T>(tokens[i].toDouble(&ok));
106  if (!ok) {
107  // invalid input string
108  return false;
109  }
110  }
111  output.transpose();
112 
113  return true;
114 }
115 
116 template <typename T>
117 bool DecodeTransformation(const QString& transformationValues,
118  ccGLMatrixTpl<T>& output) {
119  QStringList tokens =
120  transformationValues.split(" ", QtCompat::SkipEmptyParts);
121  if (tokens.size() != 16) {
122  return false;
123  }
124 
125  T* m = output.data();
126  for (int i = 0; i < 16; ++i) {
127  bool ok = true;
128  m[i] = static_cast<T>(tokens[i].toDouble(&ok));
129  if (!ok) {
130  // invalid input string
131  return false;
132  }
133  }
134  output.transpose();
135 
136  return true;
137 }
138 
139 static void DisplayCurrentNodeInfo(QXmlStreamReader& stream) {
141  QStringLiteral("--> ") + stream.name().toString() +
142  (stream.isStartElement() ? QStringLiteral(" [start]") : QString()) +
143  (stream.isEndElement() ? QStringLiteral(" [end]") : QString()));
144  for (int i = 0; i < stream.attributes().size(); ++i) {
145  CVLog::Warning(QStringLiteral("\t") +
146  stream.attributes().at(i).qualifiedName().toString());
147  }
148 }
149 
150 static ccCameraSensor* DecodeSensor(QXmlStreamReader& stream, int& sensorId) {
151  assert(stream.name() == QStringLiteral("sensor"));
152  sensorId = -1;
153 
154  QXmlStreamAttributes sensorAttributes = stream.attributes();
155  if (!sensorAttributes.hasAttribute(QStringLiteral("type")) ||
156  sensorAttributes.value(QStringLiteral("type")) !=
157  QStringLiteral("frame")) {
158  // unhandled sensor type
159  return nullptr;
160  }
161  if (!sensorAttributes.hasAttribute(QStringLiteral("id"))) {
162  // invalid sensor?!
163  assert(false);
164  return nullptr;
165  }
166  sensorId = sensorAttributes.value(QStringLiteral("id")).toInt();
167 
168  ccCameraSensor* sensor = nullptr;
170  bool hasPixelSize = false;
171 
172  while (stream.readNextStartElement()) {
173 #ifdef _DEBUG
174  DisplayCurrentNodeInfo(stream);
175 #endif
176 
177  if (stream.name() == QStringLiteral("property")) {
178  if (stream.attributes().value(QStringLiteral("name")) ==
179  QStringLiteral("pixel_width")) {
180  params.pixelSize_mm[0] = stream.attributes()
181  .value(QStringLiteral("value"))
182  .toDouble();
183  // hasPixelSize = true;
184  } else if (stream.attributes().value(QStringLiteral("name")) ==
185  QStringLiteral("pixel_height")) {
186  params.pixelSize_mm[1] = stream.attributes()
187  .value(QStringLiteral("value"))
188  .toDouble();
189  hasPixelSize = true;
190  }
191  stream.skipCurrentElement();
192  } else if (stream.name() == QStringLiteral("calibration") &&
193  stream.attributes().value(QStringLiteral("type")) ==
194  QStringLiteral("frame")) {
196  bool hasDistortion = false;
197  bool hasResolution = false;
198  bool hasVertFocal = false;
199  bool hasCentralPoint = false;
200  while (stream.readNextStartElement()) {
201 #ifdef _DEBUG
202  // DisplayCurrentNodeInfo(stream);
203 #endif
204 
205  if (stream.name() == QStringLiteral("resolution")) {
206  int width = stream.attributes()
207  .value(QStringLiteral("width"))
208  .toInt();
209  int height = stream.attributes()
210  .value(QStringLiteral("height"))
211  .toInt();
212  if (width > 0 && height > 0) {
213  params.arrayWidth = width;
214  params.arrayHeight = height;
215  hasResolution = true;
216  }
217  stream.skipCurrentElement();
218  } else if (stream.name() == QStringLiteral("fx")) {
219  double horizFocal_pix = stream.readElementText().toDouble();
220  //++paramsCount;
221  } else if (stream.name() == QStringLiteral("fy")) {
222  params.vertFocal_pix = stream.readElementText().toDouble();
223  hasVertFocal = true;
224  } else if (stream.name() == QStringLiteral("cx")) {
225  params.principal_point[0] =
226  stream.readElementText().toDouble();
227  hasCentralPoint = true;
228  } else if (stream.name() == QStringLiteral("cy")) {
229  params.principal_point[1] =
230  stream.readElementText().toDouble();
231  hasCentralPoint = true;
232  } else if (stream.name() == QStringLiteral("k1")) {
233  distParams.k1 = stream.readElementText().toDouble();
234  hasDistortion = true;
235  } else if (stream.name() == QStringLiteral("k2")) {
236  distParams.k2 = stream.readElementText().toDouble();
237  hasDistortion = true;
238  } else if (stream.name() == QStringLiteral("k3")) {
239  distParams.k3 = stream.readElementText().toDouble();
240  hasDistortion = true;
241  } else {
242  stream.skipCurrentElement();
243  }
244  }
245 
246  if (hasResolution && hasVertFocal) {
247  if (!hasCentralPoint) {
248  // we define an arbitrary principal point
249  params.principal_point[0] = params.arrayWidth / 2.0f;
250  params.principal_point[1] = params.arrayHeight / 2.0f;
251  }
252  if (!hasPixelSize) {
253  // we use an arbitrary 'pixel size'
254  params.pixelSize_mm[0] = params.pixelSize_mm[1] =
255  1.0f /
256  std::max(params.arrayWidth, params.arrayHeight);
257  }
259  params.vertFocal_pix, params.arrayHeight);
260 
261  sensor = new ccCameraSensor(params);
262  if (sensorAttributes.hasAttribute("label")) {
263  sensor->setName(sensorAttributes.value("label").toString());
264  }
265  if (hasDistortion) {
266  sensor->setDistortionParameters(
268  new ccCameraSensor::
269  ExtendedRadialDistortionParameters(
270  distParams)));
271  }
272  }
273 
274  } //"calibration"
275  else {
276  stream.skipCurrentElement();
277  }
278  }
279 
280  return sensor;
281 }
282 
283 static bool DecodeCamera(QXmlStreamReader& stream, CameraDesc& camera) {
284  assert(stream.name() == QStringLiteral("camera"));
285 
286  QXmlStreamAttributes cameraAttributes = stream.attributes();
287  if (!cameraAttributes.hasAttribute(QStringLiteral("id")) ||
288  !cameraAttributes.hasAttribute(QStringLiteral("sensor_id")) ||
289  !cameraAttributes.hasAttribute(QStringLiteral("label"))) {
290  // invalid camera?!
291  assert(false);
292  return false;
293  }
294 
295  camera.id = cameraAttributes.value(QStringLiteral("id")).toInt();
296  camera.sensorId =
297  cameraAttributes.value(QStringLiteral("sensor_id")).toInt();
298  camera.imageFilename =
299  cameraAttributes.value(QStringLiteral("label")).toString();
300 
301  while (stream.readNextStartElement()) {
302 #ifdef _DEBUG
303  // DisplayCurrentNodeInfo(stream);
304 #endif
305 
306  if (stream.name() == QStringLiteral("transform")) {
307  QString transformationValues = stream.readElementText();
308  DecodeTransformation<float>(transformationValues, camera.trans);
309  } else if (stream.name() == QStringLiteral("reference")) {
310  QXmlStreamAttributes attributes = stream.attributes();
311  if (attributes.value(QStringLiteral("enabled")).toString() ==
312  QStringLiteral("true")) {
313  CCVector3d T = {
314  attributes.value(QStringLiteral("x")).toDouble(),
315  attributes.value(QStringLiteral("y")).toDouble(),
316  attributes.value(QStringLiteral("z")).toDouble()};
317  // What is exactly the "reference" point?!
318  // camera.trans.setTranslation(CCVector3::fromArray(T.u));
319  }
320  stream.skipCurrentElement();
321  } else // orientation? Not sure what it corresponds to!
322  {
323  stream.skipCurrentElement();
324  }
325  }
326 
327  return true;
328 }
329 
330 static QString CreateTempFile(QuaZip& zip, QString zipFilename) {
331  if (!zip.setCurrentFile(zipFilename)) {
332  CVLog::Warning(QString("[Photoscan] Failed to locate '%1' in the "
333  "Photoscan archive")
334  .arg(zipFilename));
335  return QString();
336  }
337 
338  // decompress the file
339  QuaZipFile zipFile(&zip);
340  if (!zipFile.open(QFile::ReadOnly)) {
341  CVLog::Warning(QString("[Photoscan] Failed to extract '%1' from "
342  "Photoscan archive")
343  .arg(zipFilename));
344  return QString();
345  }
346 
347  QDir tempDir = QDir::temp();
348  QString tempFilename = tempDir.absoluteFilePath(zipFilename);
349  QFile tempFile(tempFilename);
350  if (!tempFile.open(QFile::WriteOnly)) {
351  CVLog::Warning(QString("[Photoscan] Failed to create temp file '%1'")
352  .arg(tempFilename));
353  return QString();
354  }
355  tempFile.write(zipFile.readAll());
356  tempFile.close();
357 
358  return tempFilename;
359 }
360 
362  : FileIOFilter({"_PhotoScan Filter",
363  18.0f, // priority
364  QStringList{"psz"}, "psz",
365  QStringList{"Photoscan project (*.psz)"}, QStringList(),
366  Import}) {}
367 
369  ccHObject& container,
370  LoadParameters& parameters) {
371  QuaZip zip(filename);
372 
373  if (!zip.open(QuaZip::mdUnzip)) {
374  // failed to open or read the zip file
375  return CC_FERR_READING;
376  }
377 
378  QStringList fileList = zip.getFileNameList();
379  if (fileList.isEmpty()) {
380  // empty archive?
381  return CC_FERR_NO_LOAD;
382  }
383 
384  static const QString s_defaultXMLFilename("doc.xml");
385 
386  if (!fileList.contains(s_defaultXMLFilename)) {
387  // empty archive?
389  QString("[Photoscan] Couldn't find '%1' in Photoscan archive")
390  .arg(s_defaultXMLFilename));
391  return CC_FERR_NO_LOAD;
392  }
393 
394  // look for the XML file
395  if (!zip.setCurrentFile(s_defaultXMLFilename)) {
396  CVLog::Warning(QString("[Photoscan] Failed to locate '%1' in the "
397  "Photoscan archive")
398  .arg(s_defaultXMLFilename));
399  return CC_FERR_MALFORMED_FILE;
400  }
401 
402  // decompress the XML file
403  QuaZipFile zipXML(&zip);
404  if (!zipXML.open(QFile::ReadOnly)) {
405  CVLog::Warning(QString("[Photoscan] Failed to extract '%1' from "
406  "Photoscan archive")
407  .arg(s_defaultXMLFilename));
408  return CC_FERR_NO_LOAD;
409  }
410 
411  QXmlStreamReader stream(&zipXML);
412 
413  // expected: "document"
414  if (!stream.readNextStartElement() ||
415  stream.name() != QStringLiteral("document")) {
416  return CC_FERR_MALFORMED_FILE;
417  }
418 
419  std::vector<Sections> sections;
420  sections.push_back(DOCUMENT);
421 
422  QMap<int, ccCameraSensor*> sensors;
423  QMap<int, CameraDesc> cameras;
424  QList<CloudDesc> clouds;
425  QList<MeshDesc> meshes;
426  ccGLMatrixd globalTransform;
427  bool hasGlobalTransform = false;
428 
429  while (true) {
430  if (!stream.readNextStartElement()) {
431  // end of section?
432  if (!sections.empty()) {
434  QStringLiteral(" < ") + stream.name().toString() +
435  QStringLiteral(" [%1]").arg(ToName(sections.back())));
436  sections.pop_back();
437  // stream.skipCurrentElement();
438  continue;
439  } else {
440  // end of file
441  break;
442  }
443  }
444  CVLog::PrintDebug(QStringLiteral(" > ") + stream.name().toString());
445 
446  switch (sections.back()) {
447  case DOCUMENT:
448  if (stream.name() == QStringLiteral("chunks")) {
449  sections.push_back(CHUNKS);
450  } else {
451  // not handled
452  stream.skipCurrentElement();
453  }
454  break;
455 
456  case CHUNKS:
457  if (stream.name() == QStringLiteral("chunk")) {
458  sections.push_back(CHUNK);
459  } else {
460  // not handled
461  stream.skipCurrentElement();
462  }
463  break;
464 
465  case CHUNK:
466  if (stream.name() == QStringLiteral("sensors")) {
467  sections.push_back(SENSORS);
468  } else if (stream.name() == QStringLiteral("cameras")) {
469  sections.push_back(CAMERAS);
470  } else if (stream.name() == QStringLiteral("frames")) {
471  sections.push_back(FRAMES);
472  } else if (stream.name() == QStringLiteral("transform")) {
473  // inner loop
474  while (stream.readNextStartElement()) {
475  if (stream.name() == QStringLiteral("rotation")) {
476  QString rotationValues = stream.readElementText();
477  if (DecodeRotation<double>(rotationValues,
478  globalTransform)) {
479  hasGlobalTransform = true;
480  } else {
481  assert(false);
482  }
483  }
484  stream.skipCurrentElement();
485  }
486  } else // frames, reference, region, settings, meta, etc.
487  {
488  // not handled for now
489  stream.skipCurrentElement();
490  }
491  break;
492 
493  case SENSORS:
494  if (stream.name() == QStringLiteral("sensor")) {
495  int sensorId = -1;
496  ccCameraSensor* sensor = DecodeSensor(stream, sensorId);
497  if (sensor) {
498  assert(!sensors.contains(sensorId));
499  sensors.insert(sensorId, sensor);
500  // currentContainer->addChild(sensor);
501  }
502  } else {
503  // not handled
504  stream.skipCurrentElement();
505  }
506  break;
507 
508  case CAMERAS:
509  if (stream.name() == QStringLiteral("camera")) {
510  CameraDesc camera;
511  ccGLMatrix trans;
512  if (DecodeCamera(stream, camera)) {
513  assert(!cameras.contains(camera.id));
514  cameras.insert(camera.id, camera);
515  // currentContainer->addChild(camera.image);
516  }
517  } else {
518  // not handled
519  stream.skipCurrentElement();
520  }
521  break;
522 
523  case FRAMES:
524  if (stream.name() == QStringLiteral("frame")) {
525  sections.push_back(FRAME);
526  } else {
527  // not handled
528  stream.skipCurrentElement();
529  }
530  break;
531 
532  case FRAME:
533  if (stream.name() == QStringLiteral("point_cloud") ||
534  stream.name() == QStringLiteral("dense_cloud")) {
535  // inner loop
536  bool denseCloud =
537  (stream.name() == QStringLiteral("dense_cloud"));
538  while (stream.readNextStartElement()) {
539  if (stream.name() == QStringLiteral("points")) {
540  if (stream.attributes().hasAttribute(
541  QStringLiteral("path"))) {
542  CloudDesc desc;
543  desc.filename =
544  stream.attributes()
545  .value(QStringLiteral("path"))
546  .toString();
547  desc.type =
548  (denseCloud
549  ? QStringLiteral("dense cloud")
550  : QStringLiteral("keypoints"));
551  clouds.push_back(desc);
552  } else {
553  assert(false);
554  }
555  }
556  stream.skipCurrentElement();
557  }
558  } else if (stream.name() == QStringLiteral("model")) {
559  MeshDesc desc;
560 
561  // inner loop
562  while (stream.readNextStartElement()) {
563  if (stream.name() == QStringLiteral("mesh")) {
564  if (stream.attributes().hasAttribute(
565  QStringLiteral("path"))) {
566  desc.filename =
567  stream.attributes()
568  .value(QStringLiteral("path"))
569  .toString();
570  } else {
571  assert(false);
572  }
573  } else if (stream.name() == QStringLiteral("texture")) {
574  if (stream.attributes().hasAttribute(
575  QStringLiteral("path"))) {
576  desc.texture =
577  stream.attributes()
578  .value(QStringLiteral("path"))
579  .toString();
580  } else {
581  assert(false);
582  }
583  }
584  stream.skipCurrentElement();
585  }
586  if (!desc.filename.isEmpty()) {
587  meshes.push_back(desc);
588  }
589  } else {
590  // not handled
591  stream.skipCurrentElement();
592  }
593  break;
594 
595  case TRANSFORM:
596  // not handled
597  stream.skipCurrentElement();
598  break;
599 
600  default:
601  break;
602  }
603  }
604 
605  QScopedPointer<ecvProgressDialog> progressDialog(nullptr);
606  if (parameters.parentWidget) {
607  progressDialog.reset(new ecvProgressDialog(parameters.parentWidget));
608  progressDialog->setRange(
609  0, cameras.size() + clouds.size() + meshes.size());
610  progressDialog->setWindowTitle(QStringLiteral("Loading data"));
611  progressDialog->start();
612  }
613  bool wasCanceled = false;
614  int currentProgress = 0;
615 
616  // end of file: now we can sort the various extracted components
617  QDir dir = QFileInfo(filename).dir();
618  ccHObject* imageGroup = new ccHObject(QStringLiteral("Images"));
619  if (progressDialog && !cameras.empty()) {
620  progressDialog->setInfo(
621  QString("Loading %1 image(s)").arg(cameras.size()));
622  }
623  for (CameraDesc& camera : cameras) {
624  // progress
625  if (progressDialog) {
626  progressDialog->setValue(++currentProgress);
627  if (progressDialog->wasCanceled()) {
628  wasCanceled = true;
629  break;
630  }
631  }
632  if (camera.imageFilename.isEmpty()) {
633  assert(false);
634  continue;
635  }
636 
637  // DGM: the images are not in the archive!
638  // if (!zip.setCurrentFile(camera.imageFilename))
639  //{
640  // CVLog::Warning(QString("[Photoscan] Failed to locate image '%1'
641  // in the Photoscan archive").arg(camera.imageFilename)); continue;
642  //}
643 
645  // QuaZipFile zipImage(&zip);
646  // if (!zipImage.open(QFile::ReadOnly))
647  //{
648  // CVLog::Warning(QString("[Photoscan] Failed to extract '%1' from
649  // Photoscan archive").arg(camera.imageFilename)); continue;
650  //}
651 
652  QImage qImage;
653  QString absoluteImageFilename =
654  dir.absoluteFilePath(camera.imageFilename);
655  // if (!qImage.load(&zipImage,
656  // qPrintable(QFileInfo(camera.imageFilename).suffix())))
657  if (!qImage.load(absoluteImageFilename)) {
658  CVLog::Warning(QString("[Photoscan] Failed to load image '%1'")
659  .arg(camera.imageFilename));
660  continue;
661  }
662 
663  ccCameraSensor* const origSensor = sensors[camera.sensorId];
664  if (origSensor) {
665  origSensor->undistort(qImage);
666  }
667 
668  ccImage* image = new ccImage(qImage);
669  image->setName(camera.imageFilename);
670  image->setAlpha(0.5f);
671  image->setVisible(false);
672 
673  // associated sensor (if any)
674  if (origSensor) {
675  // make a copy of the original sensor
676  ccCameraSensor* sensor = new ccCameraSensor(*origSensor);
677 
678  camera.trans.setColumn(1, -camera.trans.getColumnAsVec3D(1));
679  camera.trans.setColumn(2, -camera.trans.getColumnAsVec3D(2));
680 
681  // FIXME: we would have to transform the clouds and meshes as well!
682  // if (hasGlobalTransform)
683  //{
684  // //apply global transformation (if any)
685  // camera.trans = ccGLMatrix(globalTransform.data()) *
686  // camera.trans;
687  // }
688  sensor->setRigidTransformation(camera.trans);
689  sensor->setVisible(true);
690  sensor->setGraphicScale(0.1f);
691  image->setAssociatedSensor(sensor);
692  image->addChild(sensor);
693  imageGroup->addChild(image);
694  }
695  }
696  if (imageGroup->getChildrenNumber()) {
697  container.addChild(imageGroup);
698  } else {
699  // no image?!
700  delete imageGroup;
701  imageGroup = nullptr;
702  }
703 
704  // we can get rid of the original sensors
705  for (ccCameraSensor*& sensor : sensors) {
706  delete sensor;
707  sensor = nullptr;
708  }
709  sensors.clear();
710 
711  // clouds
712  if (!wasCanceled) {
713  if (progressDialog && !clouds.empty()) {
714  progressDialog->setInfo(
715  QString("Loading %1 cloud(s)").arg(cameras.size()));
716  }
717  for (CloudDesc& desc : clouds) {
718  // progress
719  if (progressDialog) {
720  progressDialog->setValue(++currentProgress);
721  if (progressDialog->wasCanceled()) {
722  wasCanceled = true;
723  break;
724  }
725  }
726 
727  if (desc.filename.isEmpty()) {
728  assert(false);
729  continue;
730  }
731 
732  if (desc.filename.endsWith(QStringLiteral(".oc3"),
735  QString("[Photoscan] OC3 format not supported. Can't "
736  "import %1 from the Photoscan archive")
737  .arg(desc.type));
738  continue;
739  }
740 
741  QString tempFilename = CreateTempFile(zip, desc.filename);
742  if (tempFilename.isNull()) {
743  continue;
744  }
745 
746  ccHObject tempContainer;
748  params.alwaysDisplayLoadDialog = false;
749  params.autoComputeNormals = false;
750  params.parentWidget = nullptr;
752  ccHObject* newGroup =
753  FileIOFilter::LoadFromFile(tempFilename, params, result);
754  if (newGroup) {
755  newGroup->setName(desc.type);
756  if (desc.type == QStringLiteral("keypoints")) {
757  newGroup->setEnabled(false);
758  }
759  container.addChild(newGroup);
760  } else {
761  CVLog::Warning(QString("[Photoscan] Failed to extract '%1' "
762  "from Photoscan archive")
763  .arg(desc.filename));
764  }
765  QFile::remove(tempFilename);
766  }
767  }
768 
769  // meshes
770  if (!wasCanceled) {
771  if (progressDialog && !meshes.empty()) {
772  progressDialog->setInfo(
773  QString("Loading %1 mesh(es)").arg(cameras.size()));
774  }
775  for (MeshDesc& desc : meshes) {
776  // progress
777  if (progressDialog) {
778  progressDialog->setValue(++currentProgress);
779  if (progressDialog->wasCanceled()) {
780  wasCanceled = true;
781  break;
782  }
783  }
784 
785  if (desc.filename.isEmpty()) {
786  assert(false);
787  continue;
788  }
789 
790  QString tempFilename = CreateTempFile(zip, desc.filename);
791  if (tempFilename.isNull()) {
792  continue;
793  }
794 
796  params.alwaysDisplayLoadDialog = false;
797  params.autoComputeNormals = false;
798  params.parentWidget = nullptr;
799 
800  bool success = false;
801  if (!desc.texture.isEmpty() &&
802  desc.filename.endsWith(QStringLiteral("ply"),
804  QString tempTextureFilename = CreateTempFile(zip, desc.texture);
805 
806  ccHObject tempContainer;
807  if (PlyFilter().loadFile(tempFilename, desc.texture,
808  tempContainer,
809  params) == CC_FERR_NO_ERROR) {
810  success = true;
811  // transfer the loaded entities to the current container
812  for (unsigned i = 0; i < tempContainer.getChildrenNumber();
813  ++i) {
814  container.addChild(tempContainer.getChild(i));
815  }
816  tempContainer.detachAllChildren();
817  }
818 
819  if (!tempTextureFilename.isNull()) {
820  QFile::remove(tempTextureFilename);
821  }
822  } else {
825  tempFilename, params, result);
826  if (newGroup) {
827  success = true;
828  // transfer the loaded entities to the current container
829  for (unsigned i = 0; i < newGroup->getChildrenNumber();
830  ++i) {
831  container.addChild(newGroup->getChild(i));
832  }
833  newGroup->detachAllChildren();
834  delete newGroup;
835  newGroup = nullptr;
836  }
837  }
838 
839  if (!success) {
840  CVLog::Warning(QString("[Photoscan] Failed to extract '%1' "
841  "from Photoscan archive")
842  .arg(desc.filename));
843  }
844 
845  QFile::remove(tempFilename);
846  }
847  }
848 
849  if (progressDialog) {
850  progressDialog->stop();
851  }
852 
853  return wasCanceled ? CC_FERR_CANCELED_BY_USER : CC_FERR_NO_ERROR;
854 }
std::string filename
std::shared_ptr< core::Tensor > image
int width
int size
int height
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_NO_ERROR
Definition: FileIOFilter.h:21
@ CC_FERR_READING
Definition: FileIOFilter.h:26
Sections
@ FRAMES
@ CHUNK
@ DOCUMENT
@ CAMERAS
@ FRAME
@ TRANSFORM
@ SENSORS
@ CHUNKS
QString ToName(Sections section)
bool DecodeTransformation(const QString &transformationValues, ccGLMatrixTpl< T > &output)
static ccCameraSensor * DecodeSensor(QXmlStreamReader &stream, int &sensorId)
static QString CreateTempFile(QuaZip &zip, QString zipFilename)
bool DecodeRotation(const QString &rotationValues, ccGLMatrixTpl< T > &output)
static void DisplayCurrentNodeInfo(QXmlStreamReader &stream)
static bool DecodeCamera(QXmlStreamReader &stream, CameraDesc &camera)
cmdLineReadable * params[]
core::Tensor result
Definition: VtkUtils.cpp:76
static bool PrintDebug(const char *format,...)
Same as Print, but works only in Debug mode.
Definition: CVLog.cpp:153
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
Generic file I/O filter.
Definition: FileIOFilter.h:46
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.
virtual CC_FILE_ERROR loadFile(const QString &filename, ccHObject &container, LoadParameters &parameters)
Loads one or more entities from a file.
Stanford PLY file I/O filter.
Definition: PlyFilter.h:46
A file inside ZIP archive.
Definition: quazipfile.h:74
virtual bool open(OpenMode mode)
Opens a file for reading.
Definition: quazipfile.cpp:231
ZIP archive.
Definition: quazip.h:128
QStringList getFileNameList() const
Returns a list of files inside the archive.
Definition: quazip.cpp:665
@ mdUnzip
ZIP file is open for reading files inside it.
Definition: quazip.h:141
bool setCurrentFile(const QString &fileName, CaseSensitivity cs=csDefault)
Sets current file by its name.
Definition: quazip.cpp:398
bool open(Mode mode, zlib_filefunc_def *ioApi=NULL)
Opens ZIP file.
Definition: quazip.cpp:208
Vector3Tpl< double > toDouble() const
Cast operator to a double vector (explicit call version)
Definition: CVGeom.h:255
Camera (projective) sensor.
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.
A 4x4 'transformation' matrix (column major order)
T * data()
Returns a pointer to internal data.
void transpose()
Transposes matrix (in place)
Float version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:19
Double version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:56
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
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.
void detachAllChildren()
Removes a specific child.
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
Definition: ecvHObject.h:325
Generic image.
Definition: ecvImage.h:19
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
virtual void setRigidTransformation(const ccGLMatrix &mat)
Definition: ecvSensor.h:99
void setGraphicScale(PointCoordinateType scale)
Sets the sensor graphic representation scale.
Definition: ecvSensor.h:125
Graphical progress indicator (thread-safe)
int max(int a, int b)
Definition: cutil_math.h:48
constexpr QRegularExpression::PatternOption CaseInsensitive
Definition: QtCompat.h:174
constexpr Qt::SplitBehavior SkipEmptyParts
Definition: QtCompat.h:302
std::string toString(T x)
Definition: Common.h:80
ccGLMatrix trans
QString imageFilename
QString filename
Generic loading parameters.
Definition: FileIOFilter.h:51
QWidget * parentWidget
Parent widget (if any)
Definition: FileIOFilter.h:78
QString filename
QString texture
float k3
3rd radial distortion coefficient
Intrinsic parameters of the camera sensor.
QSharedPointer< LensDistortionParameters > Shared
Shared pointer type.
float k2
2nd radial distortion coefficient
float k1
1st radial distortion coefficient
voidp zipFile
Definition: zip.h:80