Skip to content

Commit

Permalink
Add image algorithms with position indication.
Browse files Browse the repository at this point in the history
Add functions for_each_pixel_with_position,
transform_pixels_with_position, transform_pixels_with_position_expr.
  • Loading branch information
kmhofmann committed Mar 7, 2019
1 parent 1662c8a commit ed42b33
Show file tree
Hide file tree
Showing 8 changed files with 314 additions and 37 deletions.
1 change: 1 addition & 0 deletions selene/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ target_sources(selene_img_ops PRIVATE
${CMAKE_CURRENT_LIST_DIR}/img_ops/_impl/ImageConversionAlphaExpr.hpp
${CMAKE_CURRENT_LIST_DIR}/img_ops/_impl/ImageConversionExpr.hpp
${CMAKE_CURRENT_LIST_DIR}/img_ops/_impl/TransformExpr.hpp
${CMAKE_CURRENT_LIST_DIR}/img_ops/_impl/TransformWithPositionExpr.hpp
${CMAKE_CURRENT_LIST_DIR}/img_ops/_impl/TransposeExpr.hpp
)

Expand Down
192 changes: 166 additions & 26 deletions selene/img_ops/Algorithms.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,83 +12,223 @@

#include <selene/img_ops/Allocate.hpp>
#include <selene/img_ops/_impl/TransformExpr.hpp>
#include <selene/img_ops/_impl/TransformWithPositionExpr.hpp>

#include <functional>
#include <type_traits>

