Commit 18dc504c authored by Cédric Traizet's avatar Cédric Traizet
Browse files

Merge branch 'refactor_no_data' into 'develop'

Use ImageMetadata to manage NoData and TileHint

See merge request !863
parents ab413a4c ec03fe30
Pipeline #8967 passed with stages
in 2 minutes and 44 seconds
......@@ -35,12 +35,10 @@ Dictionary keys:
- ProjectionRef
- ResolutionFactor
- SubDatasetIndex
- TileHintX
- TileHintY
- UpperLeftCorner
- UpperRightCorner
RAM usage (in Bytes): 520
Input requested: ImageRegion (0x7ffcb9287dc0)
Input requested: ImageRegion (0x7fff772acea0)
Dimension: 2
Index: [9, 9]
Size: [9, 9]
......
......@@ -26,7 +26,6 @@
#include "itkMaskImageFilter.h"
#include "otbVectorImageToImageListFilter.h"
#include "otbImageListToVectorImageFilter.h"
#include "otbChangeInformationImageFilter.h"
namespace otb
{
......@@ -55,7 +54,6 @@ public:
typedef otb::VectorImageToImageListFilter<FloatVectorImageType, ImageListType> VectorToListFilterType;
typedef otb::ImageListToVectorImageFilter<ImageListType, FloatVectorImageType> ListToVectorFilterType;
typedef itk::MaskImageFilter<FloatImageType, UInt8ImageType, FloatImageType> MaskFilterType;
typedef otb::ChangeInformationImageFilter<FloatVectorImageType> ChangeInfoFilterType;
private:
void DoInit() override
......@@ -135,48 +133,49 @@ private:
{
FloatVectorImageType::Pointer inputPtr = this->GetParameterImage("in");
m_Filter = FilterType::New();
m_Filter->SetInsideValue(this->GetParameterFloat("mode.buildmask.inv"));
m_Filter->SetOutsideValue(this->GetParameterFloat("mode.buildmask.outv"));
m_Filter->SetNaNIsNoData(GetParameterInt("usenan"));
m_Filter->SetInput(inputPtr);
m_ChangeNoDataFilter = ChangeNoDataFilterType::New();
m_ChangeNoDataFilter->SetInput(inputPtr);
m_ChangeNoDataFilter->SetNaNIsNoData(GetParameterInt("usenan"));
std::vector<double> newNoData(inputPtr->GetNumberOfComponentsPerPixel(), GetParameterFloat("mode.changevalue.newv"));
m_ChangeNoDataFilter->SetNewNoDataValues(newNoData);
if (GetParameterString("mode") == "buildmask")
{
SetParameterOutputImage("out", m_Filter->GetOutput());
auto filter = FilterType::New();
filter->SetInsideValue(this->GetParameterFloat("mode.buildmask.inv"));
filter->SetOutsideValue(this->GetParameterFloat("mode.buildmask.outv"));
filter->SetNaNIsNoData(GetParameterInt("usenan"));
filter->SetInput(inputPtr);
SetParameterOutputImage("out", filter->GetOutput());
RegisterPipeline();
}
else if (GetParameterString("mode") == "changevalue")
{
SetParameterOutputImage("out", m_ChangeNoDataFilter->GetOutput());
auto changeNoDataFilter = ChangeNoDataFilterType::New();
changeNoDataFilter->SetInput(inputPtr);
changeNoDataFilter->SetNaNIsNoData(GetParameterInt("usenan"));
std::vector<double> newNoData(inputPtr->GetNumberOfComponentsPerPixel(), GetParameterFloat("mode.changevalue.newv"));
changeNoDataFilter->SetNewNoDataValues(newNoData);
SetParameterOutputImage("out", changeNoDataFilter->GetOutput());
RegisterPipeline();
}
else if (GetParameterString("mode") == "apply")
{
m_MaskFilters.clear();
UInt8ImageType::Pointer maskPtr = this->GetParameterUInt8Image("mode.apply.mask");
unsigned int nbBands = inputPtr->GetNumberOfComponentsPerPixel();
itk::MetaDataDictionary& dict = inputPtr->GetMetaDataDictionary();
const auto & imd = inputPtr->GetImageMetadata();
std::vector<bool> flags;
std::vector<double> values;
bool ret = otb::ReadNoDataFlags(dict, flags, values);
bool ret = otb::ReadNoDataFlags(imd, flags, values);
if (!ret)
{
flags.resize(nbBands, true);
values.resize(nbBands, GetParameterFloat("mode.apply.ndval"));
flags = std::vector<bool>(nbBands, true);
values = std::vector<double>(nbBands, GetParameterFloat("mode.apply.ndval"));
}
m_V2L = VectorToListFilterType::New();
m_V2L->SetInput(inputPtr);
ImageListType::Pointer inputList = m_V2L->GetOutput();
auto V2L = VectorToListFilterType::New();
V2L->SetInput(inputPtr);
ImageListType::Pointer inputList = V2L->GetOutput();
inputList->UpdateOutputInformation();
ImageListType::Pointer outputList = ImageListType::New();
std::vector<MaskFilterType::Pointer> maskFilters;
for (unsigned int i = 0; i < nbBands; ++i)
{
if (flags[i])
......@@ -187,36 +186,25 @@ private:
masker->SetMaskingValue(0);
masker->SetOutsideValue(values[i]);
outputList->PushBack(masker->GetOutput());
m_MaskFilters.push_back(masker);
maskFilters.push_back(masker);
}
else
{
outputList->PushBack(inputList->GetNthElement(i));
}
}
m_L2V = ListToVectorFilterType::New();
m_L2V->SetInput(outputList);
auto L2V = ListToVectorFilterType::New();
L2V->SetInput(outputList);
if (!ret)
{
m_MetaDataChanger = ChangeInfoFilterType::New();
m_MetaDataChanger->SetInput(m_L2V->GetOutput());
m_MetaDataChanger->SetOutputMetaData<std::vector<bool>>(otb::MetaDataKey::NoDataValueAvailable, &flags);
m_MetaDataChanger->SetOutputMetaData<std::vector<double>>(otb::MetaDataKey::NoDataValue, &values);
SetParameterOutputImage("out", m_MetaDataChanger->GetOutput());
}
else
{
SetParameterOutputImage("out", m_L2V->GetOutput());
// write the new NoData values in the output metadata (mode.apply.ndval)
L2V->UpdateOutputInformation();
otb::WriteNoDataFlags(flags, values, L2V->GetOutput()->GetImageMetadata());
}
SetParameterOutputImage("out", L2V->GetOutput());
RegisterPipeline();
}
}
FilterType::Pointer m_Filter;
ChangeNoDataFilterType::Pointer m_ChangeNoDataFilter;
std::vector<MaskFilterType::Pointer> m_MaskFilters;
VectorToListFilterType::Pointer m_V2L;
ListToVectorFilterType::Pointer m_L2V;
ChangeInfoFilterType::Pointer m_MetaDataChanger;
};
}
}
......
......@@ -23,6 +23,7 @@
#include "otbCoordinateToName.h"
#include "otbGroundSpacingImageFunction.h"
#include "vnl/vnl_random.h"
#include "otbNoDataHelper.h"
namespace otb
{
......@@ -272,10 +273,9 @@ private:
ossOutput << "\tData type : " << GetParameterString("datatype") << std::endl;
std::vector<bool> noDataValueAvailable;
bool ret = itk::ExposeMetaData<std::vector<bool>>(inImage->GetMetaDataDictionary(), MetaDataKey::NoDataValueAvailable, noDataValueAvailable);
std::vector<double> noDataValues;
itk::ExposeMetaData<std::vector<double>>(inImage->GetMetaDataDictionary(), MetaDataKey::NoDataValue, noDataValues);
auto ret = ReadNoDataFlags(inImage->GetImageMetadata(), noDataValueAvailable, noDataValues);
ossOutput << "\tNo data flags :";
......
......@@ -465,8 +465,8 @@ private:
std::vector<bool> noDataFlags;
std::vector<double> noDataValues;
itk::MetaDataDictionary& dict = GetParameterFloatVectorImage("in")->GetMetaDataDictionary();
bool ret = otb::ReadNoDataFlags(dict, noDataFlags, noDataValues);
const auto & imd = GetParameterFloatVectorImage("in")->GetImageMetadata();
bool ret = otb::ReadNoDataFlags(imd, noDataFlags, noDataValues);
if (ret)
{
......
......@@ -38,6 +38,8 @@ public:
const ImageMetadata & GetImageMetadata() const;
ImageMetadata & GetImageMetadata();
// boilerplate code...
/** Get the projection coordinate system of the image. */
......
......@@ -38,6 +38,11 @@ const ImageMetadata & ImageCommons::GetImageMetadata() const
return m_Imd;
}
ImageMetadata & ImageCommons::GetImageMetadata()
{
return m_Imd;
}
std::string ImageCommons::GetProjectionRef(void) const
{
// TODO: support EPSG and proj as fallback
......
......@@ -107,12 +107,6 @@ public:
double GetGCPZ(unsigned int GCPnum) const;
// otbMetadataGetGCPnumMacro(GCPZ, double, GCPnum, unsigned int);
/**
* Get The no data flags if existing
* return False otherwise
*/
bool GetNoDataFlags(std::vector<bool>& flags, std::vector<double>& values) const;
/** Get the six coefficients of affine geoTtransform. */
VectorType GetGeoTransform() const;
......
......@@ -26,6 +26,8 @@
#include <itkVariableLengthVector.h>
#include "OTBMetadataExport.h"
#include "otbImageMetadata.h"
namespace itk
{
class MetaDataDictionary;
......@@ -33,20 +35,19 @@ class MetaDataDictionary;
namespace otb
{
/**
* Reads no data flag from the MetaDataDictionary dict to flags and values
* Reads no data flag from the ImageMetadata to flags and values
* vectors. Returns true upon success.
* \deprecated
*/
bool OTBMetadata_EXPORT ReadNoDataFlags(const itk::MetaDataDictionary& dict, std::vector<bool>& flags, std::vector<double>& values);
bool OTBMetadata_EXPORT ReadNoDataFlags(const ImageMetadata & imd, std::vector<bool>& flags, std::vector<double>& values);
/**
* Write no data flags to the MetaDataDictionary dict from flags and values
* Write no data flags to the ImageMetadata from flags and values
* vectors. Returns true upon success.
* \deprecated
*/
void OTBMetadata_EXPORT WriteNoDataFlags(const std::vector<bool>& flags, const std::vector<double>& values, itk::MetaDataDictionary& dict);
void OTBMetadata_EXPORT WriteNoDataFlags(const std::vector<bool>& flags, const std::vector<double>& values, ImageMetadata & imd);
/**
* Test if the pixel corresponds to a no data pixel according to a
......
......@@ -21,7 +21,6 @@
#include "otbImageMetadataInterfaceBase.h"
#include "otbNoDataHelper.h"
#include "otbGeometryMetadata.h"
#include "itkMetaDataObject.h"
#include "itksys/SystemTools.hxx"
......@@ -244,11 +243,6 @@ double ImageMetadataInterfaceBase::GetGCPZ(unsigned int GCPnum) const
return (0);
}
bool ImageMetadataInterfaceBase::GetNoDataFlags(std::vector<bool>& flags, std::vector<double>& values) const
{
return ReadNoDataFlags(this->GetMetaDataDictionary(), flags, values);
}
ImageMetadataInterfaceBase::VectorType ImageMetadataInterfaceBase::GetGeoTransform() const
{
VectorType adfGeoTransform;
......
......@@ -26,21 +26,75 @@
namespace otb
{
bool ReadNoDataFlags(const itk::MetaDataDictionary& dict, std::vector<bool>& flags, std::vector<double>& values)
bool ReadNoDataFlags(const ImageMetadata & imd, std::vector<bool>& flags, std::vector<double>& values)
{
bool ret = itk::ExposeMetaData<std::vector<bool>>(dict, MetaDataKey::NoDataValueAvailable, flags);
flags.clear();
values.clear();
// at least one no data flag
bool ret = false;
// 1) Search for no data in bands
for (const auto & band: imd.Bands)
{
if (band.Has(MDNum::NoData))
{
ret = true;
flags.push_back(true);
values.push_back(band[MDNum::NoData]);
}
else
{
flags.push_back(false);
values.push_back(0.);
}
}
if (ret)
ret = itk::ExposeMetaData<std::vector<double>>(dict, MetaDataKey::NoDataValue, values);
{
return true;
}
// 2) Search in the main metadata domain
// Handle the case where there is no band metadata
unsigned int outputSize = imd.Bands.size() > 0 ? imd.Bands.size() : 1;
if (imd.Has(MDNum::NoData))
{
ret = true;
flags = std::vector<bool>(outputSize, true);
values = std::vector<double>(outputSize, imd[MDNum::NoData]);
}
else
{
flags = std::vector<bool>(outputSize, false);
values = std::vector<double>(outputSize, 0.);
}
return ret;
}
void WriteNoDataFlags(const std::vector<bool>& flags, const std::vector<double>& values, itk::MetaDataDictionary& dict)
void WriteNoDataFlags(const std::vector<bool>& flags, const std::vector<double>& values, ImageMetadata & imd)
{
itk::EncapsulateMetaData<std::vector<bool>>(dict, MetaDataKey::NoDataValueAvailable, flags);
itk::EncapsulateMetaData<std::vector<double>>(dict, MetaDataKey::NoDataValue, values);
auto size = flags.size();
assert(size == values.size());
if (imd.Bands.size() < size)
{
imd.Bands.resize(size);
}
auto itFlags = flags.cbegin();
auto itValues = values.cbegin();
auto itBands = imd.Bands.begin();
for (; itFlags != flags.cend(); itFlags++, itValues++, itBands++)
{
if (*itFlags)
{
itBands->Add(MDNum::NoData, *itValues);
}
}
}
} // End namespace otb
......@@ -28,10 +28,10 @@ int otbNoDataHelperTest(int itkNotUsed(argc), char* itkNotUsed(argv)[])
std::vector<bool> b1(1, true);
std::vector<double> v1(1, 0);
itk::MetaDataDictionary dict;
otb::ImageMetadata imd;
otb::WriteNoDataFlags(b1, v1, dict);
otb::ReadNoDataFlags(dict, b1, v1);
otb::WriteNoDataFlags(b1, v1, imd);
otb::ReadNoDataFlags(imd, b1, v1);
otbControlConditionTestMacro(otb::IsNoData(10, b1, v1), " wrong output of IsNoData function");
otbControlConditionTestMacro(!otb::IsNoData(0, b1, v1), " wrong output of IsNoData function");
......
......@@ -47,14 +47,23 @@ void RAMDrivenAdaptativeStreamingManager<TImage>::PrepareStreaming(itk::DataObje
typename otb::ImageRegionAdaptativeSplitter<itkGetStaticConstMacro(ImageDimension)>::SizeType tileHint;
unsigned int tileHintX(0), tileHintY(0);
itk::ExposeMetaData<unsigned int>(input->GetMetaDataDictionary(), MetaDataKey::TileHintX, tileHintX);
itk::ExposeMetaData<unsigned int>(input->GetMetaDataDictionary(), MetaDataKey::TileHintY, tileHintY);
tileHint[0] = tileHintX;
tileHint[1] = tileHintY;
auto inputImage = dynamic_cast<TImage*>(input);
tileHint[0] = 0;
tileHint[1] = 0;
if (inputImage)
{
const auto & imd = inputImage->GetImageMetadata();
if (imd.Has(MDNum::TileHintX))
{
tileHint[0] = imd[MDNum::TileHintX];
}
if (imd.Has(MDNum::TileHintY))
{
tileHint[1] = imd[MDNum::TileHintY];
}
}
typename otb::ImageRegionAdaptativeSplitter<itkGetStaticConstMacro(ImageDimension)>::Pointer splitter =
otb::ImageRegionAdaptativeSplitter<itkGetStaticConstMacro(ImageDimension)>::New();
......
......@@ -44,10 +44,9 @@ ImageType::Pointer makeImage(ImageType::RegionType region)
image->SetRegions(region);
image->SetNumberOfComponentsPerPixel(10);
itk::MetaDataDictionary& dict = image->GetMetaDataDictionary();
itk::EncapsulateMetaData<unsigned int>(dict, otb::MetaDataKey::TileHintX, 64);
itk::EncapsulateMetaData<unsigned int>(dict, otb::MetaDataKey::TileHintY, 64);
auto & imd = image->GetImageMetadata();
imd.Add(otb::MDNum::TileHintX, 64);
imd.Add(otb::MDNum::TileHintY, 64);
return image;
}
......
......@@ -27,6 +27,7 @@
#include "itkDefaultConvertPixelTraits.h"
#include "itkMetaDataObject.h"
#include "otbMetaDataKey.h"
#include "otbNoDataHelper.h"
namespace otb
{
......@@ -253,19 +254,17 @@ void StreamingWarpImageFilter<TInputImage, TOutputImage, TDisplacementField>::Ge
Superclass::GenerateOutputInformation();
// Set the NoData flag to the edge padding value
itk::MetaDataDictionary& dict = this->GetOutput()->GetMetaDataDictionary();
std::vector<bool> noDataValueAvailable;
bool ret = itk::ExposeMetaData<std::vector<bool>>(dict, MetaDataKey::NoDataValueAvailable, noDataValueAvailable);
if (!ret)
std::vector<double> noDataValue;
auto res = ReadNoDataFlags(this->GetOutput()->GetImageMetadata(), noDataValueAvailable, noDataValue);
if (!res)
{
noDataValueAvailable.resize(this->GetOutput()->GetNumberOfComponentsPerPixel(), false);
}
std::vector<double> noDataValue;
ret = itk::ExposeMetaData<std::vector<double>>(dict, MetaDataKey::NoDataValue, noDataValue);
if (!ret)
{
noDataValue.resize(this->GetOutput()->GetNumberOfComponentsPerPixel(), 0.0);
}
PixelType edgePadding = this->GetEdgePaddingValue();
if (itk::NumericTraits<PixelType>::GetLength(edgePadding) != this->GetOutput()->GetNumberOfComponentsPerPixel())
{
......@@ -280,8 +279,8 @@ void StreamingWarpImageFilter<TInputImage, TOutputImage, TDisplacementField>::Ge
noDataValue[i] = itk::DefaultConvertPixelTraits<PixelType>::GetNthComponent(i, edgePadding);
}
}
itk::EncapsulateMetaData<std::vector<bool>>(dict, MetaDataKey::NoDataValueAvailable, noDataValueAvailable);
itk::EncapsulateMetaData<std::vector<double>>(dict, MetaDataKey::NoDataValue, noDataValue);
WriteNoDataFlags(noDataValueAvailable, noDataValue, this->GetOutput()->GetImageMetadata());
}
template <class TInputImage, class TOutputImage, class TDisplacementField>
......
/*
* 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.
*/
#ifndef otbChangeInformationImageFilter_h
#define otbChangeInformationImageFilter_h
#include "itkChangeInformationImageFilter.h"
#include "otbMetaDataKey.h"
namespace otb
{
/**
* \class ChangeInformationImageFilter
* \brief Filter to modify image metadata
*
* The base class is itk::ChangeInformationImageFilter that allows
* modifying origin, spacing, direction and buffered region. This deriving
* filter adds the support of MetaDataDictionary.
*
* \ingroup OTBImageManipulation
*/
template <typename TInputImage>
class ChangeInformationImageFilter : public itk::ChangeInformationImageFilter<TInputImage>
{
public:
/** Standard class typedefs. */
typedef ChangeInformationImageFilter Self;
typedef itk::ChangeInformationImageFilter<TInputImage> Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
/** Method for creation through the object factory. */
itkNewMacro(Self);
/** Creation through object factory macro */
itkTypeMacro(ChangeInformationImageFilter, itk::ChangeInformationImageFilter);
/** Set key names to change */
void SetChangeMetaData(const char* keyname, bool flag);
/** Ask if a metadata will be changed */
bool GetChangeMetaData(const char* keyname);
/** Set output values for metadata, passing a NULL value will remove the
* metadata from output. If not set for a key name in the change list,
* the metadata will also be set.
*/
template <typename T>
void SetOutputMetaData(const char* keyname, const T* value);
protected:
ChangeInformationImageFilter()
{
}
~ChangeInformationImageFilter() override
{
}
/** Apply changes to the output image metadata. */
void GenerateOutputInformation() override;
private:
ChangeInformationImageFilter(const Self&) = delete;
void operator=(const Self&) = delete;
/** List of metadata keys to change */
std::set<std::string> m_ChangedKeys;
};
} // End of namespace OTB
#ifndef OTB_MANUAL_INSTANTIATION
#include "otbChangeInformationImageFilter.hxx"
#endif
#endif
/*
* 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