This is a follow-up question for SIFT Keypoint Detection for Image in C++, difference_of_gaussian Template Function Implementation for Image in C++, conv2 Template Function Implementation for Image in C++ and imgaussfilt Template Function Implementation for Image in C++. I am trying to implement circle drawing feature in TinyDIP library in C++. The usage of draw_circle
template function here is to plot the maximum gradient magnitude of the neighborhood of each SIFT keypoint in the test image.
The experimental implementation
draw_circle
template function implementation (in fileimage_operations.h
)namespace TinyDIP { // draw_circle template function implementation template<typename ElementT> constexpr static auto draw_circle( const Image<ElementT>& input, std::tuple<std::size_t, std::size_t> central_point, std::size_t radius = 2, ElementT draw_value = ElementT{} ) { if (input.getDimensionality() != 2) { throw std::runtime_error("Unsupported dimension!"); } auto point_x = std::get<0>(central_point); auto point_y = std::get<1>(central_point); auto output = input; auto height = input.getHeight(); auto width = input.getWidth(); #pragma omp parallel for collapse(2) for (std::size_t y = point_y - radius; y <= point_y + radius; ++y) { for (std::size_t x = point_x - radius; x <= point_x + radius; ++x) { if (x >= width || y >= height) { continue; } if(std::abs(std::pow(static_cast<double>(x) - static_cast<double>(point_x), 2.0) + std::pow(static_cast<double>(y) - static_cast<double>(point_y), 2.0) - std::pow(radius, 2)) < radius * 2) { output.at_without_boundary_check(x, y) = draw_value; } } } return output; } }
get_orientation_histogram
template function implementation (in fileimage_operations.h
)namespace TinyDIP { namespace SIFT_impl { // get_orientation_histogram template function implementation template<typename ElementT> requires((std::floating_point<ElementT> || std::integral<ElementT>)) constexpr static auto get_orientation_histogram( const Image<ElementT>& input, std::tuple<std::size_t, std::size_t> point, std::size_t block_size = 3 ) { if (input.getDimensionality() != 2) { throw std::runtime_error("Unsupported dimension!"); } std::vector<double> raw_histogram; raw_histogram.resize(37); for (std::size_t y = std::get<1>(point) - block_size; y <= std::get<1>(point) + block_size; ++y) { for (std::size_t x = std::get<0>(point) - block_size; x <= std::get<0>(point) + block_size; ++x) { if (x >= input.getWidth() || y >= input.getHeight()) { continue; } auto each_pixel_orientation = compute_each_pixel_orientation(subimage(input, 3, 3, x, y)); std::size_t bin_index = static_cast<std::size_t>(std::get<1>(each_pixel_orientation) / 10.0); raw_histogram[bin_index] += std::get<0>(each_pixel_orientation); } } return raw_histogram; } } }
compute_each_pixel_orientation
template function implementation (in fileimage_operations.h
)namespace TinyDIP { namespace SIFT_impl { // compute_each_pixel_orientation template function implementation /* input is 3 * 3 image, calculate the gradient magnitude * M(1, 1) = ((input(2, 1) - input(0, 1))^(2) + (input(1, 2) - input(1, 0))^(2))^(1/2) * orientation * θ(1, 1) = tan^(-1)((input(1, 2) - input(1, 0)) / (input(2, 1) - input(0, 1))) * the value range of orientation is 0° ~ 360° */ template<typename ElementT> constexpr static auto compute_each_pixel_orientation(const Image<ElementT>& input) { if (input.getDimensionality() != 2) { throw std::runtime_error("Unsupported dimension!"); } if (input.getWidth() != 3 || input.getHeight() != 3) throw std::runtime_error("Input size error!"); double gradient_magnitude = std::sqrt( std::pow((static_cast<double>(input.at_without_boundary_check(2, 1)) - static_cast<double>(input.at_without_boundary_check(0, 1))), 2.0) + std::pow((static_cast<double>(input.at_without_boundary_check(1, 2)) - static_cast<double>(input.at_without_boundary_check(1, 0))), 2.0) ); double orientation = std::atan2( static_cast<double>(input.at_without_boundary_check(1, 2)) - static_cast<double>(input.at_without_boundary_check(1, 0)), static_cast<double>(input.at_without_boundary_check(2, 1)) - static_cast<double>(input.at_without_boundary_check(0, 1)) ); orientation *= (180.0 / std::numbers::pi_v<double>); orientation += 180; return std::make_tuple(gradient_magnitude, orientation); } } }
The usage of draw_circle
template function:
int main()
{
auto start = std::chrono::system_clock::now();
std::string file_path = "InputImages/1";
auto bmp1 = TinyDIP::bmp_read(file_path.c_str(), false);
bmp1 = copyResizeBicubic(bmp1, bmp1.getWidth() * 2, bmp1.getHeight() * 2);
auto v_plane = TinyDIP::getVplane(TinyDIP::rgb2hsv(bmp1));
auto SIFT_keypoints = TinyDIP::SIFT_impl::get_potential_keypoint(v_plane);
std::cout << "SIFT_keypoints = " << SIFT_keypoints.size() << "\n";
bmp1 = TinyDIP::draw_points(bmp1, SIFT_keypoints);
for (auto&& each_SIFT_keypoint : SIFT_keypoints)
{
auto orientation_histogram = TinyDIP::SIFT_impl::get_orientation_histogram(v_plane, each_SIFT_keypoint);
RGB rgb{ 255, 255, 255 };
bmp1 = TinyDIP::draw_circle(bmp1, each_SIFT_keypoint, TinyDIP::recursive_max(orientation_histogram), rgb);
}
TinyDIP::bmp_write("test20240816", bmp1);
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
std::cout << "Computation finished at " << std::ctime(&end_time) << "elapsed time: " << elapsed_seconds.count() << " seconds\n";
return EXIT_SUCCESS;
}
The output image:
All suggestions are welcome.
The summary information:
Which question it is a follow-up to?
SIFT Keypoint Detection for Image in C++,
difference_of_gaussian Template Function Implementation for Image in C++,
conv2 Template Function Implementation for Image in C++ and
imgaussfilt Template Function Implementation for Image in C++
What changes has been made in the code since last question?
I am trying to implement circle drawing feature in TinyDIP library in C++.
Why a new review is being asked for?
Please review the implementation of
draw_circle
template function and its tests.