diff --git a/tools/python/src/image.cpp b/tools/python/src/image.cpp index bd43ce5ab..7a152e84c 100644 --- a/tools/python/src/image.cpp +++ b/tools/python/src/image.cpp @@ -26,6 +26,62 @@ string print_rgb_pixel_repr(const rgb_pixel& p) return sout.str(); } +// ---------------------------------------------------------------------------------------- + +template +numpy_image py_threshold_image2( + const numpy_image& in_img, + typename pixel_traits::basic_pixel_type thresh +) +{ + numpy_image out_img; + threshold_image(in_img, out_img); + return out_img; +} + +template +numpy_image py_threshold_image( + const numpy_image& in_img +) +{ + numpy_image out_img; + threshold_image(in_img, out_img); + return out_img; +} + +// ---------------------------------------------------------------------------------------- + +template +typename pixel_traits::basic_pixel_type py_partition_pixels ( + const numpy_image& img +) +{ + return partition_pixels(img); +} + +template +py::tuple py_partition_pixels2 ( + const numpy_image& img, + int num_thresholds +) +{ + DLIB_CASSERT(1 <= num_thresholds && num_thresholds <= 6); + + typename pixel_traits::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, py::arg("img") ); + m.def("threshold_image", &py_threshold_image, py::arg("img") ); + m.def("threshold_image", &py_threshold_image, py::arg("img") ); + m.def("threshold_image", &py_threshold_image, py::arg("img") ); + m.def("threshold_image", &py_threshold_image, py::arg("img") ); + m.def("threshold_image", &py_threshold_image,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, py::arg("img"), py::arg("thresh") ); + m.def("threshold_image", &py_threshold_image2, py::arg("img"), py::arg("thresh") ); + m.def("threshold_image", &py_threshold_image2, py::arg("img"), py::arg("thresh") ); + m.def("threshold_image", &py_threshold_image2, py::arg("img"), py::arg("thresh") ); + m.def("threshold_image", &py_threshold_image2, py::arg("img"), py::arg("thresh") ); + m.def("threshold_image", &py_threshold_image2,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, py::arg("img") ); + m.def("partition_pixels", &py_partition_pixels, py::arg("img") ); + m.def("partition_pixels", &py_partition_pixels, py::arg("img") ); + m.def("partition_pixels", &py_partition_pixels, py::arg("img") ); + m.def("partition_pixels", &py_partition_pixels, py::arg("img") ); + m.def("partition_pixels", &py_partition_pixels,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, py::arg("img"), py::arg("num_thresholds") ); + m.def("partition_pixels", &py_partition_pixels2, py::arg("img"), py::arg("num_thresholds") ); + m.def("partition_pixels", &py_partition_pixels2, py::arg("img"), py::arg("num_thresholds") ); + m.def("partition_pixels", &py_partition_pixels2, py::arg("img"), py::arg("num_thresholds") ); + m.def("partition_pixels", &py_partition_pixels2, py::arg("img"), py::arg("num_thresholds") ); + m.def("partition_pixels", &py_partition_pixels2,docs, py::arg("img"), py::arg("num_thresholds") ); } + diff --git a/tools/python/test/test_numpy_returns.py b/tools/python/test/test_numpy_returns.py index 7917cd4f0..c19661e6e 100644 --- a/tools/python/test/test_numpy_returns.py +++ b/tools/python/test/test_numpy_returns.py @@ -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(): @@ -63,4 +79,4 @@ def test_regression_issue_1220_get_face_chips(): count = sys.getrefcount(face_chips) assert count == 2 count = sys.getrefcount(face_chips[0]) - assert count == 2 \ No newline at end of file + assert count == 2