Commit 70c8667b authored by Julien Osman's avatar Julien Osman

Merge branch 'develop' into 2024_GeomMetadataSupplier

parents c644e75a 03b12c74
......@@ -161,6 +161,25 @@ struct OTBMetadata_EXPORT RPCParam
oss << "]";
return oss.str();
};
// Equality comparison operator (hidden friend idiom)
friend bool operator==(const RPCParam & lhs, const RPCParam & rhs)
{
return lhs.LineOffset == rhs.LineOffset
&& lhs.SampleOffset == rhs.SampleOffset
&& lhs.LatOffset == rhs.LatOffset
&& lhs.LonOffset == rhs.LonOffset
&& lhs.HeightOffset == rhs.HeightOffset
&& lhs.LineScale == rhs.LineScale
&& lhs.SampleScale == rhs.SampleScale
&& lhs.LatScale == rhs.LatScale
&& lhs.LonScale == rhs.LonScale
&& lhs.HeightScale == rhs.HeightScale
&& std::equal(std::begin(lhs.LineNum), std::end(lhs.LineNum), std::begin(rhs.LineNum))
&& std::equal(std::begin(lhs.LineDen), std::end(lhs.LineDen), std::begin(rhs.LineDen))
&& std::equal(std::begin(lhs.SampleNum), std::end(lhs.SampleNum), std::begin(rhs.SampleNum))
&& std::equal(std::begin(lhs.SampleDen), std::end(lhs.SampleDen), std::begin(rhs.SampleDen));
}
};
......
......@@ -238,8 +238,15 @@ struct OTBMetadata_EXPORT Time : tm
friend OTBMetadata_EXPORT std::istream& operator>>(std::istream& is, Time& val);
friend OTBMetadata_EXPORT bool operator==(const Time & lhs, const Time & rhs)
{
tm tmLhs = lhs;
tm tmRhs = rhs;
return mktime(&tmLhs) + lhs.frac_sec == mktime(&tmRhs) + rhs.frac_sec;
}
};
struct LUTAxis
{
/** number of measurements on this axis */
......@@ -252,6 +259,14 @@ struct LUTAxis
std::vector<double> Values;
/** Export to JSON */
std::string ToJSON(bool multiline=false) const;
friend bool operator==(const LUTAxis & lhs, const LUTAxis & rhs)
{
return lhs.Size == rhs.Size
&& lhs.Origin == rhs.Origin
&& lhs.Spacing == rhs.Spacing
&& lhs.Values == rhs.Values;
}
};
template <unsigned int VDim> class LUT
......@@ -266,8 +281,24 @@ public:
std::string OTBMetadata_EXPORT ToString() const;
void OTBMetadata_EXPORT FromString(std::string);
friend bool operator==(const LUT<VDim> & lhs, const LUT<VDim> & rhs)
{
return std::equal(std::begin(lhs.Array), std::end(lhs.Array), std::begin(rhs.Array) )
&& lhs.Array == rhs.Array;
}
};
template <unsigned int VDim>
std::ostream& operator<<(std::ostream& os, const LUT<VDim>& val)
{
os << val.ToString();
return os;
}
typedef LUT<1> LUT1D;
typedef LUT<2> LUT2D;
......@@ -300,6 +331,32 @@ extern OTBMetadata_EXPORT MDL1DBmType MDL1DNames;
typedef boost::bimap<MDL2D, std::string> MDL2DBmType;
extern OTBMetadata_EXPORT MDL2DBmType MDL2DNames;
template<class T>
std::string EnumToString(T t);
template<>
std::string EnumToString(MDGeom value);
template<>
std::string EnumToString(MDNum value);
template<>
std::string EnumToString(MDStr value);
template<>
std::string EnumToString(MDL1D value);
template<>
std::string EnumToString(MDL2D value);
template<>
std::string EnumToString(MDTime value);
// Specialization for extra keys
template<>
std::string EnumToString(std::string value);
} // end namespace MetaData
namespace Utils
......
......@@ -187,6 +187,7 @@ std::istream& operator>>(std::istream& is, Time& val)
#undef _OTB_ISTREAM_EXPECT
std::string LUTAxis::ToJSON(bool multiline) const
{
std::ostringstream oss;
......@@ -394,6 +395,54 @@ MDGeomBmType MDGeomNames = bimapGenerator<MDGeom>(std::map<MDGeom, std::string>
{MDGeom::Adjustment, "Adjustment"}
});
template<>
std::string EnumToString(MDGeom value)
{
return MetaData::MDGeomNames.left.at(value);
}
template<>
std::string EnumToString(MDNum value)
{
return MetaData::MDNumNames.left.at(value);
}
template<>
std::string EnumToString(MDStr value)
{
return MetaData::MDStrNames.left.at(value);
}
template<>
std::string EnumToString(MDL1D value)
{
return MetaData::MDL1DNames.left.at(value);
}
template<>
std::string EnumToString(MDL2D value)
{
return MetaData::MDL2DNames.left.at(value);
}
template<>
std::string EnumToString(MDTime value)
{
return MetaData::MDTimeNames.left.at(value);
}
// Specialization for extra keys
template<>
std::string EnumToString(std::string value)
{
return value;
}
} // end namespace MetaData
} // end namespace otb
......@@ -103,7 +103,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
{
this->m_Imd.Add(MDNum::SunElevation, this->GetSunElevation());
}
catch (const itk::ExceptionObject)
catch (const itk::ExceptionObject &)
{
result = false;
}
......@@ -111,7 +111,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
{
this->m_Imd.Add(MDNum::SunAzimuth, this->GetSunAzimuth());
}
catch (const itk::ExceptionObject)
catch (const itk::ExceptionObject &)
{
result = false;
}
......@@ -119,7 +119,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
{
this->m_Imd.Add(MDNum::SatElevation, this->GetSatElevation());
}
catch (const itk::ExceptionObject)
catch (const itk::ExceptionObject &)
{
result = false;
}
......@@ -127,7 +127,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
{
this->m_Imd.Add(MDNum::SatAzimuth, this->GetSatAzimuth());
}
catch (const itk::ExceptionObject)
catch (const itk::ExceptionObject &)
{
result = false;
}
......@@ -135,7 +135,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
{
this->m_Imd.Add(MDNum::PhysicalBias, this->GetPhysicalBias());
}
catch (const itk::ExceptionObject)
catch (const itk::ExceptionObject &)
{
result = false;
}
......@@ -143,7 +143,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
{
this->m_Imd.Add(MDNum::PhysicalGain, this->GetPhysicalGain());
}
catch (const itk::ExceptionObject)
catch (const itk::ExceptionObject &)
{
result = false;
}
......@@ -151,7 +151,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
{
this->m_Imd.Add(MDNum::SolarIrradiance, this->GetSolarIrradiance());
}
catch (const itk::ExceptionObject)
catch (const itk::ExceptionObject &)
{
result = false;
}
......@@ -159,7 +159,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
{
this->m_Imd.Add(MDNum::FirstWavelength, this->GetFirstWavelengths());
}
catch (const itk::ExceptionObject)
catch (const itk::ExceptionObject &)
{
result = false;
}
......@@ -167,7 +167,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
{
this->m_Imd.Add(MDNum::LastWavelength, this->GetLastWavelengths());
}
catch (const itk::ExceptionObject)
catch (const itk::ExceptionObject &)
{
result = false;
}
......
......@@ -1306,3 +1306,14 @@ otb_add_test(NAME ioTvCompoundMetadataReaderTest
otbWriteGeomFile
${INPUTDATA}/QB_Toulouse_combo.vrt
${TEMP}/ioTvCompoundMetadataReaderTest.tif)
otb_add_test(NAME ioTvCompareMetadataTest COMMAND otbImageIOTestDriver
--compare-metadata ${EPSILON_9}
LARGEINPUT{SENTINEL1/S1A_S6_SLC__1SSV_20150619T195043}
LARGEINPUT{SENTINEL1/S1A_S6_SLC__1SSV_20150619T195043}
otbImageFileReaderRGBTest
${INPUTDATA}/couleurs_extrait.png
${TEMP}/ioImageFileReaderRGB_PNG2PNG.png )
......@@ -1418,7 +1418,98 @@ int TestHelper::RegressionTestImage(int cpt, const char* testImageFilename, cons
return ret;
}
int TestHelper::RegressionTestMetaData(const char* testImageFilename, const char* baselineImageFilename, const double /*toleranceDiffPixelImage*/) const
namespace
{
/** \fn CompareMetadataDict
\brief Compare two metadata dictionaries key by key. Dictionaries are assumed to be associative containers (e.g. std::map)
\param[in] baselineMap : reference metadata dictionary
\param[in] testMap : metadata dictionary to be compared
\param[in] reportErrors : print difference between dictionaries into srd::cerr
\param[in] untestedKeys : list of keys that should be ignored during comparison
\param[in] p bianry predicate used to compare elements (mapped type) of the two input maps
\return number of different elements.
*/
template <class MapType, class BinaryPredicate >
int CompareMetadataDict( const MapType & baselineMap,
const MapType & testMap,
bool reportErrors,
std::vector< typename MapType::key_type> untestedKeys,
const BinaryPredicate & p)
{
auto first1 = testMap.begin();
auto last1 = testMap.end();
auto first2 = baselineMap.begin();
auto last2 = baselineMap.end();
if (std::distance(first1, last1) != std::distance(first2, last2))
{
if (reportErrors)
{
std::cerr << "Input metadata dictionaries have different sizes" << std::endl;
}
return 1;
}
int errorCount = 0;
while (first1 != last1)
{
if (std::find(untestedKeys.begin(), untestedKeys.end(), first1->first) == untestedKeys.end())
{
if (first1->first != first2->first)
{
errorCount++;
if (reportErrors)
{
std::cerr << "Metadata key " << otb::MetaData::EnumToString(first1->first)
<< " does not match between test and baseline images: "
<< std::endl;
}
return errorCount;
}
if (!p(first1->second, first2->second))
{
errorCount++;
if (reportErrors)
std::cerr << "Metadata " << otb::MetaData::EnumToString(first1->first)
<< " does not match between test and baseline images: "
<< std::endl
<< "Baseline image: "
<< first1->second
<< std::endl
<< "Test image: "
<< first2->second
<< std::endl;
}
}
++first1;
++first2;
}
return errorCount;
}
template <class MapType>
int CompareMetadataDict( const MapType & baselineMap,
const MapType & testMap,
bool reportErrors,
std::vector< typename MapType::key_type> untestedKeys)
{
auto p = []( const typename MapType::mapped_type & rhs,
const typename MapType::mapped_type & lhs)
{return rhs == lhs;};
return CompareMetadataDict(baselineMap, testMap, reportErrors, untestedKeys, p);
}
}
int TestHelper::RegressionTestMetaData(const char* testImageFilename, const char* baselineImageFilename, const double tolerance) const
{
// Use the factory mechanism to read the test and baseline files and convert them to double
typedef otb::Image<double, ITK_TEST_DIMENSION_MAX> ImageType;
......@@ -1449,6 +1540,7 @@ int TestHelper::RegressionTestMetaData(const char* testImageFilename, const char
}
unsigned int errcount = 0;
// The sizes of the baseline and test image must match
ImageType::SizeType baselineSize;
baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize();
......@@ -1583,12 +1675,84 @@ int TestHelper::RegressionTestMetaData(const char* testImageFilename, const char
}
}
}
const auto & baselineImageMetadata = blImPtr->GetImageMetadata();
const auto & testImageMetadata = testImPtr->GetImageMetadata();
// Compare string keys (strict equality)
errcount += CompareMetadataDict(baselineImageMetadata.StringKeys,
testImageMetadata.StringKeys,
m_ReportErrors,
{});
// Compare numeric keys
auto compareDouble = [tolerance](double lhs, double rhs)
{return fabs(lhs - rhs)
<= ( (fabs(lhs) < fabs(rhs) ? fabs(rhs) : fabs(lhs)) * tolerance);};
// Don't test TileHints and datatype, as these metadata are written by gdal drivers, not otb.
std::vector<MDNum> untestedMDNum = {MDNum::TileHintX, MDNum::TileHintY, MDNum::DataType};
errcount += CompareMetadataDict(baselineImageMetadata.NumericKeys,
testImageMetadata.NumericKeys,
m_ReportErrors,
untestedMDNum,
compareDouble);
// Compare time keys (strict equality)
errcount += CompareMetadataDict(baselineImageMetadata.TimeKeys,
testImageMetadata.TimeKeys,
m_ReportErrors,
{});
// Compare LUTs (strict equality)
errcount += CompareMetadataDict(baselineImageMetadata.LUT1DKeys,
testImageMetadata.LUT1DKeys,
m_ReportErrors,
{});
errcount += CompareMetadataDict(baselineImageMetadata.LUT2DKeys,
testImageMetadata.LUT2DKeys,
m_ReportErrors,
{});
// Compare extra keys (strict equality)
errcount += CompareMetadataDict(baselineImageMetadata.ExtraKeys,
testImageMetadata.ExtraKeys,
m_ReportErrors,
{});
if (baselineImageMetadata.Has(MDGeom::RPC))
{
if (!testImageMetadata.Has(MDGeom::RPC))
{
errcount++;
if (m_ReportErrors)
{
std::cerr << "Test image does not have RPC coefficients" << std::endl;
}
}
if (!(boost::any_cast<Projection::RPCParam>(baselineImageMetadata[MDGeom::RPC])
== boost::any_cast<Projection::RPCParam>(testImageMetadata[MDGeom::RPC])))
{
errcount++;
if (m_ReportErrors)
{
std::cerr << "RPC parameters mismatch between baseline and test images" << std::endl;
}
}
}
if (errcount > 0)
{
std::cout << "<DartMeasurement name=\"MetadataError\" type=\"numeric/int\">";
std::cout << errcount;
std::cout << "</DartMeasurement>" << std::endl;
}
return errcount;
}
......
......@@ -79,7 +79,7 @@ void PrintMetadataBase(ImageMetadataBase imdb, std::ostream& oss)
void PrintMetadata(ImageMetadata imd, std::ostream& oss)
{
PrintMetadataBase(imd, oss);
for (ImageMetadataBase imdb : imd.Bands)
for (const ImageMetadataBase & imdb : imd.Bands)
PrintMetadataBase(imdb, oss);
}
......
......@@ -29,7 +29,8 @@ set(OTBTestKernelTests
otbCompareAsciiTests.cxx
otbCopyTest.cxx
otbCompareAsciiTestsEpsilon3_WhiteSpace.cxx
otbTestKernelTestDriver.cxx )
otbTestKernelTestDriver.cxx
otbDummyTest.cxx)
add_executable(otbTestKernelTestDriver ${OTBTestKernelTests})
target_link_libraries(otbTestKernelTestDriver ${OTBTestKernel-Test_LIBRARIES})
......@@ -205,3 +206,18 @@ otb_add_test(NAME tsTvCompareImages_DifferentSizes COMMAND otbTestKernelTestDriv
${TEMP}/tsTvCompareImages_DifferentSizes.tif
)
set_property(TEST tsTvCompareImages_DifferentSizes PROPERTY WILL_FAIL true)
otb_add_test(NAME tsTvCompareMetadata1 COMMAND otbTestKernelTestDriver
--compare-metadata 0
LARGEINPUT{SENTINEL1/S1A_S6_SLC__1SSV_20150619T195043}
LARGEINPUT{SENTINEL1/S1A_S6_SLC__1SSV_20150619T195043}
otbDummyTest
)
otb_add_test(NAME tsTvCompareMetadata2 COMMAND otbTestKernelTestDriver
--compare-metadata 0
${OTB_DATA_LARGEINPUT_ROOT}/PLEIADES-PRE/TLSE_JP2_ORTHO_DIMAPv2_PMS-N_lossy_12bits/IMG_PHR1Z_PMS_N_001/IMG_PHR1A_PMS-N_201006181052297_ORT_IPU_20111011_0619-001_R1C1.JP2
${OTB_DATA_LARGEINPUT_ROOT}/PLEIADES-PRE/TLSE_TIFF_ORTHO_DIMAPv2_MS_lossless_8bits/IMG_PHR1A_MS_002/IMG_PHR1A_MS_201006181052297_ORT_IPU_20111109_7807-004_R1C1.TIF
otbDummyTest
)
set_property(TEST tsTvCompareMetadata2 PROPERTY WILL_FAIL true)
\ No newline at end of file
/*
* Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
*
* This file is part of Orfeo Toolbox
*
* https://www.orfeo-toolbox.org/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
// This test does nothing. It is used to test the TestKernel module
int otbDummyTest(int argc, char* argv[])
{
if (argc != 1)
{
std::cerr << argv[0] << "does not take any additional parameter" << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
......@@ -31,4 +31,5 @@ void RegisterTests()
REGISTER_TEST(otbCompareAsciiTests);
REGISTER_TEST(otbCompareAsciiTestsEpsilon3_WhiteSpace);
REGISTER_TEST(otbCopyTest);
REGISTER_TEST(otbDummyTest);
}
......@@ -28,7 +28,7 @@ ADDTO_DEPENDENCIES_IF_NOT_SYSTEM(PROJ SQLITE)
ExternalProject_Add(PROJ
DEPENDS ${PROJ_DEPENDENCIES}
PREFIX PROJ
URL "http://download.osgeo.org/proj/proj-6.2.1.tar.gz"
URL "http://download.osgeo.org/proj/proj-6.2.1.tar.gz" # If we update PROJ to v7+, check option PROJ_TESTS!
URL_MD5 9f874e227d221daf95f7858dc55dfa3e
BINARY_DIR ${PROJ_SB_SRC}
INSTALL_DIR ${SB_INSTALL_PREFIX}
......@@ -40,6 +40,7 @@ ExternalProject_Add(PROJ
-DBUILD_FRAMEWORKS_AND_BUNDLE:BOOL=FALSE
-DPROJ_LIB_SUBDIR:STRING=lib
-DPROJ_INCLUDE_SUBDIR:STRING=include
-DPROJ_TESTS:BOOL=OFF # Starting with PROJ 7.0, the PROJ_TESTS option has been renamed into BUILD_TESTING
CMAKE_COMMAND ${SB_CMAKE_COMMAND}
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment