From bd044664d54c150eb0a47c5e51f7f932d761fc55 Mon Sep 17 00:00:00 2001 From: Luc Hermitte <luc.hermitte@csgroup.eu> Date: Mon, 31 Aug 2020 17:14:31 +0200 Subject: [PATCH] ENH: Contribute 2 S1TilingApps to OTB --- .../AppImageUtils/app/CMakeLists.txt | 10 + .../AppImageUtils/app/otbClampROI.cxx | 129 ++++++ .../AppImageUtils/app/otbSynthetize.cxx | 134 ++++++ Modules/Core/Common/include/otbInterval.h | 99 +++++ Modules/Core/Common/include/otbLogHelpers.h | 64 +++ Modules/Core/Common/include/otbSpan.h | 251 +++++++++++ Modules/Core/Common/include/otbZipIterator.h | 419 ++++++++++++++++++ .../Functor/include/otbSynthetizeFilter.h | 239 ++++++++++ .../include/otbClampROIFilter.h | 154 +++++++ .../include/otbClampROIFilter.hxx | 216 +++++++++ .../S1TilingSupportApplications.remote.cmake | 2 +- 11 files changed, 1716 insertions(+), 1 deletion(-) create mode 100644 Modules/Applications/AppImageUtils/app/otbClampROI.cxx create mode 100644 Modules/Applications/AppImageUtils/app/otbSynthetize.cxx create mode 100644 Modules/Core/Common/include/otbInterval.h create mode 100644 Modules/Core/Common/include/otbLogHelpers.h create mode 100644 Modules/Core/Common/include/otbSpan.h create mode 100644 Modules/Core/Common/include/otbZipIterator.h create mode 100644 Modules/Core/Functor/include/otbSynthetizeFilter.h create mode 100644 Modules/Filtering/ImageManipulation/include/otbClampROIFilter.h create mode 100644 Modules/Filtering/ImageManipulation/include/otbClampROIFilter.hxx diff --git a/Modules/Applications/AppImageUtils/app/CMakeLists.txt b/Modules/Applications/AppImageUtils/app/CMakeLists.txt index 8f2981b694..f1b82f4031 100644 --- a/Modules/Applications/AppImageUtils/app/CMakeLists.txt +++ b/Modules/Applications/AppImageUtils/app/CMakeLists.txt @@ -92,3 +92,13 @@ OTB_CREATE_APPLICATION( NAME Mosaic SOURCES otbMosaic.cxx LINK_LIBRARIES ${${otb-module}_LIBRARIES}) + +OTB_CREATE_APPLICATION( + NAME ClampROI + SOURCES otbClampROI.cxx + LINK_LIBRARIES ${${otb-module}_LIBRARIES}) + +OTB_CREATE_APPLICATION( + NAME Synthetize + SOURCES otbSynthetize.cxx + LINK_LIBRARIES ${${otb-module}_LIBRARIES}) diff --git a/Modules/Applications/AppImageUtils/app/otbClampROI.cxx b/Modules/Applications/AppImageUtils/app/otbClampROI.cxx new file mode 100644 index 0000000000..0d49ca2433 --- /dev/null +++ b/Modules/Applications/AppImageUtils/app/otbClampROI.cxx @@ -0,0 +1,129 @@ +/* + * 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 "otbClampROIFilter.h" +#include "otbWrapperApplication.h" +#include "otbWrapperApplicationFactory.h" + +namespace otb +{ +namespace Wrapper +{ + +/** + * Application that fills margins to 0. + * + * This application is similar to ExtractROI with the difference the margin is + * kept, and filled with 0. + * + * This application is used to implement the _cut_ processing in S1Tiling + * chain. + * + * \author Luc Hermitte (CS Group) + * \copyright CNES + */ +class ClampROI : public Application +{ +public: + using Self = ClampROI; + using Pointer = itk::SmartPointer<Self>; + + itkNewMacro(Self); + itkTypeMacro(ClampROI, otb::Wrapper::Application); + +private: + void DoInit() override + { + SetName("ClampROI"); + + SetDescription("This is the ClampROI application, version X.X.X"); + SetDocLongDescription( + "This application is similar to ExtractROI in the sense it extracts a Region of Interrest.\n" + "However, the region outside of the ROI isn't trimmed, but set to 0.\n" + "\n" + "The filter set lines of index < threshold.y, and of index >= threshold.y to 0\n" + "The filter set columns of index < threshold.x, and of index >= threshold.x to 0"); + + SetDocLimitations("This application only works on scalar (and complex) images."); + SetDocAuthors("Luc Hermitte (CS Group)"); + SetDocSeeAlso("ManageNoData, ExtractROI"); + AddDocTag("otb::Wrapper::Tags::Manip"); + + AddParameter(ParameterType_InputImage, "in", "Input image"); + SetParameterDescription("in", "Scalar Input image"); + + AddParameter(ParameterType_OutputImage, "out", "Output Image"); + SetParameterDescription("out", "Scalar Output image"); + + AddParameter(ParameterType_Group, "threshold", "threshold group"); + AddParameter(ParameterType_Group, "threshold.y", "threshold group"); + MandatoryOff("threshold"); + MandatoryOff("threshold.y"); + AddParameter(ParameterType_Int, "threshold.x", "Column index threshold"); + SetParameterDescription("threshold.x", "Column index threshold"); + SetDefaultParameterInt("threshold.x", 0); + + AddParameter(ParameterType_Int, "threshold.y.start", "Top line index threshold"); + SetParameterDescription("threshold.y.start", "Top line index threshold"); + SetDefaultParameterInt("threshold.y.start", 0); + + AddParameter(ParameterType_Int, "threshold.y.end", "Bottom line index threshold"); + SetParameterDescription("threshold.y.end", "Bottom line index threshold"); + SetDefaultParameterInt("threshold.y.end", 0); + + AddRAMParameter(); + } + + void DoUpdateParameters() override + {} + + void DoExecute() override + { + auto const thrX = GetParameterInt("threshold.x"); + auto const thrYtop = GetParameterInt("threshold.y.start"); + auto const thrYbot = GetParameterInt("threshold.y.end"); + if (thrX < 0) + itkExceptionMacro("The column threshold is expected to be positive"); + if (thrYtop < 0) + itkExceptionMacro("The top line threshold is expected to be positive"); + if (thrYbot < 0) + itkExceptionMacro("The bottom line threshold is expected to be positive"); + if (thrX == 0 && thrYtop == 0 && thrYbot == 0) + itkExceptionMacro("Don't use ClampROI to clamp nothing!"); + + auto filter = ClampROIFilter<FloatImageType>::New(); + assert(thrX >= 0); + assert(thrYtop >= 0); + assert(thrYbot >= 0); + filter->SetThresholdX(thrX); + filter->SetThresholdYtop(thrYtop); + filter->SetThresholdYbot(thrYbot); + filter->SetInput(GetParameterFloatImage("in")); + + SetParameterOutputImage("out", filter->GetOutput()); + RegisterPipeline(); + } + +}; + +} // otb::Wrapper namespace +} // otb namespace + +OTB_APPLICATION_EXPORT(otb::Wrapper::ClampROI) diff --git a/Modules/Applications/AppImageUtils/app/otbSynthetize.cxx b/Modules/Applications/AppImageUtils/app/otbSynthetize.cxx new file mode 100644 index 0000000000..fa77fdcba8 --- /dev/null +++ b/Modules/Applications/AppImageUtils/app/otbSynthetize.cxx @@ -0,0 +1,134 @@ +/* + * 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 "otbSynthetizeFilter.h" +#include "otbWrapperApplication.h" +#include "otbWrapperApplicationFactory.h" +#include "otbImageFileReader.h" +#include <set> + +namespace otb +{ +namespace Wrapper +{ + +/** + * This application synthetizes/reduces multiple inputs into a single one. + * In that particular case, for each output pixel, this application will + * consider the corresponding pixels from all the input images, and keep the + * first one that isn't equal to 0. + * + * This application is used to implement the _concatenate_ processing in + * S1Tiling chain. + * + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \todo find a better name for the application. Alas `otbConcatenate` is + * already used... + */ +class Synthetize : public Application +{ +public: + using Self = Synthetize; + using Pointer = itk::SmartPointer<Self>; + + itkNewMacro(Self); + itkTypeMacro(Synthetize, otb::Wrapper::Application); + +private: + using ReaderType = otb::ImageFileReader<FloatImageType>; + + void DoInit() override + { + SetName("Synthetize"); + + SetDescription("This is the Synthetize application, version X.X.X"); + SetDocLongDescription("Concatenate a list of images of the same size into a single single-channel image.\n\ + It keeps the first non-null pixel value found in the input list."); + + SetDocLimitations("This application will break incoming pipelines."); + SetDocAuthors("Luc Hermitte (CS Group)"); + SetDocSeeAlso(""); + AddDocTag("otb::Wrapper::Tags::Manip"); + + AddParameter(ParameterType_StringList, "il", "Input images list"); + SetParameterDescription("il", "Input image list"); + + AddParameter(ParameterType_OutputImage, "out", "Output Image"); + SetParameterDescription("out","Output image."); + + AddRAMParameter(); + } + + void DoUpdateParameters() override + {} + + void DoExecute() override + { + // Get the input image list + auto inNameList = GetParameterStringList("il"); + + // checking the input images list validity + auto const nbImages = inNameList.size(); + + if (nbImages == 0) + { + itkExceptionMacro("No input Image set...; please set at least one input image"); + } + + auto functor = [](auto input) { + assert(!input.empty()); + auto const wh = std::find_if( + input.begin(), input.end()-1, + [](auto v){ return v != 0;}); + return *wh; + }; + auto filter = MakeSynthetizeFilter<FloatImageType, FloatImageType>(functor); + + for (unsigned int i = 0; i < nbImages; i++) + { + // Given the explicit use of a Reader, this application cannot be used in + // a in-memory pipeline + auto reader = ReaderType::New(); + // currentImage->SetExtendedFileName(inNameList[i]); + reader->SetFileName(inNameList[i]); + auto currentImage = reader->GetOutput(); + currentImage->UpdateOutputInformation(); + + otbAppLogINFO(<< "Image #" << i + 1 << " has " << currentImage->GetNumberOfComponentsPerPixel() << " components"); + + filter->SetInput(i, currentImage); + m_Cache.insert(reader); + } + + SetParameterOutputImage("out", filter->GetOutput()); + RegisterPipeline(); // TODO: check!! + } + + // Needed to register the inputs handled manually + // and not with a VectorImageList through GetParameterImageList + std::set<ReaderType::Pointer> m_Cache; +}; + +} // otb::Wrapper namespace +} // otb namespace + +OTB_APPLICATION_EXPORT(otb::Wrapper::Synthetize) diff --git a/Modules/Core/Common/include/otbInterval.h b/Modules/Core/Common/include/otbInterval.h new file mode 100644 index 0000000000..5e6ce66b9b --- /dev/null +++ b/Modules/Core/Common/include/otbInterval.h @@ -0,0 +1,99 @@ +/* + * 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 otbInterval_h +#define otbInterval_h + +#include "itkIntTypes.h" +#include <cassert> +#include <ostream> + +namespace otb +{ +/** Simplified index interval. + * Inspired by `boost::numeric::interval`. + * + * \invariant Can be empty. + * \invariant lower <= upper + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \ingroup OTBCommon + */ +class Interval +{ +public: + using IndexType = itk::IndexValueType; + using SizeType = itk::SizeValueType; + + /** Init constructor from the pair of its extremities. */ + constexpr Interval(IndexType l, IndexType u) noexcept + : m_lower(l), m_upper(u) + { + assert(l <= u); + } + + /** Alternate factory function from a position and a length. */ + static constexpr Interval OfLength(IndexType low, SizeType len) noexcept{ + return Interval{low, IndexType(low+len)}; + } + + constexpr SizeType size() const noexcept + { return m_upper - m_lower; } + + constexpr bool empty() const noexcept + { return m_lower == m_upper; } + + constexpr IndexType lower() const noexcept + { return m_lower; } + + constexpr IndexType upper() const noexcept + { return m_upper; } + + /** Computes the intersection between two interals. + * @return their intersection + * @return {0,0} if theyr don't intersect. + * + * @note this function is an hidden friend + */ + friend constexpr Interval intersect( + Interval const& lhs, Interval const& rhs) noexcept + { + auto const low = std::max(lhs.lower(), rhs.lower()); + auto const upp = std::min(lhs.upper(), rhs.upper()); + + return low <= upp ? Interval{low, upp} : Interval{0,0}; + } + + /** Stream inserter for intervals. + * @note this function is an hidden friend + */ + friend std::ostream & operator<<(std::ostream & os, Interval const& v) + { + return os << '[' << v.lower() << ".." << v.upper() << '['; + } + +private: + IndexType m_lower; + IndexType m_upper; +}; +} // otb namespace + +#endif // otbInterval_h diff --git a/Modules/Core/Common/include/otbLogHelpers.h b/Modules/Core/Common/include/otbLogHelpers.h new file mode 100644 index 0000000000..98239512b3 --- /dev/null +++ b/Modules/Core/Common/include/otbLogHelpers.h @@ -0,0 +1,64 @@ +/* + * 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 otbLogHelpers_h +#define otbLogHelpers_h + +#include "itkImageRegion.h" +#include <ostream> + +namespace otb +{ + +/** Helper class to log region in a more human readable way: + * e.g. + * \code + x ∈ [0..42[, y ∈ [12..24[, size=42x12 @(0, 12) + * \endcode + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \ingroup OTBCommon + */ +struct NeatRegionLogger +{ + using RegionType = itk::ImageRegion<2u>; + NeatRegionLogger(RegionType const& region) : m_region(region) {} + + friend std::ostream & operator<<(std::ostream & os, NeatRegionLogger const& r) + { + auto const& size = r.m_region.GetSize(); + auto const& idx = r.m_region.GetIndex(); + auto const idx_x1 = idx[0]; + int const idx_x2 = idx[0] + size[0]; + int const idx_y1 = idx[1]; + auto const idx_y2 = idx[1] + size[1]; + os + << "x ∈ ["<<idx_x1 << ".." << idx_x2 << '[' + << ", y ∈ ["<<idx_y1 << ".." << idx_y2 << '[' + << ", size=" << size[0]<<'x'<<size[1] << " @("<<idx_x1<<", "<<idx_y1<<')' + ; + return os ; + } + RegionType const& m_region; +}; +} // otb namespace + +#endif // otbLogHelpers_h diff --git a/Modules/Core/Common/include/otbSpan.h b/Modules/Core/Common/include/otbSpan.h new file mode 100644 index 0000000000..0b39f86736 --- /dev/null +++ b/Modules/Core/Common/include/otbSpan.h @@ -0,0 +1,251 @@ +/* + * 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 Span_h +#define Span_h + +#include <type_traits> +#include <iterator> +#include <cassert> + +namespace otb +{ + +/** Span class inspired by C++20 standard. + * \todo fix RW / RO interface + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \ingroup OTBCommon + */ +template <typename T> struct Span +{ + /**\name Typedefs */ + //@{ + using element_type = T; + using value_type = std::remove_cv_t<T>; + using index_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using const_pointer = T const*; + using reference = T&; + using const_reference = T const&; + using iterator = T*; + using const_iterator = T const*; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + //@} + + /**\name Constructors */ + //@{ + constexpr Span() noexcept = default; + constexpr Span(pointer ptr, index_type count) noexcept + : m_buffer(ptr), m_size(count) + { + assert(! (!ptr) xor (!count)); + } + constexpr Span(pointer first, pointer last) noexcept + : Span(first, last - first) + { + assert(! (!first) xor (!last)); + assert(first <= last); + } + template <std::size_t N> constexpr Span(element_type (&arr)[N]) noexcept + : Span(arr, N) + {} + + /** Converting constructor from a contiguous container. + * \pre The Container shall be contiguous + * \warning The lifetime of the span shall not exceed the one of the container. + * Be sure to not store the span locally, and initialize it from a rvalue. + * The use case where a span is initialize from a rvalue shall be restricted + * to function parameters. + * \code + * std::vector<T> f(); + * void g(Span<T> sp); + * ... + * Span<T> sp(f()); // NO!!! Use after release + * g(f()); // OK!! + * \endcode + * + * \todo static_assert the container is contiguous + */ + template <class Container> constexpr Span(Container&& cont) noexcept + : Span(&cont[0], cont.size()) + { + // We cannot use op[] which has an assertion sometimes. + // assert(&const[size()] == (&cont[0] + size())); + } + template <class U> constexpr Span(const otb::Span<U>& s) noexcept + : Span(s.data(), s.size()) + {} + constexpr Span(const Span& other) noexcept = default; + //@} + + /// shallow assignment + Span& operator=(Span const&) noexcept = default; + + /// No-op destructor + ~Span() = default; + + /**\name Iterators */ + //@{ + constexpr iterator begin () noexcept { return data(); } + constexpr iterator end () noexcept { return data()+size(); } + constexpr const_iterator begin () const noexcept { return data(); } + constexpr const_iterator end () const noexcept { return data()+size(); } + constexpr const_iterator cbegin() const noexcept { return data(); } + constexpr const_iterator cend () const noexcept { return data()+size(); } + + constexpr reverse_iterator rbegin () noexcept { return reverse_iterator(end()); } + constexpr reverse_iterator rend () noexcept { return reverse_iterator(begin()); } + constexpr const_reverse_iterator crbegin() const noexcept { return reverse_const_iterator(cend()); } + constexpr const_reverse_iterator crend () const noexcept { return reverse_const_iterator(cbegin()); } + //@} + + /**\name Element access */ + //@{ + constexpr pointer data () noexcept { return m_buffer; } + constexpr const_pointer data () const noexcept { return m_buffer; } + constexpr reference front() noexcept { assert(!empty()); return *data(); } + constexpr const_reference front() const noexcept { assert(!empty()); return *data(); } + constexpr reference back () noexcept { assert(!empty()); return *data()+size()-1; } + constexpr const_reference back () const noexcept { assert(!empty()); return *data()+size()-1; } + + constexpr reference operator[](index_type p) noexcept + { + assert(p < size()); + return data()[p]; + } + constexpr const_reference operator[](index_type p) const noexcept + { + assert(p < size()); + return data()[p]; + } + //@} + + /**\name Observers */ + //@{ + constexpr index_type size () const noexcept { return m_size; } + constexpr bool empty() const noexcept { return size() == 0; } + //@} + + /**\name Subviews */ + //@{ + constexpr Span first(index_type n) const noexcept + { assert(n < size()); return Span(data(), n);} + constexpr Span last(index_type n) const noexcept + { assert(n < size()); return Span(data()-n, n);} + //@} + +private: + pointer m_buffer = nullptr; + index_type m_size = 0; +}; + +/** + * Helper function to make a span from a range defined with pointers. + * Compensate the fact we are not have access to C++17 `span{ptr1, ptr2}`. + * \tparam T Auto deduced type of the elements + * \param[in] first start of the memory zone + * \param[in] last end of the memory zone + * \return a span over `[first, last)` + * \throw None + * \pre `NOT first != nullptr XOR last != nullptr` + * \pre `first < last` + * \pre `[first, last)` can be iterated + * \see `otb::Span<>` + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \ingroup OTBCommon + */ +template <typename T> +inline +auto make_span(T* first, T* last) noexcept +{ + return Span<T>(first, last); +} + +/** + * Helper function to make a span from a range defined with a pointer plus a + * size. + * Compensate the fact we are not have access to C++17 `span{ptr, count}`. + * \tparam T Auto deduced type of the elements + * \param[in] first start of the memory zone + * \param[in] count number of elements in the span. + * \return a span over `[first, first+count)` + * \throw None + * \pre `NOT first != nullptr XOR count != 0` + * \see `otb::Span<>` + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \ingroup OTBCommon + */ +template <typename T> +inline +auto make_span(T* first, std::size_t count) noexcept +{ + return Span<T>(first, count); +} + +/** + * Helper function to make a span from a static array. + * Compensate the fact we are not have access to C++17 `span{array}`. + * \tparam T Auto deduced type of the elements + * \tparam N Auto deduced number of elements in the array + * \param[in] array static array + * \return a span over `[&array[0], &array[N])` + * \throw None + * \see `otb::Span<>` + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \ingroup OTBCommon + */ +template <typename T, std::size_t N> +inline +auto make_span(T (&arr)[N]) noexcept +{ + return Span<T>(arr); +} + +/** + * Helper function to make a span from a contiguous container. + * Compensate the fact we are not have access to C++17 `span{container}`. + * \tparam ContiguousContainer Auto deduced type of the container + * \param[in] cont container of contiguous elements + * \return a span over `[c.data(), c.size())` + * \throw None + * \see `otb::Span<>` + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \ingroup OTBCommon + */ +template <typename ContiguousContainer> +inline +auto make_span(ContiguousContainer & c) noexcept +{ + return Span<decltype(*c.data())>(c); +} + +} +// otb namespace + + +#endif // Span_h diff --git a/Modules/Core/Common/include/otbZipIterator.h b/Modules/Core/Common/include/otbZipIterator.h new file mode 100644 index 0000000000..f95635a16a --- /dev/null +++ b/Modules/Core/Common/include/otbZipIterator.h @@ -0,0 +1,419 @@ +/* + * 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 otbZipIterator_h +#define otbZipIterator_h + +#include "otbSpan.h" +#include "itkMacro.h" +#include <type_traits> +#include <vector> +#include <cassert> + +namespace otb +{ +namespace internals +{ + +struct ConstTag {}; +struct MutableTag {}; + +/** + * Wrapper to present list of iterators as a single iterator. + * + * \invariant This class shall not be inherited. + * \invariant All sub iterators are always synchronised (same region, size...) + * \invariant `!m_iterators.empty()` + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \ingroup OTBCommon + */ +template <typename TImageIterator, typename ConstOrMutable> +class ZipIterator +{ +public: + /**\name ITK Constants and Typedefs */ + //@{ + /** Iterator type for zipped iterators. */ + using ImageIteratorType = TImageIterator; + + /** Image type alias support */ + using ImageType = typename TImageIterator::ImageType; + + /** Dimension of the image the iterator walks. This constant is needed so + * functions that are templated over image iterator type (as opposed to + * being templated over pixel type and dimension) can have compile time + * access to the dimension of the image that the iterator walks. */ + static constexpr unsigned int ImageIteratorDimension = ImageIteratorType::ImageIteratorDimension; + + using Self = ZipIterator; + + /** Run-time type information (and related methods). */ + itkTypeMacroNoParent(ZipIterator); + + /** Index type alias support */ + using IndexType = typename ImageIteratorType::IndexType; + + /** Size type alias support */ + using SizeType = typename ImageIteratorType::SizeType; + + /** Offset type alias support */ + using OffsetType = typename ImageIteratorType::OffsetType; + + /** Region type alias support */ + using RegionType = typename ImageIteratorType::RegionType; + + /** PixelContainer type alias support. Used to refer to the container for + * the pixel data. While this was already typdef'ed in the superclass + * it needs to be redone here for this subclass to compile properly with gcc. */ + using PixelContainer = typename ImageIteratorType::PixelContainer; + using PixelContainerPointer = typename PixelContainer::Pointer; + + /** Internal Pixel Type */ + using InternalPixelType = typename ImageIteratorType::InternalPixelType; + + /** External Pixel Type */ + using PixelType = typename ImageIteratorType::PixelType; + + /** Accessor type that convert data between internal and external + * representations. */ + using AccessorType = typename ImageIteratorType::AccessorType; + using AccessorFunctorType = typename ImageIteratorType::AccessorFunctorType; + //@} + + /**\name Constructions & Destruction + * + * This class follows the rule of 0/5 + */ + //@{ + ZipIterator () = default; + ~ZipIterator () = default; + ZipIterator (ZipIterator const&) = default; + ZipIterator (ZipIterator &&) = default; + ZipIterator& operator=(ZipIterator const&) = default; + ZipIterator& operator=(ZipIterator &&) = default; + + /** Convertion constructor. + * Converts from mutable to const iterator + * \see https://quuxplusone.github.io/blog/2018/12/01/const-iterator-antipatterns/ + */ + template <bool IsConst_ = std::is_same<ConstOrMutable, ConstTag>::value, class = std::enable_if<IsConst_>> + ZipIterator(ZipIterator<ImageIteratorType, MutableTag> const& rhs) + : m_iterators(rhs.m_iterators()) + {} + + /** Convertion move constructor. + * Move converts from mutable to const iterator + * \see https://quuxplusone.github.io/blog/2018/12/01/const-iterator-antipatterns/ + */ + template <bool IsConst_ = std::is_same<ConstOrMutable, ConstTag>::value, class = std::enable_if<IsConst_>> + ZipIterator(ZipIterator<ImageIteratorType, MutableTag> && rhs) + : m_iterators(move(rhs.m_iterators())) + {} + + /** + * Init Constructor. + * Constructs a `ZipIterator` from a list of Images and a Region. + * \param[in,out] images List of images + * \param[in] region Region to iterate over + * \pre There should be at least one image + */ + ZipIterator(Span<ImageType * const> images, RegionType const& region) + { + assert(! images.empty()); + m_iterators.reserve(images.size()); + + for (auto & im: images) + m_iterators.emplace_back(im, region); + } + + // static_assert(std::is_copy_constructible<Self>::value, "Requires copy construction"); + // static_assert(std::is_trivially_copy_constructible<Self>::value, "Requires trivial copy construction"); + //@} + + /**\name Comparison */ + //@{ + friend bool operator==(ZipIterator const& lhs, ZipIterator const& rhs) + { + assert(!lhs.m_iterators.empty()); + assert(lhs.m_iterators.size() == rhs.m_iterators.size()); + + return lhs.m_iterators.front() == rhs.m_iterators.front(); + } + friend bool operator!=(ZipIterator const& lhs, ZipIterator const& rhs) + { return ! (lhs == rhs); } + + friend bool operator<=(ZipIterator const& lhs, ZipIterator const& rhs) + { + assert(!lhs.m_iterators.empty()); + assert(lhs.m_iterators.size() == rhs.m_iterators.size()); + + return lhs.m_iterators.front() <= rhs.m_iterators.front(); + } + friend bool operator<(ZipIterator const& lhs, ZipIterator const& rhs) + { + assert(!lhs.m_iterators.empty()); + assert(lhs.m_iterators.size() == rhs.m_iterators.size()); + + return lhs.m_iterators.front() < rhs.m_iterators.front(); + } + friend bool operator>=(ZipIterator const& lhs, ZipIterator const& rhs) + { return ! (lhs < rhs); } + friend bool operator>(ZipIterator const& lhs, ZipIterator const& rhs) + { return ! (lhs <= rhs); } + //@} + + /**\name ITK iterator interface */ + //@{ + // What about GetIndex() and SetIndex ? + /** Fetch the region iterated by the iterator. */ + auto const& GetRegion() const + { + assert(!m_iterators.empty()); + return m_iterators.front().GetRegion(); + } + + /** Set the region iterated by the iterator. */ + void SetRegion(RegionType const& region) + { + for (auto & it : m_iterators) + it.SetRegion(region); + } + + /** Moves the iterator to the begin of the region iterated. */ + Self& GoToBegin() { + for (auto & it : m_iterators) + it.GoToBegin(); + return *this; + } + /** Moves the iterator to the end of the region iterated. */ + Self& GoToEnd() { + for (auto & it : m_iterators) + it.GoToEnd(); + return *this; + } + + /** Tells whether the iterator is at the begin of the region iterated. */ + bool IsAtBegin() const { + assert(!m_iterators.empty()); + return m_iterators.front().IsAtBegin(); + } + + /** Tells the iterator is at the end of the region iterated. */ + bool IsAtEnd() const { + assert(!m_iterators.empty()); + return m_iterators.front().IsAtEnd(); + } + + /** Pre-increment the iterator. + * As post-increment is less efficient, it hasn't been provided. + */ + Self& operator++() { + assert(!IsAtEnd()); + for (auto & it : m_iterators) + ++it; + return *this; + } + /** Removed post-increment operator. + * Please use the preincrement operator! + */ + Self operator++(int) = delete; + + /** Internal typedef to the type holding the list of ITK iterators. */ + using ImageIteratorList_t = std::vector<ImageIteratorType>; + /** Internal Pixel Proxy Type returned by `ZipIterator`. + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \ingroup OTBCommon + */ + struct PixelListProxy + { + /** Init constructor. + * @param[in] iterators List of ITK iterators + */ + explicit PixelListProxy(ImageIteratorList_t const& iterators) noexcept + : m_iterators(iterators) + {} + bool empty() const noexcept {return m_iterators.empty();} + auto size() const noexcept {return m_iterators.size();} + + /** Internal C++ iterator over the components of the Pixel Proxy. + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \ingroup OTBCommon + */ + struct iterator__ + { + using difference_type = typename ImageIteratorList_t::difference_type; + using value_type = decltype(typename ImageIteratorList_t::const_iterator{}->Get()); + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::forward_iterator_tag; + + explicit iterator__(typename ImageIteratorList_t::const_iterator ref) + : reference_to_value(ref){} + friend bool operator==(iterator__ const& lhs, iterator__ const& rhs) noexcept + { return lhs.reference_to_value == rhs.reference_to_value;} + friend bool operator!=(iterator__ const& lhs, iterator__ const& rhs) noexcept + { return ! (lhs == rhs);} + iterator__ & operator++() noexcept { + ++reference_to_value; + return *this; + } + iterator__ & operator--() noexcept { + --reference_to_value; + return *this; + } + iterator__ operator+(std::ptrdiff_t offset) const noexcept{ + return iterator__{reference_to_value + offset}; + } + iterator__ operator-(std::ptrdiff_t offset) const noexcept{ + return iterator__{reference_to_value - offset}; + } + decltype(auto) operator*() const { + return reference_to_value->Get(); + } + + private: + typename ImageIteratorList_t::const_iterator reference_to_value; + }; + + auto begin() noexcept { return iterator__(m_iterators.begin()); } + auto end() noexcept { return iterator__{m_iterators.end()}; } + auto begin() const noexcept { return iterator__{m_iterators.begin()}; } + auto end() const noexcept { return iterator__{m_iterators.end()}; } + auto cbegin() const noexcept { return iterator__{m_iterators.cbegin()}; } + auto cend() const noexcept { return iterator__{m_iterators.cend()}; } + + decltype(auto) operator[](std::size_t idx) const { + assert(idx < size()); + return m_iterators[idx].Get(); + } + decltype(auto) operator[](std::size_t idx) { + assert(idx < size()); + return m_iterators[idx].Get(); + } + + decltype(auto) front() const { + assert(!empty()); + return m_iterators.front().Get(); + } + decltype(auto) front() { + assert(!empty()); + return m_iterators.front().Get(); + } + decltype(auto) back() const { + assert(!empty()); + return m_iterators.back().Get(); + } + decltype(auto) back() { + assert(!empty()); + return m_iterators.back().Get(); + } + private: + ImageIteratorList_t const& m_iterators; + }; + + /** Fetches the value of the current pixel. + * \return an iterable proxy over the pixel made of all images. + */ + PixelListProxy Get() const { + return PixelListProxy{m_iterators}; + } + + //@} + + /**\name Mutable Iterator Interface */ + //@{ + template <typename MultiCompPixelType> + void Set(MultiCompPixelType const& p) + { + assert(p.size() == m_iterators.size()); + for (std::size_t i = 0; i!=m_iterators.size(); ++i) + { + m_iterators[i].Set(p[i]); + } + } + // PixelType & Value(); -- cannot be defined and still preserve direct access + // to memory => we don't provide it. + + // ImageType * GetImages(); + //@} + + /**\name ScanLine Iterator Interface */ + //@{ + /** Moves iterator to next line. */ + Self& NextLine() { + for (auto & it : m_iterators) + it.NextLine(); + return *this; + } + /** Moves iterator to the beginning of the current line. */ + Self& GoToBeginOfLine() { + for (auto & it : m_iterators) + it.GoToBeginOfLine(); + return *this; + } + /** Moves iterator to the end of the current line. */ + Self& GoToEndOfLine() { + for (auto & it : m_iterators) + it.GoToEndOfLine(); + return *this; + } + /** Tells whether the iterator is a the end of a line. + * \pre `!m_iterators.empty()` + */ + bool IsAtEndOfLine() const { + assert(!m_iterators.empty()); + // Const qualifier has been added to ScanLineIterator::IsAtEndOfLine in ITK + // 5.1 => Use const_cast in the mean time... + return const_cast<typename ImageIteratorList_t::value_type &>(m_iterators.front()).IsAtEndOfLine(); + } + //@} + +private: + ImageIteratorList_t m_iterators; +}; + +} // otb::internal namespace + +/** Typedef for a ZipIterator. + * ZipIterator presents a single iterator over a list of image iterators. + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \ingroup OTBCommon + * \see `otb::internals::ZipIterator<>` + */ +template <typename TImageIterator> +using ZipIterator = internals::ZipIterator<TImageIterator, internals::MutableTag>; +/** Typedef for a ZipConstIterator. + * ZipConstIterator presents a single iterator over a list of const image iterators. + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \ingroup OTBCommon + * \see `otb::internals::ZipIterator<>` + */ +template <typename TImageIterator> +using ZipConstIterator = internals::ZipIterator<TImageIterator, internals::ConstTag>; + +} // otb namespace + +#endif // otbZipIterator_h diff --git a/Modules/Core/Functor/include/otbSynthetizeFilter.h b/Modules/Core/Functor/include/otbSynthetizeFilter.h new file mode 100644 index 0000000000..e86b3780a1 --- /dev/null +++ b/Modules/Core/Functor/include/otbSynthetizeFilter.h @@ -0,0 +1,239 @@ +/* + * 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 otbSynthetizeFilter_h +#define otbSynthetizeFilter_h + +#include "otbZipIterator.h" +#include "itkImageToImageFilter.h" +#include "itkImageScanlineConstIterator.h" +#include "itkImageScanlineIterator.h" +#include "itkProgressReporter.h" + +namespace otb +{ +/** + * Filter that reduces/synthetize multiple input into a single output. + * + * This filter makes sure to avoid VectorImages. Instead it works on a + * collection of scalar images. + * \tparam TInputImage Type of the input images + * \tparam TOutputImage Type of the output image + * \tparam TFunctor Type of the functor, meant to be auto-deduced by + * `MakeSynthetizeFilter()` + * + * \author Luc Hermitte (CS Group) + * \copyright CNES + * \see for instance `otb::Wrapper::Synthetize` + */ +template <typename TInputImage, typename TOutputImage, typename TFunctor> +class SynthetizeFilter : public itk::ImageToImageFilter<TInputImage, TOutputImage> +{ +public: + + /**\name Convenient typedefs for simplifying declarations */ + //@{ + using InputImageType = TInputImage; + using OutputImageType = TOutputImage; + using FunctorType = TFunctor; + //@} + + /**\name Extract dimension from input and output images */ + //@{ + itkStaticConstMacro(InputImageDimension, unsigned int, InputImageType::ImageDimension); + itkStaticConstMacro(OutputImageDimension, unsigned int, OutputImageType::ImageDimension); + //@} + /**\name Standard class typedefs */ + //@{ + using Self = SynthetizeFilter; + using Superclass = itk::ImageToImageFilter<InputImageType, OutputImageType>; + using Pointer = itk::SmartPointer<Self>; + using ConstPointer = itk::SmartPointer<const Self>; + //@} + + /** Method for creation through the object factory. */ + static Pointer New(FunctorType functor) + { + Pointer smartPtr = new Self(std::move(functor)); + smartPtr->UnRegister(); + return smartPtr; + } + + /** Run-time type information (and related methods). */ + itkTypeMacro(SynthetizeFilter, ImageToImageFilter); + + /**\name Image typedef support */ + //@{ + using InputPixelType = typename InputImageType::PixelType; + using OutputPixelType = typename OutputImageType::PixelType; + using InputRealType = typename itk::NumericTraits<InputPixelType>::RealType; + using InputImageRegionType = typename InputImageType::RegionType; + using OutputImageRegionType = typename OutputImageType::RegionType; + using InputSizeType = typename InputImageType::SizeType; + using OutputIndexType = typename OutputImageType::IndexType; + using OutputSizeType = typename OutputImageType::SizeType; + + static_assert(InputImageDimension == OutputImageDimension, "Images have the same number of components"); + + using DataObjectPointerArraySizeType = itk::ProcessObject::DataObjectPointerArraySizeType; + //@} + + /**\name Access to N-th Input + * + * Set/Get the nth filter input with or without a specified associated + * variable name. + */ + //@{ + using Superclass::SetNthInput; + using Superclass::GetInput; + + /** Return a pointer on the nth filter input */ + InputImageType* GetNthInput(DataObjectPointerArraySizeType idx) + { + return const_cast<InputImageType*>(this->GetInput(idx)); + } + + /** Returns a vector of input images. */ + std::vector<InputImageType const*> GetInputs() const + { + std::vector<InputImageType const*> res; + auto const nbInputImages = this->GetNumberOfInputs(); + res.reserve(nbInputImages); + for (std::size_t i = 0 ; i != nbInputImages ; ++i) + res.push_back(this->GetInput(i)); + return res; + } + //@} + +protected: + /** Init constructor. */ + explicit SynthetizeFilter(FunctorType functor) + : m_functor(functor){} + ~SynthetizeFilter() = default; + + /** Overrides `GenerateOutputInformation` to check images consistency. */ + void GenerateOutputInformation() override + { + Superclass::GenerateOutputInformation(); + CheckInputImageDimensions(); + } + + // void GenerateInputRequestedRegion() override; + // +-> TODO: detect neighborhood to apply pad radius + + /** + * Main computation function called by each thread. + * \param[in] outputRegionForThread Specified output region to compute + * \param[in] threadId Id of the computing threads + */ + void ThreadedGenerateData( + OutputImageRegionType const& outputRegionForThread, + itk::ThreadIdType threadId) override + { + using ImageScanlineConstIteratorType = itk::ImageScanlineConstIterator<InputImageType const>; + using OutImageScanlineConstIteratorType = itk::ImageScanlineIterator<OutputImageType>; + using OutputIterator = itk::ImageScanlineIterator<OutputImageType>; + using InputIterator = ZipConstIterator<ImageScanlineConstIteratorType>; + + auto const regSizeY = outputRegionForThread.GetSize()[1]; + itk::ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() / regSizeY ); + + InputIterator inputIterator(this->GetInputs(), outputRegionForThread); + OutputIterator outputIterator(this->GetOutput(), outputRegionForThread); + + inputIterator.GoToBegin(); + outputIterator.GoToBegin(); + for ( + ; !inputIterator.IsAtEnd() + ; inputIterator.NextLine(), outputIterator.NextLine()) + { + assert(! outputIterator.IsAtEnd()); + // inputIterator.GoToBeginOfLine(); + // outputIterator.GoToBeginOfLine(); + for ( + ; !inputIterator.IsAtEndOfLine() + ; ++inputIterator, ++outputIterator) + { + assert(!outputIterator.IsAtEndOfLine()); + + outputIterator.Set(m_functor(inputIterator.Get())); + } + progress.CompletedPixel(); // Completed...Line() + } + } + +private: + + void CheckInputImageDimensions() + { + // Check if input image dimensions match + auto const nbInputImages = this->GetNumberOfInputs(); + auto const& inputSize = this->GetInput(0)->GetLargestPossibleRegion().GetSize(); + + for (auto p = 1U; p < nbInputImages; ++p) + { + auto const& regionSize = this->GetInput(p)->GetLargestPossibleRegion().GetSize(); + if (inputSize != regionSize) + { + itkExceptionMacro(<< "Input images must have the same dimensions.\n" + << "band #1 is [" << inputSize[0] << ";" << inputSize[1] << "]\n" + << "band #" << p + 1 << " is [" << this->GetInput(p)->GetLargestPossibleRegion().GetSize(0) << ";" + << regionSize << "]"); + } + } + } + + /** Internal functor. + * Can only be set at filter creation through call to `New`. + */ + FunctorType m_functor; + +}; + +/** + * Factory function for `SynthetizeFilter`. + * `SynthetizeFilter` objects are best made thanks to this factory function + * that'll automatically deduce the type of the functor parameter. + * + * Actually, there is no other to create a `SynthetizeFilter<>` that'll call a + * lambda on each pixel. + * \tparam TInputImage Type of the input images + * \tparam TOutputImage Type of the output image + * \tparam TFunctor Type of the functor + * \param[in] functor The functor + * + * \return a new `SynthetizeFilter` object. + * \sa `SynthetizeFilter` + */ +template <typename TInputImage, typename TOutputImage, typename TFunctor> +auto MakeSynthetizeFilter(TFunctor functor) +{ + auto filter = SynthetizeFilter<TInputImage, TOutputImage, TFunctor>::New(std::move(functor)); + return filter; +} + +} // otb namespace + +#ifndef OTB_MANUAL_INSTANTIATION +// #include "otbSynthetizeFilter.hxx" +#endif + +#endif // otbSynthetizeFilter_h diff --git a/Modules/Filtering/ImageManipulation/include/otbClampROIFilter.h b/Modules/Filtering/ImageManipulation/include/otbClampROIFilter.h new file mode 100644 index 0000000000..e62e795a2d --- /dev/null +++ b/Modules/Filtering/ImageManipulation/include/otbClampROIFilter.h @@ -0,0 +1,154 @@ +/* + * 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 otbClampROIFilter_h +#define otbClampROIFilter_h + +#include "itkImageToImageFilter.h" + +namespace otb +{ +/** + * Region clamping filter. + * This filter is a kind of ROI pass filter. Data within the ROI is kept with + * its original value. Data outside ROI is forced to 0. + * + * Also, this filter propagate the exact ROI upstream in the pipeline. This + * way, if it's piped after another filter, the upstream filter isn't executed + * on the data outside the ROI. + * + * \tparam TImage Image type. + * \sa `otb::ExtractROI<>` + * \author Luc Hermitte (CS Group) + * \copyright CNES + */ +template <typename TImage> +class ClampROIFilter : public itk::ImageToImageFilter<TImage, TImage> +{ +public: + + /**\name Convenient typedefs for simplifying declarations */ + //@{ + using InputImageType = TImage; + using OutputImageType = TImage; + //@} + + /**\name Extract dimension from input and output images */ + //@{ + itkStaticConstMacro(InputImageDimension, unsigned int, InputImageType::ImageDimension); + itkStaticConstMacro(OutputImageDimension, unsigned int, OutputImageType::ImageDimension); + //@} + /**\name Standard class typedefs */ + //@{ + using Self = ClampROIFilter; + using Superclass = itk::ImageToImageFilter<InputImageType, OutputImageType>; + using Pointer = itk::SmartPointer<Self>; + using ConstPointer = itk::SmartPointer<const Self>; + //@} + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(ClampROIFilter, unused); + + /**\name Image typedef support */ + //@{ + using InputPixelType = typename InputImageType::PixelType; + using OutputPixelType = typename OutputImageType::PixelType; + using InputRealType = typename itk::NumericTraits<InputPixelType>::RealType; + using InputImageRegionType = typename InputImageType::RegionType; + using OutputImageRegionType = typename OutputImageType::RegionType; + using InputIndexType = typename InputImageType::IndexType; + using InputSizeType = typename InputImageType::SizeType; + using OutputIndexType = typename OutputImageType::IndexType; + using OutputSizeType = typename OutputImageType::SizeType; + + static_assert(InputImageDimension == OutputImageDimension, "Images have the same number of components"); + //@} + + /** Column threshold setter. */ + void SetThresholdX(long threshold) noexcept + { m_thresholdX = threshold; } + /** Column threshold getter. */ + long GetThresholdX() const noexcept + { return m_thresholdX;} + + /** Top line threshold setter. */ + void SetThresholdYtop(long threshold) noexcept + { m_thresholdYtop = threshold; } + /** Top line threshold getter. */ + long GetThresholdYtop() const noexcept + { return m_thresholdYtop;} + + /** Bottom line threshold setter. */ + void SetThresholdYbot(long threshold) noexcept + { m_thresholdYbot = threshold; } + /** Bottom line threshold getter. */ + long GetThresholdYbot() const noexcept + { return m_thresholdYbot;} + +protected: + /// Hidden constructor + ClampROIFilter() = default; + + InputImageType * GetInputImage() { return const_cast<InputImageType*>(this->GetInput()); } + InputImageType const* GetInputImage() const { return this->GetInput(); } + + /** otbClampROIFilter doesn't need an input requested region as large as the + * output requested region. + * \sa ImageToImageFilter::GenerateInputRequestedRegion() + */ + void CallCopyOutputRegionToInputRegion( + InputImageRegionType & destRegion, + OutputImageRegionType const& srcRegion) override + { + destRegion = OutputRegionToInputRegion(srcRegion); + } + + /** + * Functional implementation of `CallCopyOutputRegionToInputRegion()`. + */ + InputImageRegionType OutputRegionToInputRegion( + OutputImageRegionType const& srcRegion); + + /** + * Main computation function called by each thread. + * \param[in] outputRegionForThread Specified output region to compute + * \param[in] threadId Id of the computing threads + */ + void ThreadedGenerateData( + OutputImageRegionType const& outputRegionForThread, + itk::ThreadIdType threadId) override; + +private: + long m_thresholdX = 0; + long m_thresholdYtop = 0; + long m_thresholdYbot = 0; +}; + +} // otb namespace + +#ifndef OTB_MANUAL_INSTANTIATION +#include "otbClampROIFilter.hxx" +#endif + +#endif // otbClampROIFilter_h diff --git a/Modules/Filtering/ImageManipulation/include/otbClampROIFilter.hxx b/Modules/Filtering/ImageManipulation/include/otbClampROIFilter.hxx new file mode 100644 index 0000000000..fa40583c8c --- /dev/null +++ b/Modules/Filtering/ImageManipulation/include/otbClampROIFilter.hxx @@ -0,0 +1,216 @@ +/* + * 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 otbClampROIFilter_hxx +#define otbClampROIFilter_hxx + +#include "otbClampROIFilter.h" +#include "otbInterval.h" +#include "otbMacro.h" +#include "otbLogHelpers.h" +#include "itkImageScanlineConstIterator.h" +#include "itkImageScanlineIterator.h" +#include "itkProgressReporter.h" +#include <boost/numeric/interval.hpp> +#include <algorithm> +#include <cassert> +#include <ostream> + +template <typename T, typename P> +inline +std::ostream & operator<<(std::ostream & os, boost::numeric::interval<T,P> const& v) +{ + return os << '[' << v.lower() << ".." << v.upper() << '['; +} + +namespace otb +{ + +template<typename TImage> +void +ClampROIFilter<TImage> +::ThreadedGenerateData( + OutputImageRegionType const& outputRegionForThread, + itk::ThreadIdType threadId) +{ + // otbMsgDevMacro("ThreadedGenerateData begin("<<NeatRegionLogger{outputRegionForThread}<<")"); + using InputIterator = itk::ImageScanlineConstIterator<InputImageType const>; + using OutputIterator = itk::ImageScanlineIterator<OutputImageType>; + + auto const* input = this->GetInput(); + auto * output = this->GetOutput(); + assert(input); + assert(output); + InputIterator inputIterator (input, OutputRegionToInputRegion(outputRegionForThread)); + OutputIterator outputIterator(output, outputRegionForThread); + + auto const& imgRegion = output->GetLargestPossibleRegion(); + auto const imgSizeX = imgRegion.GetSize()[0]; + auto const imgSizeY = imgRegion.GetSize()[1]; + itk::IndexValueType const imgEndX = imgRegion.GetIndex()[0] + imgSizeX; + itk::IndexValueType const imgEndY = imgRegion.GetIndex()[1] + imgSizeY; + + auto const& size = outputRegionForThread.GetSize(); + auto const& index = outputRegionForThread.GetIndex(); + auto const sizeX = size[0]; + auto const sizeY = size[1]; + auto const startX = index[0]; + auto const startY = index[1]; + itk::IndexValueType const endX = startX + sizeX; + itk::IndexValueType const endY = startY + sizeY; + auto const thrX1 = std::min(endX, m_thresholdX); + auto const thrX2 = std::min(endX, imgEndX - m_thresholdX); + auto const thrY1 = std::min(endY, m_thresholdYtop); + auto const thrY2 = std::min(endY, imgEndY - m_thresholdYbot); + + assert(thrX1 <= endX && "Iterations shall stay within requested region"); + assert(thrX2 <= endX && "Iterations shall stay within requested region"); + assert(thrY1 <= endY && "Iterations shall stay within requested region"); + assert(thrY2 <= endY && "Iterations shall stay within requested region"); + + // using interval_t = boost::numeric::interval<long>; + using interval_t = Interval; + auto const region = interval_t{startX, endX}; + auto const zero_left = intersect(interval_t{startX, thrX1}, region); + auto const copy_middle = intersect(interval_t{thrX1, thrX2}, region); + auto const zero_right = intersect(interval_t{thrX2, endX}, region); + otbMsgDevMacro("X in " << zero_left << " <<-- 0"); + otbMsgDevMacro("X in " << copy_middle << " <<-- copy input"); + otbMsgDevMacro("X in " << zero_right << " <<-- 0"); + otbMsgDevMacro("Y in ["<<startY<<".."<<thrY1<<"[ <<--- 0"); + + auto const nb_z_l = zero_left.upper() - zero_left.lower(); + auto const nb_c_m = copy_middle.upper() - copy_middle.lower(); + auto const nb_z_r = zero_right.upper() - zero_right.lower(); + assert(nb_z_l + nb_c_m + nb_z_r == sizeX); + + itk::ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() / sizeY ); + outputIterator.GoToBegin(); + + // TODO: Can we consider that lines are contiguous in memory? + // If so, we could have only 2 fill_n and one copy_n! + auto y = startY; + for ( + ; y < thrY1 + ; ++y, outputIterator.NextLine()) + { + // If there is any trimming of first lines, the inputIterator iterator will + // directly point to the right region. we shall not increment it! + // otbMsgDevMacro("o(" << y << ") <-- 0"); + assert(! outputIterator.IsAtEnd()); + outputIterator.GoToBeginOfLine(); + std::fill_n(&outputIterator.Value(), sizeX, OutputPixelType{}); + progress.CompletedPixel(); // Completed...Line() + } + assert(y == thrY1 || y == startY); + otbMsgDevMacro("Y in ["<<thrY1<<".."<<thrY2<<"[ <<--- Input"); + inputIterator.GoToBegin(); + for ( + ; y < thrY2 + ; ++y, inputIterator.NextLine(), outputIterator.NextLine()) + { + // otbMsgDevMacro("o(" << y << ") <-- Input"); + assert(! inputIterator.IsAtEnd()); + assert(! outputIterator.IsAtEnd()); + inputIterator.GoToBeginOfLine(); + outputIterator.GoToBeginOfLine(); + auto const t1 = std::fill_n(&outputIterator.Value(), nb_z_l, OutputPixelType{}); + // If there is any trimming of first columns, the inputIterator iterator + // will directly point to the right region. we shall not apply an offset! + auto const t2 = std::copy_n(&inputIterator.Value(), nb_c_m, t1); + std::fill_n(t2, nb_z_r, OutputPixelType{}); + progress.CompletedPixel(); // Completed...Line() + } + assert(y == thrY2 || y == startY); + otbMsgDevMacro("Y in ["<<thrY2<<".."<<endY<<"[ <<--- 0"); + for ( + ; y < endY + ; ++y, outputIterator.NextLine()) + { + // If there is any trimming of last lines, the inputIterator iterator will + // directly point to the right region. we shall not increment it! + // otbMsgDevMacro("o(" << y << ") <-- 0"); + assert(! outputIterator.IsAtEnd()); + outputIterator.GoToBeginOfLine(); + std::fill_n(&outputIterator.Value(), sizeX, OutputPixelType{}); + progress.CompletedPixel(); // Completed...Line() + } + assert(y == endY); + otbMsgDevMacro("ThreadedGenerateData end"); +} + +template<typename TImage> +typename ClampROIFilter<TImage>::InputImageRegionType +ClampROIFilter<TImage> +::OutputRegionToInputRegion(OutputImageRegionType const& srcRegion) +{ + auto const* output = this->GetOutput(); + assert(output); + + auto const& maxRegion = output->GetLargestPossibleRegion(); + auto const& maxSize = maxRegion.GetSize(); + auto const& maxStart = maxRegion.GetIndex(); + + auto const& reqRegion = srcRegion; + auto const& reqSize = reqRegion.GetSize(); + auto const& reqStart = reqRegion.GetIndex(); + + // using interval_t = boost::numeric::interval<long>; + using interval_t = Interval; + auto const maxRegionX = interval_t{ + maxStart[0]+m_thresholdX, + static_cast<itk::IndexValueType>(maxStart[0]+maxSize[0]-m_thresholdX) + }; + auto const maxRegionY = interval_t{ + maxStart[1]+m_thresholdYtop, + static_cast<itk::IndexValueType>(maxStart[1]+maxSize[1]-m_thresholdYbot) + }; + + auto const reqRegionX = interval_t::OfLength(reqStart[0], reqSize[0]); + auto const reqRegionY = interval_t::OfLength(reqStart[1], reqSize[1]); +#if 0 + otbMsgDevMacro("OutputRegionToInputRegion: " + << "out="<< NeatRegionLogger{reqRegion} + << "; max: x="<<maxRegionX << " y="<<maxRegionY + << "; req: x="<<reqRegionX << " y="<<reqRegionY + ); +#endif + + auto const inRegionX = intersect(reqRegionX, maxRegionX); + auto const inRegionY = intersect(reqRegionY, maxRegionY); + // otbMsgDevMacro(" --> ∩X: " << inRegionX << " ∩Y: " << inRegionY); + + const InputIndexType inStart{inRegionX.lower(), inRegionY.lower()}; + assert(inRegionX.lower() <= inRegionX.upper()); + assert(inRegionY.lower() <= inRegionY.upper()); + const InputSizeType inSize{ + static_cast<unsigned long>(inRegionX.upper()-inRegionX.lower()), + static_cast<unsigned long>(inRegionY.upper()-inRegionY.lower()) + }; + auto const inRegion = InputImageRegionType{inStart, inSize}; + otbMsgDevMacro("OutputRegionToInputRegion: out="<< NeatRegionLogger{reqRegion}<<" --> in="<<NeatRegionLogger{inRegion}); + return inRegion; +} + +} // otb namespace + +#endif // otbClampROIFilter_hxx + diff --git a/Modules/Remote/S1TilingSupportApplications.remote.cmake b/Modules/Remote/S1TilingSupportApplications.remote.cmake index 402f984e7b..7bafd6d7c2 100644 --- a/Modules/Remote/S1TilingSupportApplications.remote.cmake +++ b/Modules/Remote/S1TilingSupportApplications.remote.cmake @@ -27,5 +27,5 @@ https://gitlab.orfeo-toolbox.org/s1-tiling/s1tilingsupportapplications " GIT_REPOSITORY https://gitlab.orfeo-toolbox.org/s1-tiling/s1tilingsupportapplications # Commit on master branch - GIT_TAG master + GIT_TAG 2.0.0 ) -- GitLab