10 #ifndef BOOST_GIL_IMAGE_PROCESSING_DIFFUSION_HPP
11 #define BOOST_GIL_IMAGE_PROCESSING_DIFFUSION_HPP
13 #include "boost/gil/detail/math.hpp"
14 #include <boost/gil/algorithm.hpp>
15 #include <boost/gil/color_base_algorithm.hpp>
16 #include <boost/gil/image.hpp>
17 #include <boost/gil/image_view.hpp>
18 #include <boost/gil/image_view_factory.hpp>
19 #include <boost/gil/pixel.hpp>
20 #include <boost/gil/point.hpp>
21 #include <boost/gil/typedefs.hpp>
26 namespace boost {
namespace gil {
27 namespace conductivity {
28 struct perona_malik_conductivity
31 template <
typename Pixel>
32 Pixel operator()(Pixel input)
34 using channel_type =
typename channel_type<Pixel>::type;
36 static_transform(input, input, [
this](channel_type value) {
38 return std::exp(-std::abs(value));
45 struct gaussian_conductivity
48 template <
typename Pixel>
49 Pixel operator()(Pixel input)
51 using channel_type =
typename channel_type<Pixel>::type;
53 static_transform(input, input, [
this](channel_type value) {
55 return std::exp(-value * value);
62 struct wide_regions_conductivity
65 template <
typename Pixel>
66 Pixel operator()(Pixel input)
68 using channel_type =
typename channel_type<Pixel>::type;
70 static_transform(input, input, [
this](channel_type value) {
72 return 1.0 / (1.0 + value * value);
79 struct more_wide_regions_conductivity
82 template <
typename Pixel>
83 Pixel operator()(Pixel input)
85 using channel_type =
typename channel_type<Pixel>::type;
87 static_transform(input, input, [
this](channel_type value) {
89 return 1.0 / std::sqrt((1.0 + value * value));
100 namespace laplace_function {
120 template <
typename PixelType>
121 using stencil_type = std::array<PixelType, 8>;
130 double delta_t = 0.25;
132 template <
typename SubImageView>
133 stencil_type<typename SubImageView::value_type> compute_laplace(SubImageView
view,
136 auto current =
view(origin);
137 stencil_type<typename SubImageView::value_type> stencil;
140 typename SubImageView::value_type zero_pixel;
141 static_fill(zero_pixel, 0);
142 for (std::size_t index = 0; index < offsets.size(); ++index)
146 static_transform(
view(origin.x + offsets[index].x, origin.y + offsets[index].y),
147 current, stencil[index], std::minus<channel_type>{});
151 stencil[index] = zero_pixel;
157 template <
typename Pixel>
158 Pixel reduce(
const stencil_type<Pixel>& stencil)
167 for (std::size_t index : {1u, 3u, 5u, 7u})
169 static_transform(result, stencil[index], result, std::plus<channel_type>{});
172 static_fill(delta_t_pixel, delta_t);
173 static_transform(result, delta_t_pixel, result, std::multiplies<channel_type>{});
187 double delta_t = 0.125;
189 template <
typename SubImageView>
190 stencil_type<typename SubImageView::value_type> compute_laplace(SubImageView
view,
193 stencil_type<typename SubImageView::value_type> stencil;
194 auto out = stencil.begin();
195 auto current =
view(origin);
198 for (
auto offset : offsets)
200 static_transform(
view(origin.x + offset.x, origin.y + offset.y), current, *out++,
201 std::minus<channel_type>{});
207 template <
typename Pixel>
208 Pixel reduce(
const stencil_type<Pixel>& stencil)
216 for (std::size_t index : {1u, 3u, 5u, 7u})
218 static_transform(result, stencil[index], result, std::plus<channel_type>{});
221 for (std::size_t index : {0u, 2u, 4u, 6u})
225 static_transform(stencil[index], half_pixel, half_pixel,
226 std::multiplies<channel_type>{});
227 static_transform(result, half_pixel, result, std::plus<channel_type>{});
231 static_fill(delta_t_pixel, delta_t);
232 static_transform(result, delta_t_pixel, result, std::multiplies<channel_type>{});
239 namespace brightness_function {
240 using laplace_function::stencil_type;
243 template <
typename Pixel>
244 stencil_type<Pixel> operator()(
const stencil_type<Pixel>& stencil)
255 using pixel_type = rgb32f_pixel_t;
256 stencil_type<pixel_type> operator()(
const stencil_type<pixel_type>& stencil)
258 stencil_type<pixel_type> output;
259 std::transform(stencil.begin(), stencil.end(), output.begin(), [](
const pixel_type& pixel) {
260 float32_t luminance = 0.2126f * pixel[0] + 0.7152f * pixel[1] + 0.0722f * pixel[2];
261 pixel_type result_pixel;
262 static_fill(result_pixel, luminance);
271 enum class matlab_connectivity
277 enum class matlab_conduction_method
283 template <
typename InputView,
typename OutputView>
284 void classic_anisotropic_diffusion(
const InputView& input,
const OutputView& output,
285 unsigned int num_iter,
double kappa)
287 anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{},
288 brightness_function::identity{},
289 conductivity::perona_malik_conductivity{kappa});
292 template <
typename InputView,
typename OutputView>
293 void matlab_anisotropic_diffusion(
const InputView& input,
const OutputView& output,
294 unsigned int num_iter,
double kappa,
295 matlab_connectivity connectivity,
296 matlab_conduction_method conduction_method)
298 if (connectivity == matlab_connectivity::minimal)
300 if (conduction_method == matlab_conduction_method::exponential)
302 anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{},
303 brightness_function::identity{},
304 conductivity::gaussian_conductivity{kappa});
306 else if (conduction_method == matlab_conduction_method::quadratic)
308 anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{},
309 brightness_function::identity{},
310 conductivity::gaussian_conductivity{kappa});
314 throw std::logic_error(
"unhandled conduction method found");
317 else if (connectivity == matlab_connectivity::maximal)
319 if (conduction_method == matlab_conduction_method::exponential)
321 anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{},
322 brightness_function::identity{},
323 conductivity::gaussian_conductivity{kappa});
325 else if (conduction_method == matlab_conduction_method::quadratic)
327 anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{},
328 brightness_function::identity{},
329 conductivity::gaussian_conductivity{kappa});
333 throw std::logic_error(
"unhandled conduction method found");
338 throw std::logic_error(
"unhandled connectivity found");
342 template <
typename InputView,
typename OutputView>
343 void default_anisotropic_diffusion(
const InputView& input,
const OutputView& output,
344 unsigned int num_iter,
double kappa)
346 anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_9points_standard{},
347 brightness_function::identity{}, conductivity::gaussian_conductivity{kappa});
359 template <
typename InputView,
typename OutputView,
360 typename LaplaceStrategy = laplace_function::stencil_9points_standard,
361 typename BrightnessFunction = brightness_function::identity,
362 typename DiffusivityFunction = conductivity::gaussian_conductivity>
363 void anisotropic_diffusion(
const InputView& input,
const OutputView& output,
unsigned int num_iter,
364 LaplaceStrategy laplace, BrightnessFunction brightness,
365 DiffusivityFunction diffusivity)
367 using input_pixel_type =
typename InputView::value_type;
368 using pixel_type =
typename OutputView::value_type;
369 using channel_type =
typename channel_type<pixel_type>::type;
370 using computation_image = image<pixel_type>;
371 const auto width = input.width();
372 const auto height = input.height();
373 const point_t dims(width, height);
374 const auto zero_pixel = []() {
376 static_fill(pixel,
static_cast<channel_type
>(0));
380 computation_image result_image(width + 2, height + 2, zero_pixel);
381 auto result =
view(result_image);
382 computation_image scratch_result_image(width + 2, height + 2, zero_pixel);
383 auto scratch_result =
view(scratch_result_image);
385 [](
const input_pixel_type& pixel) {
386 pixel_type converted;
387 for (std::size_t i = 0; i < num_channels<pixel_type>{}; ++i)
389 converted[i] = pixel[i];
394 for (
unsigned int iteration = 0; iteration < num_iter; ++iteration)
396 for (std::ptrdiff_t relative_y = 0; relative_y < height; ++relative_y)
398 for (std::ptrdiff_t relative_x = 0; relative_x < width; ++relative_x)
400 auto x = relative_x + 1;
401 auto y = relative_y + 1;
402 auto stencil = laplace.compute_laplace(result, point_t(x, y));
403 auto brightness_stencil = brightness(stencil);
404 laplace_function::stencil_type<pixel_type> diffusivity_stencil;
405 std::transform(brightness_stencil.begin(), brightness_stencil.end(),
406 diffusivity_stencil.begin(), diffusivity);
407 laplace_function::stencil_type<pixel_type> product_stencil;
408 std::transform(stencil.begin(), stencil.end(), diffusivity_stencil.begin(),
409 product_stencil.begin(), [](pixel_type lhs, pixel_type rhs) {
410 static_transform(lhs, rhs, lhs, std::multiplies<channel_type>{});
413 static_transform(result(x, y), laplace.reduce(product_stencil),
414 scratch_result(x, y), std::plus<channel_type>{});
418 swap(result, scratch_result);