Added the max_filter() routine.

This commit is contained in:
Davis King 2012-06-30 14:39:30 -04:00
parent c1c8267ba5
commit fce8eaabe4
3 changed files with 323 additions and 0 deletions

View File

@ -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);
}
}
}
// ----------------------------------------------------------------------------------------
}

View File

@ -338,6 +338,45 @@ namespace dlib
- #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.
!*/
// ----------------------------------------------------------------------------------------
}

View File

@ -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
@ -454,6 +553,8 @@ namespace
void perform_test (
)
{
test_max_filter();
run_test1();
run_test2();
run_test3<unsigned char>(1);