diff --git a/tools/python/src/rectangles.cpp b/tools/python/src/rectangles.cpp index acca518e4..d06ec591b 100644 --- a/tools/python/src/rectangles.cpp +++ b/tools/python/src/rectangles.cpp @@ -6,6 +6,7 @@ #include #include "indexing.h" #include "opaque_types.h" +#include using namespace dlib; using namespace std; @@ -60,14 +61,32 @@ string print_rectangle_str(const rect_type& r) return sout.str(); } -template -string print_rectangle_repr(const rect_type& r) +string print_rectangle_repr(const rectangle& r) { std::ostringstream sout; sout << "rectangle(" << r.left() << "," << r.top() << "," << r.right() << "," << r.bottom() << ")"; return sout.str(); } +string print_drectangle_repr(const drectangle& r) +{ + std::ostringstream sout; + sout << "drectangle(" << r.left() << "," << r.top() << "," << r.right() << "," << r.bottom() << ")"; + return sout.str(); +} + +string print_rect_filter(const rect_filter& r) +{ + std::ostringstream sout; + sout << "rect_filter("; + sout << "measurement_noise="<(m, "rectangle", "This object represents a rectangular area of an image.") .def(py::init(), py::arg("left"),py::arg("top"),py::arg("right"),py::arg("bottom")) + .def(py::init()) .def("area", &::area) .def("left", &::left) .def("top", &::top) @@ -115,7 +135,7 @@ void bind_rectangles(py::module& m) .def("contains", &::contains_rec, py::arg("rectangle")) .def("intersect", &::intersect, py::arg("rectangle")) .def("__str__", &::print_rectangle_str) - .def("__repr__", &::print_rectangle_repr) + .def("__repr__", &::print_rectangle_repr) .def("__add__", &::add_point_to_rect) .def("__add__", &::add_rect_to_rect) .def("__iadd__", &::iadd_point_to_rect) @@ -143,12 +163,89 @@ void bind_rectangles(py::module& m) .def("contains", &::contains_rec, py::arg("rectangle")) .def("intersect", &::intersect, py::arg("rectangle")) .def("__str__", &::print_rectangle_str) - .def("__repr__", &::print_rectangle_repr) + .def("__repr__", &::print_drectangle_repr) .def(py::self == py::self) .def(py::self != py::self) .def(py::pickle(&getstate, &setstate)); } + { + typedef rect_filter type; + py::class_(m, "rect_filter", + R"asdf( + This object is a simple tool for filtering a rectangle that + measures the location of a moving object that has some non-trivial + momentum. Importantly, the measurements are noisy and the object can + experience sudden unpredictable accelerations. To accomplish this + filtering we use a simple Kalman filter with a state transition model of: + + position_{i+1} = position_{i} + velocity_{i} + velocity_{i+1} = velocity_{i} + some_unpredictable_acceleration + + and a measurement model of: + + measured_position_{i} = position_{i} + measurement_noise + + Where some_unpredictable_acceleration and measurement_noise are 0 mean Gaussian + noise sources with standard deviations of typical_acceleration and + measurement_noise respectively. + + To allow for really sudden and large but infrequent accelerations, at each + step we check if the current measured position deviates from the predicted + filtered position by more than max_measurement_deviation*measurement_noise + and if so we adjust the filter's state to keep it within these bounds. + This allows the moving object to undergo large unmodeled accelerations, far + in excess of what would be suggested by typical_acceleration, without + then experiencing a long lag time where the Kalman filter has to "catches + up" to the new position. )asdf" + ) + .def(py::init(), py::arg("measurement_noise"), py::arg("typical_acceleration"), py::arg("max_measurement_deviation")) + .def("measurement_noise", [](const rect_filter& a){return a.get_left().get_measurement_noise();}) + .def("typical_acceleration", [](const rect_filter& a){return a.get_left().get_typical_acceleration();}) + .def("max_measurement_deviation", [](const rect_filter& a){return a.get_left().get_max_measurement_deviation();}) + .def("__call__", [](rect_filter& f, const dlib::rectangle& r){return rectangle(f(r)); }, py::arg("rect")) + .def("__repr__", print_rect_filter) + .def(py::pickle(&getstate, &setstate)); + } + + m.def("find_optimal_rect_filter", + [](const std::vector& rects, const double smoothness ) { return find_optimal_rect_filter(rects, smoothness); }, + py::arg("rects"), + py::arg("smoothness")=1, +"requires \n\ + - rects.size() > 4 \n\ + - smoothness >= 0 \n\ +ensures \n\ + - This function finds the \"optimal\" settings of a rect_filter based on recorded \n\ + measurement data stored in rects. Here we assume that rects is a complete \n\ + track history of some object's measured positions. Essentially, what we do \n\ + is find the rect_filter that minimizes the following objective function: \n\ + sum of abs(predicted_location[i] - measured_location[i]) + smoothness*abs(filtered_location[i]-filtered_location[i-1]) \n\ + Where i is a time index. \n\ + The sum runs over all the data in rects. So what we do is find the \n\ + filter settings that produce smooth filtered trajectories but also produce \n\ + filtered outputs that are as close to the measured positions as possible. \n\ + The larger the value of smoothness the less jittery the filter outputs will \n\ + be, but they might become biased or laggy if smoothness is set really high. " + /*! + requires + - rects.size() > 4 + - smoothness >= 0 + ensures + - This function finds the "optimal" settings of a rect_filter based on recorded + measurement data stored in rects. Here we assume that rects is a complete + track history of some object's measured positions. Essentially, what we do + is find the rect_filter that minimizes the following objective function: + sum of abs(predicted_location[i] - measured_location[i]) + smoothness*abs(filtered_location[i]-filtered_location[i-1]) + Where i is a time index. + The sum runs over all the data in rects. So what we do is find the + filter settings that produce smooth filtered trajectories but also produce + filtered outputs that are as close to the measured positions as possible. + The larger the value of smoothness the less jittery the filter outputs will + be, but they might become biased or laggy if smoothness is set really high. + !*/ + ); + { typedef std::vector type; py::bind_vector(m, "rectangles", "An array of rectangle objects.")