Skip to content

Commit

Permalink
Implement hstack and vstack (boostorg#506)
Browse files Browse the repository at this point in the history
Allow non-equal dims along stack dim
Width can be different in hstack, height can be different in vstack
  • Loading branch information
simmplecoder authored and meshtag committed Apr 22, 2021
1 parent dab9242 commit ea18604
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 80 deletions.
148 changes: 68 additions & 80 deletions example/harris.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#include <boost/gil/image.hpp>
#include <boost/gil/image_view.hpp>
#include <boost/gil/extension/io/png.hpp>
#include <boost/gil/image_processing/numeric.hpp>
#include <boost/gil/image_processing/harris.hpp>
#include <boost/gil/extension/numeric/convolve.hpp>
#include <vector>
#include <boost/gil/image.hpp>
#include <boost/gil/image_processing/harris.hpp>
#include <boost/gil/image_processing/numeric.hpp>
#include <boost/gil/image_view.hpp>
#include <boost/gil/typedefs.hpp>

#include "hvstack.hpp"

#include <fstream>
#include <functional>
#include <set>
#include <iostream>
#include <fstream>
#include <set>
#include <vector>

namespace gil = boost::gil;

Expand All @@ -29,24 +33,28 @@ gil::gray8_image_t to_grayscale(gil::rgb8_view_t original)
gil::gray8_image_t output_image(original.dimensions());
auto output = gil::view(output_image);
constexpr double max_channel_intensity = (std::numeric_limits<std::uint8_t>::max)();
for (long int y = 0; y < original.height(); ++y) {
for (long int x = 0; x < original.width(); ++x) {
for (long int y = 0; y < original.height(); ++y)
{
for (long int x = 0; x < original.width(); ++x)
{
// scale the values into range [0, 1] and calculate linear intensity
double red_intensity = original(x, y).at(std::integral_constant<int, 0>{})
/ max_channel_intensity;
double green_intensity = original(x, y).at(std::integral_constant<int, 1>{})
/ max_channel_intensity;
double blue_intensity = original(x, y).at(std::integral_constant<int, 2>{})
/ max_channel_intensity;
auto linear_luminosity = 0.2126 * red_intensity
+ 0.7152 * green_intensity
+ 0.0722 * blue_intensity;
double red_intensity =
original(x, y).at(std::integral_constant<int, 0>{}) / max_channel_intensity;
double green_intensity =
original(x, y).at(std::integral_constant<int, 1>{}) / max_channel_intensity;
double blue_intensity =
original(x, y).at(std::integral_constant<int, 2>{}) / max_channel_intensity;
auto linear_luminosity =
0.2126 * red_intensity + 0.7152 * green_intensity + 0.0722 * blue_intensity;

// perform gamma adjustment
double gamma_compressed_luminosity = 0;
if (linear_luminosity < 0.0031308) {
if (linear_luminosity < 0.0031308)
{
gamma_compressed_luminosity = linear_luminosity * 12.92;
} else {
}
else
{
gamma_compressed_luminosity = 1.055 * std::pow(linear_luminosity, 1 / 2.4) - 0.055;
}

Expand All @@ -62,45 +70,43 @@ void apply_gaussian_blur(gil::gray8_view_t input_view, gil::gray8_view_t output_
{
constexpr static auto filterHeight = 5ull;
constexpr static auto filterWidth = 5ull;
constexpr static double filter[filterHeight][filterWidth] =
{
2, 4, 6, 4, 2,
4, 9, 12, 9, 4,
5, 12, 15, 12, 5,
4, 9, 12, 9, 4,
2, 4, 5, 4, 2,
constexpr static double filter[filterHeight][filterWidth] = {
2, 4, 6, 4, 2, 4, 9, 12, 9, 4, 5, 12, 15, 12, 5, 4, 9, 12, 9, 4, 2, 4, 5, 4, 2,
};
constexpr double factor = 1.0 / 159;
constexpr double bias = 0.0;

const auto height = input_view.height();
const auto width = input_view.width();
for (long x = 0; x < width; ++x) {
for (long y = 0; y < height; ++y) {
for (long x = 0; x < width; ++x)
{
for (long y = 0; y < height; ++y)
{
double intensity = 0.0;
for (size_t filter_y = 0; filter_y < filterHeight; ++filter_y) {
for (size_t filter_x = 0; filter_x < filterWidth; ++filter_x) {
for (size_t filter_y = 0; filter_y < filterHeight; ++filter_y)
{
for (size_t filter_x = 0; filter_x < filterWidth; ++filter_x)
{
int image_x = x - filterWidth / 2 + filter_x;
int image_y = y - filterHeight / 2 + filter_y;
if (image_x >= input_view.width() || image_x < 0
|| image_y >= input_view.height() || image_y < 0) {
if (image_x >= input_view.width() || image_x < 0 ||
image_y >= input_view.height() || image_y < 0)
{
continue;
}
auto& pixel = input_view(image_x, image_y);
intensity += pixel.at(std::integral_constant<int, 0>{})
* filter[filter_y][filter_x];
intensity +=
pixel.at(std::integral_constant<int, 0>{}) * filter[filter_y][filter_x];
}
}
auto& pixel = output_view(gil::point_t(x, y));
pixel = (std::min)((std::max)(int(factor * intensity + bias), 0), 255);
}

}
}

std::vector<gil::point_t> suppress(
gil::gray32f_view_t harris_response,
double harris_response_threshold)
std::vector<gil::point_t> suppress(gil::gray32f_view_t harris_response,
double harris_response_threshold)
{
std::vector<gil::point_t> corner_points;
for (gil::gray32f_view_t::coord_t y = 1; y < harris_response.height() - 1; ++y)
Expand All @@ -111,29 +117,17 @@ std::vector<gil::point_t> suppress(
return pixel.at(std::integral_constant<int, 0>{});
};
double values[9] = {
value(harris_response(x - 1, y - 1)),
value(harris_response(x, y - 1)),
value(harris_response(x + 1, y - 1)),
value(harris_response(x - 1, y)),
value(harris_response(x, y)),
value(harris_response(x + 1, y)),
value(harris_response(x - 1, y + 1)),
value(harris_response(x, y + 1)),
value(harris_response(x + 1, y + 1))
};
value(harris_response(x - 1, y - 1)), value(harris_response(x, y - 1)),
value(harris_response(x + 1, y - 1)), value(harris_response(x - 1, y)),
value(harris_response(x, y)), value(harris_response(x + 1, y)),
value(harris_response(x - 1, y + 1)), value(harris_response(x, y + 1)),
value(harris_response(x + 1, y + 1))};

auto maxima = *std::max_element(
values,
values + 9,
[](double lhs, double rhs)
{
return lhs < rhs;
}
);
auto maxima = *std::max_element(values, values + 9,
[](double lhs, double rhs) { return lhs < rhs; });

if (maxima == value(harris_response(x, y))
&& std::count(values, values + 9, maxima) == 1
&& maxima >= harris_response_threshold)
if (maxima == value(harris_response(x, y)) &&
std::count(values, values + 9, maxima) == 1 && maxima >= harris_response_threshold)
{
corner_points.emplace_back(x, y);
}
Expand All @@ -147,8 +141,9 @@ int main(int argc, char* argv[])
{
if (argc != 6)
{
std::cout << "usage: " << argv[0] << " <input.png> <odd-window-size>"
" <discrimination-constant> <harris-response-threshold> <output.png>\n";
std::cout << "usage: " << argv[0]
<< " <input.png> <odd-window-size>"
" <discrimination-constant> <harris-response-threshold> <output.png>\n";
return -1;
}

Expand All @@ -159,7 +154,8 @@ int main(int argc, char* argv[])
gil::rgb8_image_t input_image;

gil::read_image(argv[1], input_image, gil::png_tag{});

auto original_image = input_image;
auto original_view = gil::view(original_image);
auto input_view = gil::view(input_image);
auto grayscaled = to_grayscale(input_view);
gil::gray8_image_t smoothed_image(grayscaled.dimensions());
Expand All @@ -178,30 +174,22 @@ int main(int argc, char* argv[])
gil::gray32f_image_t m11(x_gradient.dimensions());
gil::gray32f_image_t m12_21(x_gradient.dimensions());
gil::gray32f_image_t m22(x_gradient.dimensions());
gil::compute_tensor_entries(
x_gradient,
y_gradient,
gil::view(m11),
gil::view(m12_21),
gil::view(m22)
);
gil::compute_tensor_entries(x_gradient, y_gradient, gil::view(m11), gil::view(m12_21),
gil::view(m22));

gil::gray32f_image_t harris_response(x_gradient.dimensions());
auto gaussian_kernel = gil::generate_gaussian_kernel(window_size, 0.84089642);
gil::compute_harris_responses(
gil::view(m11),
gil::view(m12_21),
gil::view(m22),
gaussian_kernel,
discrimnation_constant,
gil::view(harris_response)
);
gil::compute_harris_responses(gil::view(m11), gil::view(m12_21), gil::view(m22),
gaussian_kernel, discrimnation_constant,
gil::view(harris_response));

auto corner_points = suppress(gil::view(harris_response), harris_response_threshold);
for (auto point: corner_points)
for (auto point : corner_points)
{
input_view(point) = gil::rgb8_pixel_t(0, 0, 0);
input_view(point).at(std::integral_constant<int, 1>{}) = 255;
}
gil::write_view(argv[5], input_view, gil::png_tag{});
auto stacked = gil::hstack(std::vector<gil::rgb8_view_t>{original_view, input_view});

gil::write_view(argv[5], gil::view(stacked), gil::png_tag{});
}
116 changes: 116 additions & 0 deletions example/hvstack.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include "boost/gil/image_view_factory.hpp"
#include <boost/gil/image.hpp>
#include <boost/gil/image_view.hpp>
#include <numeric>
#include <stdexcept>
#include <vector>

namespace boost { namespace gil {
template <typename View>
void hstack(const std::vector<View>& views, const View& output_view)
{
if (views.size() == 0)
{
throw std::invalid_argument("empty views vector is passed - cannot create stacked image");
}

auto height = views.front().height();
auto width = views.front().width();
for (const auto& view : views)
{
if (view.height() != height)
{
throw std::invalid_argument("one or many views are not of the same height");
}
}

std::ptrdiff_t full_width =
std::accumulate(views.begin(), views.end(), 0,
[](std::ptrdiff_t old, const View& view) { return old + view.width(); });
if (output_view.width() != full_width || output_view.height() != height)
{
throw std::invalid_argument("the destination view is not of the right dimensions");
}

std::ptrdiff_t current_x = 0;
for (std::size_t i = 0; i < views.size(); ++i)
{
auto subview =
subimage_view(output_view, current_x, 0, views[i].width(), views[i].height());
copy_pixels(views[i], subview);
current_x += views[i].width();
}
}

template <typename View>
image<typename View::value_type> hstack(const std::vector<View>& views)
{
if (views.size() == 0)
{
throw std::invalid_argument("empty views vector is passed - cannot create stacked image");
}

auto dimensions = views.front().dimensions();
std::ptrdiff_t full_width =
std::accumulate(views.begin(), views.end(), 0,
[](std::ptrdiff_t old, const View& view) { return old + view.width(); });
std::ptrdiff_t height = views.front().height();
image<typename View::value_type> result_image(full_width, height);
hstack(views, view(result_image));
return result_image;
}

template <typename View>
void vstack(const std::vector<View>& views, const View& output_view)
{
if (views.size() == 0)
{
throw std::invalid_argument("empty views vector is passed - cannot create stacked image");
}

auto full_height =
std::accumulate(views.begin(), views.end(), 0,
[](std::ptrdiff_t old, const View& view) { return old + view.height(); });
std::ptrdiff_t width = views.front().height();

for (const auto& view : views)
{
if (view.width() != width)
{
throw std::invalid_argument("one or many views are not of the same width");
}
}

if (output_view != full_height || output_view.width() != width)
{
throw std::invalid_argument("the destination view is not of the right dimensions");
}

std::ptrdiff_t current_y = 0;
for (std::size_t i = 0; i < views.size(); ++i)
{
auto subview =
subimage_view(output_view, 0, current_y, views[i].width(), views[i].height());
copy_pixels(views[i], subview);
current_y += views[i].height();
}
}

template <typename View>
image<typename View::value_type> vstack(const std::vector<View>& views)
{
if (views.size() == 0)
{
throw std::invalid_argument("empty views vector is passed - cannot create stacked image");
}

auto full_height =
std::accumulate(views.begin(), views.end(), 0,
[](std::ptrdiff_t old, const View& view) { return old + view.height(); });
std::ptrdiff_t width = views.front().height();

image<typename View::value_type> result_image(width, full_height);
hstack(views, view(result_image));
return result_image;
}
}} // namespace boost::gil

0 comments on commit ea18604

Please sign in to comment.