From 04eb33ce062e43c778d07edf536d547083d31d5d Mon Sep 17 00:00:00 2001 From: Davis King Date: Sun, 22 Apr 2018 07:24:43 -0400 Subject: [PATCH] Added find_pixels_voting_for_lines() to the hough_transform. --- dlib/image_transforms/hough_transform.h | 169 ++++++++++++++---- .../hough_transform_abstract.h | 65 +++++++ 2 files changed, 198 insertions(+), 36 deletions(-) diff --git a/dlib/image_transforms/hough_transform.h b/dlib/image_transforms/hough_transform.h index a0f3cafbc..2bc1bb55e 100644 --- a/dlib/image_transforms/hough_transform.h +++ b/dlib/image_transforms/hough_transform.h @@ -59,7 +59,7 @@ namespace dlib } } - unsigned long size( + inline unsigned long size( ) const { return _size; } long nr( @@ -254,7 +254,7 @@ namespace dlib typedef typename image_traits::pixel_type out_pixel_type; DLIB_CASSERT(box.width() == size() && box.height() == size(), - "\t hough_transform::hough_transform(size_)" + "\t void hough_transform::operator()" << "\n\t Invalid arguments given to this function." << "\n\t box.width(): " << box.width() << "\n\t box.height(): " << box.height() @@ -270,16 +270,127 @@ namespace dlib himg.set_size(size(), size()); assign_all_pixels(himg, 0); + auto record_hit = [&](const point& hough_point, const point& /*img_point*/, const in_pixel_type& val) + { + himg[hough_point.y()][hough_point.x()] += val; + }; + perform_hough_transform(img_, box, record_hit); + } + + template < + typename in_image_type, + typename out_image_type + > + void operator() ( + const in_image_type& img_, + out_image_type& himg_ + ) const + { + rectangle box(0,0, num_columns(img_)-1, num_rows(img_)-1); + (*this)(img_, box, himg_); + } + + template < + typename in_image_type + > + std::vector> find_pixels_voting_for_lines ( + const in_image_type& img, + const rectangle& box, + const std::vector& hough_points + ) const + { + + typedef typename image_traits::pixel_type in_pixel_type; + + DLIB_CASSERT(box.width() == size() && box.height() == size(), + "\t std::vector> hough_transform::find_pixels_voting_for_lines()" + << "\n\t Invalid arguments given to this function." + << "\n\t box.width(): " << box.width() + << "\n\t box.height(): " << box.height() + << "\n\t size(): " << size() + ); +#ifdef ENABLE_ASSERTS + for (auto& p : hough_points) + DLIB_CASSERT(get_rect(*this).contains(p), + "You gave a hough_points that isn't actually in the Hough space of this object." + "\n\t get_rect(*this): "<< get_rect(*this) + "\n\t p: "<< p + ); +#endif + + std::vector> constituent_points(hough_points.size()); + + // make a map that lets us look up in constant time if a hough point is in the + // constituent_points output and if so where. + matrix hmap(size(),size()); + hmap = hough_points.size(); + for (size_t i = 0; i < hough_points.size(); ++i) + hmap(hough_points[i].y(),hough_points[i].x()) = i; + + // record that this image point voted for this Hough point + auto record_hit = [&](const point& hough_point, const point& img_point, in_pixel_type) + { + auto idx = hmap(hough_point.y(), hough_point.x()); + if (idx < constituent_points.size()) + constituent_points[idx].push_back(img_point); + }; + + perform_hough_transform(img, box, record_hit); + + return constituent_points; + } + + template < + typename in_image_type + > + std::vector> find_pixels_voting_for_lines ( + const in_image_type& img, + const std::vector& hough_points + ) const + { + rectangle box(0,0, num_columns(img)-1, num_rows(img)-1); + return find_pixels_voting_for_lines(img, box, hough_points); + } + + private: + + template < + typename in_image_type, + typename record_hit_function_type + > + void perform_hough_transform ( + const in_image_type& img_, + const rectangle& box, + record_hit_function_type record_hit + ) const + { + + typedef typename image_traits::pixel_type in_pixel_type; + + DLIB_ASSERT(box.width() == size() && box.height() == size(), + "\t void hough_transform::perform_hough_transform()" + << "\n\t Invalid arguments given to this function." + << "\n\t box.width(): " << box.width() + << "\n\t box.height(): " << box.height() + << "\n\t size(): " << size() + ); + + COMPILE_TIME_ASSERT(pixel_traits::grayscale == true); + + + const_image_view img(img_); + + const rectangle area = box.intersect(get_rect(img)); - const long max_n8 = (himg.nc()/8)*8; - const long max_n4 = (himg.nc()/4)*4; + const long max_n8 = (size()/8)*8; + const long max_n4 = (size()/4)*4; for (long r = area.top(); r <= area.bottom(); ++r) { const int32* ysin_base = &ysin_theta(r-box.top(),0); for (long c = area.left(); c <= area.right(); ++c) { - const out_pixel_type val = static_cast(img[r][c]); + const auto val = img[r][c]; if (val != 0) { /* @@ -290,12 +401,13 @@ namespace dlib const point cent = center(box); const long x = c - cent.x(); const long y = r - cent.y(); - for (long t = 0; t < himg.nc(); ++t) + for (long t = 0; t < size(); ++t) { double theta = t*pi/even_size; double radius = (x*std::cos(theta) + y*std::sin(theta))/sqrt_2 + even_size/2 + 0.5; long rr = static_cast(radius); - himg[rr][t] += val; + + record_hit(point(t,rr), point(c,r), val); } continue; */ @@ -316,14 +428,14 @@ namespace dlib long rr6 = (*xcos++ + *ysin++)>>16; long rr7 = (*xcos++ + *ysin++)>>16; - himg[rr0][t++] += val; - himg[rr1][t++] += val; - himg[rr2][t++] += val; - himg[rr3][t++] += val; - himg[rr4][t++] += val; - himg[rr5][t++] += val; - himg[rr6][t++] += val; - himg[rr7][t++] += val; + record_hit(point(t++,rr0), point(c,r), val); + record_hit(point(t++,rr1), point(c,r), val); + record_hit(point(t++,rr2), point(c,r), val); + record_hit(point(t++,rr3), point(c,r), val); + record_hit(point(t++,rr4), point(c,r), val); + record_hit(point(t++,rr5), point(c,r), val); + record_hit(point(t++,rr6), point(c,r), val); + record_hit(point(t++,rr7), point(c,r), val); } while(t < max_n4) { @@ -331,36 +443,21 @@ namespace dlib long rr1 = (*xcos++ + *ysin++)>>16; long rr2 = (*xcos++ + *ysin++)>>16; long rr3 = (*xcos++ + *ysin++)>>16; - himg[rr0][t++] += val; - himg[rr1][t++] += val; - himg[rr2][t++] += val; - himg[rr3][t++] += val; + record_hit(point(t++,rr0), point(c,r), val); + record_hit(point(t++,rr1), point(c,r), val); + record_hit(point(t++,rr2), point(c,r), val); + record_hit(point(t++,rr3), point(c,r), val); } - while(t < himg.nc()) + while(t < (long)size()) { long rr0 = (*xcos++ + *ysin++)>>16; - himg[rr0][t++] += val; + record_hit(point(t++,rr0), point(c,r), val); } } } } } - template < - typename in_image_type, - typename out_image_type - > - void operator() ( - const in_image_type& img_, - out_image_type& himg_ - ) const - { - rectangle box(0,0, num_columns(img_)-1, num_rows(img_)-1); - (*this)(img_, box, himg_); - } - - private: - unsigned long _size; unsigned long even_size; // equal to _size if _size is even, otherwise equal to _size-1. matrix xcos_theta, ysin_theta; diff --git a/dlib/image_transforms/hough_transform_abstract.h b/dlib/image_transforms/hough_transform_abstract.h index 163e8b593..a43232150 100644 --- a/dlib/image_transforms/hough_transform_abstract.h +++ b/dlib/image_transforms/hough_transform_abstract.h @@ -157,6 +157,71 @@ namespace dlib - performs: (*this)(img, get_rect(img), himg); That is, just runs the hough transform on the whole input image. !*/ + + template < + typename in_image_type + > + std::vector> find_pixels_voting_for_lines ( + const in_image_type& img, + const rectangle& box, + const std::vector& hough_points + ) const; + /*! + requires + - in_image_type == an image object that implements the interface defined in + dlib/image_processing/generic_image.h and it must contain grayscale pixels. + - box.width() == size() + - box.height() == size() + - for all valid i: + - get_rect(*this).contains(hough_points[i]) == true + (i.e. hough_points must contain points in the output Hough transform + space generated by this object.) + ensures + - This function computes the Hough transform of the part of img contained + within box. It does the same computation as operator() define above, + except instead of accumulating into an image we create an explicit list + of all the points in img that contributed to each line (i.e each point in + the Hough image). To do this we take a list of Hough points as input and + only record hits on these specifically identified Hough points. A + typical use of find_pixels_voting_for_lines() is to first run the normal + Hough transform using operator(), then find the lines you are interested + in, and then call find_pixels_voting_for_lines() to determine which + pixels in the input image belong to those lines. + - This routine returns a vector, call the returned vector CONSTITUENT_POINTS. + It has the following properties: + - #CONSTITUENT_POINTS.size() == hough_points.size() + - for all valid i: + - Any point in img with a non-zero value that lies on the line + corresponding to the Hough point hough_points[i] is added to + CONSTITUENT_POINTS[i]. Therefore, when this routine finishes, + #CONSTITUENT_POINTS[i] will contain all the points in img that voted + for the line hough_points[i]. + - #CONSTITUENT_POINTS[i].size() == the number of points in img that voted for + the line hough_points[i]. + !*/ + + template < + typename in_image_type + > + std::vector> find_pixels_voting_for_lines ( + const in_image_type& img, + const std::vector& hough_points + ) const; + /*! + requires + - in_image_type == an image object that implements the interface defined in + dlib/image_processing/generic_image.h and it must contain grayscale pixels. + - num_rows(img) == size() + - num_columns(img) == size() + - for all valid i: + - get_rect(*this).contains(hough_points[i]) == true + (i.e. hough_points must contain points in the output Hough transform + space generated by this object.) + ensures + - performs: return find_pixels_voting_for_lines(img, get_rect(img), hough_points); + That is, just runs the hough transform on the whole input image. + !*/ + }; }