Added some utility routines to the hough_transform object.

This commit is contained in:
Davis King 2018-04-25 22:57:43 -04:00
parent 26c2cc4e49
commit 4d793ddd8a
2 changed files with 280 additions and 29 deletions

View File

@ -81,17 +81,15 @@ namespace dlib
// First we compute the radius measured in pixels from the center and the theta
// angle in radians.
typedef dlib::vector<double,2> vect;
const rectangle box(0,0,size()-1,size()-1);
const vect cent = center(box);
double theta = p.x()-cent.x();
double radius = p.y()-cent.y();
theta = theta*pi/even_size;
radius = radius*sqrt_2 + 0.5;
double theta, radius;
get_line_properties(p, theta, radius);
theta *= pi/180;
// now make a line segment on the line.
vect v1 = cent + vect(size()+1000,0) + vect(0,radius);
vect v2 = cent - vect(size()+1000,0) + vect(0,radius);
const rectangle box = get_rect(*this);
const dpoint cent = center(box);
dpoint v1 = cent + dpoint(size()+1000,0) + dpoint(0,radius);
dpoint v2 = cent - dpoint(size()+1000,0) + dpoint(0,radius);
point p1 = rotate_point(cent, v1, theta);
point p2 = rotate_point(cent, v2, theta);
@ -100,6 +98,28 @@ namespace dlib
return std::make_pair(p1,p2);
}
double get_line_angle_in_degrees (
const point& p
) const
{
double angle, radius;
get_line_properties(p, angle, radius);
return angle;
}
void get_line_properties (
const point& p,
double& angle_in_degrees,
double& radius
) const
{
const dpoint cent = center(get_rect(*this));
double theta = p.x()-cent.x();
radius = p.y()-cent.y();
angle_in_degrees = 180*theta/even_size;
radius = radius*sqrt_2 + 0.5;
}
template <
typename image_type
>
@ -274,7 +294,7 @@ namespace dlib
{
himg[hough_point.y()][hough_point.x()] += val;
};
perform_hough_transform(img_, box, record_hit);
perform_generic_hough_transform(img_, box, record_hit);
}
template <
@ -296,12 +316,16 @@ namespace dlib
std::vector<std::vector<point>> find_pixels_voting_for_lines (
const in_image_type& img,
const rectangle& box,
const std::vector<point>& hough_points
const std::vector<point>& hough_points,
const unsigned long angle_window_size = 1,
const unsigned long radius_window_size = 1
) const
{
typedef typename image_traits<in_image_type>::pixel_type in_pixel_type;
DLIB_CASSERT(angle_window_size >= 1);
DLIB_CASSERT(radius_window_size >= 1);
DLIB_CASSERT(box.width() == size() && box.height() == size(),
"\t std::vector<std::vector<point>> hough_transform::find_pixels_voting_for_lines()"
<< "\n\t Invalid arguments given to this function."
@ -325,7 +349,16 @@ namespace dlib
matrix<uint32> 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;
{
rectangle area = centered_rect(hough_points[i],angle_window_size,radius_window_size).intersect(get_rect(hmap));
for (long r = area.top(); r <= area.bottom(); ++r)
{
for (long c = area.left(); c <= area.right(); ++c)
{
hmap(r,c) = 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)
@ -335,7 +368,7 @@ namespace dlib
constituent_points[idx].push_back(img_point);
};
perform_hough_transform(img, box, record_hit);
perform_generic_hough_transform(img, box, record_hit);
return constituent_points;
}
@ -345,20 +378,90 @@ namespace dlib
>
std::vector<std::vector<point>> find_pixels_voting_for_lines (
const in_image_type& img,
const std::vector<point>& hough_points
const std::vector<point>& hough_points,
const unsigned long angle_window_size = 1,
const unsigned long radius_window_size = 1
) const
{
rectangle box(0,0, num_columns(img)-1, num_rows(img)-1);
return find_pixels_voting_for_lines(img, box, hough_points);
return find_pixels_voting_for_lines(img, box, hough_points, angle_window_size, radius_window_size);
}
template <
typename image_type,
typename thresh_type
>
std::vector<point> find_strong_hough_points(
const image_type& himg_,
const thresh_type hough_count_threshold,
const double angle_nms_thresh,
const double radius_nms_thresh
)
{
const_image_view<image_type> himg(himg_);
DLIB_CASSERT(himg.nr() == size());
DLIB_CASSERT(himg.nc() == size());
DLIB_CASSERT(angle_nms_thresh >= 0)
DLIB_CASSERT(radius_nms_thresh >= 0)
std::vector<std::pair<double,point>> initial_lines;
for (long r = 0; r < himg.nr(); ++r)
{
for (long c = 0; c < himg.nc(); ++c)
{
if (himg[r][c] >= hough_count_threshold)
initial_lines.emplace_back(himg[r][c], point(c,r));
}
}
std::vector<point> final_lines;
std::vector<std::pair<double,double>> final_angle_and_radius;
// Now do non-max suppression. First, sort the initial_lines so the best lines come first.
std::sort(initial_lines.rbegin(), initial_lines.rend(),
[](const std::pair<double,point>& a, const std::pair<double,point>& b){ return a.first<b.first;});
for (auto& r : initial_lines)
{
double angle, radius;
get_line_properties(r.second, angle, radius);
// check if anything in final_lines is too close to r.second. If
// something is found then discard r.second.
auto too_close = false;
for (auto& ref : final_angle_and_radius)
{
auto& ref_angle = ref.first;
auto& ref_radius = ref.second;
// We need to check for wrap around in angle since, for instance, a
// line with angle and radius of 90 and 10 is the same line as one with
// angle -90 and radius -10.
if ((std::abs(ref_angle - angle) < angle_nms_thresh && std::abs(ref_radius-radius) < radius_nms_thresh) ||
(180 - std::abs(ref_angle - angle) < angle_nms_thresh && std::abs(ref_radius+radius) < radius_nms_thresh))
{
too_close = true;
break;
}
}
if (!too_close)
{
final_lines.emplace_back(r.second);
final_angle_and_radius.emplace_back(angle, radius);
}
}
return final_lines;
}
private:
template <
typename in_image_type,
typename record_hit_function_type
>
void perform_hough_transform (
void perform_generic_hough_transform (
const in_image_type& img_,
const rectangle& box,
record_hit_function_type record_hit
@ -368,7 +471,7 @@ namespace dlib
typedef typename image_traits<in_image_type>::pixel_type in_pixel_type;
DLIB_ASSERT(box.width() == size() && box.height() == size(),
"\t void hough_transform::perform_hough_transform()"
"\t void hough_transform::perform_generic_hough_transform()"
<< "\n\t Invalid arguments given to this function."
<< "\n\t box.width(): " << box.width()
<< "\n\t box.height(): " << box.height()
@ -458,6 +561,21 @@ namespace dlib
}
}
template <
typename in_image_type,
typename record_hit_function_type
>
void perform_generic_hough_transform (
const in_image_type& img_,
record_hit_function_type record_hit
) const
{
rectangle box(0,0, num_columns(img_)-1, num_rows(img_)-1);
perform_generic_hough_transform(img_, box, record_hit);
}
private:
unsigned long _size;
unsigned long even_size; // equal to _size if _size is even, otherwise equal to _size-1.
matrix<int32> xcos_theta, ysin_theta;

View File

@ -78,6 +78,37 @@ namespace dlib
- The returned points are inside rectangle(0,0,size()-1,size()-1).
!*/
double get_line_angle_in_degrees (
const point& p
) const;
/*!
requires
- rectangle(0,0,size()-1,size()-1).contains(p) == true
(i.e. p must be a point inside the Hough accumulator array)
ensures
- returns the angle, in degrees, of the line corresponding to the Hough
transform point p.
!*/
void get_line_properties (
const point& p,
double& angle_in_degrees,
double& radius
) const;
/*!
requires
- rectangle(0,0,size()-1,size()-1).contains(p) == true
(i.e. p must be a point inside the Hough accumulator array)
ensures
- Converts a point in the Hough transform space into an angle, in degrees,
and a radius, measured in pixels from the center of the input image.
- #angle_in_degrees == the angle of the line corresponding to the Hough
transform point p. Moreover: -90 <= #angle_in_degrees < 90.
- #radius == the distance from the center of the input image, measured in
pixels, and the line corresponding to the Hough transform point p.
Moreover: -sqrt(size()*size()/2) <= #radius <= sqrt(size()*size()/2)
!*/
template <
typename image_type
>
@ -134,7 +165,9 @@ namespace dlib
point in #himg corresponds to a line in the input box. In particular,
the line for #himg[y][x] is given by get_line(point(x,y)). Also, when
viewing the #himg image, the x-axis gives the angle of the line and the
y-axis the distance of the line from the center of the box.
y-axis the distance of the line from the center of the box. The
conversion between Hough coordinates and angle and pixel distance can be
obtained by calling get_line_properties().
!*/
template <
@ -164,7 +197,9 @@ namespace dlib
std::vector<std::vector<point>> find_pixels_voting_for_lines (
const in_image_type& img,
const rectangle& box,
const std::vector<point>& hough_points
const std::vector<point>& hough_points,
const unsigned long angle_window_size = 1,
const unsigned long radius_window_size = 1
) const;
/*!
requires
@ -176,9 +211,11 @@ namespace dlib
- 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.)
- angle_window_size >= 1
- radius_window_size >= 1
ensures
- This function computes the Hough transform of the part of img contained
within box. It does the same computation as operator() define above,
within box. It does the same computation as operator() defined 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
@ -191,13 +228,19 @@ namespace dlib
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
- Let HP[i] = centered_rect(hough_points[i], angle_window_size, radius_window_size)
- Any point in img with a non-zero value that lies on a line
corresponding to one of the Hough points in HP[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].
for the lines associated with the Hough accumulator bins in HP[i].
- #CONSTITUENT_POINTS[i].size() == the number of points in img that
voted for any of the lines HP[i] in Hough space. Note, however, that if
angle_window_size or radius_window_size are made so large that HP[i]
overlaps HP[j] for i!=j then the overlapping regions of Hough space
are assign to HP[i] or HP[j] arbitrarily. Therefore, all points in
CONSTITUENT_POINTS are unique, that is, there is no overlap in points
between any element of CONSTITUENT_POINTS.
!*/
template <
@ -205,7 +248,9 @@ namespace dlib
>
std::vector<std::vector<point>> find_pixels_voting_for_lines (
const in_image_type& img,
const std::vector<point>& hough_points
const std::vector<point>& hough_points,
const unsigned long angle_window_size = 1,
const unsigned long radius_window_size = 1
) const;
/*!
requires
@ -217,9 +262,97 @@ namespace dlib
- 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.)
- angle_window_size >= 1
- radius_window_size >= 1
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.
- performs: return find_pixels_voting_for_lines(img, get_rect(img), hough_points, angle_window_size, radius_window_size);
That is, just runs the routine on the whole input image.
!*/
template <
typename image_type,
typename thresh_type
>
std::vector<point> find_strong_hough_points(
const image_type& himg,
const thresh_type hough_count_threshold,
const double angle_nms_thresh,
const double radius_nms_thresh
);
/*!
requires
- image_type == an image object that implements the interface defined in
dlib/image_processing/generic_image.h and it must contain grayscale pixels.
- himg.nr() == size()
- himg.nc() == size()
- angle_nms_thresh >= 0
- radius_nms_thresh >= 0
ensures
- This routine finds strong lines in a Hough transform and performs
non-maximum suppression on the detected lines. Recall that each point in
Hough space is associated with a line. Therefore, this routine finds all
the pixels in himg (a Hough transform image) with values >=
hough_count_threshold and performs non-maximum suppression on the
identified list of pixels. It does this by discarding lines that are
within angle_nms_thresh degrees of a stronger line or within
radius_nms_thresh distance (in terms of radius as defined by
get_line_properties()) to a stronger Hough point.
- The identified lines is returned as a list of coordinates in himg.
!*/
template <
typename in_image_type,
typename record_hit_function_type
>
void perform_generic_hough_transform (
const in_image_type& img,
const rectangle& box,
record_hit_function_type record_hit
) 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()
- record_hit is a function object with the signature:
void record_hit(const point& hough_point, const point& img_point, in_image_pixel_type value)
ensures
- Computes the Hough transform of the part of img contained within box.
This routine is very general and allows you to implement a wide variety
of Hough transforms, in fact, the operator() and
find_pixels_voting_for_lines() routines defined above are implemented in
terms of perform_generic_hough_transform(). The behavior is described by
the following pseudo-code:
for (image_coordinate : all_coordinates_in_img)
for (hough_point : all_Hough_space_coordinates_for_lines_passing_through_image_coordinate)
record_hit(hough_point, image_coordinate, img[image_coordinate.y][image_coordinate.x()]);
That is, we perform the Hough transform, but rather than accumulating
into a Hough accumulator image, we call record_hit() and record_hit()
does whatever it wants. For example, in the operator() method defined
above record_hit() simply accumulates into an image, and therefor
performs the classic Hough transform. But there are many other options.
!*/
template <
typename in_image_type,
typename record_hit_function_type
>
void perform_generic_hough_transform (
const in_image_type& img,
record_hit_function_type record_hit
) 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.
- record_hit is a function object with the signature:
void record_hit(const point& hough_point, const point& img_point, in_image_pixel_type value)
- num_rows(img) == size()
- num_columns(img) == size()
ensures
- performs: perform_generic_hough_transform(img, get_rect(img), record_hit);
That is, just runs the routine on the whole input image.
!*/
};