rectangles.cpp 13.1 KB
Newer Older
1
2
3
4
5
// Copyright (C) 2015 Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.

#include <dlib/python.h>
#include <dlib/geometry.h>
6
#include <pybind11/stl_bind.h>
7
#include "indexing.h"
Davis King's avatar
Davis King committed
8
#include "opaque_types.h"
9
#include <dlib/filtering.h>
10
11
12

using namespace dlib;
using namespace std;
13
14
15

namespace py = pybind11;

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

// ----------------------------------------------------------------------------------------

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>
bool contains(const rect_type& r, const point& 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();
}

64
string print_rectangle_repr(const rectangle& r)
65
66
67
68
69
70
{
    std::ostringstream sout;
    sout << "rectangle(" << r.left() << "," << r.top() << "," << r.right() << "," << r.bottom() << ")";
    return sout.str();
}

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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();
}


90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
rectangle add_point_to_rect(const rectangle& r, const point& p)
{
    return r + p;
}

rectangle add_rect_to_rect(const rectangle& r, const rectangle& p)
{
    return r + p;
}

rectangle& iadd_point_to_rect(rectangle& r, const point& p)
{
    r += p;
    return r;
}

rectangle& iadd_rect_to_rect(rectangle& r, const rectangle& p)
{
    r += p;
    return r;
}



114
115
// ----------------------------------------------------------------------------------------

116
void bind_rectangles(py::module& m)
117
118
119
{
    {
    typedef rectangle type;
120
121
    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"))
122
123
        .def(py::init<drectangle>(), py::arg("rect"))
        .def(py::init<rectangle>(), py::arg("rect"))
124
        .def(py::init())
125
126
127
128
129
130
131
        .def("area",   &::area)
        .def("left",   &::left)
        .def("top",    &::top)
        .def("right",  &::right)
        .def("bottom", &::bottom)
        .def("width",  &::width)
        .def("height", &::height)
132
133
134
135
        .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.")
136
137
138
        .def("is_empty", &::is_empty<type>)
        .def("center", &::center<type>)
        .def("dcenter", &::dcenter<type>)
139
140
141
142
        .def("contains", &::contains<type>, 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"))
143
        .def("__str__", &::print_rectangle_str<type>)
144
        .def("__repr__", &::print_rectangle_repr)
145
146
147
148
        .def("__add__", &::add_point_to_rect)
        .def("__add__", &::add_rect_to_rect)
        .def("__iadd__", &::iadd_point_to_rect)
        .def("__iadd__", &::iadd_rect_to_rect)
149
150
151
        .def(py::self == py::self)
        .def(py::self != py::self)
        .def(py::pickle(&getstate<type>, &setstate<type>));
152
153
154
    }
    {
    typedef drectangle type;
155
156
    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"))
157
158
159
        .def(py::init<rectangle>(), py::arg("rect"))
        .def(py::init<drectangle>(), py::arg("rect"))
        .def(py::init<>())
160
161
162
163
164
165
166
167
168
169
        .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>)
170
171
172
173
        .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.")
174
175
176
177
        .def("contains", &::contains<type>, 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"))
178
        .def("__str__", &::print_rectangle_str<type>)
179
        .def("__repr__", &::print_drectangle_repr)
180
181
182
        .def(py::self == py::self)
        .def(py::self != py::self)
        .def(py::pickle(&getstate<type>, &setstate<type>));
183
    }
Davis King's avatar
Davis King committed
184

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
    {
        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. " 
    /*!
        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. 
    !*/
    );

262
263
    {
    typedef std::vector<rectangle> type;
264
    py::bind_vector<type>(m, "rectangles", "An array of rectangle objects.")
265
266
        .def("clear", &type::clear)
        .def("resize", resize<type>)
267
268
        .def("extend", extend_vector_with_python_list<rectangle>)
        .def(py::pickle(&getstate<type>, &setstate<type>));
269
    }
Davis King's avatar
Davis King committed
270
271
272
273
274
275
276
277
278

    {
    typedef std::vector<std::vector<rectangle>> type;
    py::bind_vector<type>(m, "rectangless", "An array of arrays of rectangle objects.")
        .def("clear", &type::clear)
        .def("resize", resize<type>)
        .def("extend", extend_vector_with_python_list<rectangle>)
        .def(py::pickle(&getstate<type>, &setstate<type>));
    }
279
280
281
282
283
284
285
286
287
288
289

    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"));

290
291
292
293
294
295
296
    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"));
297
298
299
}

// ----------------------------------------------------------------------------------------