Skip to content

Commit

Permalink
feat: Added apply_rasterizer() free function (#695)
Browse files Browse the repository at this point in the history
This PR implements the gil::apply_rasterizer()
free function mentioned in #680.
  • Loading branch information
marco-langer authored Jun 27, 2022
1 parent adddbec commit d5492e1
Show file tree
Hide file tree
Showing 14 changed files with 240 additions and 121 deletions.
10 changes: 3 additions & 7 deletions example/hough_transform_circle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <boost/gil.hpp>
#include <boost/gil/extension/io/png.hpp>
#include <boost/gil/extension/rasterization/circle.hpp>

#include <iostream>
#include <limits>
Expand All @@ -33,13 +34,8 @@ int main()

const std::ptrdiff_t circle_radius = 16;
const gil::point_t circle_center = {64, 64};
const auto rasterizer = gil::midpoint_circle_rasterizer{};
std::vector<gil::point_t> circle_points(rasterizer.point_count(circle_radius));
rasterizer(circle_radius, circle_center, circle_points.begin());
for (const auto& point : circle_points)
{
input(point) = std::numeric_limits<gil::uint8_t>::max();
}
const auto rasterizer = gil::midpoint_circle_rasterizer{circle_center, circle_radius};
gil::apply_rasterizer(input, rasterizer, gil::gray8_pixel_t{255});

const auto radius_parameter =
gil::hough_parameter<std::ptrdiff_t>::from_step_count(circle_radius, 3, 3);
Expand Down
15 changes: 4 additions & 11 deletions example/rasterizer_circle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@

#include <boost/gil.hpp>
#include <boost/gil/extension/io/png.hpp>

#include <cmath>
#include <limits>
#include <vector>
#include <boost/gil/extension/rasterization/circle.hpp>

namespace gil = boost::gil;

Expand All @@ -30,14 +27,10 @@ int main()
gil::gray8_image_t buffer_image(size, size);
auto buffer = gil::view(buffer_image);

const gil::point_t center = {128, 128};
const std::ptrdiff_t radius = 64;
const auto rasterizer = gil::trigonometric_circle_rasterizer{};
std::vector<gil::point_t> circle_points(rasterizer.point_count(radius));
rasterizer(radius, {128, 128}, circle_points.begin());
for (const auto& point : circle_points)
{
buffer(point) = std::numeric_limits<gil::uint8_t>::max();
}
const auto rasterizer = gil::trigonometric_circle_rasterizer{center, radius};
gil::apply_rasterizer(buffer, rasterizer, gil::gray8_pixel_t{255});

gil::write_view("circle.png", buffer, gil::png_tag{});
}
13 changes: 6 additions & 7 deletions example/rasterizer_ellipse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,16 @@ int main()
// and vertical semi-axis respectively.

gil::gray8_image_t gray_buffer_image(256, 256);
auto gray_ellipse_rasterizer = gil::midpoint_ellipse_rasterizer{};
gray_ellipse_rasterizer(view(gray_buffer_image), gil::gray8_pixel_t{128}, {128, 128}, {100, 50});
auto gray_ellipse_rasterizer = gil::midpoint_ellipse_rasterizer{{128, 128}, {100, 50}};
gil::apply_rasterizer(view(gray_buffer_image), gray_ellipse_rasterizer, gil::gray8_pixel_t{128});

gil::rgb8_image_t rgb_buffer_image(256, 256);
auto rgb_ellipse_rasterizer = gil::midpoint_ellipse_rasterizer{};
rgb_ellipse_rasterizer(view(rgb_buffer_image), gil::rgb8_pixel_t{0, 0, 255}, {128, 128}, {50, 100});
auto rgb_ellipse_rasterizer = gil::midpoint_ellipse_rasterizer{{128, 128}, {50, 100}};
gil::apply_rasterizer(view(rgb_buffer_image), rgb_ellipse_rasterizer, gil::rgb8_pixel_t{0, 0, 255});