namespace sln {

/// \addtogroup group-img-ops
/// @{

/** \brief Applies a unary function to each pixel value of an image.
/** \brief Applies a function to each pixel element of an image.
*
* Each pixel element in the image is overwritten with the result of the function application.
*
* The supplied function receives a reference to the respective pixel element as first (and only) parameter.
* Its return type, if non-void, will be ignored.
*
* @tparam DerivedSrc The typed image type.
* @tparam UnaryFunction The unary function type.
* @tparam Function The function type.
* @param[in,out] img The image to apply the function on.
* @param f The unary function to apply to each pixel. Its signature should be `void f(PixelType&)`.
* @return `std::move(f)`.
* @param func The function to apply to each pixel element in-place.
* Its signature should be `void f(PixelType&)`, or any compatible callable.
* @return `std::move(func)`.
*/
template <typename DerivedSrc, typename UnaryFunction>
UnaryFunction for_each_pixel(ImageBase<DerivedSrc>& img, UnaryFunction f)
template <typename DerivedSrc, typename Function>
Function for_each_pixel(ImageBase<DerivedSrc>& img, Function func)
{
using PixelType = typename ImageBase<DerivedSrc>::PixelType;
static_assert(std::is_invocable_v<Function, PixelType&>,
"Callable supplied to for_each_pixel must be of (or convertible to) type 'void(PixelType&)'.");

for (auto y = 0_idx; y < img.height(); ++y)
{
for (auto ptr = img.data(y), end = img.data_row_end(y); ptr != end; ++ptr)
{
f(*ptr);
std::invoke(func, *ptr);
}
}

return std::move(f);
return std::move(func);
}

/** \brief Transforms one image into another by applying a unary operation to each pixel value.
/** \brief Applies a function to each pixel element of an image.
*
* `allocate` is called on the destination image prior to performing the operation.
* Each pixel element in the image is overwritten with the result of the function application.
*
* The supplied function receives a reference to the respective pixel element as first parameter, followed by both the
* x and y pixel coordinates for the respective invocation.
* Its return type, if non-void, will be ignored.
*
* @tparam DerivedSrc The typed image type.
* @tparam Function The function type.
* @param[in,out] img The image to apply the function on.
* @param func The function to apply to each pixel element in-place.
* Its signature should be `void f(PixelType&, PixelIndex, PixelIndex)`, or any compatible callable.
* @return `std::move(func)`.
*/
template <typename DerivedSrc, typename Function>
Function for_each_pixel_with_position(ImageBase<DerivedSrc>& img, Function func)
{
using PixelType = typename ImageBase<DerivedSrc>::PixelType;
static_assert(std::is_invocable_v<Function, PixelType&, PixelIndex, PixelIndex>,
"Callable supplied to for_each_pixel_with_position must be of (or convertible to) type 'void(PixelType&, PixelIndex, PixelIndex)'.");

for (auto y = 0_idx; y < img.height(); ++y)
{
auto x = 0_idx;
for (auto ptr = img.data(y), end = img.data_row_end(y); ptr != end; ++ptr, ++x)
{
std::invoke(func, *ptr, x, y);
}
}

return std::move(func);
}

/** \brief Transforms one image into another by applying a function to each pixel element.
*
* The supplied function receives a constant reference (or value) to the respective pixel element as first (and only)
* parameter.
* Its return type shall be of the type of a pixel element of the destination image.
*
* `allocate` is called on the destination image prior to performing the operation; i.e. it may be that a memory
* allocation will take place.
*
* @tparam DerivedDst The typed destination image type.
* @tparam DerivedSrc The typed source image type.
* @tparam UnaryOperation The unary operation type.
* @tparam Function The function type.
* @param img_src The source image.
* @param[out] img_dst The destination image.
* @param op The unary operation. Its signature should be `PixelTypeDst f(const PixelTypeSrc&)` or `PixelTypeDst
* f(PixelTypeSrc)`.
* @param func The function to apply to each pixel element.
* Its signature should be `PixelTypeDst f(const PixelTypeSrc&)` or `PixelTypeDst f(PixelTypeSrc)`.
*/
template <typename DerivedDst, typename DerivedSrc, typename UnaryOperation>
void transform_pixels(const ImageBase<DerivedSrc>& img_src, ImageBase<DerivedDst>& img_dst, UnaryOperation op)
template <typename DerivedDst, typename DerivedSrc, typename Function>
void transform_pixels(const ImageBase<DerivedSrc>& img_src, ImageBase<DerivedDst>& img_dst, Function func)
{
using PixelTypeSrc = typename ImageBase<DerivedSrc>::PixelType;
using PixelTypeDst = typename ImageBase<DerivedDst>::PixelType;
static_assert(std::is_invocable_r_v<PixelTypeDst, Function, PixelTypeSrc&>,
"Callable supplied to transform_pixels must be of (or convertible to) type 'PixelTypeDst f(const PixelTypeSrc&)'.");

allocate(img_dst, img_src.layout());

for (auto y = 0_idx; y < img_dst.height(); ++y)
{
auto ptr_src = img_src.data(y);
for (auto ptr_dst = img_dst.data(y), ptr_dst_end = img_dst.data_row_end(y); ptr_dst != ptr_dst_end;)
{
*ptr_dst++ = op(*ptr_src++);
*ptr_dst++ = std::invoke(func, *ptr_src++);
}
}
}

/** \brief Transforms one image into another by applying a unary operation to each pixel value.
/** \brief Transforms one image into another by applying a function to each pixel element.
*
* The supplied function receives a constant reference (or value) to the respective pixel element as first (and only)
* parameter.
* Its return type shall be of the type of a pixel element of the destination image, as specified by the user via
* the first template parameter.
*
* @tparam PixelTypeDst The pixel type of the destination image.
* @tparam DerivedSrc The typed source image type.
* @tparam UnaryOperation The unary operation type.
* @tparam Function The function type.
* @param img_src The source image.
* @param op The unary operation. Its signature should be `PixelTypeDst f(const PixelTypeSrc&)` or `PixelTypeDst
* f(PixelTypeSrc)`.
* @param func The function to apply to each pixel element.
* Its signature should be `PixelTypeDst f(const PixelTypeSrc&)` or `PixelTypeDst f(PixelTypeSrc)`.
* @return The destination image.
*/
template <typename PixelTypeDst, typename DerivedSrc, typename UnaryOperation>
Image<PixelTypeDst> transform_pixels(const ImageBase<DerivedSrc>& img, UnaryOperation op)
template <typename PixelTypeDst, typename DerivedSrc, typename Function>
Image<PixelTypeDst> transform_pixels(const ImageBase<DerivedSrc>& img, Function func)
{
Image<PixelTypeDst> img_dst({img.width(), img.height()});
transform_pixels(img, img_dst, op);
transform_pixels(img, img_dst, func);
return img_dst;
}

template <typename DerivedSrc, typename UnaryFunction>
auto transform_pixels_expr(const ImageExpr<DerivedSrc>& img, UnaryFunction func)
/** \brief Transforms one image into another by applying a function to each pixel element.
*
* The supplied function receives a constant reference (or value) to the respective pixel element as first parameter,
* followed by both the x and y pixel coordinates for the respective invocation.
* Its return type shall be of the type of a pixel element of the destination image.
*
* `allocate` is called on the destination image prior to performing the operation; i.e. it may be that a memory
* allocation will take place.
*
* @tparam DerivedDst The typed destination image type.
* @tparam DerivedSrc The typed source image type.
* @tparam Function The function type.
* @param img_src The source image.
* @param[out] img_dst The destination image.
* @param func The function to apply to each pixel element.
* Its signature should be `PixelTypeDst f(const PixelTypeSrc&)` or `PixelTypeDst f(PixelTypeSrc)`.
*/
template <typename DerivedDst, typename DerivedSrc, typename Function>
void transform_pixels_with_position(const ImageBase<DerivedSrc>& img_src, ImageBase<DerivedDst>& img_dst, Function func)
{
return impl::TransformExpr<ImageExpr<DerivedSrc>, UnaryFunction>(img, func);
using PixelTypeSrc = typename ImageBase<DerivedSrc>::PixelType;
using PixelTypeDst = typename ImageBase<DerivedDst>::PixelType;
static_assert(std::is_invocable_r_v<PixelTypeDst, Function, PixelTypeSrc&, PixelIndex, PixelIndex>,
"Callable supplied to transform_pixels_with_position must be of (or convertible to) type 'PixelTypeDst f(const PixelTypeSrc&, PixelIndex, PixelIndex)'.");

allocate(img_dst, img_src.layout());

for (auto y = 0_idx; y < img_dst.height(); ++y)
{
auto x = 0_idx;
auto ptr_src = img_src.data(y);
for (auto ptr_dst = img_dst.data(y), ptr_dst_end = img_dst.data_row_end(y); ptr_dst != ptr_dst_end;)
{
*ptr_dst++ = std::invoke(func, *ptr_src++, x++, y);
}
}
}

/** \brief Transforms one image into another by applying a function to each pixel element.
*
* The supplied function receives a constant reference (or value) to the respective pixel element as first parameter,
* followed by both the x and y pixel coordinates for the respective invocation.
* Its return type shall be of the type of a pixel element of the destination image, as specified by the user via
* the first template parameter.
*
* @tparam PixelTypeDst The pixel type of the destination image.
* @tparam DerivedSrc The typed source image type.
* @tparam Function The function type.
* @param img_src The source image.
* @param func The function to apply to each pixel element.
* Its signature should be `PixelTypeDst f(const PixelTypeSrc&)` or `PixelTypeDst f(PixelTypeSrc)`.
* @return The destination image.
*/
template <typename PixelTypeDst, typename DerivedSrc, typename Function>
Image<PixelTypeDst> transform_pixels_with_position(const ImageBase<DerivedSrc>& img, Function func)
{
Image<PixelTypeDst> img_dst({img.width(), img.height()});
transform_pixels_with_position(img, img_dst, func);
return img_dst;
}

// TODO: Add documentation.
template <typename DerivedSrc, typename Function>
auto transform_pixels_expr(const ImageExpr<DerivedSrc>& img, Function func)
{
using PixelTypeSrc = typename ImageBase<DerivedSrc>::PixelType;
static_assert(std::is_invocable_v<Function, PixelTypeSrc&>,
"Callable supplied to transform_pixels_expr must be of (or convertible to) type 'PixelTypeDst f(const PixelTypeSrc&)'.");

return impl::TransformExpr<ImageExpr<DerivedSrc>, Function>(img, func);
}

// TODO: Add documentation.
template <typename DerivedSrc, typename Function>
auto transform_pixels_with_position_expr(const ImageExpr<DerivedSrc>& img, Function func)
{
using PixelTypeSrc = typename ImageBase<DerivedSrc>::PixelType;
static_assert(std::is_invocable_v<Function, PixelTypeSrc&, PixelIndex, PixelIndex>,
"Callable supplied to transform_pixels_with_position_expr must be of (or convertible to) type 'PixelTypeDst f(const PixelTypeSrc&, PixelIndex, PixelIndex)'.");

return impl::TransformWithPositionExpr<ImageExpr<DerivedSrc>, Function>(img, func);
}

/// @}
Expand Down
1 change: 1 addition & 0 deletions selene/img_ops/Crop.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ void crop(ImageBase<DerivedSrcDst>& img, const BoundingBox& region)
img = std::move(cropped_clone);
}

