mirror of
https://github.com/davisking/dlib.git
synced 2024-11-01 10:14:53 +08:00
Added the max_filter() routine.
This commit is contained in:
parent
c1c8267ba5
commit
fce8eaabe4
@ -715,6 +715,189 @@ namespace dlib
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace impl
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
class fast_deque
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
This is a fast and minimal implementation of std::deque for
|
||||||
|
use with the max_filter.
|
||||||
|
|
||||||
|
This object assumes that no more than max_size elements
|
||||||
|
will ever be pushed into it at a time.
|
||||||
|
*/
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit fast_deque(unsigned long max_size)
|
||||||
|
{
|
||||||
|
// find a power of two that upper bounds max_size
|
||||||
|
mask = 2;
|
||||||
|
while (mask < max_size)
|
||||||
|
mask *= 2;
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
data.resize(mask);
|
||||||
|
--mask; // make into bit mask
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
first = 1;
|
||||||
|
last = 0;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_back()
|
||||||
|
{
|
||||||
|
last = (last-1)&mask;
|
||||||
|
--size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(const T& item)
|
||||||
|
{
|
||||||
|
last = (last+1)&mask;
|
||||||
|
++size;
|
||||||
|
data[last] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_front()
|
||||||
|
{
|
||||||
|
first = (first+1)&mask;
|
||||||
|
--size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& front() const
|
||||||
|
{
|
||||||
|
return data[first];
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& back() const
|
||||||
|
{
|
||||||
|
return data[last];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<T> data;
|
||||||
|
unsigned long mask;
|
||||||
|
unsigned long first;
|
||||||
|
unsigned long last;
|
||||||
|
unsigned long size;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename image_type1,
|
||||||
|
typename image_type2
|
||||||
|
>
|
||||||
|
void max_filter (
|
||||||
|
image_type1& img,
|
||||||
|
image_type2& out,
|
||||||
|
const long width,
|
||||||
|
const long height,
|
||||||
|
const typename image_type1::type& thresh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DLIB_ASSERT((width%2)==1 &&
|
||||||
|
(height%2)==1 &&
|
||||||
|
width > 0 &&
|
||||||
|
height > 0 &&
|
||||||
|
out.nr() == img.nr() &&
|
||||||
|
out.nc() == img.nc() &&
|
||||||
|
is_same_object(img,out) == false,
|
||||||
|
"\t void max_filter()"
|
||||||
|
<< "\n\t Invalid arguments given to this function."
|
||||||
|
<< "\n\t img.nr(): " << img.nr()
|
||||||
|
<< "\n\t img.nc(): " << img.nc()
|
||||||
|
<< "\n\t out.nr(): " << out.nr()
|
||||||
|
<< "\n\t out.nc(): " << out.nc()
|
||||||
|
<< "\n\t width: " << width
|
||||||
|
<< "\n\t height: " << height
|
||||||
|
<< "\n\t is_same_object(img,out): " << is_same_object(img,out)
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef typename image_type1::type pixel_type;
|
||||||
|
|
||||||
|
|
||||||
|
dlib::impl::fast_deque<std::pair<long,pixel_type> > Q(std::max(width,height));
|
||||||
|
|
||||||
|
const long last_col = std::max(img.nc(), (width/2));
|
||||||
|
const long last_row = std::max(img.nr(), (height/2));
|
||||||
|
|
||||||
|
// run max filter along rows of img
|
||||||
|
for (long r = 0; r < img.nr(); ++r)
|
||||||
|
{
|
||||||
|
Q.clear();
|
||||||
|
for (long c = 0; c < width/2 && c < img.nc(); ++c)
|
||||||
|
{
|
||||||
|
while (!Q.empty() && img[r][c] >= Q.back().second)
|
||||||
|
Q.pop_back();
|
||||||
|
Q.push_back(make_pair(c,img[r][c]));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (long c = width/2; c < img.nc(); ++c)
|
||||||
|
{
|
||||||
|
while (!Q.empty() && img[r][c] >= Q.back().second)
|
||||||
|
Q.pop_back();
|
||||||
|
while (!Q.empty() && Q.front().first <= c-width)
|
||||||
|
Q.pop_front();
|
||||||
|
Q.push_back(make_pair(c,img[r][c]));
|
||||||
|
|
||||||
|
img[r][c-(width/2)] = Q.front().second;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (long c = last_col; c < img.nc() + (width/2); ++c)
|
||||||
|
{
|
||||||
|
while (!Q.empty() && Q.front().first <= c-width)
|
||||||
|
Q.pop_front();
|
||||||
|
|
||||||
|
img[r][c-(width/2)] = Q.front().second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// run max filter along columns of img. Store result in out.
|
||||||
|
for (long cc = 0; cc < img.nc(); ++cc)
|
||||||
|
{
|
||||||
|
Q.clear();
|
||||||
|
for (long rr = 0; rr < height/2 && rr < img.nr(); ++rr)
|
||||||
|
{
|
||||||
|
while (!Q.empty() && img[rr][cc] >= Q.back().second)
|
||||||
|
Q.pop_back();
|
||||||
|
Q.push_back(make_pair(rr,img[rr][cc]));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (long rr = height/2; rr < img.nr(); ++rr)
|
||||||
|
{
|
||||||
|
while (!Q.empty() && img[rr][cc] >= Q.back().second)
|
||||||
|
Q.pop_back();
|
||||||
|
while (!Q.empty() && Q.front().first <= rr-height)
|
||||||
|
Q.pop_front();
|
||||||
|
Q.push_back(make_pair(rr,img[rr][cc]));
|
||||||
|
|
||||||
|
out[rr-(height/2)][cc] += std::max(Q.front().second, thresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (long rr = last_row; rr < img.nr() + (height/2); ++rr)
|
||||||
|
{
|
||||||
|
while (!Q.empty() && Q.front().first <= rr-height)
|
||||||
|
Q.pop_front();
|
||||||
|
|
||||||
|
out[rr-(height/2)][cc] += std::max(Q.front().second, thresh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -338,6 +338,45 @@ namespace dlib
|
|||||||
- #out[r][c] == out[r][c] + SUM(r,c)
|
- #out[r][c] == out[r][c] + SUM(r,c)
|
||||||
!*/
|
!*/
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename image_type1,
|
||||||
|
typename image_type2
|
||||||
|
>
|
||||||
|
void max_filter (
|
||||||
|
image_type1& img,
|
||||||
|
image_type2& out,
|
||||||
|
const long width,
|
||||||
|
const long height,
|
||||||
|
const typename image_type1::type& thresh
|
||||||
|
);
|
||||||
|
/*!
|
||||||
|
requires
|
||||||
|
- out.nr() == img.nr()
|
||||||
|
- out.nc() == img.nc()
|
||||||
|
- image_type1 == an implementation of array2d/array2d_kernel_abstract.h
|
||||||
|
and it must contain a scalar type
|
||||||
|
- image_type2 == an implementation of array2d/array2d_kernel_abstract.h
|
||||||
|
and it must contain a scalar type
|
||||||
|
- is_same_object(img,out) == false
|
||||||
|
- (width%2)==1 && (height%2)==1
|
||||||
|
(i.e. width and height must be odd)
|
||||||
|
- width > 0 && height > 0
|
||||||
|
ensures
|
||||||
|
- for all valid r and c:
|
||||||
|
- let MAX(r,c) == maximum of pixels from img which are inside the rectangle
|
||||||
|
centered_rect(point(c,r), width, height)
|
||||||
|
- if (MAX(r,c) >= thresh)
|
||||||
|
- #out[r][c] == out[r][c] + MAX(r,c)
|
||||||
|
- else
|
||||||
|
- #out[r][c] == out[r][c] + thresh
|
||||||
|
- Does not change the size of img.
|
||||||
|
- Uses img as scratch space. Therefore, the pixel values in img will have
|
||||||
|
been modified by this function. That is, max_filter() destroys the contents
|
||||||
|
of img.
|
||||||
|
!*/
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -440,6 +440,105 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename image_type1,
|
||||||
|
typename image_type2
|
||||||
|
>
|
||||||
|
void naive_max_filter (
|
||||||
|
const image_type1& img,
|
||||||
|
image_type2& out,
|
||||||
|
const rectangle& rect,
|
||||||
|
typename image_type1::type thresh
|
||||||
|
)
|
||||||
|
{
|
||||||
|
typedef typename image_type1::type pixel_type;
|
||||||
|
|
||||||
|
const rectangle area = get_rect(img);
|
||||||
|
for (long r = 0; r < img.nr(); ++r)
|
||||||
|
{
|
||||||
|
for (long c = 0; c < img.nc(); ++c)
|
||||||
|
{
|
||||||
|
const rectangle win = translate_rect(rect,point(c,r)).intersect(area);
|
||||||
|
if (!win.is_empty())
|
||||||
|
out[r][c] += std::max(dlib::max(subm(array_to_matrix(img),win)), thresh);
|
||||||
|
else
|
||||||
|
out[r][c] += thresh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void test_max_filter(long rows, long cols, long width, long height, dlib::rand& rnd)
|
||||||
|
{
|
||||||
|
array2d<int> img(rows, cols);
|
||||||
|
rectangle rect = centered_rect(0,0, width, height);
|
||||||
|
|
||||||
|
array2d<int> out(img.nr(),img.nc());
|
||||||
|
assign_all_pixels(out, 0);
|
||||||
|
array2d<int> out2(img.nr(),img.nc());
|
||||||
|
assign_all_pixels(out2, 0);
|
||||||
|
|
||||||
|
for (long r = 0; r < img.nr(); ++r)
|
||||||
|
{
|
||||||
|
for (long c = 0; c < img.nc(); ++c)
|
||||||
|
{
|
||||||
|
img[r][c] = rnd.get_random_32bit_number();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int thresh = rnd.get_random_32bit_number();
|
||||||
|
|
||||||
|
naive_max_filter(img, out2, rect, thresh);
|
||||||
|
max_filter(img, out, rect.width(), rect.height(), thresh);
|
||||||
|
|
||||||
|
DLIB_TEST_MSG(array_to_matrix(out) == array_to_matrix(out2),
|
||||||
|
"rows: "<< rows
|
||||||
|
<< "\ncols: "<< rows
|
||||||
|
<< "\nwidth: "<< width
|
||||||
|
<< "\nheight: "<< height );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void test_max_filter()
|
||||||
|
{
|
||||||
|
dlib::rand rnd;
|
||||||
|
for (int iter = 0; iter < 300; ++iter)
|
||||||
|
{
|
||||||
|
print_spinner();
|
||||||
|
test_max_filter(0,0,1,1,rnd);
|
||||||
|
test_max_filter(0,0,3,1,rnd);
|
||||||
|
test_max_filter(0,0,3,3,rnd);
|
||||||
|
test_max_filter(0,0,1,3,rnd);
|
||||||
|
test_max_filter(1,1,1,1,rnd);
|
||||||
|
test_max_filter(2,2,1,1,rnd);
|
||||||
|
test_max_filter(3,3,1,1,rnd);
|
||||||
|
test_max_filter(3,3,3,3,rnd);
|
||||||
|
test_max_filter(3,3,3,5,rnd);
|
||||||
|
test_max_filter(20,20,901,901,rnd);
|
||||||
|
test_max_filter(5,5,1,5,rnd);
|
||||||
|
test_max_filter(50,50,9,9,rnd);
|
||||||
|
test_max_filter(50,50,9,9,rnd);
|
||||||
|
test_max_filter(50,50,9,9,rnd);
|
||||||
|
test_max_filter(20,20,1,901,rnd);
|
||||||
|
test_max_filter(20,20,3,901,rnd);
|
||||||
|
test_max_filter(20,20,901,1,rnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int iter = 0; iter < 200; ++iter)
|
||||||
|
{
|
||||||
|
print_spinner();
|
||||||
|
test_max_filter((int)rnd.get_random_8bit_number()%100+1,
|
||||||
|
(int)rnd.get_random_8bit_number()%100+1,
|
||||||
|
(int)rnd.get_random_8bit_number()*2+1,
|
||||||
|
(int)rnd.get_random_8bit_number()*2+1,
|
||||||
|
rnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class scan_image_tester : public tester
|
class scan_image_tester : public tester
|
||||||
@ -454,6 +553,8 @@ namespace
|
|||||||
void perform_test (
|
void perform_test (
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
test_max_filter();
|
||||||
|
|
||||||
run_test1();
|
run_test1();
|
||||||
run_test2();
|
run_test2();
|
||||||
run_test3<unsigned char>(1);
|
run_test3<unsigned char>(1);
|
||||||
|
Loading…
Reference in New Issue
Block a user