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

Added the spatially_filter_image_separable_down() routine which is just a version

of spatially_filter_image_separable() that does downsampling at the same time
as it does the filtering.
parent ea897b9e
......@@ -251,6 +251,149 @@ namespace dlib
spatially_filter_image_separable(in_img,out_img,row_filter,col_filter,1);
}
// ----------------------------------------------------------------------------------------
template <
typename in_image_type,
typename out_image_type,
typename EXP1,
typename EXP2,
typename T
>
void spatially_filter_image_separable_down (
const unsigned long downsample,
const in_image_type& in_img,
out_image_type& out_img,
const matrix_exp<EXP1>& row_filter,
const matrix_exp<EXP2>& col_filter,
T scale,
bool use_abs = false,
bool add_to = 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 );
DLIB_ASSERT(downsample > 0 &&
scale != 0 &&
row_filter.size()%2 == 1 &&
col_filter.size()%2 == 1 &&
is_vector(row_filter) &&
is_vector(col_filter),
"\tvoid spatially_filter_image_separable_down()"
<< "\n\t Invalid inputs were given to this function."
<< "\n\t downsample: "<< downsample
<< "\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_separable_down()"
<< "\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((long)(std::ceil((double)in_img.nr()/downsample)),
(long)(std::ceil((double)in_img.nc()/downsample)));
const double col_border = std::floor(col_filter.size()/2.0);
const double row_border = std::floor(row_filter.size()/2.0);
// figure out the range that we should apply the filter to
const long first_row = (long)std::ceil(col_border/downsample);
const long first_col = (long)std::ceil(row_border/downsample);
const long last_row = (long)std::ceil((in_img.nr() - col_border)/downsample);
const long last_col = (long)std::ceil((in_img.nc() - row_border)/downsample);
// zero border pixels
border_enumerator be(get_rect(out_img), rectangle(first_col, first_row, last_col-1, last_row-1));
while (be.move_next())
{
out_img[be.element().y()][be.element().x()] = 0;
}
typedef typename EXP1::type ptype;
typedef typename out_image_type::mem_manager_type mem_manager_type;
array2d<ptype,mem_manager_type> temp_img;
temp_img.set_size(in_img.nr(), out_img.nc());
// apply the row filter
for (long r = 0; r < temp_img.nr(); ++r)
{
for (long c = first_col; c < last_col; ++c)
{
ptype p;
ptype temp = 0;
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*downsample-row_filter.size()/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)
{
ptype temp = 0;
for (long m = 0; m < col_filter.size(); ++m)
{
temp += temp_img[r*downsample-col_filter.size()/2+m][c]*col_filter(m);
}
temp /= scale;
if (use_abs && temp < 0)
{
temp = -temp;
}
// save this pixel to the output image
if (add_to == false)
{
assign_pixel(out_img[r][c], in_img[r*downsample][c*downsample]);
assign_pixel_intensity(out_img[r][c], temp);
}
else
{
assign_pixel(out_img[r][c], temp + get_pixel_intensity(out_img[r][c]));
}
}
}
}
template <
typename in_image_type,
typename out_image_type,
typename EXP1,
typename EXP2
>
void spatially_filter_image_separable_down (
const unsigned long downsample,
const in_image_type& in_img,
out_image_type& out_img,
const matrix_exp<EXP1>& row_filter,
const matrix_exp<EXP2>& col_filter
)
{
spatially_filter_image_separable_down(downsample,in_img,out_img,row_filter,col_filter,1);
}
// ----------------------------------------------------------------------------------------
template <
......
......@@ -110,6 +110,49 @@ namespace dlib
- #out_img.nr() == in_img.nr()
!*/
// ----------------------------------------------------------------------------------------
template <
typename in_image_type,
typename out_image_type,
typename EXP1,
typename EXP2,
typename T
>
void spatially_filter_image_separable_down (
const unsigned long downsample,
const in_image_type& in_img,
out_image_type& out_img,
const matrix_exp<EXP1>& row_filter,
const matrix_exp<EXP2>& col_filter,
T scale = 1,
bool use_abs = false,
bool add_to = 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
- T must be some scalar type
- scale != 0
- 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)
- downsample > 0
ensures
- This function is equivalent to calling
spatially_filter_image_separable(in_img,out_img,row_filter,col_filter,scale,use_abs,add_to)
and then downsampling the output image by a factor of downsample. Therefore,
we will have that:
- #out_img.nr() == ceil((double)in_img.nr()/downsample)
- #out_img.nc() == ceil((double)in_img.nc()/downsample)
- #out_img[r][c] == filtered pixel corresponding to in_img[r*downsample][c*downsample]
!*/
// ----------------------------------------------------------------------------------------
template <
......
......@@ -1155,6 +1155,139 @@ namespace
}
}
// ----------------------------------------------------------------------------------------
template <
typename in_image_type,
typename out_image_type
>
void downsample_image (
const unsigned long downsample,
const in_image_type& in_img,
out_image_type& out_img,
bool add_to
)
{
out_img.set_size((in_img.nr()+downsample-1)/downsample,
(in_img.nc()+downsample-1)/downsample);
for (long r = 0; r < out_img.nr(); ++r)
{
for (long c = 0; c < out_img.nc(); ++c)
{
if (add_to)
out_img[r][c] += in_img[r*downsample][c*downsample];
else
out_img[r][c] = in_img[r*downsample][c*downsample];
}
}
}
template <
typename in_image_type,
typename out_image_type,
typename EXP1,
typename EXP2,
typename T
>
void spatially_filter_image_separable_down_simple (
const unsigned long downsample,
const in_image_type& in_img,
out_image_type& out_img,
const matrix_exp<EXP1>& row_filter,
const matrix_exp<EXP2>& col_filter,
T scale,
bool use_abs = false,
bool add_to = false
)
{
out_image_type temp;
spatially_filter_image_separable(in_img, temp, row_filter, col_filter, scale, use_abs, false);
downsample_image(downsample, temp, out_img, add_to);
}
template <unsigned long downsample>
void test_downsampled_filtering_helper(long row_filt_size, long col_filt_size)
{
print_spinner();
dlog << LTRACE << "***********************************";
dlog << LTRACE << "downsample: " << downsample;
dlog << LTRACE << "row_filt_size: "<< row_filt_size;
dlog << LTRACE << "col_filt_size: "<< col_filt_size;
dlib::rand rnd;
array2d<int> out1, out2;
for (long nr = 0; nr < 3; ++nr)
{
for (long nc = 0; nc < 3; ++nc)
{
dlog << LTRACE << "nr: "<< nr;
dlog << LTRACE << "nc: "<< nc;
array2d<unsigned char> img(25+nr,25+nc);
for (int k = 0; k < 5; ++k)
{
for (long r = 0; r < img.nr(); ++r)
{
for (long c = 0; c < img.nc(); ++c)
{
img[r][c] = rnd.get_random_8bit_number();
}
}
matrix<int,0,1> row_filter(row_filt_size);
matrix<int,0,1> col_filter(col_filt_size);
row_filter = matrix_cast<int>(10*randm(row_filt_size,1, rnd));
col_filter = matrix_cast<int>(10*randm(col_filt_size,1, rnd));
row_filter -= 3;
col_filter -= 3;
spatially_filter_image_separable_down_simple(downsample, img, out1, row_filter, col_filter,1 );
spatially_filter_image_separable_down(downsample, img, out2, row_filter, col_filter);
DLIB_TEST(get_rect(out1) == get_rect(out2));
DLIB_TEST(array_to_matrix(out1) == array_to_matrix(out2));
spatially_filter_image_separable_down_simple(downsample, img, out1, row_filter, col_filter,3, true, true );
spatially_filter_image_separable_down(downsample, img, out2, row_filter, col_filter, 3, true, true);
DLIB_TEST(get_rect(out1) == get_rect(out2));
DLIB_TEST(array_to_matrix(out1) == array_to_matrix(out2));
}
}
}
}
void test_downsampled_filtering()
{
test_downsampled_filtering_helper<1>(5,5);
test_downsampled_filtering_helper<2>(5,5);
test_downsampled_filtering_helper<3>(5,5);
test_downsampled_filtering_helper<1>(3,5);
test_downsampled_filtering_helper<2>(3,5);
test_downsampled_filtering_helper<3>(3,5);
test_downsampled_filtering_helper<1>(5,3);
test_downsampled_filtering_helper<2>(5,3);
test_downsampled_filtering_helper<3>(5,3);
test_downsampled_filtering_helper<1>(3,3);
test_downsampled_filtering_helper<2>(3,3);
test_downsampled_filtering_helper<3>(3,3);
test_downsampled_filtering_helper<1>(1,1);
test_downsampled_filtering_helper<2>(1,1);
test_downsampled_filtering_helper<3>(1,1);
}
// ----------------------------------------------------------------------------------------
class image_tester : public tester
{
public:
......@@ -1186,6 +1319,7 @@ namespace
test_label_connected_blobs();
test_label_connected_blobs2();
test_downsampled_filtering();
}
} 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