// TODO: Add documentation.
template <typename DerivedSrc>
auto crop_expr(const ImageExpr<DerivedSrc>& img, const BoundingBox& region)
{
Expand Down
4 changes: 4 additions & 0 deletions selene/img_ops/ImageConversions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ inline auto convert_image(const ImageBase<DerivedSrc>& img_src, ElementType alph
return impl::ImageConversion<pixel_format_src, pixel_format_dst>::apply(img_src, alpha_value);
}

// TODO: Add documentation.
template <PixelFormat pixel_format_dst,
typename DerivedSrc,
typename,
Expand All @@ -380,6 +381,7 @@ auto convert_image_expr(const ImageExpr<DerivedSrc>& img_src)
return impl::ImageConversionExpr<pixel_format_src, pixel_format_dst, PixelSrc, PixelDst, ImageExpr<DerivedSrc>>(img_src);
}

// TODO: Add documentation.
template <PixelFormat pixel_format_dst,
typename DerivedSrc,
typename ElementType,
Expand Down Expand Up @@ -564,6 +566,7 @@ inline auto convert_image(const ImageBase<DerivedSrc>& img_src, ElementType alph
return impl::ImageConversion<pixel_format_src, pixel_format_dst>::apply(img_src, alpha_value);
}

// TODO: Add documentation.
template <PixelFormat pixel_format_src,
PixelFormat pixel_format_dst,
typename DerivedSrc,
Expand All @@ -580,6 +583,7 @@ auto convert_image_expr(const ImageExpr<DerivedSrc>& img_src)
return impl::ImageConversionExpr<pixel_format_src, pixel_format_dst, PixelSrc, PixelDst, ImageExpr<DerivedSrc>>(img_src);
}

