Commit 73a34f7f authored by Davis King's avatar Davis King

Added an overload of spatially_filter_image() that uses a separable filter.

parent 16ae5eac
......@@ -7,6 +7,7 @@
#include "spatial_filtering_abstract.h"
#include "../algs.h"
#include "../assert.h"
#include "../array2d.h"
#include <limits>
namespace dlib
......@@ -102,6 +103,114 @@ namespace dlib
}
}
// ----------------------------------------------------------------------------------------
template <
typename in_image_type,
typename out_image_type,
typename filter_type,
long M,
long N
>
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],
unsigned long scale = 1,
bool use_abs = 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(M%2 == 1);
COMPILE_TIME_ASSERT(N%2 == 1);
DLIB_ASSERT(scale > 0,
"\tvoid spatially_filter_image()"
<< "\n\tYou can't give a scale of zero"
);
DLIB_ASSERT(is_same_object(in_img, out_img) == false,
"\tvoid spatially_filter_image()"
<< "\n\tYou must give two different image objects"
);
// if there isn't any input image then don't do anything
if (in_img.size() == 0)
{
out_img.clear();
return;
}
out_img.set_size(in_img.nr(),in_img.nc());
zero_border_pixels(out_img, M/2, N/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;
typedef typename pixel_traits<typename in_image_type::type>::basic_pixel_type bp_type;
typedef typename out_image_type::mem_manager_type mem_manager_type;
array2d<typename promote<bp_type>::type,mem_manager_type> temp_img;
temp_img.set_size(in_img.nr(), in_img.nc());
// apply the row filter
for (long r = 0; r < in_img.nr(); ++r)
{
for (long c = first_col; c < last_col; ++c)
{
typename promote<bp_type>::type p;
typename promote<bp_type>::type temp = 0;
for (long n = 0; n < N; ++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];
}
temp_img[r][c] = temp;
}
}
// apply the column filter
for (long r = first_row; r < last_row; ++r)
{
for (long c = first_col; c < last_col; ++c)
{
typename promote<bp_type>::type temp = 0;
for (long m = 0; m < M; ++m)
{
temp += temp_img[r-M/2+m][c]*col_filter[m];
}
temp /= scale;
// Catch any underflow or apply abs as appropriate
if (temp < 0)
{
if (use_abs)
{
temp = -temp;
}
else
{
temp = 0;
}
}
// save this pixel to the output image
assign_pixel(out_img[r][c], in_img[r][c]);
assign_pixel_intensity(out_img[r][c], temp);
}
}
}
// ----------------------------------------------------------------------------------------
template <
......
......@@ -47,8 +47,55 @@ namespace dlib
- else
- pixel values after filtering that are < 0 are assigned the value of 0
- Pixels close enough to the edge of in_img to not have the filter still fit
inside the image are not modified. i.e. Whatever value the border of out_img
had to begin with is what it will have after this function returns.
inside the image are set to zero.
- #out_img.nc() == in_img.nc()
- #out_img.nr() == in_img.nr()
!*/
// ----------------------------------------------------------------------------------------
template <
typename in_image_type,
typename out_image_type,
typename filter_type,
long M,
long N
>
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],
unsigned long scale = 1,
bool use_abs = false
);
/*!
requires
- in_image_type == is an implementation of array2d/array2d_kernel_abstract.h
- out_image_type == is an implementation of array2d/array2d_kernel_abstract.h
- pixel_traits<typename in_image_type::type>::has_alpha == false
- 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)
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]
- 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
- the pixel values are converted to the HSI color space and the filtering
is done on the intensity channel only.
- if (use_abs == true) then
- pixel values after filtering that are < 0 are converted to their absolute values
- else
- pixel values after filtering that are < 0 are assigned the value of 0
- Pixels close enough to the edge of in_img to not have the filter still fit
inside the image are set to zero.
- #out_img.nc() == in_img.nc()
- #out_img.nr() == in_img.nr()
!*/
......
......@@ -762,6 +762,87 @@ namespace
}
template <typename T>
void test_filtering(bool use_abs, unsigned long scale )
{
print_spinner();
dlog << LINFO << "test_filtering(" << use_abs << "," << scale << ")";
array2d<T> img, img2, img3;
img.set_size(10,11);
assign_all_pixels(img, 10);
int filter[3][3] = { 1,1,1,
1,1,1,
1,1,1};
assign_all_pixels(img2,3);
spatially_filter_image(img, img2, filter);
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))
{
DLIB_TEST(img2[r][c] == 90);
}
else
{
DLIB_TEST(img2[r][c] == 0);
}
}
}
assign_all_pixels(img2,3);
assign_all_pixels(img3,3);
spatially_filter_image(img, img2, filter);
int row_filter[] = {1,1,1};
int col_filter[] = {1,1,1};
spatially_filter_image(img, img3, row_filter, col_filter);
DLIB_TEST(array_to_matrix(img2) == array_to_matrix(img3));
dlib::rand rnd;
for (int i = 0; i < 30; ++i)
{
for (long r = 0; r < img.nr(); ++r)
{
for (long c = 0; c < img.nc(); ++c)
{
img[r][c] = rnd.get_random_8bit_number();
}
}
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;
for (long rr = 0; rr < 3; ++rr)
{
for (long cc = 0; cc < 3; ++cc)
{
filter[rr][cc] = row_filter[cc]*col_filter[rr];
}
}
assign_all_pixels(img2,3);
assign_all_pixels(img3,3);
// Just make sure both filtering methods give the same results.
spatially_filter_image(img, img2, filter, scale, use_abs);
spatially_filter_image(img, img3, row_filter, col_filter, scale, use_abs);
DLIB_TEST(array_to_matrix(img2) == array_to_matrix(img3));
}
}
class image_tester : public tester
{
......@@ -780,6 +861,15 @@ namespace
test_integral_image<double, int>();
test_integral_image<long, unsigned char>();
test_integral_image<double, float>();
test_filtering<unsigned char>(false,1);
test_filtering<unsigned char>(true,1);
test_filtering<unsigned char>(false,3);
test_filtering<unsigned char>(true,3);
test_filtering<int>(false,1);
test_filtering<int>(true,1);
test_filtering<int>(false,3);
test_filtering<int>(true,3);
}
} a;
......
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