Commit 807114a7 authored by Julien Osman's avatar Julien Osman
Browse files

Merge branch 'RemoveAllocOnImageCasting' into 'develop'

PERF: Remove allocation on image casting

See merge request !797
parents bc0cb8dd 80bd885e
Pipeline #7960 passed with stages
in 10 minutes and 33 seconds
/*
* Copyright (C) 2005-2021 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 otbAlgoClamp_h
#define otbAlgoClamp_h
namespace otb
{
/**
* Backport of C++17 `std::clamp` to C++14.
* Clamps the value `v` to [`lo`, `hi`] interval.
* @throw None
*/
template <typename T>
constexpr
T const& clamp(T const& v, T const& lo, T const& hi) noexcept
{
assert( lo <= hi );
return (v < lo) ? lo
: (hi < v) ? hi
: v;
}
} // otb namespace
#endif // otbAlgoClamp_h
/*
* Copyright (C) 2005-2021 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 otbArrayTraits_h
#define otbArrayTraits_h
#include "itkDefaultConvertPixelTraits.h"
#include "itkVariableLengthVector.h"
#include "itkFixedArray.h"
namespace otb
{
namespace mpl
{
/*===============================[ otb::is_array<> ]=========================*/
namespace internals
{
/** SFINAE workaround to match types that inherit from `itk::FixedArray`.
* As we cannot use `std::is_base_of<RGBAPixel<T>, itk::FixedArray<T, N>`
* when trying to see if `RGBAPixel` inherits from `itk::FixedArray`
* without knowing the `N` parameter beforehand, here is this
* workaround.
*
* We take the parameter by pointer to avoid possible convertions.
* \ingroup OTBCommonInternals
*/
template <typename T, unsigned int N>
constexpr std::true_type is_array_f(itk::FixedArray<T, N> const*);
/** SFINAE part that says: "ignore anything else".
* \ingroup OTBCommonInternals
*/
template <typename... T>
constexpr std::false_type is_array_f(...);
static_assert(decltype(internals::is_array_f(std::declval<itk::FixedArray<int, 1>*>()))::value, "it::FixedArray should be matched");
} // internals namespace
/**
* Type traits to identify any array pixel types.
* \tparam T Type to check
*
* This generic flavour recognizes types that inherit from
* `itk::FixedArray`, and says "no" to any thing else.
*
* At this moment, there is no specialization for `std::vector` nor types
* that inherit from `vnl_vector`.
*
* \ingroup OTBCommon
* \sa `is_array_v<>`
*
* \internal it could be extended/specialized either through
* `internals::is_array_f()` or by specializing this type (when
* possible).
*/
template <typename T>
struct is_array
: decltype(internals::is_array_f(std::declval<T*>()))
{};
/** `is_array<>` specialization for recognizing `itk::VariableLengthVector<>`.
* \ingroup OTBCommon
*/
template <typename T>
struct is_array<itk::VariableLengthVector<T>>
: std::true_type
{};
/** `is_array<>` specialization for recognizing expression templates
* based on `itk::VariableLengthVector<>`.
* \ingroup OTBCommon
*/
template <typename TExpr1, typename TExpr2, typename TBinaryOp>
struct is_array<itk::VariableLengthVectorExpression<TExpr1, TExpr2,TBinaryOp>>
: std::true_type
{};
/**
* Helper variable to identify any array pixel types.
* \tparam T Type to check
*
* \return `true` for types that inherit from `itk::FixedArray`
* \return `true` for `itk::VariableLengthVector`, and expression
* templates made of VLV.
* \return `false` otherwise
*
* At this moment, there is no specialization for `std::vector` nor types
* that inherit from `vnl_vector`.
*
* \ingroup OTBCommon
* \sa `is_array<>`
*/
template <typename T>
constexpr bool is_array_v = is_array<T>::value;
// static_assert(decltype(internals::is_array_f(std::declval<itk::RGBAPixel<int>*>()))::value, "");
// static_assert(is_array_v<itk::RGBAPixel<double>>, "");
static_assert(is_array_v<itk::FixedArray<double, 42>>, "");
static_assert(is_array_v<itk::FixedArray<std::complex<double>, 42>>, "");
static_assert(is_array_v<itk::VariableLengthVector<double>>, "");
static_assert(is_array_v<itk::VariableLengthVector<std::complex<double>>>, "");
/*===============================[ GetNumberOfComponents ]===================*/
namespace internals
{
/**
* Traits for accessing the number of components associated to a pixel.
* \tparam PixelType automatically deduced.
*
* \internal While there is `itk::DefaultConvertPixelTraits` and
* `otb::DefaultConvertPixelTraits`, they don't handle correctly
* - `std::complex<>`, as there is no
* `itk::DefaultConvertPixelTraits<complex<>>::GetNumberOfComponents(pix)`
* - nor `itk::VariableLengthVector<>`, as it copies the VLV, which is
* an expensive operation.
*
* \sa `otb::mpl::GetNumberOfComponents()`
* \ingroup OTBCommonInternals
* \todo it should have been `constexpr` and `noexcept`, but the ITK
* flavours we rely upon aren't...
*/
template <typename PixelType>
struct NumberOfComponents
{
static unsigned int For(PixelType const& pix)
{
return itk::DefaultConvertPixelTraits<std::remove_const_t<PixelType>>::GetNumberOfComponents(pix);
}
};
/** Specialization for `itk::VariableLengthVector<>`
* \ingroup OTBCommonInternals
*/
template <typename RealType>
struct NumberOfComponents<itk::VariableLengthVector<RealType>>
{
// ITK definition of
// DefaultConvertPixelTraits<VLV>::GetNumberOfComponents() taks the
// parameter by value, and thus create a new VLV by copy.
// This is too expensive! Let's work around it.
static unsigned int For(itk::VariableLengthVector<RealType> const& pix)
{
return pix.GetSize();
}
};
/** Specialization for `std::complex<>`
* \ingroup OTBCommonInternals
*/
template <typename T>
struct NumberOfComponents<std::complex<T>>
{
static constexpr unsigned int For(std::complex<T> const&) noexcept
{
return 2;
}
};
/** Specialization for `const` types.
* This specialization automatically removes the `const` qualifier.
* \ingroup OTBCommonInternals
*/
template <typename T>
struct NumberOfComponents<T const>
{
static unsigned int For(T const& pix)
{
return NumberOfComponents<T>::For(pix);
}
};
} // internals namespace
/**
* Returns the number of components in the pixel parameter.
* While `itk::DefaultConvertPixelTraits` and
* `otb::DefaultConvertPixelTraits` already exist, they don't handle
* correctly:
* - `std::complex<>`, as there is no
* `itk::DefaultConvertPixelTraits<complex<>>::GetNumberOfComponents(pix)`
* - nor `itk::VariableLengthVector<>`, as it copies the VLV, which is
* an expensive operation.
*
* \tparam PixelType automatically deduced pixel type
* \param[in] pix pixel parameter
* \return The number of components in the pixel.
* \throw None
*/
template <typename PixelType>
inline
unsigned int GetNumberOfComponents(PixelType const& pix)
{
return internals::NumberOfComponents<PixelType>::For(pix);
}
} // otb::mpl namespace
} // otb namespace
#endif // otbArrayTraits_h
/*
* Copyright (C) 2005-2021 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 otbIteratorHelpers_h
#define otbIteratorHelpers_h
namespace otb
{
namespace internals
{
struct ConstTag {};
struct MutableTag {};
}
} // otb::internals namespaces
#endif // otbIteratorHelpers_h
/*
* Copyright (C) 2005-2021 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 otbNotNull_h
#define otbNotNull_h
#include <cstddef>
#include <cassert>
namespace otb
{
/**
* Simplified version of `GSL::not_null`.
*
* Permits to holds pointers that shall never be null.
*
* `GSL::not_null` is under MIT licence
*/
template <typename T>
class NotNull
{
public:
constexpr NotNull(T p) : m_p(p) { assert(p); }
NotNull (std::nullptr_t) = delete;
NotNull& operator=(std::nullptr_t) = delete;
constexpr T get() const
{
assert(m_p);
return m_p;
}
constexpr operator T() const { return get(); }
constexpr T operator->() const { return get(); }
constexpr decltype(auto) operator*() const { return *get(); }
// unwanted operators...pointers only point to single objects!
NotNull& operator++() = delete;
NotNull& operator--() = delete;
NotNull operator++(int) = delete;
NotNull operator--(int) = delete;
NotNull& operator+=(std::ptrdiff_t) = delete;
NotNull& operator-=(std::ptrdiff_t) = delete;
void operator[](std::ptrdiff_t) const = delete;
private:
T m_p;
};
} // otb namespace
#endif // otbNotNull_h
......@@ -21,6 +21,7 @@
#ifndef otbZipIterator_h
#define otbZipIterator_h
#include "otbIteratorHelpers.h"
#include "otbSpan.h"
#include "itkMacro.h"
#include <type_traits>
......@@ -32,9 +33,6 @@ namespace otb
namespace internals
{
struct ConstTag {};
struct MutableTag {};
/**
* Wrapper to present list of iterators as a single iterator.
*
......
This diff is collapsed.
......@@ -775,3 +775,16 @@ otb_add_test(NAME ioTvOtbVectorImageTestRadarsat COMMAND otbImageBaseTestDriver
otbVectorImageLegacyTest
LARGEINPUT{/RADARSAT1/GOMA/SCENE01/}
${TEMP}/ioOtbVectorImageTestRadarsat.txt)
if (Boost_UNIT_TEST_FRAMEWORK_FOUND)
# Unit tests
add_executable(otbPixelComponentIteratorTests otbPixelComponentIteratorTest.cxx)
target_link_libraries(
otbPixelComponentIteratorTests
${OTBImageBase-Test_LIBRARIES}
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} )
add_test(NAME coTuPixelComponentIterator
COMMAND otbPixelComponentIteratorTests
WORKING_DIRECTORY ${TEMP})
endif()
This diff is collapsed.
......@@ -46,37 +46,34 @@ namespace otb
*/
template <class TInputImage, class TOutputImage = TInputImage>
class ITK_EXPORT ClampImageFilter
: public itk::UnaryFunctorImageFilter<TInputImage, TOutputImage,
Functor::ConvertTypeFunctor<typename TInputImage::PixelType, typename TOutputImage::PixelType>>
: public itk::ImageToImageFilter<TInputImage, TOutputImage>
{
public:
/** Standard class typedefs. */
typedef ClampImageFilter Self;
typedef itk::UnaryFunctorImageFilter<TInputImage, TOutputImage,
Functor::ConvertTypeFunctor<typename TInputImage::PixelType, typename TOutputImage::PixelType>>
Superclass;
typedef itk::SmartPointer<Self> Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;
using Self = ClampImageFilter;
using Superclass = itk::ImageToImageFilter<TInputImage, TOutputImage>;
using FunctorType = Functor::ConvertTypeFunctor<typename TInputImage::PixelType, typename TOutputImage::PixelType>;
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(ClampImageFilter, itk::UnaryFunctorImageFilter);
itkTypeMacro(ClampImageFilter, OSEF);
/** Some additional typedefs. */
typedef TInputImage InputImageType;
typedef typename InputImageType::RegionType InputImageRegionType;
typedef typename InputImageType::PixelType InputImagePixelType;
using InputImageType = TInputImage;
using InputImageRegionType = typename InputImageType::RegionType;
using InputImagePixelType = typename InputImageType::PixelType;
/** Some additional typedefs. */
typedef TOutputImage OutputImageType;
typedef typename OutputImageType::RegionType OutputImageRegionType;
typedef typename OutputImageType::PixelType OutputImagePixelType;
typedef typename OutputImageType::InternalPixelType OutputInternalPixelType;
typedef typename itk::NumericTraits<OutputInternalPixelType>::ValueType OutputPixelValueType;
using OutputImageType = TOutputImage;
using OutputImageRegionType = typename OutputImageType::RegionType;
using OutputImagePixelType = typename OutputImageType::PixelType;
using OutputInternalPixelType = typename OutputImageType::InternalPixelType;
using OutputPixelValueType = typename itk::NumericTraits<OutputInternalPixelType>::ValueType;
/** The values greater than or equal to the value are set to \p thresh. */
void ClampAbove(const OutputPixelValueType& thresh);
......@@ -100,7 +97,7 @@ public:
protected:
ClampImageFilter();
~ClampImageFilter() override{};
~ClampImageFilter() override = default;
void PrintSelf(std::ostream& os, itk::Indent indent) const override;
void GenerateOutputInformation(void) override
......@@ -111,12 +108,18 @@ protected:
this->GetOutput()->SetNumberOfComponentsPerPixel(this->GetFunctor().GetOutputSize());
}
void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType threadId) override;
FunctorType & GetFunctor() noexcept { return m_Functor; }
FunctorType const& GetFunctor() const noexcept { return m_Functor; }
private:
ClampImageFilter(const Self&) = delete;
void operator=(const Self&) = delete;
OutputPixelValueType m_Lower;
OutputPixelValueType m_Upper;
FunctorType m_Functor;
};
......
......@@ -23,11 +23,13 @@
#define otbClampImageFilter_hxx
#include "otbClampImageFilter.h"
#include "itkImageScanlineIterator.h"
#include "itkImageScanlineConstIterator.h"
#include "itkImageRegionIterator.h"
#include "itkNumericTraits.h"
#include <limits>
#include "itkObjectFactory.h"
#include "itkProgressReporter.h"
#include <limits>
namespace otb
{
......@@ -131,6 +133,46 @@ void ClampImageFilter<TInputImage, TOutputImage>::ClampOutside(const OutputPixel
}
}
} // end namespace itk
template <class TInputImage, class TOutputImage>
void
ClampImageFilter<TInputImage, TOutputImage>
::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType threadId)
{
const auto& regionSize = outputRegionForThread.GetSize();
if (regionSize[0] == 0)
{
return;
}
const auto numberOfLinesToProcess = outputRegionForThread.GetNumberOfPixels() / regionSize[0];
itk::ProgressReporter p(this, threadId, numberOfLinesToProcess);
// Build output iterator
itk::ImageScanlineConstIterator<InputImageType> inIt(this->GetInput(), outputRegionForThread);
itk::ImageScanlineIterator<OutputImageType> outIt(this->GetOutput(), outputRegionForThread);
// Build a default value
typename OutputImageType::PixelType outputValueHolder;
// Luc: I'm not sure this will be the correct size given the convoluted
// size computations done in the ConvertTypeFunctor
itk::NumericTraits<typename OutputImageType::PixelType>::SetLength(outputValueHolder, this->GetOutput()->GetNumberOfComponentsPerPixel());
while (!outIt.IsAtEnd())
{
// MoveIterartors will ++ all iterators in the tuple
for (; !outIt.IsAtEndOfLine(); ++outIt, ++inIt)
{
// This will call the operator with inputIterators Get() results
// and fill outputValueHolder with the result.
m_Functor(outputValueHolder, inIt.Get());
outIt.Set(outputValueHolder);
}
outIt.NextLine();
inIt.NextLine();
p.CompletedPixel(); // may throw
}
}
} // end namespace otb
#endif
/*
* Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
* Copyright (C) 2005-2021 Centre National d'Etudes Spatiales (CNES)
*
* This file is part of Orfeo Toolbox
*
......@@ -21,13 +21,13 @@
#ifndef otbConvertTypeFunctor_h
#define otbConvertTypeFunctor_h
#include <limits>
#include <type_traits>
#include <boost/type_traits/is_complex.hpp>
#include <boost/type_traits/is_scalar.hpp>
#include "otbPixelComponentIterator.h"
#include "otbAlgoClamp.h"
#include "itkNumericTraits.h"
#include "otbDefaultConvertPixelTraits.h"
#include <boost/type_traits/is_complex.hpp>
#include <limits>
#include <type_traits>
namespace otb
{
......@@ -38,38 +38,24 @@ template <class TInputPixelType, class TOutputPixelType>
class ConvertTypeFunctor
{
public:
typedef TInputPixelType InputPixelType;
typedef TOutputPixelType OutputPixelType;
typedef ConvertTypeFunctor Self;
using InputPixelType = TInputPixelType;
using OutputPixelType = TOutputPixelType;
using Self = ConvertTypeFunctor;
typedef typename itk::NumericTraits<InputPixelType>::ValueType InputInternalPixelType;
typedef typename itk::NumericTraits<OutputPixelType>::ValueType OutputInternalPixelType;
using InputInternalPixelType = typename itk::NumericTraits<InputPixelType>::ValueType;
using OutputInternalPixelType = typename itk::NumericTraits<OutputPixelType>::ValueType;
typedef typename itk::NumericTraits<InputInternalPixelType>::ValueType InputPixelValueType;
typedef typename itk::NumericTraits<OutputInternalPixelType>::ValueType OutputPixelValueType;
using InputPixelValueType = typename itk::NumericTraits<InputInternalPixelType>::ValueType;
using OutputPixelValueType = typename itk::NumericTraits<OutputInternalPixelType>::ValueType;
using ThresholdPixelValueType = std::common_type_t<InputPixelValueType, OutputPixelValueType>;
static constexpr bool m_cInPix = boost::is_complex<InputPixelType>::value;
static constexpr bool m_cOutPix = boost::is_complex<OutputPixelType>::value;
<