// TODO: Add documentation.
template <PixelFormat pixel_format_src,
PixelFormat pixel_format_dst,
typename DerivedSrc,
Expand Down
3 changes: 3 additions & 0 deletions selene/img_ops/Transformations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ Image<typename DerivedSrc::PixelType> flip(const ImageBase<DerivedSrc>& img)
return img_flip;
}

// TODO: Add documentation.
template <FlipDirection flip_dir, typename DerivedSrc>
auto flip_expr(const ImageExpr<DerivedSrc>& img)
{
Expand Down Expand Up @@ -247,6 +248,7 @@ Image<typename DerivedSrc::PixelType> transpose(const ImageBase<DerivedSrc>& img
return img_t;
}

// TODO: Add documentation.
template <bool flip_h, bool flip_v, typename DerivedSrc>
auto transpose_expr(const ImageExpr<DerivedSrc>& img)
{
Expand Down Expand Up @@ -299,6 +301,7 @@ Image<typename DerivedSrc::PixelType> rotate(const ImageBase<DerivedSrc>& img)
return img_r;
}

// TODO: Add documentation.
template <RotationDirection rot_dir, typename DerivedSrc>
auto rotate_expr(const ImageExpr<DerivedSrc>& img)
{
Expand Down
18 changes: 9 additions & 9 deletions selene/img_ops/_impl/TransformExpr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@

namespace sln::impl {

template <typename Expr, typename UnaryFunction> class TransformExpr;
template <typename Expr, typename Function> class TransformExpr;

template <typename Expr, typename UnaryFunction>
struct ImageExprTraits<TransformExpr<Expr, UnaryFunction>>
template <typename Expr, typename Function>
struct ImageExprTraits<TransformExpr<Expr, Function>>
: public ExprTraitsBase
{
using PixelType = decltype(std::declval<UnaryFunction>().operator()(typename Expr::PixelType{}));
using PixelType = decltype(std::declval<Function>().operator()(typename Expr::PixelType{}));
};

template <typename Expr, typename UnaryFunction>
class TransformExpr : public ImageExpr<TransformExpr<Expr, UnaryFunction>>
template <typename Expr, typename Function>
class TransformExpr : public ImageExpr<TransformExpr<Expr, Function>>
{
public:
using PixelType = typename ImageExprTraits<TransformExpr<Expr, UnaryFunction>>::PixelType;
using PixelType = typename ImageExprTraits<TransformExpr<Expr, Function>>::PixelType;

explicit TransformExpr(const Expr& e, const UnaryFunction& func) : e_(e), func_(func) {}
explicit TransformExpr(const Expr& e, const Function& func) : e_(e), func_(func) {}

const TypedLayout& layout() const noexcept { return e_.layout(); }

Expand All @@ -49,7 +49,7 @@ class TransformExpr : public ImageExpr<TransformExpr<Expr, UnaryFunction>>

private:
const Expr& e_;
const UnaryFunction& func_;
const Function& func_;
};

} // namespace sln::impl
Expand Down
Loading

0 comments on commit ed42b33

Please sign in to comment.