-
Notifications
You must be signed in to change notification settings - Fork 170
Added FAST feature detector by Edward Rosten #604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Sayan-Chaudhuri
wants to merge
11
commits into
boostorg:develop
Choose a base branch
from
Sayan-Chaudhuri:develop
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 9 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
d058a2d
Added FAST feature detector by Edward Rosten
Sayan-Chaudhuri e94b1fb
Merge pull request #5 from Sayan-Chaudhuri/Sayan-Chaudhuri-patch-5
Sayan-Chaudhuri 75e9214
Test file for FAST feature detector
Sayan-Chaudhuri a37f6e8
Delete fast.hpp
Sayan-Chaudhuri 863d656
Delete fast.cpp
Sayan-Chaudhuri 9cd390b
Issues with newlines,camelcase,template parameters addressed
Sayan-Chaudhuri ef95dcd
Readded the test file with clang formatting
Sayan-Chaudhuri 5ea049d
Fixed Issues with cases,blank lines,eastconst, variable placement,var…
Sayan-Chaudhuri 4604f9c
Inserted blank lines to improve code readability
Sayan-Chaudhuri 7e96e7c
Fix clang-format issues,#include header guards, maximum columns of te…
Sayan-Chaudhuri 6194f9a
Added copyright, fixed clang-format issues
Sayan-Chaudhuri File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
210 changes: 210 additions & 0 deletions
210
include/boost/gil/image_processing/fast_feature_detector.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,210 @@ | ||
| #ifndef BOOST_GIL_IMAGE_PROCESSING_FAST_HPP | ||
| #define BOOST_GIL_IMAGE_PROCESSING_FAST_HPP | ||
|
|
||
| #include <boost/gil/image.hpp> | ||
| #include <boost/gil/image_view.hpp> | ||
| #include <boost/gil/locator.hpp> | ||
| #include <boost/gil/point.hpp> | ||
| #include <algorithm> | ||
| #include <cmath> | ||
| #include <vector> | ||
|
|
||
| namespace boost { namespace gil { | ||
|
Sayan-Chaudhuri marked this conversation as resolved.
|
||
| namespace detail { | ||
|
|
||
| /// \brief Implements the FAST corner detection algorithm by Edward Rosten. | ||
| /// Algorithm :- | ||
| /// 1.Consider a circle of 16 pixels around a given pixel(Bresenham circle) | ||
| /// 2.Decide a threshold t | ||
| /// 3.Let I_p be the intensity of the central pixel. | ||
| /// 4.Find if there are n consecutive pixels in those 16 pixels where the intensities of all pixels are <I_p-t or >I_p+t.Here n is taken as 9. | ||
| /// 5.For step 4, to detect that a pixel is not a corner, check first whether the intensities at pixels from 0 to 6 (with a step of 2)or the 8th pixel from each of them satisfies | ||
| /// condition of step 4.If not,check the same condition on odd numbered pixels or their corresponding 8th pixel | ||
| /// 6.If the checks in step 5 is not affirmative, the given pixel cannot be a corner. | ||
| /// 7.Otherwise, proceed to find whether there are n consecutive pixels to satisfy the criterion. If yes, p is a corner, else no. | ||
| /// | ||
| /// @param buffer -type of source image.Must be grayscale | ||
| /// @param r- row index of the candidate pixel | ||
| /// @param c- column index of candidate pixel | ||
| /// @param points- pixel locations for bresenham circle around the given pixel | ||
| /// @param t- threshold | ||
|
|
||
| template <typename SrcView> | ||
| bool fast_feature_detector(SrcView const& buffer, std::size_t r, std::size_t c, std::vector<point_t>& points, int t) | ||
| { | ||
| auto src_loc = buffer.xy_at(c, r); | ||
| std::vector<int> threshold_indicator; | ||
| std::vector<int> intensity_array(16); | ||
| std::vector<decltype(src_loc.cache_location(0, -1))> pointers(16); | ||
| //stroring intensities of pixels on circumference beforehand to decrease runtime | ||
| for (std::ptrdiff_t i = 0; i < 16; i++) | ||
| { | ||
| pointers[i] = src_loc.cache_location(points[i][0], points[i][1]); | ||
| intensity_array[i] = src_loc[pointers[i]]; | ||
| } | ||
| //calculating the flags to be used during segment test | ||
| auto const I_p = buffer(point_t(c, r)); | ||
| //int low,high; | ||
| std::transform( | ||
| intensity_array.begin(), | ||
| intensity_array.end(), | ||
| back_inserter(threshold_indicator), | ||
| [low = I_p - t, hi = I_p + t](auto const& intensity) { | ||
| if (intensity < low) | ||
| return -1; | ||
| else if (intensity > hi) | ||
| return 1; | ||
| else | ||
| return 0; | ||
| }); | ||
| std::transform( | ||
| intensity_array.begin(), | ||
| intensity_array.end(), | ||
| back_inserter(threshold_indicator), | ||
| [low = I_p - t, hi = I_p + t](auto const& intensity) { | ||
| if (intensity < low) | ||
| return -1; | ||
| else if (intensity > hi) | ||
| return 1; | ||
| else | ||
| return 0; | ||
| }); | ||
| //high speed test for eliminating non-corners | ||
| for (std::ptrdiff_t i = 0; i <= 6; i += 2) | ||
| { | ||
| if (threshold_indicator[i] == 0 && threshold_indicator[i + 8] == 0) | ||
| return false; | ||
| } | ||
| for (std::ptrdiff_t i = 1; i <= 7; i += 2) | ||
| { | ||
| if (threshold_indicator[i] == 0 && threshold_indicator[i + 8] == 0) | ||
| return false; | ||
| } | ||
| //final segment test | ||
| bool is_feature_point = | ||
| threshold_indicator.end() != | ||
| std::search_n(threshold_indicator.begin(), threshold_indicator.end(), 9, -1) || | ||
| threshold_indicator.end() != | ||
| std::search_n(threshold_indicator.begin(), threshold_indicator.end(), 9, 1); | ||
| return is_feature_point; | ||
| } | ||
|
|
||
| ///\brief assigns a score to each detected corner to measure their degree of cornerness. | ||
| /// Algorithm | ||
| /// Perform a binary search on threshold t to find out the maximum threshold | ||
| /// for which a corner remains a corner | ||
| /// | ||
| /// @param src -type of input image | ||
| /// @param i -row index of the detected corner | ||
| /// @param j -column index of the detected corner | ||
| /// @param points -pixel locations for bresenham circle around the given pixel | ||
| /// @param threshold -initial threshold given as input | ||
|
|
||
| template <typename SrcView> | ||
| std::size_t | ||
| calculate_score(SrcView const& src,std::size_t i,std::size_t j, std::vector<point_t>& points,std::size_t threshold) | ||
| { | ||
| std::size_t low = threshold; | ||
| std::size_t high = 255; | ||
| //score measure used= highest threshold for which a corner remains a corner. The cornerness of a corner decreases with increasing threshold | ||
| while (high - low > 1) | ||
| { | ||
| std::size_t mid = (low + high) / 2; | ||
| if (fast_feature_detector(src, i, j, points, mid)) | ||
| { | ||
| low = mid; | ||
| } | ||
| else | ||
| { | ||
| high = mid - 1; | ||
| } | ||
| } | ||
| return low - 1; | ||
| } | ||
| } // namespace detail | ||
|
|
||
| /// \brief public function for using fast feature detector | ||
| /// @param src -type of input image | ||
| /// @param keypoints -vector for storing the locations of keypoints(corners) | ||
| /// @param scores -vector for scores of each detected keypoint | ||
| /// @param nonmax -indicates whether to perform nonmaximum suppression or not | ||
| /// @param threshold -initial threshold given as input | ||
|
|
||
| template <typename SrcView> | ||
| void fast( | ||
| SrcView const& src, | ||
| std::vector<point_t>& keypoints, | ||
| std::vector<std::size_t>& scores, | ||
| bool nonmax = true, | ||
| std::size_t threshold = 10) | ||
| { | ||
| //coordinates of a bresenham circle of radius 3 | ||
| std::vector<point_t> final_points_clockwise{ | ||
| point_t(3, 0), | ||
| point_t(3, 1), | ||
| point_t(2, 2), | ||
| point_t(1, 3), | ||
| point_t(0, 3), | ||
| point_t(-1, 3), | ||
| point_t(-2, 2), | ||
| point_t(-3, 1), | ||
| point_t(-3, 0), | ||
| point_t(-3, -1), | ||
| point_t(-2, -2), | ||
| point_t(-1, -3), | ||
| point_t(0, -3), | ||
| point_t(1, -3), | ||
| point_t(2, -2), | ||
| point_t(3, -1)}; | ||
| //FAST features only calculated on grayscale images | ||
| auto input_image_view = color_converted_view<gray8_pixel_t>(src); | ||
| gray8_image_t fast_image(src.dimensions()); | ||
| // scores to be used during nonmaximum suppression | ||
| gray8_view_t fast_score_matrix = view(fast_image); | ||
| fill_pixels(fast_score_matrix, gray8_pixel_t(0)); | ||
| std::vector<point_t> kp; | ||
|
|
||
| for (std::size_t i = 3; i < src.height() - 3; i++) | ||
| { | ||
| for (std::size_t j = 3; j < src.width() - 3; j++) | ||
| { | ||
| if (detail::fast_feature_detector( | ||
| input_image_view, i, j, final_points_clockwise, threshold)) | ||
| { | ||
| kp.push_back(point_t(j, i)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| for (auto u : kp) | ||
| { | ||
| int score = 0; | ||
| score = detail::calculate_score( | ||
| input_image_view, u[1], u[0], final_points_clockwise, threshold); | ||
| fast_score_matrix(u[0], u[1])[0] = gray8_pixel_t(score); | ||
| } | ||
|
|
||
| for (auto u : kp) | ||
| { | ||
| std::size_t i = u[1]; | ||
| std::size_t j = u[0]; | ||
| std::size_t score = 0; | ||
| score = int(fast_score_matrix(j, i)[0]); | ||
| //performing nonmaximum suppression | ||
| if (!nonmax || score > fast_score_matrix(j - 1, i)[0] && | ||
| score > fast_score_matrix(j + 1, i)[0] && | ||
| score > fast_score_matrix(j - 1, i - 1)[0] && | ||
| score > fast_score_matrix(j, i - 1)[0] && | ||
| score > fast_score_matrix(j + 1, i - 1)[0] && | ||
| score > fast_score_matrix(j - 1, i + 1)[0] && | ||
| score > fast_score_matrix(j, i + 1)[0] && | ||
| score > fast_score_matrix(j + 1, i + 1)[0]) | ||
| { | ||
| keypoints.push_back(u); | ||
| scores.push_back(score); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| }} // namespace boost::gil | ||
| #endif | ||
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| //References- Following papers of Dr.Edward Rosten | ||
| /*1.Fusing points and lines for high performance tracking. | ||
| 2.Machine learning for high-speed corner detection. | ||
| 3.Faster and better: A machine learning approach to corner detection*/ | ||
|
|
||
| #include <boost/gil/extension/io/jpeg.hpp> | ||
| #include <boost/gil/extension/io/png.hpp> | ||
| #include <boost/gil/image_processing/fast_feature_detector.hpp> | ||
| #include <boost/assert.hpp> | ||
| #include <boost/core/lightweight_test.hpp> | ||
| #include <iostream> | ||
|
|
||
| std::uint8_t null_matrix[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | ||
|
|
||
| //testing an image without a feature point | ||
| void test1() | ||
| { | ||
| boost::gil::gray8_view_t image1 = boost::gil::interleaved_view( | ||
| 5, 8, reinterpret_cast<boost::gil::gray8_pixel_t*>(null_matrix), 5); | ||
| std::vector<boost::gil::point_t> keypoints; | ||
| std::vector<std::size_t> scores; | ||
| boost::gil::fast(image1, keypoints, scores, 20); | ||
| std::vector<boost::gil::point_t> expected_keypoints; | ||
| BOOST_ASSERT_MSG( | ||
| expected_keypoints.size() == keypoints.size(), "dimensions do not match for keypoints"); | ||
| } | ||
|
|
||
| //testing color image | ||
| void test2() | ||
|
Sayan-Chaudhuri marked this conversation as resolved.
|
||
| { | ||
| boost::gil::rgb8_image_t input_color_image; | ||
| boost::gil::read_image("box.jpg", input_color_image, boost::gil::jpeg_tag{}); | ||
| std::vector<boost::gil::point_t> keypoints; | ||
| std::vector<std::size_t> scores; | ||
| boost::gil::fast(boost::gil::view(input_color_image), keypoints, scores, 20); | ||
| std::vector<boost::gil::point_t> expected_keypoints{ | ||
| boost::gil::point_t(218, 19), boost::gil::point_t(44, 56), boost::gil::point_t(280, 62), | ||
| boost::gil::point_t(302, 77), boost::gil::point_t(321, 93), boost::gil::point_t(323, 93), | ||
| boost::gil::point_t(329, 96), boost::gil::point_t(45, 105), boost::gil::point_t(101, 110), | ||
| boost::gil::point_t(55, 118), boost::gil::point_t(140, 141), boost::gil::point_t(326, 141), | ||
| boost::gil::point_t(138, 143), boost::gil::point_t(314, 151), boost::gil::point_t(120, 179), | ||
| boost::gil::point_t(130, 186), boost::gil::point_t(128, 187), boost::gil::point_t(132, 191), | ||
| boost::gil::point_t(137, 195), boost::gil::point_t(139, 195), boost::gil::point_t(143, 197), | ||
| boost::gil::point_t(59, 219), boost::gil::point_t(63, 223), boost::gil::point_t(70, 231), | ||
| boost::gil::point_t(75, 237), boost::gil::point_t(81, 244), boost::gil::point_t(303, 261), | ||
| boost::gil::point_t(107, 273), boost::gil::point_t(266, 273), boost::gil::point_t(260, 275), | ||
| boost::gil::point_t(245, 280), boost::gil::point_t(115, 283), boost::gil::point_t(234, 284), | ||
| boost::gil::point_t(231, 285), boost::gil::point_t(216, 289), boost::gil::point_t(125, 293), | ||
| boost::gil::point_t(205, 294), boost::gil::point_t(132, 297), boost::gil::point_t(187, 299), | ||
| boost::gil::point_t(171, 304), boost::gil::point_t(142, 313)}; | ||
|
|
||
| BOOST_ASSERT_MSG( | ||
| expected_keypoints.size() == keypoints.size(), "dimensions do not match for keypoints"); | ||
| BOOST_ASSERT_MSG(expected_keypoints == keypoints, "keypoints do not match"); | ||
| } | ||
|
|
||
| //testing grayscale image | ||
| void test3() | ||
| { | ||
| boost::gil::rgb8_image_t input_color_image; | ||
| boost::gil::read_image("box.jpg", input_color_image, boost::gil::jpeg_tag{}); | ||
| std::vector<boost::gil::point_t> keypoints; | ||
| std::vector<std::size_t> scores; | ||
| std::vector<boost::gil::point_t> expected_keypoints{ | ||
| boost::gil::point_t(218, 19), boost::gil::point_t(44, 56), boost::gil::point_t(280, 62), | ||
| boost::gil::point_t(302, 77), boost::gil::point_t(321, 93), boost::gil::point_t(323, 93), | ||
| boost::gil::point_t(329, 96), boost::gil::point_t(45, 105), boost::gil::point_t(101, 110), | ||
| boost::gil::point_t(55, 118), boost::gil::point_t(140, 141), boost::gil::point_t(326, 141), | ||
| boost::gil::point_t(138, 143), boost::gil::point_t(314, 151), boost::gil::point_t(120, 179), | ||
| boost::gil::point_t(130, 186), boost::gil::point_t(128, 187), boost::gil::point_t(132, 191), | ||
| boost::gil::point_t(137, 195), boost::gil::point_t(139, 195), boost::gil::point_t(143, 197), | ||
| boost::gil::point_t(59, 219), boost::gil::point_t(63, 223), boost::gil::point_t(70, 231), | ||
| boost::gil::point_t(75, 237), boost::gil::point_t(81, 244), boost::gil::point_t(303, 261), | ||
| boost::gil::point_t(107, 273), boost::gil::point_t(266, 273), boost::gil::point_t(260, 275), | ||
| boost::gil::point_t(245, 280), boost::gil::point_t(115, 283), boost::gil::point_t(234, 284), | ||
| boost::gil::point_t(231, 285), boost::gil::point_t(216, 289), boost::gil::point_t(125, 293), | ||
| boost::gil::point_t(205, 294), boost::gil::point_t(132, 297), boost::gil::point_t(187, 299), | ||
| boost::gil::point_t(171, 304), boost::gil::point_t(142, 313)}; | ||
|
|
||
| boost::gil::fast(boost::gil::view(input_color_image), keypoints, scores, 10); | ||
|
|
||
| BOOST_ASSERT_MSG( | ||
| expected_keypoints.size() == keypoints.size(), "dimensions do not match for keypoints"); | ||
| BOOST_ASSERT_MSG(expected_keypoints == keypoints, "keypoints do not match"); | ||
| } | ||
|
|
||
| int main() | ||
| { | ||
| test1(); | ||
| test2(); | ||
| test3(); | ||
| return boost::report_errors(); | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.