Changed the interface for the spatially_filter_image() routines to take the filter

as a matrix rather than C-array.  I also fixed a bug which showed up when using
non-square filters.  The bug would cause the edges of the output image to be incorrect.
This commit is contained in:
Davis King 2011-09-09 22:59:03 -04:00
parent 3dc0f5a756
commit 4d8e96ef66
3 changed files with 119 additions and 74 deletions

View File

@ -8,6 +8,7 @@
#include "../algs.h"
#include "../assert.h"
#include "../array2d.h"
#include "../matrix.h"
#include <limits>
namespace dlib
@ -18,14 +19,12 @@ namespace dlib
template <
typename in_image_type,
typename out_image_type,
typename filter_type,
long M,
long N
typename EXP
>
void spatially_filter_image (
const in_image_type& in_img,
out_image_type& out_img,
const filter_type (&filter)[M][N],
const matrix_exp<EXP>& filter,
unsigned long scale = 1,
bool use_abs = false
)
@ -33,11 +32,14 @@ namespace dlib
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename out_image_type::type>::has_alpha == false );
COMPILE_TIME_ASSERT(M%2 == 1);
COMPILE_TIME_ASSERT(N%2 == 1);
DLIB_ASSERT(scale > 0,
DLIB_ASSERT(scale > 0 &&
filter.nr()%2 == 1 &&
filter.nc()%2 == 1,
"\tvoid spatially_filter_image()"
<< "\n\tYou can't give a scale of zero"
<< "\n\t You can't give a scale of zero or a filter with even dimensions"
<< "\n\t scale: "<< scale
<< "\n\t filter.nr(): "<< filter.nr()
<< "\n\t filter.nc(): "<< filter.nc()
);
DLIB_ASSERT(is_same_object(in_img, out_img) == false,
"\tvoid spatially_filter_image()"
@ -55,13 +57,13 @@ namespace dlib
out_img.set_size(in_img.nr(),in_img.nc());
zero_border_pixels(out_img, M/2, N/2);
zero_border_pixels(out_img, filter.nc()/2, filter.nr()/2);
// figure out the range that we should apply the filter to
const long first_row = M/2;
const long first_col = N/2;
const long last_row = in_img.nr() - M/2;
const long last_col = in_img.nc() - N/2;
const long first_row = filter.nr()/2;
const long first_col = filter.nc()/2;
const long last_row = in_img.nr() - filter.nr()/2;
const long last_col = in_img.nc() - filter.nc()/2;
// apply the filter to the image
for (long r = first_row; r < last_row; ++r)
@ -71,13 +73,13 @@ namespace dlib
typedef typename pixel_traits<typename in_image_type::type>::basic_pixel_type bp_type;
typename promote<bp_type>::type p;
typename promote<bp_type>::type temp = 0;
for (long m = 0; m < M; ++m)
for (long m = 0; m < filter.nr(); ++m)
{
for (long n = 0; n < N; ++n)
for (long n = 0; n < filter.nc(); ++n)
{
// pull out the current pixel and put it into p
p = get_pixel_intensity(in_img[r-M/2+m][c-N/2+n]);
temp += p*filter[m][n];
p = get_pixel_intensity(in_img[r-filter.nr()/2+m][c-filter.nc()/2+n]);
temp += p*filter(m,n);
}
}
@ -108,15 +110,14 @@ namespace dlib
template <
typename in_image_type,
typename out_image_type,
typename filter_type,
long M,
long N
typename EXP1,
typename EXP2
>
void spatially_filter_image (
const in_image_type& in_img,
out_image_type& out_img,
const filter_type (&row_filter)[N],
const filter_type (&col_filter)[M],
const matrix_exp<EXP1>& row_filter,
const matrix_exp<EXP2>& col_filter,
unsigned long scale = 1,
bool use_abs = false
)
@ -124,11 +125,18 @@ namespace dlib
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename out_image_type::type>::has_alpha == false );
COMPILE_TIME_ASSERT(M%2 == 1);
COMPILE_TIME_ASSERT(N%2 == 1);
DLIB_ASSERT(scale > 0,
DLIB_ASSERT(scale > 0 &&
row_filter.size()%2 == 1 &&
col_filter.size()%2 == 1 &&
is_vector(row_filter) &&
is_vector(col_filter),
"\tvoid spatially_filter_image()"
<< "\n\tYou can't give a scale of zero"
<< "\n\t Invalid inputs were given to this function."
<< "\n\t scale: "<< scale
<< "\n\t row_filter.size(): "<< row_filter.size()
<< "\n\t col_filter.size(): "<< col_filter.size()
<< "\n\t is_vector(row_filter): "<< is_vector(row_filter)
<< "\n\t is_vector(col_filter): "<< is_vector(col_filter)
);
DLIB_ASSERT(is_same_object(in_img, out_img) == false,
"\tvoid spatially_filter_image()"
@ -146,13 +154,13 @@ namespace dlib
out_img.set_size(in_img.nr(),in_img.nc());
zero_border_pixels(out_img, M/2, N/2);
zero_border_pixels(out_img, row_filter.size()/2, col_filter.size()/2);
// figure out the range that we should apply the filter to
const long first_row = M/2;
const long first_col = N/2;
const long last_row = in_img.nr() - M/2;
const long last_col = in_img.nc() - N/2;
const long first_row = col_filter.size()/2;
const long first_col = row_filter.size()/2;
const long last_row = in_img.nr() - col_filter.size()/2;
const long last_col = in_img.nc() - row_filter.size()/2;
typedef typename pixel_traits<typename in_image_type::type>::basic_pixel_type bp_type;
@ -168,11 +176,11 @@ namespace dlib
{
typename promote<bp_type>::type p;
typename promote<bp_type>::type temp = 0;
for (long n = 0; n < N; ++n)
for (long n = 0; n < row_filter.size(); ++n)
{
// pull out the current pixel and put it into p
p = get_pixel_intensity(in_img[r][c-N/2+n]);
temp += p*row_filter[n];
p = get_pixel_intensity(in_img[r][c-row_filter.size()/2+n]);
temp += p*row_filter(n);
}
temp_img[r][c] = temp;
}
@ -184,9 +192,9 @@ namespace dlib
for (long c = first_col; c < last_col; ++c)
{
typename promote<bp_type>::type temp = 0;
for (long m = 0; m < M; ++m)
for (long m = 0; m < col_filter.size(); ++m)
{
temp += temp_img[r-M/2+m][c]*col_filter[m];
temp += temp_img[r-col_filter.size()/2+m][c]*col_filter(m);
}
temp /= scale;

View File

@ -4,6 +4,7 @@
#ifdef DLIB_SPATIAL_FILTERINg_ABSTRACT_
#include "../pixel.h"
#include "../matrix.h"
namespace dlib
{
@ -13,14 +14,12 @@ namespace dlib
template <
typename in_image_type,
typename out_image_type,
typename filter_type,
long M,
long N
typename EXP
>
void spatially_filter_image (
const in_image_type& in_img,
out_image_type& out_img,
const filter_type (&filter)[M][N],
const matrix_exp<EXP>& filter,
unsigned long scale = 1,
bool use_abs = false
);
@ -32,8 +31,8 @@ namespace dlib
- pixel_traits<typename out_image_type::type>::has_alpha == false
- is_same_object(in_img, out_img) == false
- scale > 0
- M % 2 == 1 (i.e. M must be odd)
- N % 2 == 1 (i.e. N must be odd)
- filter.nr() % 2 == 1 (i.e. must be odd)
- filter.nc() % 2 == 1 (i.e. must be odd)
ensures
- Applies the given spatial filter to in_img and stores the result in out_img. Also
divides each resulting pixel by scale.
@ -57,15 +56,14 @@ namespace dlib
template <
typename in_image_type,
typename out_image_type,
typename filter_type,
long M,
long N
typename EXP1,
typename EXP2
>
void spatially_filter_image (
const in_image_type& in_img,
out_image_type& out_img,
const filter_type (&row_filter)[N],
const filter_type (&col_filter)[M],
const matrix_exp<EXP1>& row_filter,
const matrix_exp<EXP2>& col_filter,
unsigned long scale = 1,
bool use_abs = false
);
@ -77,14 +75,16 @@ namespace dlib
- pixel_traits<typename out_image_type::type>::has_alpha == false
- is_same_object(in_img, out_img) == false
- scale > 0
- M % 2 == 1 (i.e. M must be odd)
- N % 2 == 1 (i.e. N must be odd)
- is_vector(row_filter) == true
- is_vector(col_filter) == true
- row_filter.size() % 2 == 1 (i.e. must be odd)
- col_filter.size() % 2 == 1 (i.e. must be odd)
ensures
- Applies the given separable spatial filter to in_img and stores the result in out_img.
Also divides each resulting pixel by scale. Calling this function has the same
effect as calling the regular spatially_filter_image() routine with a filter,
FILT, defined as follows:
- FILT[r][c] == col_filter[r]*row_filter[c]
- FILT(r,c) == col_filter(r)*row_filter(c)
- pixel values after filtering that are > pixel_traits<out_image_type>::max() are
set to pixel_traits<out_image_type>::max()
- if (pixel_traits<typename in_image_type::type>::grayscale == false) then

View File

@ -772,25 +772,27 @@ namespace
assign_all_pixels(img, 10);
int filter[3][3] = { {1,1,1},
{1,1,1},
{1,1,1}};
matrix<int,3,5> filter2;
filter2 = 1,1,1,1,1,
1,1,1,1,1,
1,1,1,1,1;
assign_all_pixels(img2,3);
spatially_filter_image(img, img2, filter);
spatially_filter_image(img, img2, filter2);
const rectangle rect(2,1,img.nc()-3,img.nr()-2);
for (long r = 0; r<img2.nr(); ++r)
{
for (long c = 0; c<img2.nc(); ++c)
{
if (shrink_rect(get_rect(img2),1).contains(c,r))
if (rect.contains(c,r))
{
DLIB_TEST(img2[r][c] == 90);
DLIB_TEST_MSG(img2[r][c] == 150, (int)img2[r][c]);
}
else
{
DLIB_TEST(img2[r][c] == 0);
DLIB_TEST_MSG(img2[r][c] == 0,(int)img2[r][c]);
}
}
}
@ -798,10 +800,13 @@ namespace
assign_all_pixels(img2,3);
assign_all_pixels(img3,3);
spatially_filter_image(img, img2, filter);
spatially_filter_image(img, img2, filter2);
int row_filter[] = {1,1,1};
int col_filter[] = {1,1,1};
matrix<int,1,5> row_filter;
matrix<int,1,3> col_filter;
row_filter = 1,1,1,1,1;
col_filter = 1,1,1;
spatially_filter_image(img, img3, row_filter, col_filter);
@ -820,20 +825,16 @@ namespace
}
}
row_filter[0] = ((int)rnd.get_random_8bit_number() - 100)/10;
row_filter[1] = ((int)rnd.get_random_8bit_number() - 100)/10;
row_filter[2] = ((int)rnd.get_random_8bit_number() - 100)/10;
col_filter[0] = ((int)rnd.get_random_8bit_number() - 100)/10;
col_filter[1] = ((int)rnd.get_random_8bit_number() - 100)/10;
col_filter[2] = ((int)rnd.get_random_8bit_number() - 100)/10;
row_filter(0) = ((int)rnd.get_random_8bit_number() - 100)/10;
row_filter(1) = ((int)rnd.get_random_8bit_number() - 100)/10;
row_filter(2) = ((int)rnd.get_random_8bit_number() - 100)/10;
row_filter(3) = ((int)rnd.get_random_8bit_number() - 100)/10;
row_filter(4) = ((int)rnd.get_random_8bit_number() - 100)/10;
col_filter(0) = ((int)rnd.get_random_8bit_number() - 100)/10;
col_filter(1) = ((int)rnd.get_random_8bit_number() - 100)/10;
col_filter(2) = ((int)rnd.get_random_8bit_number() - 100)/10;
for (long rr = 0; rr < 3; ++rr)
{
for (long cc = 0; cc < 3; ++cc)
{
filter[rr][cc] = row_filter[cc]*col_filter[rr];
}
}
const matrix<int,3,5> filter = trans(col_filter)*row_filter;
assign_all_pixels(img2,3);
assign_all_pixels(img3,3);
@ -844,6 +845,40 @@ namespace
}
}
void test_zero_border_pixels(
)
{
array2d<unsigned char> img;
img.set_size(4,5);
assign_all_pixels(img, 1);
zero_border_pixels(img, 2,1);
DLIB_TEST(img[0][0] == 0);
DLIB_TEST(img[1][0] == 0);
DLIB_TEST(img[2][0] == 0);
DLIB_TEST(img[3][0] == 0);
DLIB_TEST(img[0][1] == 0);
DLIB_TEST(img[1][1] == 0);
DLIB_TEST(img[2][1] == 0);
DLIB_TEST(img[3][1] == 0);
DLIB_TEST(img[0][3] == 0);
DLIB_TEST(img[1][3] == 0);
DLIB_TEST(img[2][3] == 0);
DLIB_TEST(img[3][3] == 0);
DLIB_TEST(img[0][4] == 0);
DLIB_TEST(img[1][4] == 0);
DLIB_TEST(img[2][4] == 0);
DLIB_TEST(img[3][4] == 0);
DLIB_TEST(img[0][2] == 0);
DLIB_TEST(img[3][2] == 0);
DLIB_TEST(img[1][2] == 1);
DLIB_TEST(img[2][2] == 1);
}
class image_tester : public tester
{
public:
@ -862,6 +897,8 @@ namespace
test_integral_image<long, unsigned char>();
test_integral_image<double, float>();
test_zero_border_pixels();
test_filtering<unsigned char>(false,1);
test_filtering<unsigned char>(true,1);
test_filtering<unsigned char>(false,3);