From 4445ec6cfe0c95110f358d52a819f515656a1360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Traizet?= Date: Fri, 8 Oct 2021 15:55:26 +0200 Subject: [PATCH 1/5] ENH: add keywordlist import and export method in GCP and GCPParam --- .../Metadata/include/otbGeometryMetadata.h | 13 +++ .../Core/Metadata/src/otbGeometryMetadata.cxx | 89 +++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/Modules/Core/Metadata/include/otbGeometryMetadata.h b/Modules/Core/Metadata/include/otbGeometryMetadata.h index b2be419245..acde2439bd 100644 --- a/Modules/Core/Metadata/include/otbGeometryMetadata.h +++ b/Modules/Core/Metadata/include/otbGeometryMetadata.h @@ -69,8 +69,15 @@ public: void Print(std::ostream& os) const; std::string ToJSON(bool multiline=false) const; + + /** Keywordlist export */ + void ToKeywordlist(MetaData::Keywordlist & kwl, const std::string & prefix) const; + + /** Keywordlist import */ + static GCP FromKeywordlist(const MetaData::Keywordlist & kwl, const std::string & prefix); }; + namespace Projection { @@ -86,6 +93,12 @@ struct OTBMetadata_EXPORT GCPParam // JSON export std::string ToJSON(bool multiline=false) const; + + /** Keywordlist export */ + void ToKeywordlist(MetaData::Keywordlist & kwl, const std::string & prefix) const; + + /** Keywordlist import */ + void FromKeywordlist(const MetaData::Keywordlist & kwl, const std::string & prefix); }; /** \struct RPCParam diff --git a/Modules/Core/Metadata/src/otbGeometryMetadata.cxx b/Modules/Core/Metadata/src/otbGeometryMetadata.cxx index 0dadc355c4..8fcc827202 100644 --- a/Modules/Core/Metadata/src/otbGeometryMetadata.cxx +++ b/Modules/Core/Metadata/src/otbGeometryMetadata.cxx @@ -22,6 +22,53 @@ #include +namespace +{ +constexpr int STRING_PRECISION = 20; + +// the precision of std::to_string is limited to 6 digits +template +std::string to_string_with_precision(const T value) +{ + std::ostringstream out; + out.precision(STRING_PRECISION); + out << std::fixed << value; + return out.str(); +} + +template +void KeywordlistToVector(std::vector & vector, + const otb::MetaData::Keywordlist & kwl, + const std::string & prefix) +{ + vector.clear(); + + const auto size = std::stoi(kwl.at(prefix + ".number")); + for (int i = 0; i < size; i++) + { + auto t = T::FromKeywordlist(kwl, prefix + "_" + to_string_with_precision(i) + "."); + vector.push_back(t); + } +} + + +template +void VectorToKeywordList(otb::MetaData::Keywordlist & kwl, + const std::vector & input, + const std::string & prefix) +{ + int i = 0; + for (const auto & elem: input) + { + elem.ToKeywordlist(kwl, prefix + "_" + to_string_with_precision(i) + "."); + i++; + } + kwl.insert({prefix + ".number" , to_string_with_precision(i)}); +} + +} + + namespace otb { @@ -58,6 +105,36 @@ std::string GCP::ToJSON(bool multiline) const return oss.str(); } +void GCP::ToKeywordlist(MetaData::Keywordlist & kwl, const std::string & prefix) const +{ + kwl.insert({prefix + "Id", m_Id}); + kwl.insert({prefix + "Info", m_Info}); + kwl.insert({prefix + "Row", to_string_with_precision(m_GCPRow)}); + kwl.insert({prefix + "Col", to_string_with_precision(m_GCPCol)}); + kwl.insert({prefix + "X", to_string_with_precision(m_GCPX)}); + kwl.insert({prefix + "Y", to_string_with_precision(m_GCPY)}); + kwl.insert({prefix + "Z", to_string_with_precision(m_GCPZ)}); +} + +GCP GCP::FromKeywordlist(const MetaData::Keywordlist & kwl, const std::string & prefix) +{ + //Info is optional in GCPs, the key might not be in the keywordlist + std::string info; + auto infoFound = kwl.find(prefix + "Info"); + if (infoFound != kwl.end()) + { + info = infoFound->second; + } + + return GCP(kwl.at(prefix + "Id"), + info, + std::stod(kwl.at(prefix + "Row")), + std::stod(kwl.at(prefix + "Col")), + std::stod(kwl.at(prefix + "X")), + std::stod(kwl.at(prefix + "Y")), + std::stod(kwl.at(prefix + "Z"))); +} + namespace Projection { std::string GCPParam::ToJSON(bool multiline) const @@ -77,6 +154,18 @@ std::string GCPParam::ToJSON(bool multiline) const return oss.str(); } +void GCPParam::ToKeywordlist(MetaData::Keywordlist & kwl, const std::string & prefix) const +{ + kwl.insert({prefix + "GCPProjection", GCPProjection}); + VectorToKeywordList(kwl, GCPs, prefix + "GCP"); +} + +void GCPParam::FromKeywordlist(const MetaData::Keywordlist & kwl, const std::string & prefix) +{ + GCPProjection = kwl.at(prefix + "GCPProjection"); + KeywordlistToVector(GCPs, kwl, prefix + "GCP"); +} + std::string RPCParam::ToJSON(bool multiline) const { std::ostringstream oss; -- GitLab From f84904c94ac04cb42848daeabcd30088e7b6d8d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Traizet?= Date: Fri, 8 Oct 2021 15:56:35 +0200 Subject: [PATCH 2/5] BUG: handle S1 product that have only one azimuth noise vector (e.g. IW SLC products) --- .../Metadata/src/otbSentinel1ImageMetadataInterface.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Modules/Core/Metadata/src/otbSentinel1ImageMetadataInterface.cxx b/Modules/Core/Metadata/src/otbSentinel1ImageMetadataInterface.cxx index cfe31f1a6c..006c9f81d8 100644 --- a/Modules/Core/Metadata/src/otbSentinel1ImageMetadataInterface.cxx +++ b/Modules/Core/Metadata/src/otbSentinel1ImageMetadataInterface.cxx @@ -265,14 +265,16 @@ NoiseVectorLists ReadNoiseVectorListsFromXML(const MetadataSupplierInterface& md } std::string azimuthNoisePrefix = "noise.noiseAzimuthVectorList."; - std::string azimuthVectorName = "noiseAzimuthVector_"; + std::string azimuthVectorName = "noiseAzimuthVector"; std::string azimuthLUTName = "noiseAzimuthLut"; const int azimuthCount = mds.GetAs(0, azimuthNoisePrefix + "count"); for (int i = 0; i < azimuthCount; i++) { - const auto prefix = azimuthNoisePrefix + azimuthVectorName + std::to_string(i+1) + "."; + const auto prefix = azimuthCount == 1 + ? azimuthNoisePrefix + azimuthVectorName + "." + : azimuthNoisePrefix + azimuthVectorName + "_" + std::to_string(i+1) + "."; Sentinel1AzimuthNoiseStruct azimuthNoiseVector; azimuthNoiseVector.firstAzimuthLine = mds.GetAs(prefix + "firstAzimuthLine"); -- GitLab From ba3d8ec218cb5f82bc4268a30fab26eb52ec96a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Traizet?= Date: Fri, 8 Oct 2021 15:57:23 +0200 Subject: [PATCH 3/5] REFAC: import and export GCP as metadata for SAR sensor images --- Modules/IO/IOGDAL/src/otbGDALImageIO.cxx | 27 ++++++++++++++---------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx b/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx index 3eb2cf14da..7ade52b2fd 100644 --- a/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx +++ b/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx @@ -41,7 +41,6 @@ #include "ogr_spatialref.h" #include "ogr_srs_api.h" - #include "itksys/RegularExpression.hxx" #include "otbGDALDriverManagerWrapper.h" @@ -1314,15 +1313,6 @@ void GDALImageIO::InternalWriteImageInformation(const void* buffer) m_Imd.Bands = bandRangeMetadata; } - // TODO : this should be a warning instead of an exception - // For complex pixels the number of bands is twice the number of components (in GDAL sense) - if ( !m_Imd.Bands.empty() - && static_cast(m_NbBands) != m_Imd.Bands.size() - && !((m_Imd.Bands.size() == static_cast(2 * m_NbBands)) && this->GetPixelType() == COMPLEX)) - { - itkExceptionMacro(<< "Number of bands in metadata inconsistent with actual image."); - } - if ((m_Dimensions[0] == 0) && (m_Dimensions[1] == 0)) { itkExceptionMacro(<< "Dimensions are not defined."); @@ -1550,6 +1540,18 @@ void GDALImageIO::InternalWriteImageInformation(const void* buffer) MetaData::Keywordlist SARKwl; const auto & param = boost::any_cast(m_Imd[MDGeom::SAR]); param.ToKeywordlist(SARKwl, "SAR."); + + + // Write GCP as metadata (not GDAL gcps) because SAR sensor images needs GCPs and + // might also have a geotransform (e.g. after a ROI extraction), which is undefined + // in GDAL. Note that the geotransform is not applied to the GCPs in this case + if (m_Imd.Has(MDGeom::GCP)) + { + const Projection::GCPParam & gcpParam = + boost::any_cast(m_Imd[MDGeom::GCP]); + gcpParam.ToKeywordlist(SARKwl, "SAR."); + } + for (auto & key: SARKwl) { dataset->SetMetadataItem(key.first.c_str(), key.second.c_str()); @@ -1939,6 +1941,10 @@ void GDALImageIO::ImportMetadata() otb::SARParam sar; sar.FromKeywordlist(kwl, "SAR."); m_Imd.Add(MDGeom::SAR, sar); + + otb::Projection::GCPParam gcps; + gcps.FromKeywordlist(kwl, "SAR."); + m_Imd.Add(MDGeom::GCP, gcps); } catch(const std::exception& e) { @@ -1955,7 +1961,6 @@ void GDALImageIO::ImportMetadata() GDALMetadataToKeywordlist(m_Dataset->GetDataSet()->GetRasterBand(band+1)->GetMetadata(), kwl); m_Imd.Bands[band].FromKeywordlist(kwl); } - } void GDALImageIO::KeywordlistToMetadata(ImageMetadataBase::Keywordlist kwl, int band) -- GitLab From 773167a3b46c5c15207a10ecb0fb3934ec73999c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Traizet?= Date: Fri, 8 Oct 2021 17:12:02 +0200 Subject: [PATCH 4/5] ENH: export/import sar calibration metadata in GDALImageIO --- Modules/IO/IOGDAL/src/otbGDALImageIO.cxx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx b/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx index 7ade52b2fd..18ff394e39 100644 --- a/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx +++ b/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx @@ -1909,6 +1909,12 @@ void GDALImageIO::ExportMetadata() // be prefixed by: MDGeomNames[MDGeom::SensorGeometry] + '.' ImageMetadataBase::Keywordlist kwl; m_Imd.ToKeywordlist(kwl); + if (m_Imd.Has(MDGeom::SARCalib)) + { + const auto & param = boost::any_cast(m_Imd[MDGeom::SARCalib]); + param.ToKeywordlist(kwl, "SARCalib."); + } + KeywordlistToMetadata(kwl); int bIdx = 0; @@ -1952,6 +1958,20 @@ void GDALImageIO::ImportMetadata() } } + // Decode SAR metadata + if (kwl.find("SARCalib") != kwl.end()) + { + try + { + otb::SARCalib sarCalib; + sarCalib.FromKeywordlist(kwl, "SARCalib."); + m_Imd.Add(MDGeom::SARCalib, sarCalib); + } + catch(const std::exception& e) + { + otbLogMacro(Warning, << "Input image has SAR calibration metadata, but OTB was not able to read it: " << e.what()); + } + } m_Imd.FromKeywordlist(kwl); // Parsing the bands -- GitLab From 70579cfdc3827145948b8d991f8cb1d17a1165b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Traizet?= Date: Fri, 8 Oct 2021 17:42:27 +0200 Subject: [PATCH 5/5] STY: remove noisy log --- Modules/Core/Metadata/src/otbSARMetadata.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/Core/Metadata/src/otbSARMetadata.cxx b/Modules/Core/Metadata/src/otbSARMetadata.cxx index 74a381b31b..f257ed3328 100644 --- a/Modules/Core/Metadata/src/otbSARMetadata.cxx +++ b/Modules/Core/Metadata/src/otbSARMetadata.cxx @@ -392,7 +392,6 @@ void SARCalib::ToKeywordlist(MetaData::Keywordlist & kwl, const std::string & pr // MetaData::Time std::ostringstream oss; oss << calibrationStartTime; - std::cout << "***** " << oss.str() << std::endl; kwl.insert({prefix + "CalibrationStartTime", oss.str()}); oss.str(""); oss << calibrationStopTime; -- GitLab