28 #include <QApplication>
39 constexpr
char CC_E57_INTENSITY_FIELD_NAME[] =
"Intensity";
40 constexpr
char CC_E57_RETURN_INDEX_FIELD_NAME[] =
"Return index";
41 constexpr
char s_e57PoseKey[] =
"E57_pose";
43 unsigned s_absoluteScanIndex = 0;
44 bool s_cancelRequestedByUser =
false;
46 unsigned s_absoluteImageIndex = 0;
48 ScalarType s_maxIntensity = 0;
49 ScalarType s_minIntensity = 0;
57 std::vector<double> xData;
58 std::vector<double> yData;
59 std::vector<double> zData;
60 std::vector<int8_t> isInvalidData;
63 std::vector<double> xNormData;
64 std::vector<double> yNormData;
65 std::vector<double> zNormData;
68 std::vector<double> intData;
69 std::vector<int8_t> isInvalidIntData;
72 std::vector<int8_t> scanIndexData;
75 std::vector<colorFieldType> redData;
76 std::vector<colorFieldType> greenData;
77 std::vector<colorFieldType> blueData;
80 inline QString GetNewGuid() {
return QUuid::createUuid().toString(); }
86 QStringList{
"e57"},
"e57", QStringList{
"E57 cloud (*.e57)"},
91 bool& exclusive)
const {
105 parentNode.
set(
"pose", pose);
115 pose.
set(
"rotation", rotation);
124 pose.
set(
"translation", translation);
136 unsigned pointCount = cloud->
size();
137 if (pointCount == 0) {
138 CVLog::Error(QString(
"[E57Filter::SaveScan] Cloud '%1' is empty!")
145 bool hasPoseMat =
false;
147 double globalScale = 1.0;
148 bool isScaled =
false;
151 assert(globalScale != 0);
152 isScaled = (globalScale != 1.0);
155 QString poseStr = cloud->
getMetaData(s_e57PoseKey).toString();
156 if (!poseStr.isEmpty()) {
166 "[E57Filter::saveFile] Pose meta-data is invalid");
172 if (Tshift.
norm2d() != 0) {
173 shiftedPoseMat = localPoseMat;
177 }
else if (hasPoseMat) {
178 shiftedPoseMat = localPoseMat;
187 for (
unsigned i = 0; i < pointCount; ++i) {
200 bbMax.
x = bbMin.
x = Pg.
x;
201 bbMax.
y = bbMin.
y = Pg.
y;
202 bbMax.
z = bbMin.
z = Pg.
z;
207 CVLog::Error(QString(
"[E57Filter::SaveScan] Internal error: cloud "
208 "'%1' has an invalid bounding box?!")
219 if (!cloud->
getName().isEmpty())
225 .arg(s_absoluteScanIndex)
229 scanNode.
set(
"description",
263 int minReturnIndex = 0;
264 int maxReturnIndex = 0;
267 CC_E57_RETURN_INDEX_FIELD_NAME);
268 if (returnIndexSFIndex >= 0) {
273 assert(sf->
getMin() >= 0);
276 double minIndex =
static_cast<double>(sf->
getMin());
277 double maxIndex =
static_cast<double>(sf->
getMax());
281 double fracMin = modf(minIndex, &intMin);
282 double fracMax = modf(maxIndex, &intMax);
284 if (fracMin == 0 && fracMax == 0 &&
285 static_cast<int>(intMax - intMin) < 256) {
286 int minScanIndex =
static_cast<int>(intMin);
287 int maxScanIndex =
static_cast<int>(intMax);
289 minReturnIndex = minScanIndex;
290 maxReturnIndex = maxScanIndex;
291 if (maxReturnIndex > minReturnIndex) {
298 ibox.
set(
"rowMaximum",
302 ibox.
set(
"returnMinimum",
304 ibox.
set(
"returnMaximum",
306 scanNode.
set(
"indexBounds", ibox);
315 bool hasInvalidIntensities =
false;
317 int intensitySFIndex =
319 if (intensitySFIndex < 0) {
321 if (intensitySFIndex >= 0)
323 "[E57] No 'intensity' scalar field found, we'll use "
324 "the currently displayed one instead (%s)",
327 if (intensitySFIndex >= 0) {
333 intbox.
set(
"intensityMinimum",
335 intbox.
set(
"intensityMaximum",
337 scanNode.
set(
"intensityLimits", intbox);
340 for (
unsigned i = 0; i < intensitySF->
currentSize(); ++i) {
341 ScalarType d = intensitySF->
getValue(i);
343 hasInvalidIntensities =
true;
360 scanNode.
set(
"colorLimits", colorbox);
372 scanNode.
set(
"cartesianBounds", bboxNode);
395 const unsigned chunkSize = std::min<unsigned>(
396 pointCount, (1 << 20));
399 std::vector<e57::SourceDestBuffer> dbufs;
409 proto.
set(
"cartesianX",
411 arrays.xData.resize(chunkSize);
412 dbufs.emplace_back(imf,
"cartesianX", arrays.xData.data(), chunkSize,
415 proto.
set(
"cartesianY",
417 arrays.yData.resize(chunkSize);
418 dbufs.emplace_back(imf,
"cartesianY", arrays.yData.data(), chunkSize,
421 proto.
set(
"cartesianZ",
423 arrays.zData.resize(chunkSize);
424 dbufs.emplace_back(imf,
"cartesianZ", arrays.zData.data(), chunkSize,
435 proto.
set(
"nor:normalX",
437 arrays.xNormData.resize(chunkSize);
438 dbufs.emplace_back(imf,
"nor:normalX", arrays.xNormData.data(),
439 chunkSize,
true,
true);
441 proto.
set(
"nor:normalY",
443 arrays.yNormData.resize(chunkSize);
444 dbufs.emplace_back(imf,
"nor:normalY", arrays.yNormData.data(),
445 chunkSize,
true,
true);
447 proto.
set(
"nor:normalZ",
449 arrays.zNormData.resize(chunkSize);
450 dbufs.emplace_back(imf,
"nor:normalZ", arrays.zNormData.data(),
451 chunkSize,
true,
true);
456 assert(maxReturnIndex > minReturnIndex);
457 proto.
set(
"returnIndex",
460 arrays.scanIndexData.resize(chunkSize);
461 dbufs.emplace_back(imf,
"returnIndex", arrays.scanIndexData.data(),
462 chunkSize,
true,
true);
466 proto.
set(
"intensity",
471 arrays.intData.resize(chunkSize);
472 dbufs.emplace_back(imf,
"intensity", arrays.intData.data(), chunkSize,
475 if (hasInvalidIntensities) {
477 arrays.isInvalidIntData.resize(chunkSize);
478 dbufs.emplace_back(imf,
"isIntensityInvalid",
479 arrays.isInvalidIntData.data(), chunkSize,
true,
487 arrays.redData.resize(chunkSize);
488 dbufs.emplace_back(imf,
"colorRed", arrays.redData.data(), chunkSize,
491 arrays.greenData.resize(chunkSize);
492 dbufs.emplace_back(imf,
"colorGreen", arrays.greenData.data(),
493 chunkSize,
true,
true);
495 arrays.blueData.resize(chunkSize);
496 dbufs.emplace_back(imf,
"colorBlue", arrays.blueData.data(), chunkSize,
529 progressDlg->setMethodTitle(QObject::tr(
"Write E57 file"));
530 progressDlg->setInfo(QObject::tr(
"Scan #%1 - %2 points")
531 .arg(s_absoluteScanIndex)
533 progressDlg->start();
534 QApplication::processEvents();
543 unsigned remainingPointCount = pointCount;
544 while (remainingPointCount != 0) {
545 unsigned thisChunkSize =
std::min(remainingPointCount, chunkSize);
548 for (
unsigned i = 0; i < thisChunkSize; ++i, ++index) {
553 Pglobal = inversePoseMat * Pglobal;
555 arrays.xData[i] = Pglobal.
x;
556 arrays.yData[i] = Pglobal.
y;
557 arrays.zData[i] = Pglobal.
z;
560 assert(!arrays.intData.empty());
561 ScalarType sfVal = intensitySF->
getValue(index);
562 arrays.intData[i] =
static_cast<double>(sfVal);
563 if (!arrays.isInvalidIntData.empty())
564 arrays.isInvalidIntData[i] =
570 arrays.xNormData[i] =
static_cast<double>(N.
x);
571 arrays.yNormData[i] =
static_cast<double>(N.
y);
572 arrays.zNormData[i] =
static_cast<double>(N.
z);
578 arrays.redData[i] =
static_cast<double>(C.
r);
579 arrays.greenData[i] =
static_cast<double>(C.
g);
580 arrays.blueData[i] =
static_cast<double>(C.
b);
584 assert(!arrays.scanIndexData.empty());
585 arrays.scanIndexData[i] =
586 static_cast<int8_t
>(returnIndexSF->
getValue(index));
590 QApplication::processEvents();
591 s_cancelRequestedByUser =
true;
596 writer.
write(thisChunkSize);
598 assert(thisChunkSize <= remainingPointCount);
599 remainingPointCount -= thisChunkSize;
608 const QString& scanGUID,
621 if (!
image->getName().isEmpty())
622 imageNode.
set(
"name",
625 imageNode.
set(
"name",
627 .arg(s_absoluteImageIndex)
647 imageNode.
set(
"associatedData3DGuid",
662 static_cast<const ccImage*
>(
image)->getAssociatedSensor();
676 buffer.open(QIODevice::WriteOnly);
677 image->data().save(&buffer,
680 int imageSize = ba.size();
683 QString cameraRepresentationStr(
"visualReferenceRepresentation");
686 cameraRepresentation.
set(
"pngImage", blob);
687 cameraRepresentation.
set(
"imageHeight",
689 cameraRepresentation.
set(
"imageWidth",
713 imageNode.
set(cameraRepresentationStr.toStdString(), cameraRepresentation);
714 images2D.
append(imageNode);
715 blob.
write((uint8_t*)ba.data(), 0,
static_cast<size_t>(imageSize));
723 std::vector<ccPointCloud*> scans;
760 root.
set(
"formatName",
784 creationDateTime.
set(
"isAtomicClockReferenced",
786 root.
set(
"creationDateTime", creationDateTime);
790 root.
set(
"data3D", data3D);
794 root.
set(
"images2D", images2D);
799 QMap<ccHObject*, QString> scansGUID;
800 s_absoluteScanIndex = 0;
803 QScopedPointer<ecvProgressDialog> progressDlg(
nullptr);
807 progressDlg->setAutoClose(
false);
809 s_cancelRequestedByUser =
false;
812 bool hasNormals =
false;
814 for (
auto cloud : scans) {
815 QString scanGUID = GetNewGuid();
818 if (!hasNormals && cloud->hasNormals()) {
822 "http://www.libe57.org/E57_NOR_surface_normals.txt");
827 if (
SaveScan(cloud, scanNode, imf, data3D, scanGUID,
828 progressDlg.data())) {
829 ++s_absoluteScanIndex;
830 scansGUID.insert(cloud, scanGUID);
836 if (s_cancelRequestedByUser) {
844 s_absoluteImageIndex = 0;
845 size_t scanCount = scans.size();
846 for (
size_t i = 0; i < scanCount; ++i) {
849 unsigned imageCount =
852 if (imageCount != 0) {
855 progressDlg.data(), imageCount);
857 progressDlg->setMethodTitle(
858 QObject::tr(
"Write E57 file"));
859 progressDlg->setInfo(
860 QObject::tr(
"Cloud #%1 - Images: %2")
863 progressDlg->start();
864 QApplication::processEvents();
867 for (
unsigned j = 0; j < imageCount; ++j) {
869 assert(scansGUID.contains(cloud));
870 QString scanGUID = scansGUID.value(cloud);
873 ++s_absoluteImageIndex;
875 s_cancelRequestedByUser =
true;
888 QStringLiteral(
"[E57] Error: %1 (%2 line %3)")
896 .arg(QString::fromStdString(e.
context())));
910 assert(currentTreeNode);
915 QString infoStr = QString(
name.c_str() ==
nullptr ||
name.c_str()[0] == 0
919 switch (currentE57Node.
type()) {
921 infoStr += QString(
" [STRUCTURE]");
928 infoStr += QString(
" [VECTOR]");
936 infoStr += QString(
" [COMPRESSED VECTOR (%1 elements)]")
941 infoStr += QString(
" [INTEGER: %1]").arg(i.
value());
946 infoStr += QString(
" [SCALED INTEGER: %1]").arg(si.
scaledValue());
950 infoStr += QString(
" [FLOAT: %1]").arg(f.
value());
954 infoStr += QString(
" [STRING: %1]").arg(s.
value().c_str());
958 infoStr += QString(
" [BLOB (%1 bytes)]").arg(b.
byteCount());
961 infoStr += QString(
"[INVALID]");
972 QString infoStr = QString(
"[E57] '%1' - ").arg(node.
elementName().c_str());
973 switch (node.
type()) {
976 infoStr += QString(
"STRUCTURE, %1 child(ren)").arg(s.
childCount());
980 infoStr += QString(
"VECTOR, %1 child(ren)").arg(v.
childCount());
985 infoStr += QString(
"COMPRESSED VECTOR, %1 elements")
990 infoStr += QString(
"%1 (INTEGER)").arg(i.
value());
995 infoStr += QString(
"%1 (SCALED INTEGER)").arg(si.
scaledValue());
999 infoStr += QString(
"%1 (FLOAT)").arg(f.
value());
1003 infoStr += QString(s.
value().c_str());
1007 infoStr += QString(
"BLOB, size=%1").arg(b.
byteCount());
1010 infoStr += QString(
"INVALID");
1022 CVLog::Warning(
"[E57] Couldn't find element named '%s'", childName);
1028 ex.
report(__FILE__, __LINE__, __FUNCTION__);
1035 CVLog::Warning(
"[E57] Couldn't find element named '%s'", childName);
1041 ex.
report(__FILE__, __LINE__, __FUNCTION__);
1047 "[E57] Element '%s' has no child (not a structure nor a "
1065 proto.
isDefined(
"cartesianInvalidState");
1091 }
else if (proto.
isDefined(
"sphericalRange")) {
1121 proto.
isDefined(
"sphericalInvalidState");
1127 if (proto.
isDefined(
"sphericalAzimuth")) {
1224 if (scan.
isDefined(
"intensityLimits")) {
1268 minimum * scale +
offset;
1270 maximum * scale +
offset;
1428 bool validPoseMat =
false;
1433 double quaternion[4];
1442 validPoseMat =
true;
1453 validPoseMat =
true;
1457 return validPoseMat;
1470 CVLog::Print(QString(
"[E57] Reading new scan node (%1)")
1492 const int64_t pointCount =
points.childCount();
1499 bool sphericalMode =
false;
1509 "'%1'! (only cartesian and spherical "
1510 "coordinates are supported right now)")
1514 sphericalMode =
true;
1520 cloud->
setName(QString::fromStdString(
1524 if (scanNode.
isDefined(
"description")) {
1526 QStringLiteral(
"[E57] Internal description: %1")
1527 .arg(QString::fromStdString(
1554 bool poseMatWasShifted =
false;
1560 bool preserveCoordinateShift =
true;
1562 s_loadParameters)) {
1564 if (preserveCoordinateShift) {
1567 poseMatWasShifted =
true;
1569 "[E57Filter::loadFile] Cloud %s has been recentered! "
1570 "Translation: (%.2f ; %.2f ; %.2f)",
1571 qPrintable(guidStr), Tshift.
x, Tshift.
y, Tshift.
z);
1582 const unsigned chunkSize = std::min<unsigned>(
1583 pointCount, (1 << 20));
1586 std::vector<e57::SourceDestBuffer> dbufs;
1588 if (!cloud->
reserve(
static_cast<unsigned>(pointCount))) {
1594 if (sphericalMode) {
1597 arrays.xData.
resize(chunkSize);
1599 arrays.xData.data(), chunkSize,
true,
1600 (prototype.
get(
"sphericalRange").
type() ==
1604 arrays.yData.resize(chunkSize);
1605 dbufs.emplace_back(node.
destImageFile(),
"sphericalAzimuth",
1606 arrays.yData.data(), chunkSize,
true,
1607 (prototype.
get(
"sphericalAzimuth").
type() ==
1611 arrays.zData.resize(chunkSize);
1612 dbufs.emplace_back(node.
destImageFile(),
"sphericalElevation",
1613 arrays.zData.data(), chunkSize,
true,
1614 (prototype.
get(
"sphericalElevation").
type() ==
1620 arrays.isInvalidData.resize(chunkSize);
1621 dbufs.emplace_back(node.
destImageFile(),
"sphericalInvalidState",
1622 arrays.isInvalidData.data(), chunkSize,
true,
1623 (prototype.
get(
"sphericalInvalidState").
type() ==
1629 arrays.xData.resize(chunkSize);
1631 arrays.xData.data(), chunkSize,
true,
1632 (prototype.
get(
"cartesianX").
type() ==
1636 arrays.yData.resize(chunkSize);
1638 arrays.yData.data(), chunkSize,
true,
1639 (prototype.
get(
"cartesianY").
type() ==
1643 arrays.zData.resize(chunkSize);
1645 arrays.zData.data(), chunkSize,
true,
1646 (prototype.
get(
"cartesianZ").
type() ==
1652 arrays.isInvalidData.resize(chunkSize);
1653 dbufs.emplace_back(node.
destImageFile(),
"cartesianInvalidState",
1654 arrays.isInvalidData.data(), chunkSize,
true,
1655 (prototype.
get(
"cartesianInvalidState").
type() ==
1672 arrays.xNormData.resize(chunkSize);
1674 arrays.xNormData.data(), chunkSize,
true,
1675 (prototype.
get(
"nor:normalX").
type() ==
1679 arrays.yNormData.resize(chunkSize);
1681 arrays.yNormData.data(), chunkSize,
true,
1682 (prototype.
get(
"nor:normalY").
type() ==
1686 arrays.zNormData.resize(chunkSize);
1688 arrays.zNormData.data(), chunkSize,
true,
1689 (prototype.
get(
"nor:normalZ").
type() ==
1701 intensitySF =
new ccScalarField(CC_E57_INTENSITY_FIELD_NAME);
1702 if (!intensitySF->
resizeSafe(
static_cast<unsigned>(pointCount))) {
1710 arrays.intData.resize(chunkSize);
1720 arrays.isInvalidIntData.resize(chunkSize);
1721 dbufs.emplace_back(node.
destImageFile(),
"isIntensityInvalid",
1722 arrays.isInvalidIntData.data(), chunkSize,
true,
1723 (prototype.
get(
"isIntensityInvalid").
type() ==
1729 double colorRedRange = 1;
1730 double colorRedOffset = 0;
1731 double colorGreenRange = 1;
1732 double colorGreenOffset = 0;
1733 double colorBlueRange = 1;
1734 double colorBlueOffset = 0;
1745 arrays.redData.
resize(chunkSize);
1749 if (colorRedRange <= 0.0) colorRedRange = 1.0;
1751 arrays.redData.data(), chunkSize,
true,
1752 (prototype.
get(
"colorRed").
type() ==
1756 arrays.greenData.resize(chunkSize);
1760 if (colorGreenRange <= 0.0) colorGreenRange = 1.0;
1762 arrays.greenData.data(), chunkSize,
true,
1763 (prototype.
get(
"colorGreen").
type() ==
1767 arrays.blueData.resize(chunkSize);
1771 if (colorBlueRange <= 0.0) colorBlueRange = 1.0;
1773 arrays.blueData.data(), chunkSize,
true,
1774 (prototype.
get(
"colorBlue").
type() ==
1784 returnIndexSF =
new ccScalarField(CC_E57_RETURN_INDEX_FIELD_NAME);
1785 if (!returnIndexSF->
resizeSafe(
static_cast<unsigned>(pointCount))) {
1792 arrays.scanIndexData.resize(chunkSize);
1794 arrays.scanIndexData.data(), chunkSize,
true,
1795 (prototype.
get(
"returnIndex").
type() ==
1804 progressDlg,
static_cast<unsigned>(pointCount / chunkSize));
1806 progressDlg->setMethodTitle(QObject::tr(
"Read E57 file"));
1807 progressDlg->setInfo(QObject::tr(
"Scan #%1 - %2 points")
1808 .arg(s_absoluteScanIndex)
1810 progressDlg->start();
1811 QApplication::processEvents();
1816 int64_t realCount = 0;
1817 int64_t invalidCount = 0;
1818 while ((
size = dataReader.
read())) {
1819 for (
unsigned i = 0; i <
size; ++i) {
1821 if (!arrays.isInvalidData.empty() && arrays.isInvalidData[i] != 0) {
1827 if (sphericalMode) {
1828 double r = (arrays.xData.empty() ? 0 : arrays.xData[i]);
1830 (arrays.yData.empty() ? 0
1833 (arrays.zData.empty() ? 0
1836 double cos_phi = cos(phi);
1837 Pd.
x = r * cos_phi * cos(theta);
1838 Pd.
y = r * cos_phi * sin(theta);
1839 Pd.
z = r * sin(phi);
1856 if (!arrays.xData.empty()) Pd.
x = arrays.xData[i];
1857 if (!arrays.yData.empty()) Pd.
y = arrays.yData[i];
1858 if (!arrays.zData.empty()) Pd.
z = arrays.zData[i];
1862 if (realCount == 0 && (!validPoseMat || !poseMatWasShifted)) {
1863 bool preserveCoordinateShift =
true;
1865 preserveCoordinateShift,
1866 s_loadParameters)) {
1867 if (preserveCoordinateShift) {
1871 "[E57Filter::loadFile] Cloud %s has been "
1872 "recentered! Translation: (%.2f ; %.2f ; %.2f)",
1873 qPrintable(guidStr), Pshift.
x, Pshift.
y, Pshift.
z);
1882 if (!arrays.xNormData.empty())
1884 if (!arrays.yNormData.empty())
1886 if (!arrays.zNormData.empty())
1892 if (!arrays.intData.empty()) {
1893 assert(intensitySF);
1895 arrays.isInvalidIntData[i] != 0) {
1898 const ScalarType intensity =
1899 static_cast<ScalarType
>(arrays.intData[i]);
1900 intensitySF->
setValue(
static_cast<unsigned>(realCount),
1904 if (s_absoluteScanIndex != 0 || realCount != 0) {
1905 if (s_maxIntensity < intensity)
1906 s_maxIntensity = intensity;
1907 else if (s_minIntensity > intensity)
1908 s_minIntensity = intensity;
1910 s_maxIntensity = s_minIntensity = intensity;
1914 static_cast<unsigned>(realCount));
1921 if (!arrays.redData.empty())
1923 ((arrays.redData[i] - colorRedOffset) * 255) /
1925 if (!arrays.greenData.empty())
1927 ((arrays.greenData[i] - colorGreenOffset) * 255) /
1929 if (!arrays.blueData.empty())
1931 ((arrays.blueData[i] - colorBlueOffset) * 255) /
1937 if (!arrays.scanIndexData.empty()) {
1938 assert(returnIndexSF);
1939 const ScalarType s =
1940 static_cast<ScalarType
>(arrays.scanIndexData[i]);
1941 returnIndexSF->
setValue(
static_cast<unsigned>(realCount), s);
1947 if (progressDlg && !nprogress.
oneStep()) {
1948 QApplication::processEvents();
1949 s_cancelRequestedByUser =
true;
1956 if (realCount == 0) {
1961 }
else if (realCount < pointCount) {
1962 if ((realCount + invalidCount) != pointCount) {
1963 CVLog::Warning(QString(
"[E57] We read fewer points than expected "
1964 "for scan '%1' (%2/%3)")
1970 cloud->
resize(
static_cast<unsigned>(realCount));
1976 if (intensitySF->
getMin() >= 0 && intensitySF->
getMax() <= 1.0)
1987 if (returnIndexSF) {
1992 "[E57] Cloud has multiple echoes: use 'Edit > Scalar Fields > "
1993 "Filter by value' to extract one component");
2025 QString& associatedData3DGuid) {
2033 CVLog::Print(QString(
"[E57] Reading new image node (%1)")
2045 if (imageNode.
isDefined(
"associatedData3DGuid"))
2046 associatedData3DGuid =
2051 associatedData3DGuid.clear();
2060 if (imageNode.
isDefined(
"visualReferenceRepresentation")) {
2062 cameraRepresentationStr =
"visualReferenceRepresentation";
2063 }
else if (imageNode.
isDefined(
"pinholeRepresentation")) {
2065 cameraRepresentationStr =
"pinholeRepresentation";
2066 }
else if (imageNode.
isDefined(
"sphericalRepresentation")) {
2068 cameraRepresentationStr =
"sphericalRepresentation";
2069 }
else if (imageNode.
isDefined(
"cylindricalRepresentation")) {
2071 cameraRepresentationStr =
"cylindricalRepresentation";
2074 if (!cameraRepresentation) {
2075 CVLog::Warning(QString(
"[E57] Image %1 has no associated camera "
2077 .arg(
name.c_str()));
2082 assert(cameraRepresentationStr !=
"none");
2085 imageNode.
get(cameraRepresentationStr));
2093 if (cameraRepresentationNode.
isDefined(
"jpegImage")) {
2098 }
else if (cameraRepresentationNode.
isDefined(
"pngImage")) {
2105 "[E57] Image format not handled (only jpg and png for the "
2110 if (visualRefRepresentation->
imageSize == 0) {
2115 uint8_t* imageBits =
new uint8_t[visualRefRepresentation->
imageSize];
2121 if (cameraRepresentationNode.
isDefined(
"imageMask"))
2126 visualRefRepresentation->
imageHeight =
static_cast<int32_t
>(
2129 visualRefRepresentation->
imageWidth =
static_cast<int32_t
>(
2134 switch (cameraType) {
2177 char imageFormat[4] =
"jpg";
2178 switch (visualRefRepresentation->
imageType) {
2180 assert(cameraRepresentationNode.
isDefined(
"jpegImage"));
2184 static_cast<size_t>(visualRefRepresentation->
imageSize));
2193 strcpy(imageFormat,
"png");
2194 assert(cameraRepresentationNode.
isDefined(
"pngImage"));
2197 (uint8_t*)imageBits, 0,
2198 static_cast<size_t>(visualRefRepresentation->
imageSize));
2207 assert(cameraRepresentationNode.
isDefined(
"imageMask"));
2215 bool loadResult = qImage.loadFromData(
2216 imageBits,
static_cast<int>(visualRefRepresentation->
imageSize),
2219 imageBits =
nullptr;
2227 switch (cameraType) {
2231 "[E57] Unhandled camera type (image will be loaded as is)");
2232 #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
2241 float focal_mm =
static_cast<float>(pinhole->
focalLength * 1000.0);
2242 float pixelWidth_mm =
2243 static_cast<float>(pinhole->
pixelWidth * 1000.0);
2244 float pixelHeight_mm =
2245 static_cast<float>(pinhole->
pixelHeight * 1000.0);
2246 float ccdHeight_mm =
2247 static_cast<float>(pinhole->
imageHeight * pixelHeight_mm);
2251 focal_mm, pixelHeight_mm);
2254 params.principal_point[0] =
2256 params.principal_point[1] =
2258 params.pixelSize_mm[0] = pixelWidth_mm;
2259 params.pixelSize_mm[1] = pixelHeight_mm;
2261 focal_mm, ccdHeight_mm);
2276 imageObj = calibImage;
2297 delete cameraRepresentation;
2305 s_loadParameters = parameters;
2321 "http://www.libe57.org/E57_NOR_surface_normals.txt");
2324 "nor", _normalsExtension))
2350 container.
addChild(fileStructureTree);
2355 QMap<QString, ccHObject*> scans;
2360 root.
get(
"/data3D");
2369 unsigned scanCount =
static_cast<unsigned>(data3D.
childCount());
2372 QScopedPointer<ecvProgressDialog> progressDlg(
nullptr);
2376 progressDlg->setAutoClose(
false);
2379 bool showGlobalProgress = (scanCount > 10);
2380 if (progressDlg && showGlobalProgress) {
2382 progressDlg->setMethodTitle(QObject::tr(
"Read E57 file"));
2383 progressDlg->setInfo(QObject::tr(
"Scans: %1").arg(scanCount));
2384 progressDlg->start();
2385 QApplication::processEvents();
2388 progressDlg.data(), showGlobalProgress ? scanCount : 100);
2391 s_absoluteScanIndex = 0;
2392 s_cancelRequestedByUser =
false;
2393 s_minIntensity = s_maxIntensity = 0;
2394 for (
unsigned i = 0; i < scanCount; ++i) {
2400 showGlobalProgress ?
nullptr : progressDlg.data());
2403 if (scan->
getName().isEmpty()) {
2404 QString
name(
"Scan ");
2407 if (!nodeName.empty())
2408 name += QString::fromStdString(nodeName);
2410 name += QString::number(i);
2417 if (!scanGUID.isEmpty()) {
2418 scans.insert(scanGUID, scan);
2422 if ((showGlobalProgress && progressDlg &&
2424 s_cancelRequestedByUser) {
2427 ++s_absoluteScanIndex;
2431 progressDlg->stop();
2432 QApplication::processEvents();
2450 parameters = s_loadParameters;
2453 if (!s_cancelRequestedByUser && root.
isDefined(
"/images2D")) {
2464 unsigned imageCount =
static_cast<unsigned>(images2D.
childCount());
2467 QScopedPointer<ecvProgressDialog> progressDlg(
nullptr);
2471 progressDlg->setMethodTitle(QObject::tr(
"Read E57 file"));
2472 progressDlg->setInfo(
2473 QObject::tr(
"Images: %1").arg(imageCount));
2474 progressDlg->start();
2475 QApplication::processEvents();
2480 for (
unsigned i = 0; i < imageCount; ++i) {
2482 QString associatedData3DGuid;
2484 LoadImage(imageNode, associatedData3DGuid);
2487 if (
image->getName().isEmpty()) {
2488 QString
name(
"Image");
2490 if (nodeName.c_str() !=
nullptr &&
2491 nodeName.c_str()[0] != 0)
2492 name += QString(nodeName.c_str());
2494 name += QString::number(i);
2497 image->setEnabled(
false);
2501 if (!associatedData3DGuid.isEmpty()) {
2502 if (scans.contains(associatedData3DGuid))
2503 parentScan = scans.value(associatedData3DGuid);
2512 if (progressDlg && !nprogress.
oneStep()) {
2513 s_cancelRequestedByUser =
true;
2523 QString(
"[E57] Error: %1")
2529 .arg(QString::fromStdString(e.
context())));
float PointCoordinateType
Type of the coordinates of a (N-D) point.
int64_t CV_CLASS_ENUM
Type of object type flags (64 bits)
static bool SaveScan(ccPointCloud *cloud, e57::StructureNode &scanNode, e57::ImageFile &imf, e57::VectorNode &data3D, QString &guidStr, ecvProgressDialog *progressDlg=nullptr)
static bool GetPoseInformation(const e57::StructureNode &node, ccGLMatrixd &poseMat)
static void NodeToConsole(const e57::Node &node)
static ccHObject * LoadImage(const e57::Node &node, QString &associatedData3DGuid)
static bool NodeStructureToTree(ccHObject *currentTreeNode, const e57::Node ¤tE57Node)
void SaveImage(const ccImage *image, const QString &scanGUID, e57::ImageFile &imf, e57::VectorNode &images2D)
static ccHObject * LoadScan(const e57::Node &node, QString &guidStr, ecvProgressDialog *progressDlg=nullptr)
static void SavePoseInformation(e57::StructureNode &parentNode, const e57::ImageFile &imf, const ccGLMatrixd &poseMat)
static void DecodePrototype(const e57::StructureNode &scan, const e57::StructureNode &proto, E57ScanHeader &header)
static bool ChildNodeToConsole(const e57::Node &node, const char *childName)
std::shared_ptr< core::Tensor > image
CC_FILE_ERROR
Typical I/O filter errors.
@ CC_FERR_CANCELED_BY_USER
@ CC_FERR_THIRD_PARTY_LIB_EXCEPTION
cmdLineReadable * params[]
virtual void release()
Decrease counter and deletes object when 0.
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
static bool Print(const char *format,...)
Prints out a formatted message in console.
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Interface for E57 camera representation.
virtual Image2DProjection getType()
CC_FILE_ERROR loadFile(const QString &filename, ccHObject &container, LoadParameters ¶meters) override
Loads one or more entities from a file.
bool canSave(CV_CLASS_ENUM type, bool &multiple, bool &exclusive) const override
Returns whether this I/O filter can save the specified type of entity.
CC_FILE_ERROR saveToFile(ccHObject *entity, const QString &filename, const SaveParameters ¶meters) override
Saves an entity (or a group of) to a file.
static bool HandleGlobalShift(const CCVector3d &P, CCVector3d &Pshift, bool &preserveCoordinateShift, LoadParameters &loadParameters, bool useInputCoordinatesShiftIfPossible=false)
Shortcut to the ecvGlobalShiftManager mechanism specific for files.
static QString createdBy()
bool isIntensityInvalidField
bool sphericalAzimuthField
double angleScaledInteger
bool sphericalInvalidStateField
double intensityScaledInteger
uint32_t columnIndexMaximum
double pointRangeScaledInteger
bool sphericalElevationField
double normRangeScaledInteger
bool cartesianInvalidStateField
bool isTimeStampInvalidField
void normalize()
Sets vector norm to unity.
double norm2d() const
Returns vector square norm (forces double precision output)
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
int32_t imageWidth
image width (in pixels). Shall be positive
int64_t imageMaskSize
size of image mask data in BlobNode (if any).
int32_t imageHeight
image height (in pixels). Shall be positive
Image2DType imageType
image type.
int64_t imageSize
size of image data in BlobNode.
Camera (projective) sensor.
static float ConvertFocalMMToPix(float focal_mm, float ccdPixelSize_mm)
Helper: converts camera focal from mm to pixels.
static float ComputeFovRadFromFocalMm(float focal_mm, float ccdSize_mm)
Helper: deduces camera f.o.v. (in radians) from focal (in mm)
static ccColorScale::Shared GetDefaultScale(DEFAULT_SCALES scale=BGYR)
Returns a pre-defined color scale (static shortcut)
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showNormals(bool state)
Sets normals visibility.
virtual void showColors(bool state)
Sets colors visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
Ground-based Laser sensor.
QString toString(int precision=12, QChar separator=' ') const
Returns matrix as a string.
Vector3Tpl< T > getTranslationAsVec3D() const
Returns a copy of the translation as a CCVector3.
T * getTranslation()
Retruns a pointer to internal translation.
T * data()
Returns a pointer to internal data.
ccGLMatrixTpl< T > inverse() const
Returns inverse transformation.
void setTranslation(const Vector3Tpl< float > &Tr)
Sets translation (float version)
static ccGLMatrixTpl< double > FromString(const QString &matText, bool &success)
Converts a 'text' matrix to a ccGLMatrix.
Float version of ccGLMatrixTpl.
Double version of ccGLMatrixTpl.
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
Hierarchical CLOUDVIEWER Object.
virtual const ccGLMatrix & getGLTransformationHistory() const
Returns the transformation 'history' matrix.
virtual void resetGLTransformationHistory_recursive()
void applyGLTransformation_recursive(const ccGLMatrix *trans=nullptr)
Applies the active OpenGL transformation to the entity (recursive)
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.
unsigned filterChildren(Container &filteredChildren, bool recursive=false, CV_CLASS_ENUM filter=CV_TYPES::OBJECT, bool strict=false) const
Collects the children corresponding to a certain pattern.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
void setData(const QImage &image)
Sets image data.
void setAspectRatio(float ar)
Manually sets aspect ratio.
float getAspectRatio() const
Returns aspect ratio.
void setAssociatedSensor(ccCameraSensor *sensor)
Sets associated sensor.
virtual QString getName() const
Returns object name.
void setMetaData(const QString &key, const QVariant &data)
Sets a meta-data element.
bool isA(CV_CLASS_ENUM type) const
QVariant getMetaData(const QString &key) const
Returns a given associated meta data.
virtual void setName(const QString &name)
Sets object name.
virtual void setEnabled(bool state)
Sets the "enabled" property.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void setCurrentDisplayedScalarField(int index)
Sets the currently displayed scalar field.
void addNorm(const CCVector3 &N)
Pushes a normal vector on stack (shortcut)
int addScalarField(const char *uniqueName) override
Creates a new scalar field and registers it.
bool hasNormals() const override
Returns whether normals are enabled or not.
bool reserve(unsigned numberOfPoints) override
Reserves memory for all the active features.
bool reserveTheNormsTable()
Reserves memory to store the compressed normals.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
int getCurrentDisplayedScalarFieldIndex() const
Returns the currently displayed scalar field index (or -1 if none)
bool hasColors() const override
Returns whether colors are enabled or not.
bool resize(unsigned numberOfPoints) override
Resizes all the active features arrays.
ccScalarField * getCurrentDisplayedScalarField() const
Returns the currently displayed scalar (or 0 if none)
const ecvColor::Rgb & getPointColor(unsigned pointIndex) const override
Returns color corresponding to a given point.
const CCVector3 & getPointNormal(unsigned pointIndex) const override
Returns normal corresponding to a given point.
void addRGBColor(const ecvColor::Rgb &C)
Pushes an RGB color on stack.
A scalar field associated to display-related parameters.
void setSaturationStop(ScalarType val)
Sets the value at which to stop color gradient.
void setColorScale(ccColorScale::Shared scale)
Sets associated color scale.
void computeMinAndMax() override
Determines the min and max values.
void setSaturationStart(ScalarType val)
Sets the value at which to start color gradient.
bool getActiveAbsoluteTransformation(ccIndexedTransformation &trans) const
Gets currently active absolute transformation.
virtual void setRigidTransformation(const ccGLMatrix &mat)
void setGraphicScale(PointCoordinateType scale)
Sets the sensor graphic representation scale.
bool getOwnGlobalBB(CCVector3d &minCorner, CCVector3d &maxCorner) override
virtual void setGlobalShift(double x, double y, double z)
Sets shift applied to original coordinates (information storage only)
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
T getDiagNorm() const
Returns diagonal length.
bool oneStep()
Increments total progress value of a single unit.
int getScalarFieldIndexByName(const char *name) const
Returns the index of a scalar field represented by its name.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
const CCVector3 * getPointPersistentPtr(unsigned index) override
unsigned size() const override
const char * getScalarFieldName(int index) const
Returns the name of a specific scalar field.
ScalarType getMin() const
Returns the minimum value.
ScalarType & getValue(std::size_t index)
void setValue(std::size_t index, ScalarType value)
void flagValueAsInvalid(std::size_t index)
Sets the value as 'invalid' (i.e. NAN_VALUE)
const char * getName() const
Returns scalar field name.
bool resizeSafe(std::size_t count, bool initNewElements=false, ScalarType valueForNewElements=0)
Resizes memory (no exception thrown)
static bool ValidValue(ScalarType value)
Returns whether a scalar value is valid or not.
unsigned currentSize() const
ScalarType getMax() const
Returns the maximum value.
void initFromQuaternion(const float q[])
Creates a rotation matrix from a quaternion (float version)
bool toQuaternion(double q[])
Converts rotation matrix to quaternion.
void toGlMatrix(float M16f[]) const
Converts a 3*3 or 4*4 matrix to an OpenGL-style float matrix (float[16])
int64_t byteCount() const
Get size of blob declared when it was created.
void write(uint8_t *buf, int64_t start, size_t count)
Write a buffer of bytes to a blob.
void read(uint8_t *buf, int64_t start, size_t count)
Read a buffer of bytes from a blob.
int64_t childCount() const
Get current number of records in a CompressedVectorNode.
unsigned read()
Request transfer of blocks of data from CompressedVectorNode into previously designated destination b...
void close()
End the read operation.
void write(const size_t recordCount)
Request transfer of blocks of data to CompressedVectorNode from previously designated source buffers.
void close()
End the write operation.
Object thrown by E57 API functions to communicate the conditions of an error.
const char * sourceFileName() const
Get name of source file where exception occurred, for debugging.
std::string context() const
Get human-readable string that describes the context of the error.
int sourceLineNumber() const
Get line number in source code file where exception occurred, for debugging.
ErrorCode errorCode() const
Get numeric ErrorCode associated with the exception.
void report(const char *reportingFileName=nullptr, int reportingLineNumber=0, const char *reportingFunctionName=nullptr, std::ostream &os=std::cout) const
Print error information on a given output stream.
double minimum() const
Get the declared minimum that the value may take.
double maximum() const
Get the declared maximum that the value may take.
double value() const
Get IEEE floating point value stored.
StructureNode root() const
Get the pre-established root StructureNode of the E57 ImageFile.
bool isOpen() const
Test whether ImageFile is still open for accessing.
bool extensionsLookupPrefix(const ustring &prefix, ustring &uri) const
Get URI associated with an E57 extension prefix in the ImageFile.
void extensionsAdd(const ustring &prefix, const ustring &uri)
Declare the use of an E57 extension in an ImageFile being written.
void close()
Complete any write operations on an ImageFile, and close the file on the disk.
int64_t value() const
Get integer value stored.
int64_t maximum() const
Get the declared maximum that the value may take.
int64_t minimum() const
Get the declared minimum that the value may take.
ustring elementName() const
Get element name of node.
NodeType type() const
Return the NodeType of a generic Node.
ImageFile destImageFile() const
Get the ImageFile that was declared as the destination for the node when it was created.
double scaledValue() const
Get scaled value of element.
double scale() const
Get declared scaling factor.
int64_t minimum() const
Get the declared minimum that the raw value may take.
int64_t maximum() const
Get the declared maximum that the raw value may take.
double offset() const
Get declared offset.
ustring value() const
Get Unicode character string value stored.
ustring elementName() const
Get elementName string, that identifies the node in its parent.
bool isDefined(const ustring &pathName) const
Is the given pathName defined relative to this node.
void set(const ustring &pathName, const Node &n)
Add a new child at a given path.
Node get(int64_t index) const
Get a child element by positional index.
int64_t childCount() const
Return number of child nodes contained by this StructureNode.
Node get(int64_t index) const
Get a child element by positional index.
bool isDefined(const ustring &pathName) const
Is the given pathName defined relative to this node.
void append(const Node &n)
Append a child element to end of VectorNode.
int64_t childCount() const
Get number of child elements in this VectorNode.
Graphical progress indicator (thread-safe)
unsigned char ColorCompType
Default color components type (R,G and B)
E57_DLL std::string errorCodeToString(ErrorCode ecode)
Get short string description of an E57 ErrorCode.
E57_DLL void getVersions(int &astmMajor, int &astmMinor, std::string &libraryId)
Get the version of ASTM E57 standard that the API implementation supports, and library id string.
constexpr char E57_V1_0_URI[]
Verify all checksums. This is the default. (slow)
FloatPrecision
The IEEE floating point number precisions supported.
@ E57_SINGLE
32 bit IEEE floating point number format
@ E57_DOUBLE
64 bit IEEE floating point number format
std::string ustring
UTF-8 encodeded Unicode string.
constexpr ReadChecksumPolicy CHECKSUM_POLICY_SPARSE
Do not verify the checksums. (fast)
@ E57_COMPRESSED_VECTOR
CompressedVectorNode class.
@ E57_BLOB
BlobNode class.
@ E57_STRUCTURE
StructureNode class.
@ E57_VECTOR
VectorNode class.
@ E57_INTEGER
IntegerNode class.
@ E57_SCALED_INTEGER
ScaledIntegerNode class.
@ E57_FLOAT
FloatNode class.
@ E57_STRING
StringNode class.
Generic loading parameters.
QWidget * parentWidget
Parent widget (if any)
Generic saving parameters.
QWidget * parentWidget
Parent widget (if any)
Intrinsic parameters of the camera sensor.