Commit e340f1cb authored by Davis King's avatar Davis King

Added an overload of partition_pixels() that provides multiple output thresholds.

parent 69a53cc9
......@@ -21,47 +21,19 @@ namespace dlib
namespace impl
{
template <
typename image_type
typename U,
typename V,
typename basic_pixel_type
>
typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type
partition_pixels_float (
const image_type& img_
void partition_pixels_float_work (
unsigned long begin,
unsigned long end,
U&& cumsum,
V&& sorted,
basic_pixel_type& pix_thresh,
unsigned long& int_thresh
)
{
/*
This is a version of partition_pixels() that doesn't use the histogram to
perform a radix sort but rather uses std::sort() as the first processing
step. It is therefor useful in cases where the range of possible pixels is
too large for the faster histogram version.
Also, the reason we have this function in namespace impl is so we can call it
for easy testing in dlib's unit tests.
*/
COMPILE_TIME_ASSERT( pixel_traits<typename image_traits<image_type>::pixel_type>::has_alpha == false );
typedef typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type basic_pixel_type;
const_image_view<image_type> img(img_);
std::vector<basic_pixel_type> sorted;
sorted.reserve(img.size());
for (long r = 0; r < img.nr(); ++r)
{
for (long c = 0; c < img.nc(); ++c)
sorted.emplace_back(get_pixel_intensity(img[r][c]));
}
std::sort(sorted.begin(), sorted.end());
std::vector<double> cumsum;
cumsum.reserve(sorted.size()+1);
// create integral array
cumsum.emplace_back(0);
for (auto& v : sorted)
cumsum.emplace_back(cumsum.back()+v);
auto histsum = [&](long begin, long end)
{
......@@ -80,12 +52,12 @@ namespace dlib
unsigned long right_idx = 0;
auto total_abs = [&](unsigned long thresh)
{
auto left_avg = histsumi(0,thresh);
auto tmp = histsum(0,thresh);
auto left_avg = histsumi(begin,thresh);
auto tmp = histsum(begin,thresh);
if (tmp != 0)
left_avg /= tmp;
auto right_avg = histsumi(thresh,img.size());
tmp = histsum(thresh,img.size());
auto right_avg = histsumi(thresh,end);
tmp = histsum(thresh,end);
if (tmp != 0)
right_avg /= tmp;
......@@ -96,17 +68,18 @@ namespace dlib
++right_idx;
double score = 0;
score += left_avg*histsum(0,left_idx) - histsumi(0,left_idx);
score += left_avg*histsum(begin,left_idx) - histsumi(begin,left_idx);
score -= left_avg*histsum(left_idx,thresh) - histsumi(left_idx,thresh);
score += right_avg*histsum(thresh,right_idx) - histsumi(thresh,right_idx);
score -= right_avg*histsum(right_idx,img.size()) - histsumi(right_idx,img.size());
score -= right_avg*histsum(right_idx,end) - histsumi(right_idx,end);
return score;
};
unsigned long thresh = 0;
int_thresh = begin;
double min_sad = std::numeric_limits<double>::infinity();
for (unsigned long i = 0; i < img.size(); ++i)
for (unsigned long i = begin; i < end; ++i)
{
// You can't drop a threshold in-between pixels with identical values. So
// skip thresholds corresponding to this degenerate case.
......@@ -117,16 +90,100 @@ namespace dlib
if (sad <= min_sad)
{
min_sad = sad;
thresh = i;
int_thresh = i;
}
}
return sorted[thresh];
pix_thresh = sorted[int_thresh];
}
template <
typename U,
typename V,
typename basic_pixel_type
>
void recursive_partition_pixels_float (
unsigned long begin,
unsigned long end,
U&& cumsum,
V&& sorted,
basic_pixel_type& pix_thresh
)
{
unsigned long int_thresh;
partition_pixels_float_work(begin, end, cumsum, sorted, pix_thresh, int_thresh);
}
template <
typename U,
typename V,
typename basic_pixel_type,
typename ...T
>
void recursive_partition_pixels_float (
unsigned long begin,
unsigned long end,
U&& cumsum,
V&& sorted,
basic_pixel_type& pix_thresh,
T&& ...more_thresholds
)
{
unsigned long int_thresh;
partition_pixels_float_work(begin, end, cumsum, sorted, pix_thresh, int_thresh);
recursive_partition_pixels_float(int_thresh, end, cumsum, sorted, more_thresholds...);
}
template <
typename image_type,
typename ...T
>
void partition_pixels_float (
const image_type& img_,
typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type& pix_thresh,
T&& ...more_thresholds
)
{
/*
This is a version of partition_pixels() that doesn't use the histogram to
perform a radix sort but rather uses std::sort() as the first processing
step. It is therefor useful in cases where the range of possible pixels is
too large for the faster histogram version.
*/
COMPILE_TIME_ASSERT( pixel_traits<typename image_traits<image_type>::pixel_type>::has_alpha == false );
typedef typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type basic_pixel_type;
const_image_view<image_type> img(img_);
std::vector<basic_pixel_type> sorted;
sorted.reserve(img.size());
for (long r = 0; r < img.nr(); ++r)
{
for (long c = 0; c < img.nc(); ++c)
sorted.emplace_back(get_pixel_intensity(img[r][c]));
}
std::sort(sorted.begin(), sorted.end());
std::vector<double> cumsum;
cumsum.reserve(sorted.size()+1);
// create integral array
cumsum.emplace_back(0);
for (auto& v : sorted)
cumsum.emplace_back(cumsum.back()+v);
recursive_partition_pixels_float(0, img.size(), cumsum, sorted, pix_thresh, more_thresholds...);
}
template <typename image_type>
struct is_u16img
struct is_u16img_or_less
{
typedef typename image_traits<image_type>::pixel_type pixel_type;
typedef typename pixel_traits<pixel_type>::basic_pixel_type basic_pixel_type;
......@@ -136,22 +193,95 @@ namespace dlib
}
template <
typename image_type
typename image_type,
typename ...T
>
typename disable_if<impl::is_u16img<image_type>, typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type>::type
typename disable_if<impl::is_u16img_or_less<image_type>>::type
partition_pixels (
const image_type& img
const image_type& img,
typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type& pix_thresh,
T&& ...more_thresholds
)
{
return impl::partition_pixels_float(img);
impl::partition_pixels_float(img, pix_thresh, more_thresholds...);
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
namespace impl
{
template <
typename U,
typename basic_pixel_type
>
void partition_pixels_work (
unsigned long begin,
unsigned long end,
U&& total_abs,
basic_pixel_type& pix_thresh,
unsigned long& int_thresh
)
{
int_thresh = begin;
double min_sad = std::numeric_limits<double>::infinity();
for (unsigned long i = begin; i < end; ++i)
{
double sad = total_abs(begin, i);
if (sad <= min_sad)
{
min_sad = sad;
int_thresh = i;
}
}
pix_thresh = int_thresh;
}
template <
typename U,
typename basic_pixel_type
>
void recursive_partition_pixels (
unsigned long begin,
unsigned long end,
U&& total_abs,
basic_pixel_type& pix_thresh
)
{
unsigned long int_thresh;
partition_pixels_work(begin, end, total_abs, pix_thresh, int_thresh);
}
template <
typename U,
typename basic_pixel_type,
typename ...T
>
void recursive_partition_pixels (
unsigned long begin,
unsigned long end,
U&& total_abs,
basic_pixel_type& pix_thresh,
T&& ...more_thresholds
)
{
unsigned long int_thresh;
partition_pixels_work(begin, end, total_abs, pix_thresh, int_thresh);
recursive_partition_pixels(int_thresh, end, total_abs, more_thresholds...);
}
}
template <
typename image_type
typename image_type,
typename ...T
>
typename enable_if<impl::is_u16img<image_type>, typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type>::type
typename enable_if<impl::is_u16img_or_less<image_type>>::type
partition_pixels (
const image_type& img
const image_type& img,
typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type& pix_thresh,
T&& ...more_thresholds
)
{
COMPILE_TIME_ASSERT( pixel_traits<typename image_traits<image_type>::pixel_type>::has_alpha == false );
......@@ -184,10 +314,10 @@ namespace dlib
// those >= thresh (the right group), what would the sum of absolute deviations of
// each pixel from the mean of its group be? total_abs(thresh) computes that
// value.
auto total_abs = [&](unsigned long thresh)
auto total_abs = [&](unsigned long begin, unsigned long thresh)
{
auto left_avg = histsumi(0,thresh);
auto tmp = histsum(0,thresh);
auto left_avg = histsumi(begin,thresh);
auto tmp = histsum(begin,thresh);
if (tmp != 0)
left_avg /= tmp;
auto right_avg = histsumi(thresh,hist.size());
......@@ -200,7 +330,7 @@ namespace dlib
const long right_idx = (long)std::ceil(right_avg);
double score = 0;
score += left_avg*histsum(0,left_idx) - histsumi(0,left_idx);
score += left_avg*histsum(begin,left_idx) - histsumi(begin,left_idx);
score -= left_avg*histsum(left_idx,thresh) - histsumi(left_idx,thresh);
score += right_avg*histsum(thresh,right_idx) - histsumi(thresh,right_idx);
score -= right_avg*histsum(right_idx,hist.size()) - histsumi(right_idx,hist.size());
......@@ -208,21 +338,26 @@ namespace dlib
};
unsigned long thresh = 0;
double min_sad = std::numeric_limits<double>::infinity();
for (long i = 0; i < hist.size(); ++i)
{
double sad = total_abs(i);
if (sad <= min_sad)
{
min_sad = sad;
thresh = i;
}
}
impl::recursive_partition_pixels(0, hist.size(), total_abs, pix_thresh, more_thresholds...);
}
// ----------------------------------------------------------------------------------------
template <
typename image_type
>
typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type
partition_pixels (
const image_type& img
)
{
typedef typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type basic_pixel_type;
basic_pixel_type thresh;
partition_pixels(img, thresh);
return thresh;
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
template <
......
......@@ -34,6 +34,35 @@ namespace dlib
deviations between each pixel and the mean of its group is minimized.
!*/
template <
typename image_type,
typename ...T
>
void partition_pixels (
const image_type& img,
typename pixel_traits<typename image_traits<image_type>::pixel_type>::basic_pixel_type& pix_thresh,
T&& ...more_thresholds
);
/*!
requires
- image_type == an image object that implements the interface defined in
dlib/image_processing/generic_image.h
- pixel_traits<typename image_traits<image_type>::pixel_type>::has_alpha == false
- more_thresholds == a bunch of parameters of the same type as pix_thresh.
ensures
- This version of partition_pixels() finds multiple partitions rather than just
one partition. It does this by first partitioning the pixels just as the
above partition_pixels(img) does. Then it forms a new image with only pixels
>= that first partition value and recursively partitions this new image.
However, the recursion is implemented in an efficient way which is faster than
explicitly forming these images and calling partition_pixels(), but the
output is the same as if you did. For example, suppose you called
partition_pixels(img, t1, t2, t3). Then we would have:
- t1 == partition_pixels(img)
- t2 == partition_pixels(an image with only pixels with values >= t1 in it)
- t3 == partition_pixels(an image with only pixels with values >= t2 in it)
!*/
// ----------------------------------------------------------------------------------------
template <
......
......@@ -1904,10 +1904,39 @@ namespace
p = rnd.get_random_8bit_number();
DLIB_TEST(simple_partition_pixels(img) == partition_pixels(img));
DLIB_TEST(simple_partition_pixels(img) == impl::partition_pixels_float(img));
unsigned char thresh;
impl::partition_pixels_float(img,thresh);
DLIB_TEST(simple_partition_pixels(img) == thresh);
matrix<float> fimg = matrix_cast<float>(img);
DLIB_TEST(simple_partition_pixels(img) == partition_pixels(fimg));
std::vector<unsigned char> tmp;
for (auto& v : img)
if (v >= thresh)
tmp.push_back(v);
matrix<unsigned char> img2 = mat(tmp);
unsigned char thresh2;
impl::partition_pixels_float(img,thresh, thresh2);
DLIB_TEST(simple_partition_pixels(img) == thresh);
DLIB_TEST(simple_partition_pixels(img2) == thresh2);
partition_pixels(img,thresh, thresh2);
DLIB_TEST(simple_partition_pixels(img) == thresh);
DLIB_TEST(simple_partition_pixels(img2) == thresh2);
std::vector<float> ftmp;
for (auto& v : fimg)
if (v >= thresh)
ftmp.push_back(v);
matrix<float> fimg2 = mat(ftmp);
float fthresh, fthresh2;
partition_pixels(fimg,fthresh, fthresh2);
DLIB_TEST(simple_partition_pixels(img) == fthresh);
DLIB_TEST(simple_partition_pixels(img2) == fthresh2);
}
......@@ -1919,10 +1948,42 @@ namespace
p = rnd.get_random_8bit_number();
DLIB_TEST(simple_partition_pixels(img) == partition_pixels(img));
DLIB_TEST(simple_partition_pixels(img) == impl::partition_pixels_float(img));
unsigned char thresh;
impl::partition_pixels_float(img,thresh);
DLIB_TEST(simple_partition_pixels(img) == thresh);
matrix<float> fimg = matrix_cast<float>(img);
DLIB_TEST(simple_partition_pixels(img) == partition_pixels(fimg));
std::vector<unsigned char> tmp;
for (auto& v : img)
if (v >= thresh)
tmp.push_back(v);
matrix<unsigned char> img2 = mat(tmp);
unsigned char thresh2;
impl::partition_pixels_float(img,thresh, thresh2);
DLIB_TEST(simple_partition_pixels(img) == thresh);
DLIB_TEST(simple_partition_pixels(img2) == thresh2);
partition_pixels(img,thresh, thresh2);
DLIB_TEST(simple_partition_pixels(img) == thresh);
DLIB_TEST(simple_partition_pixels(img2) == thresh2);
std::vector<float> ftmp;
for (auto& v : fimg)
if (v >= thresh)
ftmp.push_back(v);
matrix<float> fimg2 = mat(ftmp);
float fthresh, fthresh2;
partition_pixels(fimg,fthresh, fthresh2);
DLIB_TEST(simple_partition_pixels(img) == fthresh);
DLIB_TEST(simple_partition_pixels(img2) == fthresh2);
}
}
......
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