From a3ca78de9cc3cd5a209b711c42289292d7f57b7b Mon Sep 17 00:00:00 2001 From: Davis King Date: Sun, 20 May 2018 19:21:34 -0400 Subject: [PATCH] Added convert_rgb_to_grayscale(), pyramid_down, find_bright_keypoints(), find_bright_lines(), find_dark_lines(), find_dark_keypoints(), suppress_non_maximum_edges(), and find_peaks() to the Python API. --- tools/python/src/image.cpp | 681 ++++++++++++++++++++++++++++++++++++- 1 file changed, 679 insertions(+), 2 deletions(-) diff --git a/tools/python/src/image.cpp b/tools/python/src/image.cpp index 7f1e515d3..62d4438be 100644 --- a/tools/python/src/image.cpp +++ b/tools/python/src/image.cpp @@ -2,6 +2,7 @@ #include #include "dlib/pixel.h" #include +#include using namespace dlib; using namespace std; @@ -175,7 +176,9 @@ numpy_image py_randomly_color_image ( ) { numpy_image temp; - assign_image(temp, randomly_color_image(img)); + matrix itemp; + assign_image(itemp, numpy_image(img)); + assign_image(temp, randomly_color_image(itemp)); return temp; } @@ -187,7 +190,9 @@ numpy_image py_jet ( ) { numpy_image temp; - assign_image(temp, jet(img)); + matrix itemp; + assign_image(itemp, numpy_image(img)); + assign_image(temp, jet(itemp)); return temp; } @@ -218,6 +223,15 @@ py::array convert_image ( "uint8, int8, uint16, int16, uint32, int32, uint64, int64, float32, float, float64, double, or rgb_pixel"); } +numpy_image convert_rgb_to_grayscale( + const numpy_image& img +) +{ + numpy_image out; + assign_image(out, img); + return out; +} + // ---------------------------------------------------------------------------------------- template @@ -246,6 +260,302 @@ py::array convert_image_scaled ( "uint8, int8, uint16, int16, uint32, int32, uint64, int64, float32, float, float64, double, or rgb_pixel"); } +// ---------------------------------------------------------------------------------------- + +struct py_pyramid_down +{ + + void dostuff(point) {} + + py_pyramid_down( + ) = default; + + py_pyramid_down ( + unsigned int N_ + ) : N(N_) + { + DLIB_CASSERT( 1 <= N && N <= 20, "pyramid downsampling rate must be between 1 and 20."); + } + + unsigned int pyramid_downsampling_rate ( + ) const { return N; } + + template + dlib::vector point_down ( + const dlib::vector& p + ) const + { + switch(N) + { + case 1: return pyr1.point_down(p); + case 2: return pyr2.point_down(p); + case 3: return pyr3.point_down(p); + case 4: return pyr4.point_down(p); + case 5: return pyr5.point_down(p); + case 6: return pyr6.point_down(p); + case 7: return pyr7.point_down(p); + case 8: return pyr8.point_down(p); + case 9: return pyr9.point_down(p); + case 10: return pyr10.point_down(p); + case 11: return pyr11.point_down(p); + case 12: return pyr12.point_down(p); + case 13: return pyr13.point_down(p); + case 14: return pyr14.point_down(p); + case 15: return pyr15.point_down(p); + case 16: return pyr16.point_down(p); + case 17: return pyr17.point_down(p); + case 18: return pyr18.point_down(p); + case 19: return pyr19.point_down(p); + case 20: return pyr20.point_down(p); + } + + DLIB_CASSERT(false, "This should never happen"); + } + + template + dlib::vector point_up ( + const dlib::vector& p + ) const + { + switch(N) + { + case 1: return pyr1.point_up(p); + case 2: return pyr2.point_up(p); + case 3: return pyr3.point_up(p); + case 4: return pyr4.point_up(p); + case 5: return pyr5.point_up(p); + case 6: return pyr6.point_up(p); + case 7: return pyr7.point_up(p); + case 8: return pyr8.point_up(p); + case 9: return pyr9.point_up(p); + case 10: return pyr10.point_up(p); + case 11: return pyr11.point_up(p); + case 12: return pyr12.point_up(p); + case 13: return pyr13.point_up(p); + case 14: return pyr14.point_up(p); + case 15: return pyr15.point_up(p); + case 16: return pyr16.point_up(p); + case 17: return pyr17.point_up(p); + case 18: return pyr18.point_up(p); + case 19: return pyr19.point_up(p); + case 20: return pyr20.point_up(p); + } + DLIB_CASSERT(false, "This should never happen"); + } + +// ----------------------------- + + template + dlib::vector point_down2 ( + const dlib::vector& p, + unsigned int levels + ) const + { + dlib::vector temp = p; + for (unsigned int i = 0; i < levels; ++i) + temp = point_down(temp); + return temp; + } + + template + dlib::vector point_up2 ( + const dlib::vector& p, + unsigned int levels + ) const + { + dlib::vector temp = p; + for (unsigned int i = 0; i < levels; ++i) + temp = point_up(temp); + return temp; + } + +// ----------------------------- + + template + rect_type rect_up ( + const rect_type& rect + ) const + { + return rect_type(point_up(rect.tl_corner()), point_up(rect.br_corner())); + } + + template + rect_type rect_up2 ( + const rect_type& rect, + unsigned int levels + ) const + { + return rect_type(point_up2(rect.tl_corner(),levels), point_up2(rect.br_corner(),levels)); + } + +// ----------------------------- + + template + rect_type rect_down ( + const rect_type& rect + ) const + { + return rect_type(point_down(rect.tl_corner()), point_down(rect.br_corner())); + } + + template + rect_type rect_down2 ( + const rect_type& rect, + unsigned int levels + ) const + { + return rect_type(point_down2(rect.tl_corner(),levels), point_down2(rect.br_corner(),levels)); + } + + template < + typename T + > + numpy_image down ( + const numpy_image& img + ) const + { + + numpy_image down; + switch(N) + { + case 1: pyr1(img,down); break; + case 2: pyr2(img,down); break; + case 3: pyr3(img,down); break; + case 4: pyr4(img,down); break; + case 5: pyr5(img,down); break; + case 6: pyr6(img,down); break; + case 7: pyr7(img,down); break; + case 8: pyr8(img,down); break; + case 9: pyr9(img,down); break; + case 10: pyr10(img,down); break; + case 11: pyr11(img,down); break; + case 12: pyr12(img,down); break; + case 13: pyr13(img,down); break; + case 14: pyr14(img,down); break; + case 15: pyr15(img,down); break; + case 16: pyr16(img,down); break; + case 17: pyr17(img,down); break; + case 18: pyr18(img,down); break; + case 19: pyr19(img,down); break; + case 20: pyr20(img,down); break; + } + + return down; + } + +private: + unsigned int N = 2; + + pyramid_down<1> pyr1; + pyramid_down<2> pyr2; + pyramid_down<3> pyr3; + pyramid_down<4> pyr4; + pyramid_down<5> pyr5; + pyramid_down<6> pyr6; + pyramid_down<7> pyr7; + pyramid_down<8> pyr8; + pyramid_down<9> pyr9; + pyramid_down<10> pyr10; + pyramid_down<11> pyr11; + pyramid_down<12> pyr12; + pyramid_down<13> pyr13; + pyramid_down<14> pyr14; + pyramid_down<15> pyr15; + pyramid_down<16> pyr16; + pyramid_down<17> pyr17; + pyramid_down<18> pyr18; + pyramid_down<19> pyr19; + pyramid_down<20> pyr20; + +}; + +// ---------------------------------------------------------------------------------------- + +py::tuple py_find_bright_lines ( + const numpy_image& xx, + const numpy_image& xy, + const numpy_image& yy +) +{ + numpy_image horz, vert; + find_bright_lines(xx,xy,yy,horz,vert); + return py::make_tuple(horz,vert); +} + +py::tuple py_find_dark_lines ( + const numpy_image& xx, + const numpy_image& xy, + const numpy_image& yy +) +{ + numpy_image horz, vert; + find_dark_lines(xx,xy,yy,horz,vert); + return py::make_tuple(horz,vert); +} + +numpy_image py_find_bright_keypoints ( + const numpy_image& xx, + const numpy_image& xy, + const numpy_image& yy +) +{ + numpy_image sal; + find_bright_keypoints(xx,xy,yy,sal); + return sal; +} + +numpy_image py_find_dark_keypoints ( + const numpy_image& xx, + const numpy_image& xy, + const numpy_image& yy +) +{ + numpy_image sal; + find_dark_keypoints(xx,xy,yy,sal); + return sal; +} + +numpy_image py_suppress_non_maximum_edges ( + const numpy_image& horz, + const numpy_image& vert +) +{ + numpy_image out; + suppress_non_maximum_edges(horz,vert,out); + return out; +} + +numpy_image py_suppress_non_maximum_edges2 ( + const py::tuple& horz_and_vert_gradients +) +{ + numpy_image out, horz, vert; + horz = horz_and_vert_gradients[0]; + vert = horz_and_vert_gradients[1]; + suppress_non_maximum_edges(horz,vert,out); + return out; +} + +template +std::vector py_find_peaks ( + const numpy_image& img, + const double non_max_suppression_radius, + const T& thresh +) +{ + return find_peaks(img, non_max_suppression_radius, thresh); +} + +template +std::vector py_find_peaks2 ( + const numpy_image& img, + const double non_max_suppression_radius +) +{ + return find_peaks(img, non_max_suppression_radius, partition_pixels(img)); +} + + // ---------------------------------------------------------------------------------------- void bind_image_classes(py::module& m) @@ -719,6 +1029,8 @@ than 0 are converted to 0."; m.def("convert_image", convert_image, py::arg("img"), py::arg("dtype")); m.def("convert_image", convert_image, docs, py::arg("img"), py::arg("dtype")); + m.def("convert_rgb_to_grayscale", &convert_rgb_to_grayscale, + "Convert a RGB image to a uint8 grayscale image.", py::arg("img")); docs = ""; "requires \n\ @@ -785,5 +1097,370 @@ ensures \n\ m.def("convert_image_scaled", convert_image_scaled, py::arg("img"), py::arg("dtype"), py::arg("thresh")=4); m.def("convert_image_scaled", convert_image_scaled, docs, py::arg("img"), py::arg("dtype"), py::arg("thresh")=4); + + + + + class_docs = +"This is a simple object to help create image pyramids. In particular, it \n\ +downsamples images at a ratio of N to N-1. \n\ + \n\ +Note that setting N to 1 means that this object functions like \n\ +pyramid_disable (defined at the bottom of this file). \n\ + \n\ +WARNING, when mapping rectangles from one layer of a pyramid \n\ +to another you might end up with rectangles which extend slightly \n\ +outside your images. This is because points on the border of an \n\ +image at a higher pyramid layer might correspond to points outside \n\ +images at lower layers. So just keep this in mind. Note also \n\ +that it's easy to deal with. Just say something like this: \n\ + rect = rect.intersect(get_rect(my_image)); # keep rect inside my_image "; + /*! + This is a simple object to help create image pyramids. In particular, it + downsamples images at a ratio of N to N-1. + + Note that setting N to 1 means that this object functions like + pyramid_disable (defined at the bottom of this file). + + WARNING, when mapping rectangles from one layer of a pyramid + to another you might end up with rectangles which extend slightly + outside your images. This is because points on the border of an + image at a higher pyramid layer might correspond to points outside + images at lower layers. So just keep this in mind. Note also + that it's easy to deal with. Just say something like this: + rect = rect.intersect(get_rect(my_image)); # keep rect inside my_image + !*/ + + docs = +"- Downsamples img to make a new image that is roughly (pyramid_downsampling_rate()-1)/pyramid_downsampling_rate() \n\ + times the size of the original image. \n\ +- The location of a point P in original image will show up at point point_down(P) \n\ + in the downsampled image. \n\ +- Note that some points on the border of the original image might correspond to \n\ + points outside the downsampled image."; + /*! + - Downsamples img to make a new image that is roughly (pyramid_downsampling_rate()-1)/pyramid_downsampling_rate() + times the size of the original image. + - The location of a point P in original image will show up at point point_down(P) + in the downsampled image. + - Note that some points on the border of the original image might correspond to + points outside the downsampled image. + !*/ + py::class_(m, "pyramid_down", class_docs) + .def(py::init(), "Creates this class with the provided downsampling rate. i.e. pyramid_downsampling_rate()==N. \nN must be in the range 1 to 20.", py::arg("N")) + .def(py::init<>(), "Creates this class with pyramid_downsampling_rate()==2") + .def("pyramid_downsampling_rate", &py_pyramid_down::pyramid_downsampling_rate, + "Returns a number N that defines the downsampling rate. In particular, images are downsampled by a factor of N to N-1.") + .def("point_up", &py_pyramid_down::point_up, py::arg("p")) + .def("point_up", &py_pyramid_down::point_up, + "Maps from pixels in a downsampled image to pixels in the original image.", py::arg("p")) + .def("point_up", &py_pyramid_down::point_up2, py::arg("p"), py::arg("levels")) + .def("point_up", &py_pyramid_down::point_up2, + "Applies point_up() to p levels times and returns the result.", py::arg("p"), py::arg("levels")) + .def("point_down", &py_pyramid_down::point_down, py::arg("p")) + .def("point_down", &py_pyramid_down::point_down, + "Maps from pixels in a source image to the corresponding pixels in the downsampled image.", py::arg("p")) + .def("point_down", &py_pyramid_down::point_down2, py::arg("p"), py::arg("levels")) + .def("point_down", &py_pyramid_down::point_down2, "Applies point_down() to p levels times and returns the result.", + py::arg("p"), py::arg("levels")) + .def("rect_down", &py_pyramid_down::rect_down, py::arg("rect")) + .def("rect_down", &py_pyramid_down::rect_down, + "returns drectangle(point_down(rect.tl_corner()), point_down(rect.br_corner()));\n (i.e. maps rect into a downsampled)", + py::arg("rect")) + .def("rect_down", &py_pyramid_down::rect_down2, py::arg("rect"), py::arg("levels")) + .def("rect_down", &py_pyramid_down::rect_down2, "Applies rect_down() to rect levels times and returns the result.", + py::arg("rect"), py::arg("levels")) + .def("rect_up", &py_pyramid_down::rect_up, py::arg("rect")) + .def("rect_up", &py_pyramid_down::rect_up, + "returns drectangle(point_up(rect.tl_corner()), point_up(rect.br_corner()));\n (i.e. maps rect into a parent image)", + py::arg("rect")) + .def("rect_up", &py_pyramid_down::rect_up2, py::arg("rect"), py::arg("levels")) + .def("rect_up", &py_pyramid_down::rect_up2, "Applies rect_up() to rect levels times and returns the result.", + py::arg("p"), py::arg("levels")) + .def("__call__", &py_pyramid_down::down, py::arg("img")) + .def("__call__", &py_pyramid_down::down, py::arg("img")) + .def("__call__", &py_pyramid_down::down, py::arg("img")) + .def("__call__", &py_pyramid_down::down, py::arg("img")) + .def("__call__", &py_pyramid_down::down, py::arg("img")) + .def("__call__", &py_pyramid_down::down, py::arg("img")) + .def("__call__", &py_pyramid_down::down, py::arg("img")) + .def("__call__", &py_pyramid_down::down, py::arg("img")) + .def("__call__", &py_pyramid_down::down, py::arg("img")) + .def("__call__", &py_pyramid_down::down, py::arg("img")) + .def("__call__", &py_pyramid_down::down, docs, py::arg("img")); + + + docs = +"requires \n\ + - xx, xy, and yy all have the same dimensions. \n\ +ensures \n\ + - This routine is similar to sobel_edge_detector(), except instead of finding \n\ + an edge it finds a bright/white line. For example, the border between a \n\ + black piece of paper and a white table is an edge, but a curve drawn with a \n\ + pencil on a piece of paper makes a line. Therefore, the output of this \n\ + routine is a vector field encoded in the horz and vert images, which are \n\ + returned in a tuple where the first element is horz and the second is vert. \n\ + \n\ + The vector obtains a large magnitude when centered on a bright line in an image and the \n\ + direction of the vector is perpendicular to the line. To be very precise, \n\ + each vector points in the direction of greatest change in second derivative \n\ + and the magnitude of the vector encodes the derivative magnitude in that \n\ + direction. Moreover, if the second derivative is positive then the output \n\ + vector is zero. This zeroing if positive gradients causes the output to be \n\ + sensitive only to bright lines surrounded by darker pixels. \n\ + \n\ + - We assume that xx, xy, and yy are the 3 second order gradients of the image \n\ + in question. You can obtain these gradients using the image_gradients class. \n\ + - The output images will have the same dimensions as the input images. "; + /*! + requires + - xx, xy, and yy all have the same dimensions. + ensures + - This routine is similar to sobel_edge_detector(), except instead of finding + an edge it finds a bright/white line. For example, the border between a + black piece of paper and a white table is an edge, but a curve drawn with a + pencil on a piece of paper makes a line. Therefore, the output of this + routine is a vector field encoded in the horz and vert images, which are + returned in a tuple where the first element is horz and the second is vert. + + The vector obtains a large magnitude when centered on a bright line in an image and the + direction of the vector is perpendicular to the line. To be very precise, + each vector points in the direction of greatest change in second derivative + and the magnitude of the vector encodes the derivative magnitude in that + direction. Moreover, if the second derivative is positive then the output + vector is zero. This zeroing if positive gradients causes the output to be + sensitive only to bright lines surrounded by darker pixels. + + - We assume that xx, xy, and yy are the 3 second order gradients of the image + in question. You can obtain these gradients using the image_gradients class. + - The output images will have the same dimensions as the input images. + !*/ + m.def("find_bright_lines", &py_find_bright_lines, docs, py::arg("xx"), py::arg("xy"), py::arg("yy")); + + + + docs = +"requires \n\ + - xx, xy, and yy all have the same dimensions. \n\ +ensures \n\ + - This routine is similar to sobel_edge_detector(), except instead of finding \n\ + an edge it finds a dark line. For example, the border between a black piece \n\ + of paper and a white table is an edge, but a curve drawn with a pencil on a \n\ + piece of paper makes a line. Therefore, the output of this routine is a \n\ + vector field encoded in the horz and vert images, which are returned in a \n\ + tuple where the first element is horz and the second is vert. \n\ + \n\ + The vector obtains a large magnitude when centered on a dark line in an image \n\ + and the direction of the vector is perpendicular to the line. To be very \n\ + precise, each vector points in the direction of greatest change in second \n\ + derivative and the magnitude of the vector encodes the derivative magnitude \n\ + in that direction. Moreover, if the second derivative is negative then the \n\ + output vector is zero. This zeroing if negative gradients causes the output \n\ + to be sensitive only to dark lines surrounded by darker pixels. \n\ + \n\ + - We assume that xx, xy, and yy are the 3 second order gradients of the image \n\ + in question. You can obtain these gradients using the image_gradients class. \n\ + - The output images will have the same dimensions as the input images. "; + /*! + requires + - xx, xy, and yy all have the same dimensions. + ensures + - This routine is similar to sobel_edge_detector(), except instead of finding + an edge it finds a dark line. For example, the border between a black piece + of paper and a white table is an edge, but a curve drawn with a pencil on a + piece of paper makes a line. Therefore, the output of this routine is a + vector field encoded in the horz and vert images, which are returned in a + tuple where the first element is horz and the second is vert. + + The vector obtains a large magnitude when centered on a dark line in an image + and the direction of the vector is perpendicular to the line. To be very + precise, each vector points in the direction of greatest change in second + derivative and the magnitude of the vector encodes the derivative magnitude + in that direction. Moreover, if the second derivative is negative then the + output vector is zero. This zeroing if negative gradients causes the output + to be sensitive only to dark lines surrounded by darker pixels. + + - We assume that xx, xy, and yy are the 3 second order gradients of the image + in question. You can obtain these gradients using the image_gradients class. + - The output images will have the same dimensions as the input images. + !*/ + m.def("find_dark_lines", &py_find_dark_lines, docs, py::arg("xx"), py::arg("xy"), py::arg("yy")); + + + + docs = +"requires \n\ + - xx, xy, and yy all have the same dimensions. \n\ +ensures \n\ + - This routine finds bright \"keypoints\" in an image. In general, these are \n\ + bright/white localized blobs. It does this by computing the determinant of \n\ + the image Hessian at each location and storing this value into the returned \n\ + image if both eigenvalues of the Hessian are negative. If either eigenvalue \n\ + is positive then the output value for that pixel is 0. I.e. \n\ + - Let OUT denote the returned image. \n\ + - for all valid r,c: \n\ + - OUT[r][c] == a number >= 0 and larger values indicate the \n\ + presence of a keypoint at this pixel location. \n\ + - We assume that xx, xy, and yy are the 3 second order gradients of the image \n\ + in question. You can obtain these gradients using the image_gradients class. \n\ + - The output image will have the same dimensions as the input images."; + /*! + requires + - xx, xy, and yy all have the same dimensions. + ensures + - This routine finds bright "keypoints" in an image. In general, these are + bright/white localized blobs. It does this by computing the determinant of + the image Hessian at each location and storing this value into the returned + image if both eigenvalues of the Hessian are negative. If either eigenvalue + is positive then the output value for that pixel is 0. I.e. + - Let OUT denote the returned image. + - for all valid r,c: + - OUT[r][c] == a number >= 0 and larger values indicate the + presence of a keypoint at this pixel location. + - We assume that xx, xy, and yy are the 3 second order gradients of the image + in question. You can obtain these gradients using the image_gradients class. + - The output image will have the same dimensions as the input images. + !*/ + m.def("find_bright_keypoints", &py_find_bright_keypoints, docs, py::arg("xx"), py::arg("xy"), py::arg("yy")); + + + + docs = +"requires \n\ + - xx, xy, and yy all have the same dimensions. \n\ +ensures \n\ + - This routine finds dark \"keypoints\" in an image. In general, these are \n\ + dark localized blobs. It does this by computing the determinant of \n\ + the image Hessian at each location and storing this value into the returned \n\ + image if both eigenvalues of the Hessian are negative. If either eigenvalue \n\ + is negative then the output value for that pixel is 0. I.e. \n\ + - Let OUT denote the returned image. \n\ + - for all valid r,c: \n\ + - OUT[r][c] == a number >= 0 and larger values indicate the \n\ + presence of a keypoint at this pixel location. \n\ + - We assume that xx, xy, and yy are the 3 second order gradients of the image \n\ + in question. You can obtain these gradients using the image_gradients class. \n\ + - The output image will have the same dimensions as the input images."; + /*! + requires + - xx, xy, and yy all have the same dimensions. + ensures + - This routine finds dark "keypoints" in an image. In general, these are + dark localized blobs. It does this by computing the determinant of + the image Hessian at each location and storing this value into the returned + image if both eigenvalues of the Hessian are negative. If either eigenvalue + is negative then the output value for that pixel is 0. I.e. + - Let OUT denote the returned image. + - for all valid r,c: + - OUT[r][c] == a number >= 0 and larger values indicate the + presence of a keypoint at this pixel location. + - We assume that xx, xy, and yy are the 3 second order gradients of the image + in question. You can obtain these gradients using the image_gradients class. + - The output image will have the same dimensions as the input images. + !*/ + m.def("find_dark_keypoints", &py_find_dark_keypoints, docs, py::arg("xx"), py::arg("xy"), py::arg("yy")); + + + + docs = +"requires \n\ + - The two input images have the same dimensions. \n\ +ensures \n\ + - Returns an image, of the same dimensions as the input. Each element in this \n\ + image holds the edge strength at that location. Moreover, edge pixels that are not \n\ + local maximizers have been set to 0. \n\ + - let edge_strength(r,c) == sqrt(pow(horz[r][c],2) + pow(vert[r][c],2)) \n\ + (i.e. The Euclidean norm of the gradient) \n\ + - let OUT denote the returned image. \n\ + - for all valid r and c: \n\ + - if (edge_strength(r,c) is at a maximum with respect to its 2 neighboring \n\ + pixels along the line indicated by the image gradient vector (horz[r][c],vert[r][c])) then \n\ + - OUT[r][c] == edge_strength(r,c) \n\ + - else \n\ + - OUT[r][c] == 0"; + /*! + requires + - The two input images have the same dimensions. + ensures + - Returns an image, of the same dimensions as the input. Each element in this + image holds the edge strength at that location. Moreover, edge pixels that are not + local maximizers have been set to 0. + - let edge_strength(r,c) == sqrt(pow(horz[r][c],2) + pow(vert[r][c],2)) + (i.e. The Euclidean norm of the gradient) + - let OUT denote the returned image. + - for all valid r and c: + - if (edge_strength(r,c) is at a maximum with respect to its 2 neighboring + pixels along the line indicated by the image gradient vector (horz[r][c],vert[r][c])) then + - OUT[r][c] == edge_strength(r,c) + - else + - OUT[r][c] == 0 + !*/ + m.def("suppress_non_maximum_edges", &py_suppress_non_maximum_edges, docs, py::arg("horz"), py::arg("vert")); + m.def("suppress_non_maximum_edges", &py_suppress_non_maximum_edges2, + "Performs: return suppress_non_maximum_edges(horz_and_vert_gradients[0], horz_and_vert_gradients[1])", + py::arg("horz_and_vert_gradients")); + + + + docs = +"requires \n\ + - non_max_suppression_radius >= 0 \n\ +ensures \n\ + - Scans the given image and finds all pixels with values >= thresh that are \n\ + also local maximums within their 8-connected neighborhood of the image. Such \n\ + pixels are collected, sorted in decreasing order of their pixel values, and \n\ + then non-maximum suppression is applied to this list of points using the \n\ + given non_max_suppression_radius. The final list of peaks is then returned. \n\ + \n\ + Therefore, the returned list, V, will have these properties: \n\ + - len(V) == the number of peaks found in the image. \n\ + - When measured in image coordinates, no elements of V are within \n\ + non_max_suppression_radius distance of each other. That is, for all valid i!=j \n\ + it is true that length(V[i]-V[j]) > non_max_suppression_radius. \n\ + - For each element of V, that element has the maximum pixel value of all \n\ + pixels in the ball centered on that pixel with radius \n\ + non_max_suppression_radius."; + /*! + requires + - non_max_suppression_radius >= 0 + ensures + - Scans the given image and finds all pixels with values >= thresh that are + also local maximums within their 8-connected neighborhood of the image. Such + pixels are collected, sorted in decreasing order of their pixel values, and + then non-maximum suppression is applied to this list of points using the + given non_max_suppression_radius. The final list of peaks is then returned. + + Therefore, the returned list, V, will have these properties: + - len(V) == the number of peaks found in the image. + - When measured in image coordinates, no elements of V are within + non_max_suppression_radius distance of each other. That is, for all valid i!=j + it is true that length(V[i]-V[j]) > non_max_suppression_radius. + - For each element of V, that element has the maximum pixel value of all + pixels in the ball centered on that pixel with radius + non_max_suppression_radius. + !*/ + m.def("find_peaks", &py_find_peaks, py::arg("img"), py::arg("non_max_suppression_radius"), py::arg("thresh")); + m.def("find_peaks", &py_find_peaks, py::arg("img"), py::arg("non_max_suppression_radius"), py::arg("thresh")); + m.def("find_peaks", &py_find_peaks, py::arg("img"), py::arg("non_max_suppression_radius"), py::arg("thresh")); + m.def("find_peaks", &py_find_peaks, py::arg("img"), py::arg("non_max_suppression_radius"), py::arg("thresh")); + m.def("find_peaks", &py_find_peaks, py::arg("img"), py::arg("non_max_suppression_radius"), py::arg("thresh")); + m.def("find_peaks", &py_find_peaks, py::arg("img"), py::arg("non_max_suppression_radius"), py::arg("thresh")); + m.def("find_peaks", &py_find_peaks, py::arg("img"), py::arg("non_max_suppression_radius"), py::arg("thresh")); + m.def("find_peaks", &py_find_peaks, py::arg("img"), py::arg("non_max_suppression_radius"), py::arg("thresh")); + m.def("find_peaks", &py_find_peaks, py::arg("img"), py::arg("non_max_suppression_radius"), py::arg("thresh")); + m.def("find_peaks", &py_find_peaks, py::arg("img"), docs, py::arg("non_max_suppression_radius"), py::arg("thresh")); + + m.def("find_peaks", &py_find_peaks2, py::arg("img"), py::arg("non_max_suppression_radius")=0); + m.def("find_peaks", &py_find_peaks2, py::arg("img"), py::arg("non_max_suppression_radius")=0); + m.def("find_peaks", &py_find_peaks2, py::arg("img"), py::arg("non_max_suppression_radius")=0); + m.def("find_peaks", &py_find_peaks2, py::arg("img"), py::arg("non_max_suppression_radius")=0); + m.def("find_peaks", &py_find_peaks2, py::arg("img"), py::arg("non_max_suppression_radius")=0); + m.def("find_peaks", &py_find_peaks2, py::arg("img"), py::arg("non_max_suppression_radius")=0); + m.def("find_peaks", &py_find_peaks2, py::arg("img"), py::arg("non_max_suppression_radius")=0); + m.def("find_peaks", &py_find_peaks2, py::arg("img"), py::arg("non_max_suppression_radius")=0); + m.def("find_peaks", &py_find_peaks2, py::arg("img"), py::arg("non_max_suppression_radius")=0); + m.def("find_peaks", &py_find_peaks2, py::arg("img"), + "performs: return find_peaks(img, non_max_suppression_radius, partition_pixels(img))", + py::arg("non_max_suppression_radius")=0); }