Commit 4d8e96ef authored by Davis King's avatar Davis King

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.
parent 3dc0f5a7
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "../algs.h" #include "../algs.h"
#include "../assert.h" #include "../assert.h"
#include "../array2d.h" #include "../array2d.h"
#include "../matrix.h"
#include <limits> #include <limits>
namespace dlib namespace dlib
...@@ -18,14 +19,12 @@ namespace dlib ...@@ -18,14 +19,12 @@ namespace dlib
template < template <
typename in_image_type, typename in_image_type,
typename out_image_type, typename out_image_type,
typename filter_type, typename EXP
long M,
long N
> >
void spatially_filter_image ( void spatially_filter_image (
const in_image_type& in_img, const in_image_type& in_img,
out_image_type& out_img, out_image_type& out_img,
const filter_type (&filter)[M][N], const matrix_exp<EXP>& filter,
unsigned long scale = 1, unsigned long scale = 1,
bool use_abs = false bool use_abs = false
) )
...@@ -33,11 +32,14 @@ namespace dlib ...@@ -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 in_image_type::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename out_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); DLIB_ASSERT(scale > 0 &&
COMPILE_TIME_ASSERT(N%2 == 1); filter.nr()%2 == 1 &&
DLIB_ASSERT(scale > 0, filter.nc()%2 == 1,
"\tvoid spatially_filter_image()" "\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, DLIB_ASSERT(is_same_object(in_img, out_img) == false,
"\tvoid spatially_filter_image()" "\tvoid spatially_filter_image()"
...@@ -55,13 +57,13 @@ namespace dlib ...@@ -55,13 +57,13 @@ namespace dlib
out_img.set_size(in_img.nr(),in_img.nc()); 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 // figure out the range that we should apply the filter to
const long first_row = M/2; const long first_row = filter.nr()/2;
const long first_col = N/2; const long first_col = filter.nc()/2;
const long last_row = in_img.nr() - M/2; const long last_row = in_img.nr() - filter.nr()/2;
const long last_col = in_img.nc() - N/2; const long last_col = in_img.nc() - filter.nc()/2;
// apply the filter to the image // apply the filter to the image
for (long r = first_row; r < last_row; ++r) for (long r = first_row; r < last_row; ++r)
...@@ -71,13 +73,13 @@ namespace dlib ...@@ -71,13 +73,13 @@ namespace dlib
typedef typename pixel_traits<typename in_image_type::type>::basic_pixel_type bp_type; 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 p;
typename promote<bp_type>::type temp = 0; 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 // pull out the current pixel and put it into p
p = get_pixel_intensity(in_img[r-M/2+m][c-N/2+n]); p = get_pixel_intensity(in_img[r-filter.nr()/2+m][c-filter.nc()/2+n]);
temp += p*filter[m][n]; temp += p*filter(m,n);
} }
} }
...@@ -108,15 +110,14 @@ namespace dlib ...@@ -108,15 +110,14 @@ namespace dlib
template < template <
typename in_image_type, typename in_image_type,
typename out_image_type, typename out_image_type,
typename filter_type, typename EXP1,
long M, typename EXP2
long N
> >
void spatially_filter_image ( void spatially_filter_image (
const in_image_type& in_img, const in_image_type& in_img,
out_image_type& out_img, out_image_type& out_img,
const filter_type (&row_filter)[N], const matrix_exp<EXP1>& row_filter,
const filter_type (&col_filter)[M], const matrix_exp<EXP2>& col_filter,
unsigned long scale = 1, unsigned long scale = 1,
bool use_abs = false bool use_abs = false
) )
...@@ -124,11 +125,18 @@ namespace dlib ...@@ -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 in_image_type::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename out_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); DLIB_ASSERT(scale > 0 &&
COMPILE_TIME_ASSERT(N%2 == 1); row_filter.size()%2 == 1 &&
DLIB_ASSERT(scale > 0, col_filter.size()%2 == 1 &&
is_vector(row_filter) &&
is_vector(col_filter),
"\tvoid spatially_filter_image()" "\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, DLIB_ASSERT(is_same_object(in_img, out_img) == false,
"\tvoid spatially_filter_image()" "\tvoid spatially_filter_image()"
...@@ -146,13 +154,13 @@ namespace dlib ...@@ -146,13 +154,13 @@ namespace dlib
out_img.set_size(in_img.nr(),in_img.nc()); 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 // figure out the range that we should apply the filter to
const long first_row = M/2; const long first_row = col_filter.size()/2;
const long first_col = N/2; const long first_col = row_filter.size()/2;
const long last_row = in_img.nr() - M/2; const long last_row = in_img.nr() - col_filter.size()/2;
const long last_col = in_img.nc() - N/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; typedef typename pixel_traits<typename in_image_type::type>::basic_pixel_type bp_type;
...@@ -168,11 +176,11 @@ namespace dlib ...@@ -168,11 +176,11 @@ namespace dlib
{ {
typename promote<bp_type>::type p; typename promote<bp_type>::type p;
typename promote<bp_type>::type temp = 0; 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 // pull out the current pixel and put it into p
p = get_pixel_intensity(in_img[r][c-N/2+n]); p = get_pixel_intensity(in_img[r][c-row_filter.size()/2+n]);
temp += p*row_filter[n]; temp += p*row_filter(n);
} }
temp_img[r][c] = temp; temp_img[r][c] = temp;
} }
...@@ -184,9 +192,9 @@ namespace dlib ...@@ -184,9 +192,9 @@ namespace dlib
for (long c = first_col; c < last_col; ++c) for (long c = first_col; c < last_col; ++c)
{ {
typename promote<bp_type>::type temp = 0; 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; temp /= scale;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#ifdef DLIB_SPATIAL_FILTERINg_ABSTRACT_ #ifdef DLIB_SPATIAL_FILTERINg_ABSTRACT_
#include "../pixel.h" #include "../pixel.h"
#include "../matrix.h"
namespace dlib namespace dlib
{ {
...@@ -13,14 +14,12 @@ namespace dlib ...@@ -13,14 +14,12 @@ namespace dlib
template < template <
typename in_image_type, typename in_image_type,
typename out_image_type, typename out_image_type,
typename filter_type, typename EXP
long M,
long N
> >
void spatially_filter_image ( void spatially_filter_image (
const in_image_type& in_img, const in_image_type& in_img,
out_image_type& out_img, out_image_type& out_img,
const filter_type (&filter)[M][N], const matrix_exp<EXP>& filter,
unsigned long scale = 1, unsigned long scale = 1,
bool use_abs = false bool use_abs = false
); );
...@@ -32,8 +31,8 @@ namespace dlib ...@@ -32,8 +31,8 @@ namespace dlib
- pixel_traits<typename out_image_type::type>::has_alpha == false - pixel_traits<typename out_image_type::type>::has_alpha == false
- is_same_object(in_img, out_img) == false - is_same_object(in_img, out_img) == false
- scale > 0 - scale > 0
- M % 2 == 1 (i.e. M must be odd) - filter.nr() % 2 == 1 (i.e. must be odd)
- N % 2 == 1 (i.e. N must be odd) - filter.nc() % 2 == 1 (i.e. must be odd)
ensures ensures
- Applies the given spatial filter to in_img and stores the result in out_img. Also - Applies the given spatial filter to in_img and stores the result in out_img. Also
divides each resulting pixel by scale. divides each resulting pixel by scale.
...@@ -57,15 +56,14 @@ namespace dlib ...@@ -57,15 +56,14 @@ namespace dlib
template < template <
typename in_image_type, typename in_image_type,
typename out_image_type, typename out_image_type,
typename filter_type, typename EXP1,
long M, typename EXP2
long N
> >
void spatially_filter_image ( void spatially_filter_image (
const in_image_type& in_img, const in_image_type& in_img,
out_image_type& out_img, out_image_type& out_img,
const filter_type (&row_filter)[N], const matrix_exp<EXP1>& row_filter,
const filter_type (&col_filter)[M], const matrix_exp<EXP2>& col_filter,
unsigned long scale = 1, unsigned long scale = 1,
bool use_abs = false bool use_abs = false
); );
...@@ -77,14 +75,16 @@ namespace dlib ...@@ -77,14 +75,16 @@ namespace dlib
- pixel_traits<typename out_image_type::type>::has_alpha == false - pixel_traits<typename out_image_type::type>::has_alpha == false
- is_same_object(in_img, out_img) == false - is_same_object(in_img, out_img) == false
- scale > 0 - scale > 0
- M % 2 == 1 (i.e. M must be odd) - is_vector(row_filter) == true
- N % 2 == 1 (i.e. N must be odd) - 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 ensures
- Applies the given separable spatial filter to in_img and stores the result in out_img. - 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 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, effect as calling the regular spatially_filter_image() routine with a filter,
FILT, defined as follows: 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 - pixel values after filtering that are > pixel_traits<out_image_type>::max() are
set to pixel_traits<out_image_type>::max() set to pixel_traits<out_image_type>::max()
- if (pixel_traits<typename in_image_type::type>::grayscale == false) then - if (pixel_traits<typename in_image_type::type>::grayscale == false) then
......
...@@ -772,25 +772,27 @@ namespace ...@@ -772,25 +772,27 @@ namespace
assign_all_pixels(img, 10); assign_all_pixels(img, 10);
int filter[3][3] = { {1,1,1}, matrix<int,3,5> filter2;
{1,1,1}, filter2 = 1,1,1,1,1,
{1,1,1}}; 1,1,1,1,1,
1,1,1,1,1;
assign_all_pixels(img2,3); 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 r = 0; r<img2.nr(); ++r)
{ {
for (long c = 0; c<img2.nc(); ++c) 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 else
{ {
DLIB_TEST(img2[r][c] == 0); DLIB_TEST_MSG(img2[r][c] == 0,(int)img2[r][c]);
} }
} }
} }
...@@ -798,10 +800,13 @@ namespace ...@@ -798,10 +800,13 @@ namespace
assign_all_pixels(img2,3); assign_all_pixels(img2,3);
assign_all_pixels(img3,3); assign_all_pixels(img3,3);
spatially_filter_image(img, img2, filter); spatially_filter_image(img, img2, filter2);
int row_filter[] = {1,1,1}; matrix<int,1,5> row_filter;
int col_filter[] = {1,1,1}; 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); spatially_filter_image(img, img3, row_filter, col_filter);
...@@ -820,20 +825,16 @@ namespace ...@@ -820,20 +825,16 @@ namespace
} }
} }
row_filter[0] = ((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(1) = ((int)rnd.get_random_8bit_number() - 100)/10;
row_filter[2] = ((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; row_filter(3) = ((int)rnd.get_random_8bit_number() - 100)/10;
col_filter[1] = ((int)rnd.get_random_8bit_number() - 100)/10; row_filter(4) = ((int)rnd.get_random_8bit_number() - 100)/10;
col_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;
for (long rr = 0; rr < 3; ++rr) const matrix<int,3,5> filter = trans(col_filter)*row_filter;
{
for (long cc = 0; cc < 3; ++cc)
{
filter[rr][cc] = row_filter[cc]*col_filter[rr];
}
}
assign_all_pixels(img2,3); assign_all_pixels(img2,3);
assign_all_pixels(img3,3); assign_all_pixels(img3,3);
...@@ -844,6 +845,40 @@ namespace ...@@ -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 class image_tester : public tester
{ {
public: public:
...@@ -862,6 +897,8 @@ namespace ...@@ -862,6 +897,8 @@ namespace
test_integral_image<long, unsigned char>(); test_integral_image<long, unsigned char>();
test_integral_image<double, float>(); test_integral_image<double, float>();
test_zero_border_pixels();
test_filtering<unsigned char>(false,1); test_filtering<unsigned char>(false,1);
test_filtering<unsigned char>(true,1); test_filtering<unsigned char>(true,1);
test_filtering<unsigned char>(false,3); test_filtering<unsigned char>(false,3);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment