15 #include <QXmlStreamReader>
71 return QStringLiteral(
"DOCUMENT");
73 return QStringLiteral(
"CHUNKS");
75 return QStringLiteral(
"CHUNK");
77 return QStringLiteral(
"SENSORS");
79 return QStringLiteral(
"CAMERAS");
81 return QStringLiteral(
"FRAMES");
83 return QStringLiteral(
"FRAME");
85 return QStringLiteral(
"TRANSFORM");
96 if (tokens.size() != 9) {
100 T* m = output.
data();
101 for (
int i = 0; i < 9; ++i) {
105 m[col * 4 + row] =
static_cast<T
>(tokens[i].toDouble(&ok));
116 template <
typename T>
121 if (tokens.size() != 16) {
125 T* m = output.
data();
126 for (
int i = 0; i < 16; ++i) {
128 m[i] =
static_cast<T
>(tokens[i].toDouble(&ok));
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) {
146 stream.attributes().at(i).qualifiedName().toString());
151 assert(stream.name() == QStringLiteral(
"sensor"));
154 QXmlStreamAttributes sensorAttributes = stream.attributes();
155 if (!sensorAttributes.hasAttribute(QStringLiteral(
"type")) ||
156 sensorAttributes.value(QStringLiteral(
"type")) !=
157 QStringLiteral(
"frame")) {
161 if (!sensorAttributes.hasAttribute(QStringLiteral(
"id"))) {
166 sensorId = sensorAttributes.value(QStringLiteral(
"id")).toInt();
170 bool hasPixelSize =
false;
172 while (stream.readNextStartElement()) {
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"))
184 }
else if (stream.attributes().value(QStringLiteral(
"name")) ==
185 QStringLiteral(
"pixel_height")) {
186 params.pixelSize_mm[1] = stream.attributes()
187 .value(QStringLiteral(
"value"))
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()) {
205 if (stream.name() == QStringLiteral(
"resolution")) {
206 int width = stream.attributes()
207 .value(QStringLiteral(
"width"))
209 int height = stream.attributes()
210 .value(QStringLiteral(
"height"))
215 hasResolution =
true;
217 stream.skipCurrentElement();
218 }
else if (stream.name() == QStringLiteral(
"fx")) {
219 double horizFocal_pix = stream.readElementText().toDouble();
221 }
else if (stream.name() == QStringLiteral(
"fy")) {
222 params.vertFocal_pix = stream.readElementText().toDouble();
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;
242 stream.skipCurrentElement();
246 if (hasResolution && hasVertFocal) {
247 if (!hasCentralPoint) {
262 if (sensorAttributes.hasAttribute(
"label")) {
263 sensor->
setName(sensorAttributes.value(
"label").toString());
269 ExtendedRadialDistortionParameters(
276 stream.skipCurrentElement();
284 assert(stream.name() == QStringLiteral(
"camera"));
286 QXmlStreamAttributes cameraAttributes = stream.attributes();
287 if (!cameraAttributes.hasAttribute(QStringLiteral(
"id")) ||
288 !cameraAttributes.hasAttribute(QStringLiteral(
"sensor_id")) ||
289 !cameraAttributes.hasAttribute(QStringLiteral(
"label"))) {
295 camera.
id = cameraAttributes.value(QStringLiteral(
"id")).toInt();
297 cameraAttributes.value(QStringLiteral(
"sensor_id")).toInt();
299 cameraAttributes.value(QStringLiteral(
"label")).toString();
301 while (stream.readNextStartElement()) {
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")) {
314 attributes.value(QStringLiteral(
"x")).
toDouble(),
315 attributes.value(QStringLiteral(
"y")).
toDouble(),
316 attributes.value(QStringLiteral(
"z")).
toDouble()};
320 stream.skipCurrentElement();
323 stream.skipCurrentElement();
332 CVLog::Warning(QString(
"[Photoscan] Failed to locate '%1' in the "
340 if (!
zipFile.open(QFile::ReadOnly)) {
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'")
355 tempFile.write(
zipFile.readAll());
364 QStringList{
"psz"},
"psz",
365 QStringList{
"Photoscan project (*.psz)"}, QStringList(),
379 if (fileList.isEmpty()) {
384 static const QString s_defaultXMLFilename(
"doc.xml");
386 if (!fileList.contains(s_defaultXMLFilename)) {
389 QString(
"[Photoscan] Couldn't find '%1' in Photoscan archive")
390 .arg(s_defaultXMLFilename));
396 CVLog::Warning(QString(
"[Photoscan] Failed to locate '%1' in the "
398 .arg(s_defaultXMLFilename));
404 if (!zipXML.
open(QFile::ReadOnly)) {
407 .arg(s_defaultXMLFilename));
411 QXmlStreamReader stream(&zipXML);
414 if (!stream.readNextStartElement() ||
415 stream.name() != QStringLiteral(
"document")) {
419 std::vector<Sections> sections;
422 QMap<int, ccCameraSensor*> sensors;
423 QMap<int, CameraDesc> cameras;
424 QList<CloudDesc> clouds;
425 QList<MeshDesc> meshes;
427 bool hasGlobalTransform =
false;
430 if (!stream.readNextStartElement()) {
432 if (!sections.empty()) {
434 QStringLiteral(
" < ") + stream.name().toString() +
435 QStringLiteral(
" [%1]").arg(
ToName(sections.back())));
446 switch (sections.back()) {
448 if (stream.name() == QStringLiteral(
"chunks")) {
449 sections.push_back(
CHUNKS);
452 stream.skipCurrentElement();
457 if (stream.name() == QStringLiteral(
"chunk")) {
458 sections.push_back(
CHUNK);
461 stream.skipCurrentElement();
466 if (stream.name() == QStringLiteral(
"sensors")) {
468 }
else if (stream.name() == QStringLiteral(
"cameras")) {
470 }
else if (stream.name() == QStringLiteral(
"frames")) {
471 sections.push_back(
FRAMES);
472 }
else if (stream.name() == QStringLiteral(
"transform")) {
474 while (stream.readNextStartElement()) {
475 if (stream.name() == QStringLiteral(
"rotation")) {
476 QString rotationValues = stream.readElementText();
477 if (DecodeRotation<double>(rotationValues,
479 hasGlobalTransform =
true;
484 stream.skipCurrentElement();
489 stream.skipCurrentElement();
494 if (stream.name() == QStringLiteral(
"sensor")) {
498 assert(!sensors.contains(sensorId));
499 sensors.insert(sensorId, sensor);
504 stream.skipCurrentElement();
509 if (stream.name() == QStringLiteral(
"camera")) {
513 assert(!cameras.contains(camera.
id));
514 cameras.insert(camera.
id, camera);
519 stream.skipCurrentElement();
524 if (stream.name() == QStringLiteral(
"frame")) {
525 sections.push_back(
FRAME);
528 stream.skipCurrentElement();
533 if (stream.name() == QStringLiteral(
"point_cloud") ||
534 stream.name() == QStringLiteral(
"dense_cloud")) {
537 (stream.name() == QStringLiteral(
"dense_cloud"));
538 while (stream.readNextStartElement()) {
539 if (stream.name() == QStringLiteral(
"points")) {
540 if (stream.attributes().hasAttribute(
541 QStringLiteral(
"path"))) {
545 .value(QStringLiteral(
"path"))
549 ? QStringLiteral(
"dense cloud")
550 : QStringLiteral(
"keypoints"));
551 clouds.push_back(desc);
556 stream.skipCurrentElement();
558 }
else if (stream.name() == QStringLiteral(
"model")) {
562 while (stream.readNextStartElement()) {
563 if (stream.name() == QStringLiteral(
"mesh")) {
564 if (stream.attributes().hasAttribute(
565 QStringLiteral(
"path"))) {
568 .value(QStringLiteral(
"path"))
573 }
else if (stream.name() == QStringLiteral(
"texture")) {
574 if (stream.attributes().hasAttribute(
575 QStringLiteral(
"path"))) {
578 .value(QStringLiteral(
"path"))
584 stream.skipCurrentElement();
587 meshes.push_back(desc);
591 stream.skipCurrentElement();
597 stream.skipCurrentElement();
605 QScopedPointer<ecvProgressDialog> progressDialog(
nullptr);
608 progressDialog->setRange(
609 0, cameras.size() + clouds.size() + meshes.size());
610 progressDialog->setWindowTitle(QStringLiteral(
"Loading data"));
611 progressDialog->start();
613 bool wasCanceled =
false;
614 int currentProgress = 0;
617 QDir dir = QFileInfo(
filename).dir();
619 if (progressDialog && !cameras.empty()) {
620 progressDialog->setInfo(
621 QString(
"Loading %1 image(s)").arg(cameras.size()));
625 if (progressDialog) {
626 progressDialog->setValue(++currentProgress);
627 if (progressDialog->wasCanceled()) {
632 if (camera.imageFilename.isEmpty()) {
653 QString absoluteImageFilename =
654 dir.absoluteFilePath(camera.imageFilename);
657 if (!qImage.load(absoluteImageFilename)) {
659 .arg(camera.imageFilename));
669 image->setName(camera.imageFilename);
670 image->setAlpha(0.5f);
671 image->setVisible(
false);
678 camera.trans.setColumn(1, -camera.trans.getColumnAsVec3D(1));
679 camera.trans.setColumn(2, -camera.trans.getColumnAsVec3D(2));
691 image->setAssociatedSensor(sensor);
692 image->addChild(sensor);
701 imageGroup =
nullptr;
713 if (progressDialog && !clouds.empty()) {
714 progressDialog->setInfo(
715 QString(
"Loading %1 cloud(s)").arg(cameras.size()));
719 if (progressDialog) {
720 progressDialog->setValue(++currentProgress);
721 if (progressDialog->wasCanceled()) {
727 if (desc.filename.isEmpty()) {
732 if (desc.filename.endsWith(QStringLiteral(
".oc3"),
735 QString(
"[Photoscan] OC3 format not supported. Can't "
736 "import %1 from the Photoscan archive")
742 if (tempFilename.isNull()) {
748 params.alwaysDisplayLoadDialog =
false;
749 params.autoComputeNormals =
false;
750 params.parentWidget =
nullptr;
756 if (desc.type == QStringLiteral(
"keypoints")) {
762 "from Photoscan archive")
763 .arg(desc.filename));
765 QFile::remove(tempFilename);
771 if (progressDialog && !meshes.empty()) {
772 progressDialog->setInfo(
773 QString(
"Loading %1 mesh(es)").arg(cameras.size()));
777 if (progressDialog) {
778 progressDialog->setValue(++currentProgress);
779 if (progressDialog->wasCanceled()) {
785 if (desc.filename.isEmpty()) {
791 if (tempFilename.isNull()) {
796 params.alwaysDisplayLoadDialog =
false;
797 params.autoComputeNormals =
false;
798 params.parentWidget =
nullptr;
800 bool success =
false;
801 if (!desc.texture.isEmpty() &&
802 desc.filename.endsWith(QStringLiteral(
"ply"),
819 if (!tempTextureFilename.isNull()) {
820 QFile::remove(tempTextureFilename);
841 "from Photoscan archive")
842 .arg(desc.filename));
845 QFile::remove(tempFilename);
849 if (progressDialog) {
850 progressDialog->stop();
std::shared_ptr< core::Tensor > image
CC_FILE_ERROR
Typical I/O filter errors.
@ CC_FERR_CANCELED_BY_USER
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[]
static bool PrintDebug(const char *format,...)
Same as Print, but works only in Debug mode.
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
static ccHObject * LoadFromFile(const QString &filename, LoadParameters ¶meters, 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 ¶meters)
Loads one or more entities from a file.
Stanford PLY file I/O filter.
A file inside ZIP archive.
virtual bool open(OpenMode mode)
Opens a file for reading.
QStringList getFileNameList() const
Returns a list of files inside the archive.
@ mdUnzip
ZIP file is open for reading files inside it.
bool setCurrentFile(const QString &fileName, CaseSensitivity cs=csDefault)
Sets current file by its name.
bool open(Mode mode, zlib_filefunc_def *ioApi=NULL)
Opens ZIP file.
Vector3Tpl< double > toDouble() const
Cast operator to a double vector (explicit call version)
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.
Double version of ccGLMatrixTpl.
Hierarchical CLOUDVIEWER Object.
unsigned getChildrenNumber() const
Returns the number of children.
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.
virtual void setName(const QString &name)
Sets object name.
virtual void setEnabled(bool state)
Sets the "enabled" property.
virtual void setRigidTransformation(const ccGLMatrix &mat)
void setGraphicScale(PointCoordinateType scale)
Sets the sensor graphic representation scale.
Graphical progress indicator (thread-safe)
constexpr QRegularExpression::PatternOption CaseInsensitive
constexpr Qt::SplitBehavior SkipEmptyParts
std::string toString(T x)
Generic loading parameters.
QWidget * parentWidget
Parent widget (if any)
Extended radial distortion model.
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