gil::rgb8_image_t rgb_buffer_image_out_of_bound(256, 256);
auto rgb_ellipse_rasterizer_out_of_bound = gil::midpoint_ellipse_rasterizer{};
rgb_ellipse_rasterizer_out_of_bound(view(rgb_buffer_image_out_of_bound), gil::rgb8_pixel_t{255, 0, 0},
{100, 100}, {160, 160});
auto rgb_ellipse_rasterizer_out_of_bound = gil::midpoint_ellipse_rasterizer{{100, 100}, {160, 160}};
apply_rasterizer(view(rgb_buffer_image_out_of_bound), rgb_ellipse_rasterizer_out_of_bound, gil::rgb8_pixel_t{255, 0, 0});

gil::write_view("rasterized_ellipse_gray.jpg", view(gray_buffer_image), gil::jpeg_tag{});
gil::write_view("rasterized_ellipse_rgb.jpg", view(rgb_buffer_image), gil::jpeg_tag{});
Expand Down
15 changes: 3 additions & 12 deletions example/rasterizer_line.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@

#include <boost/gil.hpp>
#include <boost/gil/extension/io/png.hpp>

#include <limits>
#include <vector>
#include <boost/gil/extension/rasterization/line.hpp>

namespace gil = boost::gil;

Expand All @@ -28,18 +26,11 @@ const std::ptrdiff_t size = 256;

