difference_of_gaussian template function implementation (in file image_operations.h)
namespace TinyDIP
{
// difference_of_gaussian template function implementation
template<typename ElementT, typename SigmaT = double>
requires(std::floating_point<SigmaT> || std::integral<SigmaT>)
constexpr static auto difference_of_gaussian(
const Image<ElementT>& input,
SigmaT sigma1,
SigmaT sigma2)
{
return subtract(
imgaussfilt(input, sigma1, static_cast<int>(computeFilterSizeFromSigma(sigma1))),
imgaussfilt(input, sigma2, static_cast<int>(computeFilterSizeFromSigma(sigma2)))
);
}
}
namespace TinyDIP
{
// difference_of_gaussian template function implementation
template<typename ElementT, typename SigmaT = double>
requires(std::floating_point<SigmaT> || std::integral<SigmaT>)
constexpr static auto difference_of_gaussian(
const Image<ElementT>& input,
const SigmaT sigma1,
const SigmaT sigma2)
{
return subtract(
imgaussfilt(input, sigma1, static_cast<int>(computeFilterSizeFromSigma(sigma1))),
imgaussfilt(input, sigma2, static_cast<int>(computeFilterSizeFromSigma(sigma2)))
);
}
}
computeFilterSizeFromSigma template function implementation (in file image_operations.h)
namespace TinyDIP
{
// computeFilterSizeFromSigma template function implementation
template<typename ElementT>
constexpr static auto computeFilterSizeFromSigma(ElementT sigma)
{
return 2 * std::ceil(2 * sigma) + 1;
}
}
namespace TinyDIP
{
// computeFilterSizeFromSigma template function implementation
template<typename ElementT>
constexpr static auto computeFilterSizeFromSigma(const ElementT sigma)
{
return 2 * std::ceil(2 * sigma) + 1;
}
}
imgaussfilt template function implementation (in file image_operations.h)
namespace TinyDIP
{
// imgaussfilt template function implementation
// https://codereview.stackexchange.com/q/292985/231235
// giving filter_size a default value of 0, and having the function compute an appropriate size unless the user specifies a positive value.
template<typename ElementT, typename SigmaT = double, std::integral SizeT = int>
requires(std::floating_point<SigmaT> || std::integral<SigmaT>)
constexpr static auto imgaussfilt(
const Image<ElementT>& input,
SigmaT sigma,
SizeT filter_size = 0,
BoundaryCondition boundaryCondition = BoundaryCondition::mirror,
ElementT value_for_constant_padding = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
if (filter_size == 0)
{
return imgaussfilt(
std::execution::seq,
input,
sigma,
sigma,
static_cast<int>(computeFilterSizeFromSigma(sigma)),
static_cast<int>(computeFilterSizeFromSigma(sigma)),
boundaryCondition,
value_for_constant_padding);
}
return imgaussfilt(
std::execution::seq,
input,
sigma,
sigma,
filter_size,
filter_size,
boundaryCondition,
value_for_constant_padding);
}
// imgaussfilt template function implementation
// https://codereview.stackexchange.com/q/292985/231235
template<class ExecutionPolicy, typename ElementT, typename SigmaT = double, std::integral SizeT = int>
requires(std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>)&&
(std::floating_point<SigmaT> || std::integral<SigmaT>)
constexpr static auto imgaussfilt(
ExecutionPolicy&& execution_policy,
const Image<ElementT>& input,
SigmaT sigma1,
SigmaT sigma2,
SizeT filter_size1,
SizeT filter_size2,
BoundaryCondition boundaryCondition = BoundaryCondition::mirror,
ElementT value_for_constant_padding = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
Image<ElementT> padded_image;
switch(boundaryCondition)
{
case constant:
padded_image = generate_constant_padding_image(execution_policy, input, filter_size1, filter_size2, value_for_constant_padding);
break;
case mirror:
padded_image = generate_mirror_padding_image(execution_policy, input, filter_size1, filter_size2, value_for_constant_padding);
break;
case replicate:
padded_image = generate_replicate_padding_image(execution_policy, input, filter_size1, filter_size2, value_for_constant_padding);
break;
}
auto filter_mask_x = gaussianFigure1D(
filter_size1,
(static_cast<double>(filter_size1) + 1.0) / 2.0,
sigma1);
auto sum_result = sum(filter_mask_x);
filter_mask_x = divides(filter_mask_x, sum_result); // Normalization
auto output = conv2(padded_image, filter_mask_x, true);
auto filter_mask_y = transpose(gaussianFigure1D(
filter_size2,
(static_cast<double>(filter_size2) + 1.0) / 2.0,
sigma2));
sum_result = sum(filter_mask_y);
filter_mask_y = divides(filter_mask_y, sum_result); // Normalization
output = conv2(output, filter_mask_y, true);
output = subimage(output, input.getWidth(), input.getHeight(), static_cast<double>(output.getWidth()) / 2.0, static_cast<double>(output.getHeight()) / 2.0);
return output;
}
}
namespace TinyDIP
{
// imgaussfilt template function implementation
// https://codereview.stackexchange.com/q/292985/231235
// giving filter_size a default value of 0, and having the function compute an appropriate size unless the user specifies a positive value.
template<typename ElementT, typename SigmaT = double, std::integral SizeT = int>
requires(std::floating_point<SigmaT> || std::integral<SigmaT>)
constexpr static auto imgaussfilt(
const Image<ElementT>& input,
const SigmaT sigma,
const SizeT filter_size = 0,
const BoundaryCondition boundaryCondition = BoundaryCondition::mirror,
const ElementT value_for_constant_padding = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
if (filter_size == 0)
{
return imgaussfilt(
std::execution::seq,
input,
sigma,
sigma,
static_cast<int>(computeFilterSizeFromSigma(sigma)),
static_cast<int>(computeFilterSizeFromSigma(sigma)),
boundaryCondition,
value_for_constant_padding);
}
return imgaussfilt(
std::execution::seq,
input,
sigma,
sigma,
filter_size,
filter_size,
boundaryCondition,
value_for_constant_padding);
}
// imgaussfilt template function implementation
// https://codereview.stackexchange.com/q/292985/231235
template<class ExecutionPolicy, typename ElementT, typename SigmaT = double, std::integral SizeT = int>
requires(std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>)&&
(std::floating_point<SigmaT> || std::integral<SigmaT>)
constexpr static auto imgaussfilt(
ExecutionPolicy&& execution_policy,
const Image<ElementT>& input,
const SigmaT sigma1,
const SigmaT sigma2,
const SizeT filter_size1,
const SizeT filter_size2,
const BoundaryCondition boundaryCondition = BoundaryCondition::mirror,
const ElementT value_for_constant_padding = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
Image<ElementT> padded_image = generate_padded_image(
execution_policy, input,
filter_size1,
filter_size2,
boundaryCondition,
value_for_constant_padding
);
auto filter_mask_x = gaussianFigure1D(
filter_size1,
(static_cast<double>(filter_size1) + 1.0) / 2.0,
sigma1);
auto sum_result = sum(execution_policy, filter_mask_x);
filter_mask_x = divides(filter_mask_x, sum_result); // Normalization
auto output = conv2(padded_image, filter_mask_x, true);
auto filter_mask_y = transpose(gaussianFigure1D(
filter_size2,
(static_cast<double>(filter_size2) + 1.0) / 2.0,
sigma2));
sum_result = sum(filter_mask_y);
filter_mask_y = divides(filter_mask_y, sum_result); // Normalization
output = conv2(output, filter_mask_y, true);
output = subimage(output, input.getWidth(), input.getHeight(), static_cast<double>(output.getWidth()) / 2.0, static_cast<double>(output.getHeight()) / 2.0);
return output;
}
}
BoundaryCondition enumeration declaration
enum BoundaryCondition {
constant,
mirror,
replicate
};
enum BoundaryCondition {
constant,
mirror,
replicate
};
generate_constant_padding_image template function implementation (in file image_operations.h)
namespace TinyDIP
{
// generate_constant_padding_image template function implementation
template<typename ElementT>
constexpr static auto generate_constant_padding_image(
const Image<ElementT> input,
std::size_t width_expansion,
std::size_t height_expansion,
ElementT default_value = ElementT{})
{
return generate_constant_padding_image(std::execution::seq, input, width_expansion, height_expansion, default_value);
}
// generate_constant_padding_image template function implementation (with Execution Policy)
template<class ExecutionPolicy, typename ElementT>
requires(std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>)
constexpr static auto generate_constant_padding_image(
ExecutionPolicy&& execution_policy,
const Image<ElementT> input,
std::size_t width_expansion,
std::size_t height_expansion,
ElementT default_value = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
Image<ElementT> output(input.getWidth() + 2 * width_expansion, input.getHeight() + 2 * height_expansion);
output.setAllValue(default_value);
output = paste2D(execution_policy, output, input, width_expansion, height_expansion, default_value);
return output;
}
}
namespace TinyDIP
{
// generate_constant_padding_image template function implementation
template<typename ElementT>
constexpr static auto generate_constant_padding_image(
const Image<ElementT>& input,
const std::size_t width_expansion,
const std::size_t height_expansion,
const ElementT default_value = ElementT{})
{
return generate_constant_padding_image(std::execution::seq, input, width_expansion, height_expansion, default_value);
}
// generate_constant_padding_image template function implementation (with Execution Policy)
template<class ExecutionPolicy, typename ElementT>
requires(std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>)
constexpr static auto generate_constant_padding_image(
ExecutionPolicy&& execution_policy,
const Image<ElementT>& input,
const std::size_t width_expansion,
const std::size_t height_expansion,
const ElementT default_value = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
Image<ElementT> output(input.getWidth() + 2 * width_expansion, input.getHeight() + 2 * height_expansion);
output.setAllValue(default_value);
output = paste2D(execution_policy, output, input, width_expansion, height_expansion, default_value);
return output;
}
}
generate_mirror_padding_image template function implementation (in file image_operations.h)
namespace TinyDIP
{
// generate_mirror_padding_image template function implementation
template<typename ElementT>
constexpr static auto generate_mirror_padding_image(
const Image<ElementT> input,
std::size_t width_expansion,
std::size_t height_expansion,
ElementT default_value = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
return generate_mirror_padding_image(std::execution::seq, input, width_expansion, height_expansion, default_value);
}
// generate_mirror_padding_image template function implementation (with Execution Policy)
template<class ExecutionPolicy, typename ElementT>
requires(std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>)
constexpr static auto generate_mirror_padding_image(
ExecutionPolicy&& execution_policy,
const Image<ElementT> input,
std::size_t width_expansion,
std::size_t height_expansion,
ElementT default_value = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
auto output = generate_constant_padding_image(execution_policy, input, width_expansion, height_expansion, default_value);
auto flipped_vertical = flip_vertical(input);
output = paste2D(
execution_policy,
output,
subimage2(flipped_vertical, 0, flipped_vertical.getWidth() - 1, input.getHeight() - height_expansion - 1, flipped_vertical.getHeight() - 1),
width_expansion,
0,
default_value);
output = paste2D(
execution_policy,
output,
subimage2(flipped_vertical, 0, flipped_vertical.getWidth() - 1, 0, height_expansion),
width_expansion,
input.getHeight() + height_expansion - 1,
default_value);
auto flipped_horizontal = flip_horizontal(input);
output = paste2D(
execution_policy,
output,
subimage2(flipped_horizontal, input.getWidth() - width_expansion - 1, flipped_horizontal.getWidth() - 1, 0, flipped_horizontal.getHeight() - 1),
0,
height_expansion,
default_value);
output = paste2D(
execution_policy,
output,
subimage2(flipped_horizontal, 0, width_expansion, 0, flipped_horizontal.getHeight() - 1),
input.getWidth() + width_expansion - 1,
height_expansion,
default_value);
auto flipped_horizontal_vertical = flip_horizontal_vertical(input);
output = paste2D(
execution_policy,
output,
subimage2(
flipped_horizontal_vertical,
flipped_horizontal_vertical.getWidth() - width_expansion - 1,
flipped_horizontal_vertical.getWidth() - 1,
flipped_horizontal_vertical.getHeight() - height_expansion - 1,
flipped_horizontal_vertical.getHeight() - 1),
0,
0,
default_value);
output = paste2D(
execution_policy,
output,
subimage2(
flipped_horizontal_vertical,
0,
width_expansion,
flipped_horizontal_vertical.getHeight() - height_expansion - 1,
flipped_horizontal_vertical.getHeight() - 1),
input.getWidth() + width_expansion - 1,
0,
default_value);
output = paste2D(
execution_policy,
output,
subimage2(
flipped_horizontal_vertical,
flipped_horizontal_vertical.getWidth() - width_expansion - 1,
flipped_horizontal_vertical.getWidth() - 1,
0,
height_expansion),
0,
input.getHeight() + height_expansion - 1,
default_value);
output = paste2D(
execution_policy,
output,
subimage2(
flipped_horizontal_vertical,
0,
width_expansion,
0,
height_expansion),
input.getWidth() + width_expansion - 1,
input.getHeight() + height_expansion - 1,
default_value);
return output;
}
}
namespace TinyDIP
{
// generate_mirror_padding_image template function implementation
template<typename ElementT>
constexpr static auto generate_mirror_padding_image(
const Image<ElementT>& input,
const std::size_t width_expansion,
const std::size_t height_expansion,
const ElementT default_value = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
return generate_mirror_padding_image(std::execution::seq, input, width_expansion, height_expansion, default_value);
}
// generate_mirror_padding_image template function implementation (with Execution Policy)
template<class ExecutionPolicy, typename ElementT>
requires(std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>)
constexpr static auto generate_mirror_padding_image(
ExecutionPolicy&& execution_policy,
const Image<ElementT>& input,
const std::size_t width_expansion,
const std::size_t height_expansion,
const ElementT default_value = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
auto output = generate_constant_padding_image(execution_policy, input, width_expansion, height_expansion, default_value);
auto flipped_vertical = flip_vertical(input);
output = paste2D(
execution_policy,
output,
subimage2(flipped_vertical, 0, flipped_vertical.getWidth() - 1, input.getHeight() - height_expansion - 1, flipped_vertical.getHeight() - 1),
width_expansion,
0,
default_value);
output = paste2D(
execution_policy,
output,
subimage2(flipped_vertical, 0, flipped_vertical.getWidth() - 1, 0, height_expansion),
width_expansion,
input.getHeight() + height_expansion - 1,
default_value);
auto flipped_horizontal = flip_horizontal(input);
output = paste2D(
execution_policy,
output,
subimage2(flipped_horizontal, input.getWidth() - width_expansion - 1, flipped_horizontal.getWidth() - 1, 0, flipped_horizontal.getHeight() - 1),
0,
height_expansion,
default_value);
output = paste2D(
execution_policy,
output,
subimage2(flipped_horizontal, 0, width_expansion, 0, flipped_horizontal.getHeight() - 1),
input.getWidth() + width_expansion - 1,
height_expansion,
default_value);
auto flipped_horizontal_vertical = flip_horizontal_vertical(input);
output = paste2D(
execution_policy,
output,
subimage2(
flipped_horizontal_vertical,
flipped_horizontal_vertical.getWidth() - width_expansion - 1,
flipped_horizontal_vertical.getWidth() - 1,
flipped_horizontal_vertical.getHeight() - height_expansion - 1,
flipped_horizontal_vertical.getHeight() - 1),
0,
0,
default_value);
output = paste2D(
execution_policy,
output,
subimage2(
flipped_horizontal_vertical,
0,
width_expansion,
flipped_horizontal_vertical.getHeight() - height_expansion - 1,
flipped_horizontal_vertical.getHeight() - 1),
input.getWidth() + width_expansion - 1,
0,
default_value);
output = paste2D(
execution_policy,
output,
subimage2(
flipped_horizontal_vertical,
flipped_horizontal_vertical.getWidth() - width_expansion - 1,
flipped_horizontal_vertical.getWidth() - 1,
0,
height_expansion),
0,
input.getHeight() + height_expansion - 1,
default_value);
output = paste2D(
execution_policy,
output,
subimage2(
flipped_horizontal_vertical,
0,
width_expansion,
0,
height_expansion),
input.getWidth() + width_expansion - 1,
input.getHeight() + height_expansion - 1,
default_value);
return output;
}
}
generate_replicate_padding_image template function implementation (in file image_operations.h)
namespace TinyDIP
{
// generate_replicate_padding_image template function implementation
template<typename ElementT>
constexpr static auto generate_replicate_padding_image(
const Image<ElementT> input,
std::size_t width_expansion,
std::size_t height_expansion,
ElementT default_value = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
return generate_replicate_padding_image(std::execution::seq, input, width_expansion, height_expansion, default_value);
}
// generate_replicate_padding_image template function implementation (with Execution Policy)
// Test: https://godbolt.org/z/1hebz7hEh
template<class ExecutionPolicy, typename ElementT>
requires(std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>)
constexpr static auto generate_replicate_padding_image(
ExecutionPolicy&& execution_policy,
const Image<ElementT> input,
std::size_t width_expansion,
std::size_t height_expansion,
ElementT default_value = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
auto output = generate_constant_padding_image(execution_policy, input, width_expansion, height_expansion, default_value);
// Top block
for(std::size_t y = 0; y < height_expansion; ++y)
{
output = paste2D(
execution_policy,
output,
subimage2(input, 0, input.getWidth() - 1, 0, 0),
width_expansion,
y,
default_value);
}
// Bottom block
for(std::size_t y = input.getHeight() + height_expansion; y < input.getHeight() + 2 * height_expansion; ++y)
{
output = paste2D(
execution_policy,
output,
subimage2(input, 0, input.getWidth() - 1, input.getHeight() - 1, input.getHeight() - 1),
width_expansion,
y,
default_value);
}
// Left block
for(std::size_t x = 0; x < width_expansion; ++x)
{
output = paste2D(
execution_policy,
output,
subimage2(input, 0, 0, 0, input.getHeight() - 1),
x,
height_expansion,
default_value);
}
// Right block
for(std::size_t x = input.getWidth() + width_expansion; x < input.getWidth() + 2 * width_expansion; ++x)
{
output = paste2D(
execution_policy,
output,
subimage2(input, input.getWidth() - 1, input.getWidth() - 1, 0, input.getHeight() - 1),
x,
height_expansion,
default_value);
}
Image<ElementT> temp(width_expansion, height_expansion);
// Left-top corner
temp.setAllValue(input.at(0, 0));
output = paste2D(
execution_policy,
output,
temp,
0,
0,
default_value);
// Right-top corner
temp.setAllValue(input.at(input.getWidth() - 1, 0));
output = paste2D(
execution_policy,
output,
temp,
width_expansion + input.getWidth(),
0,
default_value);
// Left-bottom corner
temp.setAllValue(input.at(0, input.getHeight() - 1));
output = paste2D(
execution_policy,
output,
temp,
0,
height_expansion + input.getHeight(),
default_value);
// Right-bottom corner
temp.setAllValue(input.at(input.getWidth() - 1, input.getHeight() - 1));
output = paste2D(
execution_policy,
output,
temp,
width_expansion + input.getWidth(),
height_expansion + input.getHeight(),
default_value);
return output;
}
}
namespace TinyDIP
{
// generate_replicate_padding_image template function implementation
template<typename ElementT>
constexpr static auto generate_replicate_padding_image(
const Image<ElementT>& input,
const std::size_t width_expansion,
const std::size_t height_expansion,
const ElementT default_value = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
return generate_replicate_padding_image(std::execution::seq, input, width_expansion, height_expansion, default_value);
}
// generate_replicate_padding_image template function implementation (with Execution Policy)
// Test: https://godbolt.org/z/1hebz7hEh
template<class ExecutionPolicy, typename ElementT>
requires(std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>)
constexpr static auto generate_replicate_padding_image(
ExecutionPolicy&& execution_policy,
const Image<ElementT>& input,
const std::size_t width_expansion,
const std::size_t height_expansion,
const ElementT default_value = ElementT{})
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
auto output = generate_constant_padding_image(execution_policy, input, width_expansion, height_expansion, default_value);
// Top block
for(std::size_t y = 0; y < height_expansion; ++y)
{
output = paste2D(
execution_policy,
output,
subimage2(input, 0, input.getWidth() - 1, 0, 0),
width_expansion,
y,
default_value);
}
// Bottom block
for(std::size_t y = input.getHeight() + height_expansion; y < input.getHeight() + 2 * height_expansion; ++y)
{
output = paste2D(
execution_policy,
output,
subimage2(input, 0, input.getWidth() - 1, input.getHeight() - 1, input.getHeight() - 1),
width_expansion,
y,
default_value);
}
// Left block
for(std::size_t x = 0; x < width_expansion; ++x)
{
output = paste2D(
execution_policy,
output,
subimage2(input, 0, 0, 0, input.getHeight() - 1),
x,
height_expansion,
default_value);
}
// Right block
for(std::size_t x = input.getWidth() + width_expansion; x < input.getWidth() + 2 * width_expansion; ++x)
{
output = paste2D(
execution_policy,
output,
subimage2(input, input.getWidth() - 1, input.getWidth() - 1, 0, input.getHeight() - 1),
x,
height_expansion,
default_value);
}
Image<ElementT> temp(width_expansion, height_expansion);
// Left-top corner
temp.setAllValue(input.at(static_cast<std::size_t>(0), static_cast<std::size_t>(0)));
output = paste2D(
execution_policy,
output,
temp,
0,
0,
default_value);
// Right-top corner
temp.setAllValue(input.at(input.getWidth() - static_cast<std::size_t>(1), static_cast<std::size_t>(0)));
output = paste2D(
execution_policy,
output,
temp,
width_expansion + input.getWidth(),
0,
default_value);
// Left-bottom corner
temp.setAllValue(input.at(static_cast<std::size_t>(0), input.getHeight() - static_cast<std::size_t>(1)));
output = paste2D(
execution_policy,
output,
temp,
0,
height_expansion + input.getHeight(),
default_value);
// Right-bottom corner
temp.setAllValue(input.at(input.getWidth() - static_cast<std::size_t>(1), input.getHeight() - static_cast<std::size_t>(1)));
output = paste2D(
execution_policy,
output,
temp,
width_expansion + input.getWidth(),
height_expansion + input.getHeight(),
default_value);
return output;
}
}
subtract template function implementation (in file image_operations.h)
namespace TinyDIP
{
// subtract Template Function Implementation
template<class InputT>
constexpr static Image<InputT> subtract(const Image<InputT>& input1, const Image<InputT>& input2)
{
check_size_same(input1, input2);
return pixelwiseOperation(std::minus<>{}, input1, input2);
}
// subtract Template Function Implementation
template<class InputT>
constexpr static auto subtract(const std::vector<Image<InputT>>& input1, const std::vector<Image<InputT>>& input2)
{
assert(input1.size() == input2.size());
return recursive_transform<1>(
[](auto&& input1_element, auto&& input2_element)
{
return subtract(input1_element, input2_element);
}, input1, input2);
}
// subtract Function Implementation
constexpr static Image<RGB> subtract(const Image<RGB>& input1, const Image<RGB>& input2)
{
check_size_same(input1, input2);
return pixelwiseOperation(
[](RGB x, RGB y)
{
RGB rgb;
for(std::size_t channel_index = 0; channel_index < 3; ++channel_index)
{
rgb.channels[channel_index] =
std::clamp(
x.channels[channel_index] -
y.channels[channel_index],
0,
255);
}
return rgb;
},
input1,
input2
);
}
// subtract Template Function Implementation
template<class InputT>
requires((std::same_as<InputT, RGB_DOUBLE>) || (std::same_as<InputT, HSV>))
constexpr static auto subtract(const Image<InputT>& input1, const Image<InputT>& input2)
{
check_size_same(input1, input2);
return pixelwiseOperation(
[](InputT x, InputT y)
{
InputT output;
for(std::size_t channel_index = 0; channel_index < 3; ++channel_index)
{
output.channels[channel_index] = x.channels[channel_index] - y.channels[channel_index];
}
return output;
},
input1,
input2
);
}
}
namespace TinyDIP
{
// subtract Template Function Implementation
template<class InputT>
constexpr static Image<InputT> subtract(const Image<InputT>& input1, const Image<InputT>& input2)
{
check_size_same(input1, input2);
return pixelwiseOperation(std::minus<>{}, input1, input2);
}
// subtract Template Function Implementation
template<class InputT>
constexpr static auto subtract(const std::vector<Image<InputT>>& input1, const std::vector<Image<InputT>>& input2)
{
assert(input1.size() == input2.size());
return recursive_transform<1>(
[](auto&& input1_element, auto&& input2_element)
{
return subtract(input1_element, input2_element);
}, input1, input2);
}
// subtract Function Implementation
constexpr static Image<RGB> subtract(const Image<RGB>& input1, const Image<RGB>& input2)
{
check_size_same(input1, input2);
return pixelwiseOperation(
[](RGB x, RGB y)
{
RGB rgb;
for(std::size_t channel_index = 0; channel_index < 3; ++channel_index)
{
rgb.channels[channel_index] =
std::clamp(
x.channels[channel_index] -
y.channels[channel_index],
0,
255);
}
return rgb;
},
input1,
input2
);
}
// subtract Template Function Implementation
template<class InputT>
requires((std::same_as<InputT, RGB_DOUBLE>) || (std::same_as<InputT, HSV>))
constexpr static auto subtract(const Image<InputT>& input1, const Image<InputT>& input2)
{
check_size_same(input1, input2);
return pixelwiseOperation(
[](InputT x, InputT y)
{
InputT output;
for(std::size_t channel_index = 0; channel_index < 3; ++channel_index)
{
output.channels[channel_index] = x.channels[channel_index] - y.channels[channel_index];
}
return output;
},
input1,
input2
);
}
}
paste2D template function implementation (in file image_operations.h)
namespace TinyDIP
{
// paste2D template function implementation
template<typename ElementT>
constexpr static auto paste2D(const Image<ElementT>& background, const Image<ElementT>& target, std::size_t x_location, std::size_t y_location, ElementT default_value = ElementT{})
{
return paste2D(std::execution::seq, background, target, x_location, y_location, default_value);
}
// paste2D template function implementation (with execution policy)
// Test: https://godbolt.org/z/5hjns1nGP
template<class ExecutionPolicy, typename ElementT>
requires(std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>)
constexpr static auto paste2D(ExecutionPolicy&& execution_policy, const Image<ElementT>& background, const Image<ElementT>& target, std::size_t x_location, std::size_t y_location, ElementT default_value = ElementT{})
{
if (background.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
if (target.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
if((background.getWidth() >= target.getWidth() + x_location) &&
(background.getHeight() >= target.getHeight() + y_location))
{
auto output = background;
for (std::size_t y = 0; y < target.getHeight(); ++y)
{
for (std::size_t x = 0; x < target.getWidth(); ++x)
{
output.at_without_boundary_check(x_location + x, y_location + y) = target.at_without_boundary_check(x, y);
}
}
return output;
}
else
{
std::vector<ElementT> data;
auto xsize = (background.getWidth() >= target.getWidth() + x_location)?
background.getWidth():
(target.getWidth() + x_location);
auto ysize = (background.getHeight() >= target.getHeight() + y_location)?
background.getHeight():
(target.getHeight() + y_location);
data.resize(xsize * ysize);
std::fill(execution_policy, std::ranges::begin(data), std::ranges::end(data), default_value);
Image<ElementT> output(data, xsize, ysize);
for (std::size_t y = 0; y < background.getHeight(); ++y)
{
for (std::size_t x = 0; x < background.getWidth(); ++x)
{
output.at_without_boundary_check(x, y) = background.at_without_boundary_check(x, y);
}
}
for (std::size_t y = 0; y < target.getHeight(); ++y)
{
for (std::size_t x = 0; x < target.getWidth(); ++x)
{
output.at_without_boundary_check(x_location + x, y_location + y) = target.at_without_boundary_check(x, y);
}
}
return output;
}
}
}
namespace TinyDIP
{
// paste2D template function implementation
template<typename ElementT>
constexpr static auto paste2D(
const Image<ElementT>& background,
const Image<ElementT>& target,
const std::size_t x_location,
const std::size_t y_location,
const ElementT default_value = ElementT{})
{
return paste2D(std::execution::seq, background, target, x_location, y_location, default_value);
}
// paste2D template function implementation (with execution policy)
// Test: https://godbolt.org/z/5hjns1nGP
template<class ExecutionPolicy, typename ElementT>
requires(std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>)
constexpr static auto paste2D(
ExecutionPolicy&& execution_policy,
const Image<ElementT>& background,
const Image<ElementT>& target,
const std::size_t x_location,
const std::size_t y_location,
const ElementT default_value = ElementT{})
{
if (background.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
if (target.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
if((background.getWidth() >= target.getWidth() + x_location) &&
(background.getHeight() >= target.getHeight() + y_location))
{
auto output = background;
for (std::size_t y = 0; y < target.getHeight(); ++y)
{
for (std::size_t x = 0; x < target.getWidth(); ++x)
{
output.at_without_boundary_check(x_location + x, y_location + y) = target.at_without_boundary_check(x, y);
}
}
return output;
}
else
{
std::vector<ElementT> data;
auto xsize = (background.getWidth() >= target.getWidth() + x_location)?
background.getWidth():
(target.getWidth() + x_location);
auto ysize = (background.getHeight() >= target.getHeight() + y_location)?
background.getHeight():
(target.getHeight() + y_location);
data.resize(xsize * ysize);
std::fill(execution_policy, std::ranges::begin(data), std::ranges::end(data), default_value);
Image<ElementT> output(data, xsize, ysize);
for (std::size_t y = 0; y < background.getHeight(); ++y)
{
for (std::size_t x = 0; x < background.getWidth(); ++x)
{
output.at_without_boundary_check(x, y) = background.at_without_boundary_check(x, y);
}
}
for (std::size_t y = 0; y < target.getHeight(); ++y)
{
for (std::size_t x = 0; x < target.getWidth(); ++x)
{
output.at_without_boundary_check(x_location + x, y_location + y) = target.at_without_boundary_check(x, y);
}
}
return output;
}
}
}
subimage2 template function implementation (in file image_operations.h)
namespace TinyDIP
{
// subimage2 template function implementation
template<typename ElementT>
constexpr static auto subimage2(const Image<ElementT>& input, const std::size_t startx, const std::size_t endx, const std::size_t starty, const std::size_t endy)
{
assert(startx <= endx);
assert(starty <= endy);
Image<ElementT> output(endx - startx + 1, endy - starty + 1);
for (std::size_t y = 0; y < output.getHeight(); ++y)
{
for (std::size_t x = 0; x < output.getWidth(); ++x)
{
output.at_without_boundary_check(x, y) = input.at_without_boundary_check(startx + x, starty + y);
}
}
return output;
}
}
namespace TinyDIP
{
// subimage2 template function implementation
template<typename ElementT>
constexpr static auto subimage2(const Image<ElementT>& input, const std::size_t startx, const std::size_t endx, const std::size_t starty, const std::size_t endy)
{
assert(startx <= endx);
assert(starty <= endy);
Image<ElementT> output(endx - startx + 1, endy - starty + 1);
const auto width = output.getWidth();
const auto height = output.getHeight();
#pragma omp parallel for collapse(2)
for (std::size_t y = 0; y < height; ++y)
{
for (std::size_t x = 0; x < width; ++x)
{
output.at_without_boundary_check(x, y) = input.at_without_boundary_check(startx + x, starty + y);
}
}
return output;
}
}
flip_vertical template function implementation (in file image_operations.h)
namespace TinyDIP
{
// flip_vertical template function implementation
template<typename ElementT>
constexpr static auto flip_vertical(const Image<ElementT>& input)
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
Image<ElementT> output = input;
#pragma omp parallel for collapse(2)
for(std::size_t y = 0; y < input.getHeight(); ++y)
{
for(std::size_t x = 0; x < input.getWidth(); ++x)
{
output.at_without_boundary_check(x, input.getHeight() - y - 1) = input.at_without_boundary_check(x, y);
}
}
return output;
}
}
namespace TinyDIP
{
// flip_vertical template function implementation
template<typename ElementT>
constexpr static auto flip_vertical(const Image<ElementT>& input)
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
Image<ElementT> output = input;
#pragma omp parallel for collapse(2)
for(std::size_t y = 0; y < input.getHeight(); ++y)
{
for(std::size_t x = 0; x < input.getWidth(); ++x)
{
output.at_without_boundary_check(x, input.getHeight() - y - 1) = input.at_without_boundary_check(x, y);
}
}
return output;
}
}
flip_horizontal template function implementation (in file image_operations.h)
namespace TinyDIP
{
// flip_horizontal template function implementation
template<typename ElementT>
constexpr static auto flip_horizontal(const Image<ElementT>& input)
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
Image<ElementT> output = input;
#pragma omp parallel for collapse(2)
for(std::size_t y = 0; y < input.getHeight(); ++y)
{
for(std::size_t x = 0; x < input.getWidth(); ++x)
{
output.at_without_boundary_check(input.getWidth() - x - 1, y) = input.at_without_boundary_check(x, y);
}
}
return output;
}
}
namespace TinyDIP
{
// flip_horizontal template function implementation
template<typename ElementT>
constexpr static auto flip_horizontal(const Image<ElementT>& input)
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
Image<ElementT> output = input;
#pragma omp parallel for collapse(2)
for(std::size_t y = 0; y < input.getHeight(); ++y)
{
for(std::size_t x = 0; x < input.getWidth(); ++x)
{
output.at_without_boundary_check(input.getWidth() - x - 1, y) = input.at_without_boundary_check(x, y);
}
}
return output;
}
}
flip_horizontal_vertical template function implementation (in file image_operations.h)
namespace TinyDIP
{
// flip_horizontal_vertical template function implementation
template<typename ElementT>
constexpr static auto flip_horizontal_vertical(const Image<ElementT>& input)
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
Image<ElementT> output = input;
#pragma omp parallel for collapse(2)
for(std::size_t y = 0; y < input.getHeight(); ++y)
{
for(std::size_t x = 0; x < input.getWidth(); ++x)
{
output.at_without_boundary_check(
input.getWidth() - x - 1,
input.getHeight() - y - 1
) = input.at_without_boundary_check(x, y);
}
}
return output;
}
}
namespace TinyDIP
{
// flip_horizontal_vertical template function implementation
template<typename ElementT>
constexpr static auto flip_horizontal_vertical(const Image<ElementT>& input)
{
if (input.getDimensionality()!=2)
{
throw std::runtime_error("Unsupported dimension!");
}
Image<ElementT> output = input;
#pragma omp parallel for collapse(2)
for(std::size_t y = 0; y < input.getHeight(); ++y)
{
for(std::size_t x = 0; x < input.getWidth(); ++x)
{
output.at_without_boundary_check(
input.getWidth() - x - 1,
input.getHeight() - y - 1
) = input.at_without_boundary_check(x, y);
}
}
return output;
}
}