|
|
|
|
|
|
|
#include <dlib/python.h> |
|
#include <dlib/geometry.h> |
|
#include <pybind11/stl_bind.h> |
|
#include "indexing.h" |
|
#include "opaque_types.h" |
|
#include <dlib/filtering.h> |
|
|
|
using namespace dlib; |
|
using namespace std; |
|
|
|
namespace py = pybind11; |
|
|
|
|
|
|
|
|
|
long left(const rectangle& r) { return r.left(); } |
|
long top(const rectangle& r) { return r.top(); } |
|
long right(const rectangle& r) { return r.right(); } |
|
long bottom(const rectangle& r) { return r.bottom(); } |
|
long width(const rectangle& r) { return r.width(); } |
|
long height(const rectangle& r) { return r.height(); } |
|
unsigned long area(const rectangle& r) { return r.area(); } |
|
|
|
double dleft(const drectangle& r) { return r.left(); } |
|
double dtop(const drectangle& r) { return r.top(); } |
|
double dright(const drectangle& r) { return r.right(); } |
|
double dbottom(const drectangle& r) { return r.bottom(); } |
|
double dwidth(const drectangle& r) { return r.width(); } |
|
double dheight(const drectangle& r) { return r.height(); } |
|
double darea(const drectangle& r) { return r.area(); } |
|
|
|
template <typename rect_type> |
|
bool is_empty(const rect_type& r) { return r.is_empty(); } |
|
|
|
template <typename rect_type> |
|
point center(const rect_type& r) { return center(r); } |
|
|
|
template <typename rect_type> |
|
point dcenter(const rect_type& r) { return dcenter(r); } |
|
|
|
template <typename rect_type, typename ptype> |
|
bool contains(const rect_type& r, const ptype& p) { return r.contains(p); } |
|
|
|
template <typename rect_type> |
|
bool contains_xy(const rect_type& r, const long x, const long y) { return r.contains(point(x, y)); } |
|
|
|
template <typename rect_type> |
|
bool contains_rec(const rect_type& r, const rect_type& r2) { return r.contains(r2); } |
|
|
|
template <typename rect_type> |
|
rect_type intersect(const rect_type& r, const rect_type& r2) { return r.intersect(r2); } |
|
|
|
template <typename rect_type> |
|
string print_rectangle_str(const rect_type& r) |
|
{ |
|
std::ostringstream sout; |
|
sout << r; |
|
return sout.str(); |
|
} |
|
|
|
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="<<r.get_left().get_measurement_noise(); |
|
sout << ", typical_acceleration="<<r.get_left().get_typical_acceleration(); |
|
sout << ", max_measurement_deviation="<<r.get_left().get_max_measurement_deviation(); |
|
sout << ")"; |
|
return sout.str(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
void bind_rectangles(py::module& m) |
|
{ |
|
{ |
|
typedef rectangle type; |
|
py::class_<type>(m, "rectangle", "This object represents a rectangular area of an image.") |
|
.def(py::init<long,long,long,long>(), py::arg("left"),py::arg("top"),py::arg("right"),py::arg("bottom")) |
|
.def(py::init<drectangle>(), py::arg("rect")) |
|
.def(py::init<rectangle>(), py::arg("rect")) |
|
.def(py::init()) |
|
.def("area", &::area) |
|
.def("left", &::left) |
|
.def("top", &::top) |
|
.def("right", &::right) |
|
.def("bottom", &::bottom) |
|
.def("width", &::width) |
|
.def("height", &::height) |
|
.def("tl_corner", &type::tl_corner, "Returns the top left corner of the rectangle.") |
|
.def("tr_corner", &type::tr_corner, "Returns the top right corner of the rectangle.") |
|
.def("bl_corner", &type::bl_corner, "Returns the bottom left corner of the rectangle.") |
|
.def("br_corner", &type::br_corner, "Returns the bottom right corner of the rectangle.") |
|
.def("is_empty", &::is_empty<type>) |
|
.def("center", &::center<type>) |
|
.def("dcenter", &::dcenter<type>) |
|
.def("contains", &::contains<type,point>, py::arg("point")) |
|
.def("contains", &::contains<type,dpoint>, py::arg("point")) |
|
.def("contains", &::contains_xy<type>, py::arg("x"), py::arg("y")) |
|
.def("contains", &::contains_rec<type>, py::arg("rectangle")) |
|
.def("intersect", &::intersect<type>, py::arg("rectangle")) |
|
.def("__str__", &::print_rectangle_str<type>) |
|
.def("__repr__", &::print_rectangle_repr) |
|
.def(py::self += point()) |
|
.def(py::self + point()) |
|
.def(py::self += rectangle()) |
|
.def(py::self + rectangle()) |
|
.def(py::self == py::self) |
|
.def(py::self != py::self) |
|
.def(py::pickle(&getstate<type>, &setstate<type>)); |
|
} |
|
{ |
|
typedef drectangle type; |
|
py::class_<type>(m, "drectangle", "This object represents a rectangular area of an image with floating point coordinates.") |
|
.def(py::init<double,double,double,double>(), py::arg("left"), py::arg("top"), py::arg("right"), py::arg("bottom")) |
|
.def(py::init<rectangle>(), py::arg("rect")) |
|
.def(py::init<drectangle>(), py::arg("rect")) |
|
.def(py::init<>()) |
|
.def("area", &::darea) |
|
.def("left", &::dleft) |
|
.def("top", &::dtop) |
|
.def("right", &::dright) |
|
.def("bottom", &::dbottom) |
|
.def("width", &::dwidth) |
|
.def("height", &::dheight) |
|
.def("is_empty", &::is_empty<type>) |
|
.def("center", &::center<type>) |
|
.def("dcenter", &::dcenter<type>) |
|
.def("tl_corner", &type::tl_corner, "Returns the top left corner of the rectangle.") |
|
.def("tr_corner", &type::tr_corner, "Returns the top right corner of the rectangle.") |
|
.def("bl_corner", &type::bl_corner, "Returns the bottom left corner of the rectangle.") |
|
.def("br_corner", &type::br_corner, "Returns the bottom right corner of the rectangle.") |
|
.def("contains", &::contains<type,point>, py::arg("point")) |
|
.def("contains", &::contains<type,dpoint>, py::arg("point")) |
|
.def("contains", &::contains_xy<type>, py::arg("x"), py::arg("y")) |
|
.def("contains", &::contains_rec<type>, py::arg("rectangle")) |
|
.def("intersect", &::intersect<type>, py::arg("rectangle")) |
|
.def("__str__", &::print_rectangle_str<type>) |
|
.def("__repr__", &::print_drectangle_repr) |
|
.def(py::self == py::self) |
|
.def(py::self != py::self) |
|
.def(py::pickle(&getstate<type>, &setstate<type>)); |
|
} |
|
|
|
{ |
|
typedef rect_filter type; |
|
py::class_<type>(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<double,double,double>(), 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<type>, &setstate<type>)); |
|
} |
|
|
|
m.def("find_optimal_rect_filter", |
|
[](const std::vector<rectangle>& 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. " |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
); |
|
|
|
{ |
|
typedef std::vector<rectangle> type; |
|
py::bind_vector<type>(m, "rectangles", "An array of rectangle objects.") |
|
.def(py::init<size_t>(), py::arg("initial_size")) |
|
.def("clear", &type::clear) |
|
.def("resize", resize<type>) |
|
.def("extend", extend_vector_with_python_list<rectangle>) |
|
.def(py::pickle(&getstate<type>, &setstate<type>)); |
|
} |
|
|
|
{ |
|
typedef std::vector<std::vector<rectangle>> type; |
|
py::bind_vector<type>(m, "rectangless", "An array of arrays of rectangle objects.") |
|
.def(py::init<size_t>(), py::arg("initial_size")) |
|
.def("clear", &type::clear) |
|
.def("resize", resize<type>) |
|
.def("extend", extend_vector_with_python_list<rectangle>) |
|
.def(py::pickle(&getstate<type>, &setstate<type>)); |
|
} |
|
|
|
m.def("translate_rect", [](const rectangle& rect, const point& p){return translate_rect(rect,p);}, |
|
" returns rectangle(rect.left()+p.x, rect.top()+p.y, rect.right()+p.x, rect.bottom()+p.y) \n\ |
|
(i.e. moves the location of the rectangle but doesn't change its shape)", |
|
py::arg("rect"), py::arg("p")); |
|
|
|
m.def("translate_rect", [](const drectangle& rect, const point& p){return translate_rect(rect,p);}, |
|
" returns rectangle(rect.left()+p.x, rect.top()+p.y, rect.right()+p.x, rect.bottom()+p.y) \n\ |
|
(i.e. moves the location of the rectangle but doesn't change its shape)", |
|
py::arg("rect"), py::arg("p")); |
|
|
|
m.def("translate_rect", [](const rectangle& rect, const dpoint& p){return translate_rect(rect,point(p));}, |
|
" returns rectangle(rect.left()+p.x, rect.top()+p.y, rect.right()+p.x, rect.bottom()+p.y) \n\ |
|
(i.e. moves the location of the rectangle but doesn't change its shape)", |
|
py::arg("rect"), py::arg("p")); |
|
|
|
m.def("translate_rect", [](const drectangle& rect, const dpoint& p){return translate_rect(rect,p);}, |
|
" returns rectangle(rect.left()+p.x, rect.top()+p.y, rect.right()+p.x, rect.bottom()+p.y) \n\ |
|
(i.e. moves the location of the rectangle but doesn't change its shape)", |
|
py::arg("rect"), py::arg("p")); |
|
|
|
|
|
|
|
m.def("shrink_rect", [](const rectangle& rect, long num){return shrink_rect(rect,num);}, |
|
" returns rectangle(rect.left()+num, rect.top()+num, rect.right()-num, rect.bottom()-num) \n\ |
|
(i.e. shrinks the given rectangle by shrinking its border by num)", |
|
py::arg("rect"), py::arg("num")); |
|
|
|
m.def("grow_rect", [](const rectangle& rect, long num){return grow_rect(rect,num);}, |
|
"- return shrink_rect(rect, -num) \n\ |
|
(i.e. grows the given rectangle by expanding its border by num)", |
|
py::arg("rect"), py::arg("num")); |
|
|
|
m.def("scale_rect", [](const rectangle& rect, double scale){return scale_rect(rect,scale);}, |
|
"- return scale_rect(rect, scale) \n\ |
|
(i.e. resizes the given rectangle by a scale factor)", |
|
py::arg("rect"), py::arg("scale")); |
|
|
|
m.def("centered_rect", [](const point& p, unsigned long width, unsigned long height) { |
|
return centered_rect(p, width, height); }, |
|
py::arg("p"), py::arg("width"), py::arg("height")); |
|
|
|
m.def("centered_rects", [](const std::vector<point>& p, unsigned long width, unsigned long height) { |
|
return centered_rects(p, width, height); }, |
|
py::arg("pts"), py::arg("width"), py::arg("height")); |
|
|
|
m.def("centered_rect", [](const dpoint& p, unsigned long width, unsigned long height) { |
|
return centered_rect(p, width, height); }, |
|
py::arg("p"), py::arg("width"), py::arg("height")); |
|
|
|
m.def("centered_rect", [](const rectangle& rect, unsigned long width, unsigned long height) { |
|
return centered_rect(rect, width, height); }, |
|
py::arg("rect"), py::arg("width"), py::arg("height")); |
|
|
|
m.def("centered_rect", [](const drectangle& rect, unsigned long width, unsigned long height) { |
|
return centered_rect(rect, width, height); }, |
|
py::arg("rect"), py::arg("width"), py::arg("height")); |
|
|
|
|
|
m.def("center", [](const rectangle& rect){return center(rect); }, py::arg("rect"), |
|
" returns the center of the given rectangle"); |
|
m.def("center", [](const drectangle& rect){return center(rect); }, py::arg("rect"), |
|
" returns the center of the given rectangle"); |
|
} |
|
|
|
|
|
|