void line_bresenham(std::ptrdiff_t width, std::ptrdiff_t height, const std::string& output_name)
{
const auto rasterizer = gil::bresenham_line_rasterizer{};
std::vector<gil::point_t> line_points(rasterizer.point_count(width, height));
const auto rasterizer = gil::bresenham_line_rasterizer{{0, 0}, {width - 1, height - 1}};

gil::gray8_image_t image(size, size);
auto view = gil::view(image);

rasterizer({0, 0}, {width - 1, height - 1}, line_points.begin());
for (const auto& point : line_points)
{
view(point) = std::numeric_limits<gil::uint8_t>::max();
}

gil::apply_rasterizer(view, rasterizer, gil::gray8_pixel_t{255});
gil::write_view(output_name, view, gil::png_tag{});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,15 @@ void hough_circle_transform_brute(const ImageView& input,
const hough_parameter<std::ptrdiff_t> radius_parameter,
const hough_parameter<std::ptrdiff_t> x_parameter,
const hough_parameter<std::ptrdiff_t>& y_parameter,
ForwardIterator d_first, Rasterizer rasterizer)
ForwardIterator d_first, Rasterizer)
{
for (std::size_t radius_index = 0; radius_index < radius_parameter.step_count; ++radius_index)
{
const auto radius = radius_parameter.start_point +
radius_parameter.step_size * static_cast<std::ptrdiff_t>(radius_index);
std::vector<point_t> circle_points(rasterizer.point_count(radius));
rasterizer(radius, {0, 0}, circle_points.begin());
Rasterizer rasterizer{point_t{}, radius};
std::vector<point_t> circle_points(rasterizer.point_count());
rasterizer(circle_points.begin());
// sort by scanline to improve cache coherence for row major images
std::sort(circle_points.begin(), circle_points.end(),
[](const point_t& lhs, const point_t& rhs) { return lhs.y < rhs.y; });
Expand Down
28 changes: 28 additions & 0 deletions include/boost/gil/extension/rasterization/apply_rasterizer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef BOOST_GIL_EXTENSION_RASTERIZATION_APPLY_RASTERIZER
#define BOOST_GIL_EXTENSION_RASTERIZATION_APPLY_RASTERIZER

namespace boost { namespace gil {

namespace detail {

template <typename View, typename Rasterizer, typename Pixel, typename Tag>
struct apply_rasterizer_op
{
void operator()(
View const& view, Rasterizer const& rasterizer, Pixel const& pixel);
};

} // namespace detail

template <typename View, typename Rasterizer, typename Pixel>
void apply_rasterizer(
View const& view, Rasterizer const& rasterizer, Pixel const& pixel)
{
using tag_t = typename Rasterizer::type;
detail::apply_rasterizer_op<View, Rasterizer, Pixel, tag_t>{}(
view, rasterizer, pixel);
}

}} // namespace boost::gil

#endif
106 changes: 78 additions & 28 deletions include/boost/gil/extension/rasterization/circle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@
#define BOOST_GIL_EXTENSION_RASTERIZATION_CIRCLE_HPP

#include <boost/gil/detail/math.hpp>
#include <boost/gil/extension/rasterization/apply_rasterizer.hpp>
#include <boost/gil/point.hpp>

#include <cmath>
#include <cstddef>
#include <vector>

namespace boost { namespace gil {

struct circle_rasterizer_t{};

/// \defgroup CircleRasterization
/// \ingroup Rasterization
/// \brief Circle rasterization algorithms
Expand All @@ -32,39 +36,49 @@ namespace boost { namespace gil {
/// produces quite round like shapes.
struct trigonometric_circle_rasterizer
{
using type = circle_rasterizer_t;

/// \brief Creates a trigonometric circle rasterizer
/// \param center_point - Point containing positive integer x co-ordinate and y co-ordinate of the
/// center respectively.
/// \param circle_radius - Radius of the circle
trigonometric_circle_rasterizer(point_t center_point, std::ptrdiff_t circle_radius)
: center(center_point), radius(circle_radius)
{}

/// \brief Calculates minimum angle step that is distinguishable when walking on circle
///
/// It is important to not have disconnected circle and to not compute unnecessarily,
/// thus the result of this function is used when rendering.
double minimum_angle_step(std::ptrdiff_t radius) const noexcept
double minimum_angle_step() const noexcept
{
const auto diameter = radius * 2 - 1;
return std::atan2(1.0, diameter);
}

/// \brief Calculate the amount of points that rasterizer will output
std::ptrdiff_t point_count(std::ptrdiff_t radius) const noexcept
std::ptrdiff_t point_count() const noexcept
{
return 8 * static_cast<std::ptrdiff_t>(
std::round(detail::pi / 4 / minimum_angle_step(radius)) + 1);
std::round(detail::pi / 4 / minimum_angle_step()) + 1);
}

/// \brief perform rasterization and output into d_first
template <typename RandomAccessIterator>
void operator()(std::ptrdiff_t radius, point_t offset, RandomAccessIterator d_first) const
template <typename OutputIterator>
void operator()(OutputIterator d_first) const
{
const double minimum_angle_step = std::atan2(1.0, radius);
auto translate_mirror_points = [&d_first, offset](point_t p) {
*d_first++ = point_t{offset.x + p.x, offset.y + p.y};
*d_first++ = point_t{offset.x + p.x, offset.y - p.y};
*d_first++ = point_t{offset.x - p.x, offset.y + p.y};
*d_first++ = point_t{offset.x - p.x, offset.y - p.y};
*d_first++ = point_t{offset.x + p.y, offset.y + p.x};
*d_first++ = point_t{offset.x + p.y, offset.y - p.x};
*d_first++ = point_t{offset.x - p.y, offset.y + p.x};
*d_first++ = point_t{offset.x - p.y, offset.y - p.x};
auto translate_mirror_points = [this, &d_first](point_t p) {
*d_first++ = point_t{center.x + p.x, center.y + p.y};
*d_first++ = point_t{center.x + p.x, center.y - p.y};
*d_first++ = point_t{center.x - p.x, center.y + p.y};
*d_first++ = point_t{center.x - p.x, center.y - p.y};
*d_first++ = point_t{center.x + p.y, center.y + p.x};
*d_first++ = point_t{center.x + p.y, center.y - p.x};
*d_first++ = point_t{center.x - p.y, center.y + p.x};
*d_first++ = point_t{center.x - p.y, center.y - p.x};
};
const std::ptrdiff_t iteration_count = point_count(radius) / 8;
const std::ptrdiff_t iteration_count = point_count() / 8;
double angle = 0;
// do note that + 1 was done inside count estimation, thus <= is not needed, only <
for (std::ptrdiff_t i = 0; i < iteration_count; ++i, angle += minimum_angle_step)
Expand All @@ -74,6 +88,9 @@ struct trigonometric_circle_rasterizer
translate_mirror_points({x, y});
}
}

point_t center;
std::ptrdiff_t radius;
};

/// \ingroup CircleRasterization
Expand All @@ -84,8 +101,18 @@ struct trigonometric_circle_rasterizer
/// https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
struct midpoint_circle_rasterizer
{
using type = circle_rasterizer_t;

/// \brief Creates a midpoint circle rasterizer
/// \param center_point - Point containing positive integer x co-ordinate and y co-ordinate of the
/// center respectively.
/// \param circle_radius - Radius of the circle
midpoint_circle_rasterizer(point_t center_point, std::ptrdiff_t circle_radius)
: center(center_point), radius(circle_radius)
{}

/// \brief Calculate the amount of points that rasterizer will output
std::ptrdiff_t point_count(std::ptrdiff_t radius) const noexcept
std::ptrdiff_t point_count() const noexcept
{
// the reason for pulling 8 out is so that when the expression radius * cos(45 degrees)
// is used, it would yield the same result as here
Expand All @@ -95,20 +122,20 @@ struct midpoint_circle_rasterizer
}

/// \brief perform rasterization and output into d_first
template <typename RAIterator>
void operator()(std::ptrdiff_t radius, point_t offset, RAIterator d_first) const
template <typename OutputIterator>
void operator()(OutputIterator d_first) const
{
auto translate_mirror_points = [&d_first, offset](point_t p) {
*d_first++ = point_t{offset.x + p.x, offset.y + p.y};
*d_first++ = point_t{offset.x + p.x, offset.y - p.y};
*d_first++ = point_t{offset.x - p.x, offset.y + p.y};
*d_first++ = point_t{offset.x - p.x, offset.y - p.y};
*d_first++ = point_t{offset.x + p.y, offset.y + p.x};
*d_first++ = point_t{offset.x + p.y, offset.y - p.x};
*d_first++ = point_t{offset.x - p.y, offset.y + p.x};
*d_first++ = point_t{offset.x - p.y, offset.y - p.x};
auto translate_mirror_points = [this, &d_first](point_t p) {
*d_first++ = point_t{center.x + p.x, center.y + p.y};
*d_first++ = point_t{center.x + p.x, center.y - p.y};
*d_first++ = point_t{center.x - p.x, center.y + p.y};
*d_first++ = point_t{center.x - p.x, center.y - p.y};
*d_first++ = point_t{center.x + p.y, center.y + p.x};
*d_first++ = point_t{center.x + p.y, center.y - p.x};
*d_first++ = point_t{center.x - p.y, center.y + p.x};
*d_first++ = point_t{center.x - p.y, center.y - p.x};
};
std::ptrdiff_t iteration_distance = point_count(radius) / 8;
std::ptrdiff_t iteration_distance = point_count() / 8;
std::ptrdiff_t y_current = radius;
std::ptrdiff_t r_squared = radius * radius;
translate_mirror_points({0, y_current});
Expand All @@ -122,8 +149,31 @@ struct midpoint_circle_rasterizer
translate_mirror_points({x, y_current});
}
}

point_t center;
std::ptrdiff_t radius;
};

namespace detail {

template <typename View, typename Rasterizer, typename Pixel>
struct apply_rasterizer_op<View, Rasterizer, Pixel, circle_rasterizer_t>
{
void operator()(
View const& view, Rasterizer const& rasterizer, Pixel const& pixel)
{
std::vector<point_t> trajectory(rasterizer.point_count());
rasterizer(std::begin(trajectory));

for (auto const& point : trajectory)
{
view(point) = pixel;
}
}
};

} //namespace detail

}} // namespace boost::gil

#endif
Loading

0 comments on commit d5492e1

Please sign in to comment.