Added python interface to threshold_image() and partition_pixels().

This commit is contained in:
Davis King 2018-05-19 13:12:47 -04:00
parent 27dbbe2de1
commit 7e7d8e9071
2 changed files with 124 additions and 1 deletions

View File

@ -26,6 +26,62 @@ string print_rgb_pixel_repr(const rgb_pixel& p)
return sout.str();
}
// ----------------------------------------------------------------------------------------
template <typename T>
numpy_image<unsigned char> py_threshold_image2(
const numpy_image<T>& in_img,
typename pixel_traits<T>::basic_pixel_type thresh
)
{
numpy_image<unsigned char> out_img;
threshold_image(in_img, out_img);
return out_img;
}
template <typename T>
numpy_image<unsigned char> py_threshold_image(
const numpy_image<T>& in_img
)
{
numpy_image<unsigned char> out_img;
threshold_image(in_img, out_img);
return out_img;
}
// ----------------------------------------------------------------------------------------
template <typename T>
typename pixel_traits<T>::basic_pixel_type py_partition_pixels (
const numpy_image<T>& img
)
{
return partition_pixels(img);
}
template <typename T>
py::tuple py_partition_pixels2 (
const numpy_image<T>& img,
int num_thresholds
)
{
DLIB_CASSERT(1 <= num_thresholds && num_thresholds <= 6);
typename pixel_traits<T>::basic_pixel_type t1,t2,t3,t4,t5,t6;
switch(num_thresholds)
{
case 1: partition_pixels(img,t1); return py::make_tuple(t1);
case 2: partition_pixels(img,t1,t2); return py::make_tuple(t1,t2);
case 3: partition_pixels(img,t1,t2,t3); return py::make_tuple(t1,t2,t3);
case 4: partition_pixels(img,t1,t2,t3,t4); return py::make_tuple(t1,t2,t3,t4);
case 5: partition_pixels(img,t1,t2,t3,t4,t5); return py::make_tuple(t1,t2,t3,t4,t5);
case 6: partition_pixels(img,t1,t2,t3,t4,t5,t6); return py::make_tuple(t1,t2,t3,t4,t5,t6);
}
DLIB_CASSERT(false, "This should never happen.");
}
// ----------------------------------------------------------------------------------------
void bind_image_classes(py::module& m)
@ -37,4 +93,55 @@ void bind_image_classes(py::module& m)
.def_readwrite("red", &rgb_pixel::red)
.def_readwrite("green", &rgb_pixel::green)
.def_readwrite("blue", &rgb_pixel::blue);
const char* docs = "Thresholds img and returns the result. Pixels in img with grayscale values >= partition_pixels(img) \n"
"have an output value of 255 and all others have a value of 0.";
m.def("threshold_image", &py_threshold_image<unsigned char>, py::arg("img") );
m.def("threshold_image", &py_threshold_image<uint16_t>, py::arg("img") );
m.def("threshold_image", &py_threshold_image<uint32_t>, py::arg("img") );
m.def("threshold_image", &py_threshold_image<float>, py::arg("img") );
m.def("threshold_image", &py_threshold_image<double>, py::arg("img") );
m.def("threshold_image", &py_threshold_image<rgb_pixel>,docs, py::arg("img") );
docs = "Thresholds img and returns the result. Pixels in img with grayscale values >= thresh \n"
"have an output value of 255 and all others have a value of 0.";
m.def("threshold_image", &py_threshold_image2<unsigned char>, py::arg("img"), py::arg("thresh") );
m.def("threshold_image", &py_threshold_image2<uint16_t>, py::arg("img"), py::arg("thresh") );
m.def("threshold_image", &py_threshold_image2<uint32_t>, py::arg("img"), py::arg("thresh") );
m.def("threshold_image", &py_threshold_image2<float>, py::arg("img"), py::arg("thresh") );
m.def("threshold_image", &py_threshold_image2<double>, py::arg("img"), py::arg("thresh") );
m.def("threshold_image", &py_threshold_image2<rgb_pixel>,docs, py::arg("img"), py::arg("thresh") );
docs =
"Finds a threshold value that would be reasonable to use with \n\
threshold_image(img, threshold). It does this by finding the threshold that \n\
partitions the pixels in img into two groups such that the sum of absolute \n\
deviations between each pixel and the mean of its group is minimized.";
m.def("partition_pixels", &py_partition_pixels<rgb_pixel>, py::arg("img") );
m.def("partition_pixels", &py_partition_pixels<unsigned char>, py::arg("img") );
m.def("partition_pixels", &py_partition_pixels<uint16_t>, py::arg("img") );
m.def("partition_pixels", &py_partition_pixels<uint32_t>, py::arg("img") );
m.def("partition_pixels", &py_partition_pixels<float>, py::arg("img") );
m.def("partition_pixels", &py_partition_pixels<double>,docs, py::arg("img") );
docs =
"This version of partition_pixels() finds multiple partitions rather than just \n\
one partition. It does this by first partitioning the pixels just as the \n\
above partition_pixels(img) does. Then it forms a new image with only pixels \n\
>= that first partition value and recursively partitions this new image. \n\
However, the recursion is implemented in an efficient way which is faster than \n\
explicitly forming these images and calling partition_pixels(), but the \n\
output is the same as if you did. For example, suppose you called \n\
[t1,t2,t2] = partition_pixels(img). Then we would have: \n\
- t1 == partition_pixels(img) \n\
- t2 == partition_pixels(an image with only pixels with values >= t1 in it) \n\
- t3 == partition_pixels(an image with only pixels with values >= t2 in it)" ;
m.def("partition_pixels", &py_partition_pixels2<rgb_pixel>, py::arg("img"), py::arg("num_thresholds") );
m.def("partition_pixels", &py_partition_pixels2<unsigned char>, py::arg("img"), py::arg("num_thresholds") );
m.def("partition_pixels", &py_partition_pixels2<uint16_t>, py::arg("img"), py::arg("num_thresholds") );
m.def("partition_pixels", &py_partition_pixels2<uint32_t>, py::arg("img"), py::arg("num_thresholds") );
m.def("partition_pixels", &py_partition_pixels2<float>, py::arg("img"), py::arg("num_thresholds") );
m.def("partition_pixels", &py_partition_pixels2<double>,docs, py::arg("img"), py::arg("num_thresholds") );
}

View File

@ -26,6 +26,22 @@ def get_test_face_chip():
rgb_img, shape = get_test_image_and_shape()
return dlib.get_face_chip(rgb_img, shape)
def test_partition_pixels():
truth = (102, 159, 181);
img, shape = get_test_image_and_shape()
assert(dlib.partition_pixels(img) == truth[0])
assert(dlib.partition_pixels(img,3) == truth)
# Call all these versions of this mainly to make sure binding to
# various image types works.
assert(dlib.partition_pixels(img[:,:,0]) == 125)
assert(dlib.partition_pixels(img[:,:,0].astype('float32')) == 125)
assert(dlib.partition_pixels(img[:,:,0].astype('float64')) == 125)
assert(dlib.partition_pixels(img[:,:,0].astype('uint16')) == 125)
assert(dlib.partition_pixels(img[:,:,0].astype('uint32')) == 125)
# The tests below will be skipped if Numpy is not installed
@pytest.mark.skipif(not utils.is_numpy_installed(), reason="requires numpy")
def test_get_face_chip():