Commit 17cb3089 authored by Julien Osman's avatar Julien Osman

Merge branch '2024-metadata-refac' into metadata-sentinel1

parents 854f3eed c1af5690
......@@ -199,6 +199,14 @@ public:
/** Get the six coefficients of affine geoTtransform. */
virtual VectorType GetGeoTransform(void) const;
/** Get image corners. */
// TODO: GenericRSTransform should be instanciated to translate from physical
// space to EPSG:4328 ?
VectorType GetUpperLeftCorner(void) const;
VectorType GetUpperRightCorner(void) const;
VectorType GetLowerLeftCorner(void) const;
VectorType GetLowerRightCorner(void) const;
/** Get signed spacing */
SpacingType GetSignedSpacing() const;
......
......@@ -153,6 +153,54 @@ typename Image<TPixel, VImageDimension>::VectorType Image<TPixel, VImageDimensio
}
template <class TPixel, unsigned int VImageDimension>
typename Image<TPixel, VImageDimension>::VectorType
Image<TPixel, VImageDimension>::GetUpperLeftCorner(void) const
{
PointType physicalPoint;
itk::ContinuousIndex<double, VImageDimension> index;
index.Fill(-0.5);
this->TransformContinuousIndexToPhysicalPoint (index, physicalPoint) ;
return {physicalPoint[0],physicalPoint[1]};
}
template <class TPixel, unsigned int VImageDimension>
typename Image<TPixel, VImageDimension>::VectorType
Image<TPixel, VImageDimension>::GetUpperRightCorner(void) const
{
PointType physicalPoint;
itk::ContinuousIndex<double, VImageDimension> index;
index.Fill(-0.5);
index[0] = -0.5 + this->GetLargestPossibleRegion().GetSize()[0];
this->TransformContinuousIndexToPhysicalPoint (index, physicalPoint) ;
return {physicalPoint[0],physicalPoint[1]};
}
template <class TPixel, unsigned int VImageDimension>
typename Image<TPixel, VImageDimension>::VectorType
Image<TPixel, VImageDimension>::GetLowerLeftCorner(void) const
{
PointType physicalPoint;
itk::ContinuousIndex<double, VImageDimension> index;
index.Fill(-0.5);
index[1] = -0.5 + this->GetLargestPossibleRegion().GetSize()[1];
this->TransformContinuousIndexToPhysicalPoint (index, physicalPoint) ;
return {physicalPoint[0],physicalPoint[1]};
}
template <class TPixel, unsigned int VImageDimension>
typename Image<TPixel, VImageDimension>::VectorType
Image<TPixel, VImageDimension>::GetLowerRightCorner(void) const
{
PointType physicalPoint;
itk::ContinuousIndex<double, VImageDimension> index;
index.Fill(-0.5);
index[0] = -0.5 + this->GetLargestPossibleRegion().GetSize()[0];
index[1] = -0.5 + this->GetLargestPossibleRegion().GetSize()[1];
this->TransformContinuousIndexToPhysicalPoint (index, physicalPoint) ;
return {physicalPoint[0],physicalPoint[1]};
}
template <class TPixel, unsigned int VImageDimension>
void Image<TPixel, VImageDimension>::PrintSelf(std::ostream& os, itk::Indent indent) const
{
......
......@@ -63,15 +63,6 @@ public:
double GetGCPY(unsigned int GCPnum) const;
double GetGCPZ(unsigned int GCPnum) const;
/** Get image corners. */
// TODO: move back to Image and VectorImage, compute corners in physical space,
// Then a GenericRSTransform should be instanciated to translate from physical
// space to EPSG:4328
ImageMetadataInterfaceBase::VectorType GetUpperLeftCorner(void) const;
ImageMetadataInterfaceBase::VectorType GetUpperRightCorner(void) const;
ImageMetadataInterfaceBase::VectorType GetLowerLeftCorner(void) const;
ImageMetadataInterfaceBase::VectorType GetLowerRightCorner(void) const;
/** Returns true if a sensor geometric model is present */
bool HasSensorGeometry() const;
......
......@@ -125,6 +125,15 @@ public:
/** Get the six coefficients of affine geoTtransform. */
virtual VectorType GetGeoTransform(void) const;
/** Get image corners. */
// TODO: GenericRSTransform should be instanciated to translate from physical
// space to EPSG:4328 ?
VectorType GetUpperLeftCorner(void) const;
VectorType GetUpperRightCorner(void) const;
VectorType GetLowerLeftCorner(void) const;
VectorType GetLowerRightCorner(void) const;
/** Get signed spacing */
SpacingType GetSignedSpacing() const;
......
......@@ -112,7 +112,7 @@ void VectorImage<TPixel, VImageDimension>::CopyInformation(const itk::DataObject
if (imc != nullptr)
{
const auto & imd = imc->GetImageMetadata();
std::cout << "hello " << this->GetNumberOfComponentsPerPixel() << std::endl;
if (imd.Bands.size() > 0 && imd.Bands.size() != this->GetNumberOfComponentsPerPixel())
{
SetImageMetadata(ImageMetadata(imd.GeometryKeys, imd.NumericKeys, imd.StringKeys, imd.LUT1DKeys,
......@@ -157,6 +157,55 @@ typename VectorImage<TPixel, VImageDimension>::VectorType VectorImage<TPixel, VI
return (geoTransform);
}
template <class TPixel, unsigned int VImageDimension>
typename VectorImage<TPixel, VImageDimension>::VectorType
VectorImage<TPixel, VImageDimension>::GetUpperLeftCorner(void) const
{
PointType physicalPoint;
itk::ContinuousIndex<double, VImageDimension> index;
index.Fill(-0.5);
this->TransformContinuousIndexToPhysicalPoint (index, physicalPoint) ;
return {physicalPoint[0],physicalPoint[1]};
}
template <class TPixel, unsigned int VImageDimension>
typename VectorImage<TPixel, VImageDimension>::VectorType
VectorImage<TPixel, VImageDimension>::GetUpperRightCorner(void) const
{
PointType physicalPoint;
itk::ContinuousIndex<double, VImageDimension> index;
index.Fill(-0.5);
index[0] = -0.5 + this->GetLargestPossibleRegion().GetSize()[0];
this->TransformContinuousIndexToPhysicalPoint (index, physicalPoint) ;
return {physicalPoint[0],physicalPoint[1]};
}
template <class TPixel, unsigned int VImageDimension>
typename VectorImage<TPixel, VImageDimension>::VectorType
VectorImage<TPixel, VImageDimension>::GetLowerLeftCorner(void) const
{
PointType physicalPoint;
itk::ContinuousIndex<double, VImageDimension> index;
index.Fill(-0.5);
index[1] = -0.5 + this->GetLargestPossibleRegion().GetSize()[1];
this->TransformContinuousIndexToPhysicalPoint (index, physicalPoint) ;
return {physicalPoint[0],physicalPoint[1]};
}
template <class TPixel, unsigned int VImageDimension>
typename VectorImage<TPixel, VImageDimension>::VectorType
VectorImage<TPixel, VImageDimension>::GetLowerRightCorner(void) const
{
PointType physicalPoint;
itk::ContinuousIndex<double, VImageDimension> index;
index.Fill(-0.5);
index[0] = -0.5 + this->GetLargestPossibleRegion().GetSize()[0];
index[1] = -0.5 + this->GetLargestPossibleRegion().GetSize()[1];
this->TransformContinuousIndexToPhysicalPoint (index, physicalPoint) ;
return {physicalPoint[0],physicalPoint[1]};
}
template <class TPixel, unsigned int VImageDimension>
typename VectorImage<TPixel, VImageDimension>::ImageMetadataInterfacePointerType VectorImage<TPixel, VImageDimension>::GetMetaDataInterface() const
{
......
......@@ -120,34 +120,6 @@ double ImageCommons::GetGCPZ(unsigned int GCPnum) const
return GetGCPs(GCPnum).m_GCPZ;
}
ImageMetadataInterfaceBase::VectorType ImageCommons::GetUpperLeftCorner(void) const
{
//~ return {m_Imd.ULX, m_Imd.ULY};
return {};
}
ImageMetadataInterfaceBase::VectorType ImageCommons::GetUpperRightCorner(void) const
{
//~ return {m_Imd.URX, m_Imd.URY};
return {};
}
ImageMetadataInterfaceBase::VectorType ImageCommons::GetLowerLeftCorner(void) const
{
//~ return {m_Imd.LLX, m_Imd.LLY};
return {};
}
ImageMetadataInterfaceBase::VectorType ImageCommons::GetLowerRightCorner(void) const
{
//~ return {m_Imd.LRX, m_Imd.LRY};
return {};
}
bool ImageCommons::HasSensorGeometry() const
{
return m_Imd.HasSensorGeometry();
......
......@@ -79,11 +79,15 @@ int otbImageTest(int itkNotUsed(argc), char* argv[])
}
InputImageType::VectorType tab = image->GetGeoTransform();
file << "Geo Transform " << std::endl;
for (unsigned int i = 0; i < tab.size(); ++i)
// Don't write the identity geotransform.
if (tab != InputImageType::VectorType({0,1,0,0,0,1}))
{
file << " " << i << " -> " << tab[i] << std::endl;
for (unsigned int i = 0; i < tab.size(); ++i)
{
file << " " << i << " -> " << tab[i] << std::endl;
}
}
tab.clear();
......
......@@ -84,9 +84,13 @@ int otbVectorImageLegacyTest(int argc, char* argv[])
InputImageType::VectorType tab = image->GetGeoTransform();
file << "Geo Transform " << std::endl;
for (unsigned int i = 0; i < tab.size(); ++i)
// Don't write the identity geotransform.
if (tab != InputImageType::VectorType({0,1,0,0,0,1}))
{
file << " " << i << " -> " << tab[i] << std::endl;
for (unsigned int i = 0; i < tab.size(); ++i)
{
file << " " << i << " -> " << tab[i] << std::endl;
}
}
tab.clear();
......
......@@ -219,6 +219,8 @@ public:
const MetaData::Time& Fetch(MDTime key, const MetadataSupplierInterface * mds, const char *path, int band=-1);
const std::string& Fetch(std::string key, const MetadataSupplierInterface * mds, const char *path, int band=-1);
const boost::any& FetchRPC(const MetadataSupplierInterface * mds);
/** Reads into the MetaDataDictionary to find an OSSIM ImageKeywordlist,
......
......@@ -66,75 +66,79 @@ public:
class OTBMetadata_EXPORT MetadataSupplierInterface
{
public:
virtual std::string GetResourceFile(std::string="") const = 0;
virtual std::vector<std::string> GetResourceFiles() const
{
return std::vector<std::string>{this->GetResourceFile()};
}
virtual std::string GetResourceFile() const = 0;
// Maybe not needed
// virtual std::vector<std::string> GetResourceFiles() = 0;
/** Get the metadata value corresponding to a given path (meaning of this path
* depends on the specific implementation. Returns NULL when path is not found
* depends on the specific implementation. Returns empty string when path is not found,
* and hasValue is set to False.
* If band >= 0, the metadata value is looked in the specified band*/
virtual const char * GetMetadataValue(const char * path, int band=-1) const = 0;
virtual const std::string GetMetadataValue(const std::string path, bool& hasValue, int band=-1) const = 0;
bool HasValue(const char * path, int band=-1);
bool HasValue(std::string path, int band=-1);
// utility functions
template <typename T> T GetAs(const char *path, int band=-1) const
template <typename T> T GetAs(std::string path, int band=-1) const
{
bool hasValue;
std::string ret = GetMetadataValue(path, hasValue, band);
if (!hasValue)
{
const char * ret = GetMetadataValue(path, band);
if (ret == nullptr)
{
otbGenericExceptionMacro(MissingMetadataException,<<"Missing metadata '"<<path<<"'")
}
}
try
{
{
return boost::lexical_cast<T>(ret);
}
}
catch (boost::bad_lexical_cast&)
{
otbGenericExceptionMacro(MissingMetadataException,<<"Bad metadata value for '"<<path<<"', got :"<<ret)
}
{
otbGenericExceptionMacro(MissingMetadataException,<<"Bad metadata value for '"<<path<<"', got: "<<ret)
}
}
/** Parse a metadata value to a std::vector,
* If size>=0, then the final std::vector size is checked and an exception
* is raised if it doesn't match the given size.*/
template < typename T> std::vector<T> GetAsVector(const char *path, const char sep=' ', int size=-1, int band=-1) const
template < typename T> std::vector<T> GetAsVector(std::string path, const char sep=' ', int size=-1, int band=-1) const
{
bool hasValue;
std::string ret = GetMetadataValue(path, hasValue, band);
if (!hasValue)
{
const char * ret = GetMetadataValue(path, band);
if (ret == nullptr)
{
otbGenericExceptionMacro(MissingMetadataException,<<"Missing metadata '"<<path<<"'")
}
}
string_view value(ret);
string_view filt_value = rstrip(lstrip(value,"[ "), "] ");
std::vector<T> output;
typedef part_range<splitter_on_delim> range_type;
const range_type parts = split_on(filt_value, sep);
for (auto const& part : parts)
{
{
// TODO: check if we can use lexical_cast on a string_view
std::string strPart = to<std::string>(part, "casting string_view to std::string");
if (strPart.empty())
{
{
continue;
}
}
try
{
{
output.push_back(boost::lexical_cast<T>(strPart));
}
}
catch (boost::bad_lexical_cast&)
{
{
otbGenericExceptionMacro(MissingMetadataException,<<"Bad metadata vector element in '"<<path<<"', got :"<<part)
}
}
}
if ((size >= 0) && (output.size() != (size_t)size))
{
{
otbGenericExceptionMacro(MissingMetadataException,<<"Bad number of elements in vector '"<<path<<"', expected "<<size<< ", got "<<output.size())
}
return output;
}
return output;
}
};
......
......@@ -47,10 +47,13 @@ public:
/** Get the metadata value corresponding to a given path
* Returns NULL when path is not found
* If band >= 0, the metadata value is looked in the specified band*/
const char * GetMetadataValue(const char * path, int band=1) const override;
const std::string GetMetadataValue(const std::string path, bool& hasValue, int band=1) const override;
std::string GetResourceFile() const override;
std::string GetResourceFile(std::string="") const override;
std::string PrintSelf();
protected:
/**
* @brief ReadXMLToList Transform xml to list of NULL terminated name=value
* strings
......
......@@ -630,7 +630,7 @@ ImageMetadataInterfaceBase::Fetch(
if (band >= 0)
{
assert( (size_t)(band) < m_Imd.Bands.size());
m_Imd.Bands[band].Add(key, mds->GetAs<std::string>(path));
m_Imd.Bands[band].Add(key, mds->GetAs<std::string>(path, band));
return m_Imd.Bands[band][key];
}
m_Imd.Add(key, mds->GetAs<std::string>(path) );
......@@ -647,7 +647,7 @@ ImageMetadataInterfaceBase::Fetch(
if (band >= 0)
{
assert( (size_t)(band) < m_Imd.Bands.size());
m_Imd.Bands[band].Add(key, mds->GetAs<double>(path));
m_Imd.Bands[band].Add(key, mds->GetAs<double>(path, band));
return m_Imd.Bands[band][key];
}
m_Imd.Add(key, mds->GetAs<double>(path));
......@@ -664,13 +664,30 @@ ImageMetadataInterfaceBase::Fetch(
if (band >= 0)
{
assert( (size_t)(band) < m_Imd.Bands.size());
m_Imd.Bands[band].Add(key, mds->GetAs<MetaData::Time>(path));
m_Imd.Bands[band].Add(key, mds->GetAs<MetaData::Time>(path, band));
return m_Imd.Bands[band][key];
}
m_Imd.Add(key, mds->GetAs<MetaData::Time>(path));
return m_Imd[key];
}
const std::string&
ImageMetadataInterfaceBase::Fetch(
std::string key,
const MetadataSupplierInterface * mds,
const char *path,
int band)
{
if (band >= 0)
{
assert( (size_t)(band) < m_Imd.Bands.size());
m_Imd.Bands[band].Add(key, mds->GetAs<std::string>(path, band));
return m_Imd.Bands[band][key];
}
m_Imd.Add(key, mds->GetAs<std::string>(path) );
return m_Imd[key];
}
const boost::any& ImageMetadataInterfaceBase::FetchRPC(
const MetadataSupplierInterface * mds)
{
......
......@@ -23,10 +23,11 @@
namespace otb
{
bool MetadataSupplierInterface::HasValue(const char * path, int band)
bool MetadataSupplierInterface::HasValue(const std::string path, int band)
{
const char * ret = GetMetadataValue(path, band);
return ret;
bool hasValue;
const std::string ret = GetMetadataValue(path, hasValue, band);
return hasValue;
}
......
......@@ -36,16 +36,25 @@ XMLMetadataSupplier::XMLMetadataSupplier(const std::string & fileName)
else
{
otbLogMacro(Warning, <<"Unable to parse XML file " << fileName);
m_MetadataDic = nullptr;
}
CPLDestroyXMLNode(psNode);
}
const char * XMLMetadataSupplier::GetMetadataValue(const char * path, int band) const
const std::string XMLMetadataSupplier::GetMetadataValue(const std::string path, bool& hasValue, int band) const
{
return CSLFetchNameValue(m_MetadataDic, path);
const char * ret = CSLFetchNameValue(m_MetadataDic, path);
if (ret)
hasValue = true;
else
{
hasValue = false;
ret = "";
}
return std::string(ret);
}
std::string XMLMetadataSupplier::GetResourceFile() const
std::string XMLMetadataSupplier::GetResourceFile(std::string) const
{
return m_FileName;
}
......@@ -156,5 +165,13 @@ char** XMLMetadataSupplier::ReadXMLToList(CPLXMLNode* psNode, char** papszList,
return papszList;
}
std::string XMLMetadataSupplier::PrintSelf()
{
std::ostringstream oss;
oss << "XMLMetadataSupplier: " << this->m_FileName << '\n';
for (char ** string = this->m_MetadataDic; *string != nullptr ; ++string)
oss << *string << '\n';
return oss.str();
}
} // end namespace otb
......@@ -50,13 +50,23 @@ int otbChangeInformationImageFilter(int itkNotUsed(argc), char* argv[])
filter->UpdateOutputInformation();
ImageType::Pointer outImage = filter->GetOutput();
if (!outImage->GetProjectionRef().empty())
// TODO: RemoveOssim. ChangeInformationImageFilter should change the ImageMetadata object stored in
// the image instead of modifying the itk metadata dictionary. GetProjectionRef() look for the
// projection in ImageMetadata now, so we can't use this method in the test. The temporary solution
// is to look in the itk dictionary instead (this was the old behavior of GetProjectionRef). But
// when ChangeInformationImageFilter will be refactored, GetProjectionRef should be used again..
//if (!outImage->GetProjectionRef().empty())
if (outImage->GetMetaDataDictionary().HasKey(otb::MetaDataKey::ProjectionRefKey))
{
std::cout << "Projection is supposed to be removed but is still present !" << std::endl;
return EXIT_FAILURE;
}
itk::MetaDataDictionary& dict = outImage->GetMetaDataDictionary();
if (!dict.HasKey(otb::MetaDataKey::NoDataValueAvailable) || !dict.HasKey(otb::MetaDataKey::NoDataValue))
{
std::cout << "Missing no data metadata !" << std::endl;
......
......@@ -206,10 +206,11 @@ public:
// MetadataSupplierInterface overrides
/** Get main image file */
std::string GetResourceFile() const override;
std::string GetResourceFile(std::string="") const override;
std::vector<std::string> GetResourceFiles() const override;
/** Get metadata item in GDALDataset, domain can specified as "domain/key" */
const char * GetMetadataValue(const char * path, int band = -1) const override;
const std::string GetMetadataValue(const std::string path, bool& hasValue, int band = -1) const override;
/** Set metadata item in GDALDataset, domain can specified as prefix of the
* path, like "domain/key"*/
......
......@@ -42,6 +42,9 @@
#include "ogr_spatialref.h"
#include "ogr_srs_api.h"
#include "itksys/RegularExpression.hxx"
#include "otbGDALDriverManagerWrapper.h"
#include "otb_boost_string_header.h"
......@@ -1306,12 +1309,15 @@ void GDALImageIO::InternalWriteImageInformation(const void* buffer)
}
m_Imd.Bands = bandRangeMetadata;
}
std::cout << m_Imd << std::endl;
if ( !m_Imd.Bands.empty() && (std::size_t)m_NbBands != m_Imd.Bands.size())
{
itkExceptionMacro(<< "Number of bands in metadata inconsistent with actual image.");
}
// TODO : this should be a warning instead of an exception
// For complex pixels the number of bands is twice the number of compnents (in GDAL sense)
if ( !m_Imd.Bands.empty()
&& static_cast<std::size_t>(m_NbBands) != m_Imd.Bands.size()
&& !((m_Imd.Bands.size() == static_cast<std::size_t>(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))
{
......@@ -1490,6 +1496,10 @@ std::cout << m_Imd << std::endl;
std::abs(geoTransform[4]) > Epsilon ||
std::abs(geoTransform[5] - 1.0) > Epsilon;
itk::MetaDataDictionary& dict = this->GetMetaDataDictionary();
ImageKeywordlist otb_kwl;
itk::ExposeMetaData<ImageKeywordlist>(dict, MetaDataKey::OSSIMKeywordlistKey, otb_kwl);
/* -------------------------------------------------------------------- */
/* Case 1: Set the projection coordinate system of the image */
/* -------------------------------------------------------------------- */
......@@ -1531,6 +1541,25 @@ std::cout << m_Imd << std::endl;
CSLDestroy(rpcMetadata);
}
}
// ToDo : remove this part. This case is here for compatibility for images
// that still use Ossim for managing the sensor model (with OSSIMKeywordList).