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)
160 auto first = stencil.begin();
161 auto last = stencil.end();
169 for (std::size_t index : {1u, 3u, 5u, 7u})
171 static_transform(result, stencil[index], result, std::plus<channel_type>{});
174 static_fill(delta_t_pixel, delta_t);
175 static_transform(result, delta_t_pixel, result, std::multiplies<channel_type>{});
189 double delta_t = 0.125;
191 template <
typename SubImageView>
192 stencil_type<typename SubImageView::value_type> compute_laplace(SubImageView
view,
195 stencil_type<typename SubImageView::value_type> stencil;
196 auto out = stencil.begin();
197 auto current =
view(origin);
200 for (
auto offset : offsets)
202 static_transform(
view(origin.x + offset.x, origin.y + offset.y), current, *out++,
203 std::minus<channel_type>{});
209 template <
typename Pixel>
210 Pixel reduce(
const stencil_type<Pixel>& stencil)
218 for (std::size_t index : {1u, 3u, 5u, 7u})
220 static_transform(result, stencil[index], result, std::plus<channel_type>{});
223 for (std::size_t index : {0u, 2u, 4u, 6u})
227 static_transform(stencil[index], half_pixel, half_pixel,
228 std::multiplies<channel_type>{});
229 static_transform(result, half_pixel, result, std::plus<channel_type>{});
233 static_fill(delta_t_pixel, delta_t);
234 static_transform(result, delta_t_pixel, result, std::multiplies<channel_type>{});
241 namespace brightness_function {
242 using laplace_function::stencil_type;
245 template <
typename Pixel>
246 stencil_type<Pixel> operator()(
const stencil_type<Pixel>& stencil)
257 using pixel_type = rgb32f_pixel_t;
258 stencil_type<pixel_type> operator()(
const stencil_type<pixel_type>& stencil)
260 stencil_type<pixel_type> output;
261 std::transform(stencil.begin(), stencil.end(), output.begin(), [](
const pixel_type& pixel) {
262 float32_t luminance = 0.2126f * pixel[0] + 0.7152f * pixel[1] + 0.0722f * pixel[2];
263 pixel_type result_pixel;
264 static_fill(result_pixel, luminance);
273 enum class matlab_connectivity
279 enum class matlab_conduction_method
285 template <
typename InputView,
typename OutputView>
286 void classic_anisotropic_diffusion(
const InputView& input,
const OutputView& output,
287 unsigned int num_iter,
double kappa)
289 anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{},
290 brightness_function::identity{},
291 conductivity::perona_malik_conductivity{kappa});
294 template <
typename InputView,
typename OutputView>
295 void matlab_anisotropic_diffusion(
const InputView& input,
const OutputView& output,
296 unsigned int num_iter,
double kappa,
297 matlab_connectivity connectivity,
298 matlab_conduction_method conduction_method)
300 if (connectivity == matlab_connectivity::minimal)
302 if (conduction_method == matlab_conduction_method::exponential)
304 anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{},
305 brightness_function::identity{},
306 conductivity::gaussian_conductivity{kappa});
308 else if (conduction_method == matlab_conduction_method::quadratic)
310 anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{},
311 brightness_function::identity{},
312 conductivity::gaussian_conductivity{kappa});
316 throw std::logic_error(
"unhandled conduction method found");
319 else if (connectivity == matlab_connectivity::maximal)
321 if (conduction_method == matlab_conduction_method::exponential)
323 anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{},
324 brightness_function::identity{},
325 conductivity::gaussian_conductivity{kappa});
327 else if (conduction_method == matlab_conduction_method::quadratic)
329 anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_5points{},
330 brightness_function::identity{},
331 conductivity::gaussian_conductivity{kappa});
335 throw std::logic_error(
"unhandled conduction method found");
340 throw std::logic_error(
"unhandled connectivity found");
344 template <
typename InputView,
typename OutputView>
345 void default_anisotropic_diffusion(
const InputView& input,
const OutputView& output,
346 unsigned int num_iter,
double kappa)
348 anisotropic_diffusion(input, output, num_iter, laplace_function::stencil_9points_standard{},
349 brightness_function::identity{}, conductivity::gaussian_conductivity{kappa});
361 template <
typename InputView,
typename OutputView,
362 typename LaplaceStrategy = laplace_function::stencil_9points_standard,
363 typename BrightnessFunction = brightness_function::identity,
364 typename DiffusivityFunction = conductivity::gaussian_conductivity>
365 void anisotropic_diffusion(
const InputView& input,
const OutputView& output,
unsigned int num_iter,
366 LaplaceStrategy laplace, BrightnessFunction brightness,
367 DiffusivityFunction diffusivity)
369 using input_pixel_type =
typename InputView::value_type;
370 using pixel_type =
typename OutputView::value_type;
371 using channel_type =
typename channel_type<pixel_type>::type;
372 using computation_image = image<pixel_type>;
373 const auto width = input.width();
374 const auto height = input.height();
375 const point_t dims(width, height);
376 const auto zero_pixel = []() {
378 static_fill(pixel,
static_cast<channel_type
>(0));
382 computation_image result_image(width + 2, height + 2, zero_pixel);
383 auto result =
view(result_image);
384 computation_image scratch_result_image(width + 2, height + 2, zero_pixel);
385 auto scratch_result =
view(scratch_result_image);
387 [](
const input_pixel_type& pixel) {
388 pixel_type converted;
389 for (std::size_t i = 0; i < num_channels<pixel_type>{}; ++i)
391 converted[i] = pixel[i];
396 for (
unsigned int iteration = 0; iteration < num_iter; ++iteration)
398 for (std::ptrdiff_t relative_y = 0; relative_y < height; ++relative_y)
400 for (std::ptrdiff_t relative_x = 0; relative_x < width; ++relative_x)
402 auto x = relative_x + 1;
403 auto y = relative_y + 1;
404 auto stencil = laplace.compute_laplace(result, point_t(x, y));
405 auto brightness_stencil = brightness(stencil);
406 laplace_function::stencil_type<pixel_type> diffusivity_stencil;
407 std::transform(brightness_stencil.begin(), brightness_stencil.end(),
408 diffusivity_stencil.begin(), diffusivity);
409 laplace_function::stencil_type<pixel_type> product_stencil;
410 std::transform(stencil.begin(), stencil.end(), diffusivity_stencil.begin(),
411 product_stencil.begin(), [](pixel_type lhs, pixel_type rhs) {
412 static_transform(lhs, rhs, lhs, std::multiplies<channel_type>{});
415 static_transform(result(x, y), laplace.reduce(product_stencil),
416 scratch_result(x, y), std::plus<channel_type>{});
420 swap(result, scratch_result);