Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions include/boost/gil/image_processing/fast.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#ifndef BOOST_GIL_IMAGE_PROCESSING_FAST_HPP
#define BOOST_GIL_IMAGE_PROCESSING_FAST_HPP

#include <boost/gil/image_view.hpp>
#include <boost/gil/image.hpp>
#include <boost/gil/locator.hpp>
#include <boost/gil/point.hpp>
#include<vector>
#include<algorithm>
#include<cmath>
namespace boost{namespace gil{
namespace detail{
template<typename T,typename U>
bool FastCornerDetector(const T& buffer,int r,int c,std::vector<U>&points,int t)
{
int valid_points_count=16;
auto src_loc = buffer.xy_at(c,r);
std::vector<int>threshold_indicator,intensity_array(16);
std::vector<decltype(src_loc.cache_location(0,-1))>pointers(valid_points_count);
//stroring intensities of pixels on circumference beforehand to decrease runtime
for(int 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(int i=0;i<=6;i+=2)
{
if(threshold_indicator[i]==0 && threshold_indicator[i+8]==0)
return false;
}
for(int 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;
}
template<typename SrcView,typename Pointype>
std::ptrdiff_t calculate_score(SrcView &src,int i,int j,std::vector<Pointype>&points,int threshold)
{
int score=threshold;
std::ptrdiff_t low=threshold;
std::ptrdiff_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)
{
int mid=(low+high)/2;
if(FastCornerDetector(src,i,j,points,mid))
{
low=mid;
score=std::max(score,mid);
}
else
{
high=mid-1;
}
}
return low-1;
}}//detail
template<typename SrcView>
void Fast(SrcView &src,std::vector<point_t>&keypoints,std::vector<int>&scores,bool nonmax=true,int 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(int i=3;i<src.height()-3;i++)
{
for(int j=3;j<src.width()-3;j++)
{
if(detail::FastCornerDetector(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)
{
int i=u[1];
int j=u[0];
int 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);
}
}
}
}}//boost gil
#endif
Binary file added test/core/image_processing/box.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/core/image_processing/chessboard(2).png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 65 additions & 0 deletions test/core/image_processing/fast.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//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.hpp>
#include<iostream>
#include<boost/assert.hpp>
#include<boost/core/lightweight_test.hpp>
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<int>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()
{
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<int>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<int>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();
}