mirror of https://github.com/davisking/dlib.git
Added find_peaks()
This commit is contained in:
parent
5a2e0d6a4d
commit
f2845f15ad
|
@ -11,6 +11,7 @@
|
|||
#include "../rand.h"
|
||||
#include "../array2d.h"
|
||||
#include "../image_transforms/spatial_filtering.h"
|
||||
#include "../image_transforms/thresholding.h"
|
||||
|
||||
namespace dlib
|
||||
{
|
||||
|
@ -235,6 +236,143 @@ namespace dlib
|
|||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template <
|
||||
typename image_type
|
||||
>
|
||||
std::vector<point> find_peaks (
|
||||
const image_type& img_,
|
||||
const double non_max_suppression_radius,
|
||||
const typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type& thresh
|
||||
)
|
||||
{
|
||||
DLIB_CASSERT(non_max_suppression_radius >= 0);
|
||||
const_image_view<image_type> img(img_);
|
||||
|
||||
using basic_pixel_type = typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type;
|
||||
|
||||
std::vector<std::pair<basic_pixel_type,point>> peaks;
|
||||
|
||||
for (long r = 1; r+1 < img.nr(); ++r)
|
||||
{
|
||||
for (long c = 1; c+1 < img.nc(); ++c)
|
||||
{
|
||||
auto val = img[r][c];
|
||||
if (val < thresh)
|
||||
continue;
|
||||
|
||||
if (
|
||||
val <= img[r-1][c] ||
|
||||
val <= img[r+1][c] ||
|
||||
val <= img[r][c+1] ||
|
||||
val <= img[r][c-1] ||
|
||||
val <= img[r-1][c-1] ||
|
||||
val <= img[r+1][c+1] ||
|
||||
val <= img[r-1][c+1] ||
|
||||
val <= img[r+1][c-1]
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
peaks.emplace_back(val,point(c,r));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// now do non-max suppression of the peaks according to the supplied radius.
|
||||
using pt = std::pair<basic_pixel_type,point>;
|
||||
// First sort the peaks so the strongest peaks come first. We will greedily accept
|
||||
// them and then do the normal peak sorting/non-max suppression thing.
|
||||
std::sort(peaks.rbegin(), peaks.rend(), [](pt& a, pt&b ){ return a.first < b.first; });
|
||||
std::vector<point> final_peaks;
|
||||
const double radius_sqr = non_max_suppression_radius*non_max_suppression_radius;
|
||||
|
||||
// If there are a lot of peaks then we will make a mask image and use that to do
|
||||
// the non-max suppression since this is fast when peaks.size() is large. Otherwise we
|
||||
// will do the simpler thing in the else block that doesn't require us to allocate a
|
||||
// temporary mask image.
|
||||
if (peaks.size() > 500 && radius_sqr != 0)
|
||||
{
|
||||
// hit will record which areas of the image have already been accounted for by some
|
||||
// peak. So it is our mask image.
|
||||
matrix<unsigned char> hit(img.nr(), img.nc());
|
||||
// initially nothing has been hit.
|
||||
hit = 0;
|
||||
const unsigned long win_size = std::round(2*non_max_suppression_radius);
|
||||
const rectangle area = get_rect(img);
|
||||
for (auto& pp : peaks)
|
||||
{
|
||||
auto& p = pp.second;
|
||||
if (!hit(p.y(),p.x()))
|
||||
{
|
||||
final_peaks.emplace_back(p);
|
||||
|
||||
// mask out a circle around this new peak
|
||||
rectangle win = centered_rect(p, win_size, win_size).intersect(area);
|
||||
for (long r = win.top(); r <= win.bottom(); ++r)
|
||||
{
|
||||
for (long c = win.left(); c <= win.right(); ++c)
|
||||
{
|
||||
if (length_squared(point(c,r)-p) <= radius_sqr)
|
||||
hit(r,c) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if peaks.size() is relatively small then this is a faster way to do the non-max
|
||||
// suppression.
|
||||
for (auto& p : peaks)
|
||||
{
|
||||
bool hits_any_existing_peak = false;
|
||||
// If the user set the radius to 0 then just copy the peaks to the output without
|
||||
// checking anything.
|
||||
if (radius_sqr != 0)
|
||||
{
|
||||
for (auto& v : final_peaks)
|
||||
{
|
||||
if (length_squared(p.second-v) <= radius_sqr)
|
||||
{
|
||||
hits_any_existing_peak = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hits_any_existing_peak)
|
||||
{
|
||||
final_peaks.emplace_back(p.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return final_peaks;
|
||||
}
|
||||
|
||||
template <
|
||||
typename image_type
|
||||
>
|
||||
std::vector<point> find_peaks (
|
||||
const image_type& img
|
||||
)
|
||||
{
|
||||
return find_peaks(img, 0, partition_pixels(img));
|
||||
}
|
||||
|
||||
template <
|
||||
typename image_type
|
||||
>
|
||||
std::vector<point> find_peaks (
|
||||
const image_type& img,
|
||||
const double non_max_suppression_radius
|
||||
)
|
||||
{
|
||||
return find_peaks(img, non_max_suppression_radius, partition_pixels(img));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template <
|
||||
|
|
|
@ -132,6 +132,62 @@ namespace dlib
|
|||
- #dets == all the points which passed the threshold test.
|
||||
!*/
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template <
|
||||
typename image_type
|
||||
>
|
||||
std::vector<point> find_peaks (
|
||||
const image_type& img,
|
||||
const double non_max_suppression_radius,
|
||||
const typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type& thresh
|
||||
);
|
||||
/*!
|
||||
requires
|
||||
- image_type == an image object that implements the interface defined in
|
||||
dlib/image_processing/generic_image.h. Moreover, these it must contain a
|
||||
scalar pixel type (e.g. int rather than rgb_pixel)
|
||||
- non_max_suppression_radius >= 0
|
||||
ensures
|
||||
- Scans the given image and finds all pixels with values >= thresh that are
|
||||
also local maximums within their 8-connected neighborhood of the image. Such
|
||||
pixels are collected, sorted in decreasing order of their pixel values, and
|
||||
then non-maximum suppression is applied to this list of points using the
|
||||
given non_max_suppression_radius. The final list of peaks is then returned.
|
||||
|
||||
Therefore, the returned list, V, will have these properties:
|
||||
- V.size() == the number of peaks found in the image.
|
||||
- When measured in image coordinates, no elements of V are within
|
||||
non_max_suppression_radius distance of each other. That is, for all valid i!=j
|
||||
it is true that length(V[i]-V[j]) > non_max_suppression_radius.
|
||||
- For each element of V, that element has the maximum pixel value of all
|
||||
pixels in the ball centered on that pixel with radius
|
||||
non_max_suppression_radius.
|
||||
!*/
|
||||
|
||||
template <
|
||||
typename image_type
|
||||
>
|
||||
std::vector<point> find_peaks (
|
||||
const image_type& img
|
||||
);
|
||||
/*!
|
||||
ensures
|
||||
- performs: return find_peaks(img, 0, partition_pixels(img))
|
||||
!*/
|
||||
|
||||
template <
|
||||
typename image_type
|
||||
>
|
||||
std::vector<point> find_peaks (
|
||||
const image_type& img,
|
||||
const double non_max_suppression_radius
|
||||
);
|
||||
/*!
|
||||
ensures
|
||||
- performs: return find_peaks(img, non_max_suppression_radius, partition_pixels(img))
|
||||
!*/
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template <
|
||||
|
|
Loading…
Reference in New Issue