blob: 0f257650fa30f3b8145a24a95781a71bed9a6dba [file] [log] [blame]
Parker Schuh2a1447c2019-02-17 00:25:29 -08001#include "y2019/vision/target_finder.h"
2
3#include "aos/vision/blob/hierarchical_contour_merge.h"
4
5using namespace aos::vision;
6
7namespace y2019 {
8namespace vision {
9
Austin Schuh4d6e9bd2019-03-08 19:54:17 -080010TargetFinder::TargetFinder() : target_template_(Target::MakeTemplate()) {}
Parker Schuh2a1447c2019-02-17 00:25:29 -080011
12aos::vision::RangeImage TargetFinder::Threshold(aos::vision::ImagePtr image) {
13 const uint8_t threshold_value = GetThresholdValue();
14 return aos::vision::DoThreshold(image, [&](aos::vision::PixelRef &px) {
15 if (px.g > threshold_value && px.b > threshold_value &&
16 px.r > threshold_value) {
17 return true;
18 }
19 return false;
20 });
21}
22
23// Filter blobs on size.
24void TargetFinder::PreFilter(BlobList *imgs) {
25 imgs->erase(
26 std::remove_if(imgs->begin(), imgs->end(),
27 [](RangeImage &img) {
28 // We can drop images with a small number of
29 // pixels, but images
30 // must be over 20px or the math will have issues.
31 return (img.npixels() < 100 || img.height() < 25);
32 }),
33 imgs->end());
34}
35
Ben Fredricksonf7b68522019-03-02 21:19:42 -080036ContourNode* TargetFinder::GetContour(const RangeImage &blob) {
37 alloc_.reset();
38 return RangeImgToContour(blob, &alloc_);
39}
40
Ben Fredricksonec575822019-03-02 22:03:20 -080041// TODO(ben): These values will be moved into the constants.h file.
Ben Fredricksonf7b68522019-03-02 21:19:42 -080042namespace {
43
Ben Fredricksonec575822019-03-02 22:03:20 -080044constexpr double f_x = 481.4957;
45constexpr double c_x = 341.215;
46constexpr double f_y = 484.314;
47constexpr double c_y = 251.29;
Ben Fredricksonf7b68522019-03-02 21:19:42 -080048
Ben Fredricksonec575822019-03-02 22:03:20 -080049constexpr double f_x_prime = 363.1424;
50constexpr double c_x_prime = 337.9895;
51constexpr double f_y_prime = 366.4837;
52constexpr double c_y_prime = 240.0702;
Ben Fredricksonf7b68522019-03-02 21:19:42 -080053
Ben Fredricksonec575822019-03-02 22:03:20 -080054constexpr double k_1 = -0.2739;
55constexpr double k_2 = 0.01583;
56constexpr double k_3 = 0.04201;
Ben Fredricksonf7b68522019-03-02 21:19:42 -080057
58constexpr int iterations = 7;
59
60}
61
Austin Schuhe5015972019-03-09 17:47:34 -080062::Eigen::Vector2f UnWarpPoint(const Point point) {
Ben Fredricksonec575822019-03-02 22:03:20 -080063 const double x0 = ((double)point.x - c_x) / f_x;
64 const double y0 = ((double)point.y - c_y) / f_y;
Ben Fredricksonf7b68522019-03-02 21:19:42 -080065 double x = x0;
66 double y = y0;
67 for (int i = 0; i < iterations; i++) {
68 const double r_sqr = x * x + y * y;
69 const double coeff =
Ben Fredricksonec575822019-03-02 22:03:20 -080070 1.0 + r_sqr * (k_1 + k_2 * r_sqr * (1.0 + k_3 * r_sqr));
Ben Fredricksonf7b68522019-03-02 21:19:42 -080071 x = x0 / coeff;
72 y = y0 / coeff;
73 }
Austin Schuhe5015972019-03-09 17:47:34 -080074 const double nx = x * f_x_prime + c_x_prime;
75 const double ny = y * f_y_prime + c_y_prime;
76 return ::Eigen::Vector2f(nx, ny);
Ben Fredricksonf7b68522019-03-02 21:19:42 -080077}
78
Austin Schuhe5015972019-03-09 17:47:34 -080079::std::vector<::Eigen::Vector2f> TargetFinder::UnWarpContour(
80 ContourNode *start) const {
81 ::std::vector<::Eigen::Vector2f> result;
Ben Fredricksonf7b68522019-03-02 21:19:42 -080082 ContourNode *node = start;
83 while (node->next != start) {
Austin Schuhe5015972019-03-09 17:47:34 -080084 result.push_back(UnWarpPoint(node->pt));
Ben Fredricksonf7b68522019-03-02 21:19:42 -080085 node = node->next;
86 }
Austin Schuhe5015972019-03-09 17:47:34 -080087 result.push_back(UnWarpPoint(node->pt));
88 return result;
Ben Fredricksonf7b68522019-03-02 21:19:42 -080089}
90
Parker Schuh2a1447c2019-02-17 00:25:29 -080091// TODO: Try hierarchical merge for this.
92// Convert blobs into polygons.
Austin Schuh6e56faf2019-03-10 14:04:57 -070093Polygon TargetFinder::FindPolygon(::std::vector<::Eigen::Vector2f> &&contour,
94 bool verbose) {
Parker Schuh2a1447c2019-02-17 00:25:29 -080095 if (verbose) printf("Process Polygon.\n");
Parker Schuh2a1447c2019-02-17 00:25:29 -080096
Austin Schuhe5015972019-03-09 17:47:34 -080097 ::std::vector<::Eigen::Vector2f> slopes;
Parker Schuh2a1447c2019-02-17 00:25:29 -080098
99 // Collect all slopes from the contour.
Austin Schuhe5015972019-03-09 17:47:34 -0800100 ::Eigen::Vector2f previous_point = contour[0];
101 for (size_t i = 0; i < contour.size(); ++i) {
102 ::Eigen::Vector2f next_point = contour[(i + 1) % contour.size()];
Parker Schuh2a1447c2019-02-17 00:25:29 -0800103
Austin Schuhe5015972019-03-09 17:47:34 -0800104 slopes.push_back(next_point - previous_point);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800105
Austin Schuhe5015972019-03-09 17:47:34 -0800106 previous_point = next_point;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800107 }
108
Austin Schuhe5015972019-03-09 17:47:34 -0800109 const int num_points = slopes.size();
110 auto get_pt = [&slopes, num_points](int i) {
111 return slopes[(i + num_points * 2) % num_points];
Austin Schuh335eef12019-03-02 17:04:17 -0800112 };
Parker Schuh2a1447c2019-02-17 00:25:29 -0800113
Austin Schuh6a484962019-03-09 21:51:27 -0800114 // Bigger objects should be more filtered. Filter roughly proportional to the
115 // perimeter of the object.
116 const int range = slopes.size() / 50;
117 if (verbose) printf("Corner range: %d.\n", range);
118
Austin Schuhe5015972019-03-09 17:47:34 -0800119 ::std::vector<::Eigen::Vector2f> filtered_slopes = slopes;
Austin Schuh335eef12019-03-02 17:04:17 -0800120 // Three box filter makith a guassian?
121 // Run gaussian filter over the slopes 3 times. That'll get us pretty close
122 // to running a gausian over it.
123 for (int k = 0; k < 3; ++k) {
Austin Schuh6a484962019-03-09 21:51:27 -0800124 const int window_size = ::std::max(2, range);
Austin Schuhe5015972019-03-09 17:47:34 -0800125 for (size_t i = 0; i < slopes.size(); ++i) {
126 ::Eigen::Vector2f a = ::Eigen::Vector2f::Zero();
Parker Schuh2a1447c2019-02-17 00:25:29 -0800127 for (int j = -window_size; j <= window_size; ++j) {
Austin Schuhe5015972019-03-09 17:47:34 -0800128 ::Eigen::Vector2f p = get_pt(j + i);
129 a += p;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800130 }
Austin Schuhe5015972019-03-09 17:47:34 -0800131 a /= (window_size * 2 + 1);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800132
Austin Schuhe5015972019-03-09 17:47:34 -0800133 filtered_slopes[i] = a;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800134 }
Austin Schuhe5015972019-03-09 17:47:34 -0800135 slopes = filtered_slopes;
Austin Schuh335eef12019-03-02 17:04:17 -0800136 }
Austin Schuh6a484962019-03-09 21:51:27 -0800137 if (verbose) printf("Point count: %zu.\n", slopes.size());
Parker Schuh2a1447c2019-02-17 00:25:29 -0800138
Austin Schuh6a484962019-03-09 21:51:27 -0800139 ::std::vector<float> corner_metric(slopes.size(), 0.0);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800140
Austin Schuh6a484962019-03-09 21:51:27 -0800141 for (size_t i = 0; i < slopes.size(); ++i) {
142 const ::Eigen::Vector2f a = get_pt(i - ::std::max(3, range));
143 const ::Eigen::Vector2f b = get_pt(i + ::std::max(3, range));
144 corner_metric[i] = (a - b).squaredNorm();
145 }
146
147 // We want to find the Nth highest peaks.
148 // Clever algorithm: Find the highest point. Then, walk forwards and
149 // backwards to find the next valley each direction which is over x% lower
150 // than the peak.
151 // We want to ignore those points in the future. Set them to 0.
152 // Repeat until we've found the Nth highest peak.
Parker Schuh2a1447c2019-02-17 00:25:29 -0800153
154 // Find all centers of corners.
Austin Schuhe5015972019-03-09 17:47:34 -0800155 // Because they round, multiple slopes may be a corner.
156 ::std::vector<size_t> edges;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800157
Austin Schuh6a484962019-03-09 21:51:27 -0800158 constexpr float peak_acceptance_ratio = 0.16;
159 constexpr float valley_ratio = 0.75;
160
161 float highest_peak_value = 0.0;
162
163 // Nth higest points.
Austin Schuh32ffac22019-03-09 22:42:02 -0800164 while (edges.size() < 5) {
Austin Schuh6a484962019-03-09 21:51:27 -0800165 const ::std::vector<float>::iterator max_element =
166 ::std::max_element(corner_metric.begin(), corner_metric.end());
167 const size_t highest_index =
168 ::std::distance(corner_metric.begin(), max_element);
169 const float max_value = *max_element;
Austin Schuh32ffac22019-03-09 22:42:02 -0800170 if (edges.size() == 0) {
Austin Schuh6a484962019-03-09 21:51:27 -0800171 highest_peak_value = max_value;
172 }
Austin Schuh32ffac22019-03-09 22:42:02 -0800173 if (max_value < highest_peak_value * peak_acceptance_ratio &&
174 edges.size() == 4) {
Austin Schuh6a484962019-03-09 21:51:27 -0800175 if (verbose)
176 printf("Rejecting index: %zu, %f (%f %%)\n", highest_index, max_value,
177 max_value / highest_peak_value);
178 break;
179 }
180 const float valley_value = max_value * valley_ratio;
181
182 if (verbose)
183 printf("Highest index: %zu, %f (%f %%)\n", highest_index, max_value,
184 max_value / highest_peak_value);
185
Austin Schuh32ffac22019-03-09 22:42:02 -0800186 bool foothill = false;
Austin Schuh6a484962019-03-09 21:51:27 -0800187 {
188 float min_value = max_value;
189 size_t fwd_index = (highest_index + 1) % corner_metric.size();
190 while (true) {
191 const float current_value = corner_metric[fwd_index];
Austin Schuh32ffac22019-03-09 22:42:02 -0800192
193 if (current_value == -1.0) {
194 if (min_value >= valley_value) {
195 if (verbose) printf("Foothill\n");
196 foothill = true;
197 }
198 break;
199 }
200
Austin Schuh6a484962019-03-09 21:51:27 -0800201 min_value = ::std::min(current_value, min_value);
202
Austin Schuh32ffac22019-03-09 22:42:02 -0800203 if (min_value < valley_value && current_value > min_value) {
Austin Schuh6a484962019-03-09 21:51:27 -0800204 break;
205 }
206 // Kill!!!
Austin Schuh32ffac22019-03-09 22:42:02 -0800207 corner_metric[fwd_index] = -1.0;
Austin Schuh6a484962019-03-09 21:51:27 -0800208
209 fwd_index = (fwd_index + 1) % corner_metric.size();
Parker Schuh2a1447c2019-02-17 00:25:29 -0800210 }
211 }
Austin Schuh6a484962019-03-09 21:51:27 -0800212
213 {
214 float min_value = max_value;
215 size_t rev_index =
216 (highest_index - 1 + corner_metric.size()) % corner_metric.size();
217 while (true) {
218 const float current_value = corner_metric[rev_index];
Austin Schuh32ffac22019-03-09 22:42:02 -0800219
220 if (current_value == -1.0) {
221 if (min_value >= valley_value) {
222 if (verbose) printf("Foothill\n");
223 foothill = true;
224 }
225 break;
226 }
227
Austin Schuh6a484962019-03-09 21:51:27 -0800228 min_value = ::std::min(current_value, min_value);
229
Austin Schuh32ffac22019-03-09 22:42:02 -0800230 if (min_value < valley_value && current_value > min_value) {
Austin Schuh6a484962019-03-09 21:51:27 -0800231 break;
232 }
233 // Kill!!!
Austin Schuh32ffac22019-03-09 22:42:02 -0800234 corner_metric[rev_index] = -1.0;
Austin Schuh6a484962019-03-09 21:51:27 -0800235
236 rev_index =
237 (rev_index - 1 + corner_metric.size()) % corner_metric.size();
238 }
Parker Schuh2a1447c2019-02-17 00:25:29 -0800239 }
Austin Schuh6a484962019-03-09 21:51:27 -0800240
Austin Schuh32ffac22019-03-09 22:42:02 -0800241 *max_element = -1.0;
242 if (!foothill) {
243 edges.push_back(highest_index);
244 }
Parker Schuh2a1447c2019-02-17 00:25:29 -0800245 }
246
Austin Schuh6a484962019-03-09 21:51:27 -0800247 ::std::sort(edges.begin(), edges.end());
Parker Schuh2a1447c2019-02-17 00:25:29 -0800248
249 if (verbose) printf("Edge Count (%zu).\n", edges.size());
250
Parker Schuh2a1447c2019-02-17 00:25:29 -0800251 // Run best-fits over each line segment.
Austin Schuh6e56faf2019-03-10 14:04:57 -0700252 Polygon polygon;
Austin Schuh6a484962019-03-09 21:51:27 -0800253 if (edges.size() >= 3) {
Austin Schuhe5015972019-03-09 17:47:34 -0800254 for (size_t i = 0; i < edges.size(); ++i) {
255 // Include the corners in both line fits.
256 const size_t segment_start_index = edges[i];
257 const size_t segment_end_index =
258 (edges[(i + 1) % edges.size()] + 1) % contour.size();
Parker Schuh2a1447c2019-02-17 00:25:29 -0800259 float mx = 0.0;
260 float my = 0.0;
261 int n = 0;
Austin Schuhe5015972019-03-09 17:47:34 -0800262 for (size_t j = segment_start_index; j != segment_end_index;
263 (j = (j + 1) % contour.size())) {
264 mx += contour[j].x();
265 my += contour[j].y();
Parker Schuh2a1447c2019-02-17 00:25:29 -0800266 ++n;
267 // (x - [x] / N) ** 2 = [x * x] - 2 * [x] * [x] / N + [x] * [x] / N / N;
268 }
269 mx /= n;
270 my /= n;
271
272 float xx = 0.0;
273 float xy = 0.0;
274 float yy = 0.0;
Austin Schuhe5015972019-03-09 17:47:34 -0800275 for (size_t j = segment_start_index; j != segment_end_index;
276 (j = (j + 1) % contour.size())) {
277 const float x = contour[j].x() - mx;
278 const float y = contour[j].y() - my;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800279 xx += x * x;
280 xy += x * y;
281 yy += y * y;
282 }
283
284 // TODO: Extract common to hierarchical merge.
Austin Schuh335eef12019-03-02 17:04:17 -0800285 const float neg_b_over_2 = (xx + yy) / 2.0;
286 const float c = (xx * yy - xy * xy);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800287
Austin Schuh335eef12019-03-02 17:04:17 -0800288 const float sqr = sqrt(neg_b_over_2 * neg_b_over_2 - c);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800289
290 {
Austin Schuh335eef12019-03-02 17:04:17 -0800291 const float lam = neg_b_over_2 + sqr;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800292 float x = xy;
293 float y = lam - xx;
294
Austin Schuh335eef12019-03-02 17:04:17 -0800295 const float norm = hypot(x, y);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800296 x /= norm;
297 y /= norm;
298
Austin Schuh6e56faf2019-03-10 14:04:57 -0700299 polygon.segments.push_back(
Parker Schuh2a1447c2019-02-17 00:25:29 -0800300 Segment<2>(Vector<2>(mx, my), Vector<2>(mx + x, my + y)));
301 }
302
303 /* Characteristic polynomial
304 1 lam^2 - (xx + yy) lam + (xx * yy - xy * xy) = 0
305
306 [a b]
307 [c d]
308
309 // covariance matrix.
310 [xx xy] [nx]
311 [xy yy] [ny]
312 */
313 }
314 }
Austin Schuh6e56faf2019-03-10 14:04:57 -0700315 if (verbose) printf("Poly Count (%zu).\n", polygon.segments.size());
316 polygon.contour = ::std::move(contour);
317 return polygon;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800318}
319
320// Convert segments into target components (left or right)
Austin Schuh6e56faf2019-03-10 14:04:57 -0700321::std::vector<TargetComponent> TargetFinder::FillTargetComponentList(
322 const ::std::vector<Polygon> &seg_list, bool verbose) {
323 ::std::vector<TargetComponent> list;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800324 TargetComponent new_target;
Austin Schuh6e56faf2019-03-10 14:04:57 -0700325 for (const Polygon &poly : seg_list) {
Parker Schuh2a1447c2019-02-17 00:25:29 -0800326 // Reject missized pollygons for now. Maybe rectify them here in the future;
Austin Schuh6e56faf2019-03-10 14:04:57 -0700327 if (poly.segments.size() != 4) {
Austin Schuh9f859ca2019-03-06 20:46:01 -0800328 continue;
329 }
Austin Schuh32ffac22019-03-09 22:42:02 -0800330 ::std::vector<Vector<2>> corners;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800331 for (size_t i = 0; i < 4; ++i) {
Austin Schuh6e56faf2019-03-10 14:04:57 -0700332 Vector<2> corner = poly.segments[i].Intersect(poly.segments[(i + 1) % 4]);
Austin Schuh9f859ca2019-03-06 20:46:01 -0800333 if (::std::isnan(corner.x()) || ::std::isnan(corner.y())) {
334 break;
335 }
336 corners.push_back(corner);
337 }
338 if (corners.size() != 4) {
339 continue;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800340 }
341
342 // Select the closest two points. Short side of the rectangle.
343 double min_dist = -1;
Austin Schuh32ffac22019-03-09 22:42:02 -0800344 ::std::pair<size_t, size_t> closest;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800345 for (size_t i = 0; i < 4; ++i) {
346 size_t next = (i + 1) % 4;
347 double nd = corners[i].SquaredDistanceTo(corners[next]);
348 if (min_dist == -1 || nd < min_dist) {
349 min_dist = nd;
350 closest.first = i;
351 closest.second = next;
352 }
353 }
354
355 // Verify our top is above the bottom.
356 size_t bot_index = closest.first;
357 size_t top_index = (closest.first + 2) % 4;
358 if (corners[top_index].y() < corners[bot_index].y()) {
359 closest.first = top_index;
360 closest.second = (top_index + 1) % 4;
361 }
362
363 // Find the major axis.
364 size_t far_first = (closest.first + 2) % 4;
365 size_t far_second = (closest.second + 2) % 4;
366 Segment<2> major_axis(
367 (corners[closest.first] + corners[closest.second]) * 0.5,
368 (corners[far_first] + corners[far_second]) * 0.5);
369 if (major_axis.AsVector().AngleToZero() > M_PI / 180.0 * 120.0 ||
370 major_axis.AsVector().AngleToZero() < M_PI / 180.0 * 60.0) {
371 // Target is angled way too much, drop it.
372 continue;
373 }
374
375 // organize the top points.
376 Vector<2> topA = corners[closest.first] - major_axis.B();
377 new_target.major_axis = major_axis;
378 if (major_axis.AsVector().AngleToZero() > M_PI / 2.0) {
379 // We have a left target since we are leaning positive.
380 new_target.is_right = false;
381 if (topA.AngleTo(major_axis.AsVector()) > 0.0) {
382 // And our A point is left of the major axis.
383 new_target.inside = corners[closest.second];
384 new_target.top = corners[closest.first];
385 } else {
386 // our A point is to the right of the major axis.
387 new_target.inside = corners[closest.first];
388 new_target.top = corners[closest.second];
389 }
390 } else {
391 // We have a right target since we are leaning negative.
392 new_target.is_right = true;
393 if (topA.AngleTo(major_axis.AsVector()) > 0.0) {
394 // And our A point is left of the major axis.
395 new_target.inside = corners[closest.first];
396 new_target.top = corners[closest.second];
397 } else {
398 // our A point is to the right of the major axis.
399 new_target.inside = corners[closest.second];
400 new_target.top = corners[closest.first];
401 }
402 }
403
404 // organize the top points.
405 Vector<2> botA = corners[far_first] - major_axis.A();
406 if (major_axis.AsVector().AngleToZero() > M_PI / 2.0) {
407 // We have a right target since we are leaning positive.
408 if (botA.AngleTo(major_axis.AsVector()) < M_PI) {
409 // And our A point is left of the major axis.
410 new_target.outside = corners[far_second];
411 new_target.bottom = corners[far_first];
412 } else {
413 // our A point is to the right of the major axis.
414 new_target.outside = corners[far_first];
415 new_target.bottom = corners[far_second];
416 }
417 } else {
418 // We have a left target since we are leaning negative.
419 if (botA.AngleTo(major_axis.AsVector()) < M_PI) {
420 // And our A point is left of the major axis.
421 new_target.outside = corners[far_first];
422 new_target.bottom = corners[far_second];
423 } else {
424 // our A point is to the right of the major axis.
425 new_target.outside = corners[far_second];
426 new_target.bottom = corners[far_first];
427 }
428 }
429
430 // This piece of the target should be ready now.
431 list.emplace_back(new_target);
Austin Schuh32ffac22019-03-09 22:42:02 -0800432 if (verbose) printf("Happy with a target\n");
Parker Schuh2a1447c2019-02-17 00:25:29 -0800433 }
434
435 return list;
436}
437
438// Match components into targets.
439std::vector<Target> TargetFinder::FindTargetsFromComponents(
440 const std::vector<TargetComponent> component_list, bool verbose) {
441 std::vector<Target> target_list;
442 using namespace aos::vision;
443 if (component_list.size() < 2) {
444 // We don't enough parts for a target.
445 return target_list;
446 }
447
448 for (size_t i = 0; i < component_list.size(); i++) {
449 const TargetComponent &a = component_list[i];
450 for (size_t j = 0; j < i; j++) {
451 bool target_valid = false;
452 Target new_target;
453 const TargetComponent &b = component_list[j];
454
Parker Schuh2a1447c2019-02-17 00:25:29 -0800455 if (a.is_right && !b.is_right) {
456 if (a.top.x() > b.top.x()) {
457 new_target.right = a;
458 new_target.left = b;
459 target_valid = true;
460 }
461 } else if (!a.is_right && b.is_right) {
462 if (b.top.x() > a.top.x()) {
463 new_target.right = b;
464 new_target.left = a;
465 target_valid = true;
466 }
Alex Perrybac3d3f2019-03-10 14:26:51 -0700467 } else if (verbose) {
468 printf("Found same side components: %s.\n",
469 a.is_right ? "right" : "left");
Parker Schuh2a1447c2019-02-17 00:25:29 -0800470 }
471 if (target_valid) {
472 target_list.emplace_back(new_target);
473 }
474 }
475 }
476 if (verbose) printf("Possible Target: %zu.\n", target_list.size());
477 return target_list;
478}
479
480std::vector<IntermediateResult> TargetFinder::FilterResults(
Alex Perrybac3d3f2019-03-10 14:26:51 -0700481 const std::vector<IntermediateResult> &results, uint64_t print_rate,
482 bool verbose) {
Parker Schuh2a1447c2019-02-17 00:25:29 -0800483 std::vector<IntermediateResult> filtered;
484 for (const IntermediateResult &res : results) {
Alex Perrybac3d3f2019-03-10 14:26:51 -0700485 // Based on a linear regression between error and distance to target.
486 // Closer targets can have a higher error because they are bigger.
487 double acceptable_error = std::max(2 * (21 - 12 * res.extrinsics.z), 50.0);
488 if (res.solver_error < acceptable_error) {
489 if (verbose) {
490 printf("Using an 8 point solve: %f < %f \n", res.solver_error,
491 acceptable_error);
492 }
Parker Schuh2a1447c2019-02-17 00:25:29 -0800493 filtered.emplace_back(res);
Alex Perrybac3d3f2019-03-10 14:26:51 -0700494 } else if (res.backup_solver_error < acceptable_error) {
495 if (verbose) {
496 printf("Using a 4 point solve: %f < %f \n", res.backup_solver_error,
497 acceptable_error);
498 }
499 IntermediateResult backup;
500 backup.extrinsics = res.backup_extrinsics;
501 backup.solver_error= res.backup_solver_error;
502 filtered.emplace_back(backup);
503 } else if (verbose) {
504 printf("Rejecting a target with errors: (%f, %f) > %f \n",
505 res.solver_error, res.backup_solver_error, acceptable_error);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800506 }
507 }
Ben Fredricksona8c3d552019-03-03 14:14:53 -0800508 frame_count_++;
509 if (!filtered.empty()) {
510 valid_result_count_++;
511 }
512 if (print_rate > 0 && frame_count_ > print_rate) {
513 LOG(INFO, "Found (%zu / %zu)(%.2f) targets.\n", valid_result_count_,
514 frame_count_, (double)valid_result_count_ / (double)frame_count_);
515 frame_count_ = 0;
516 valid_result_count_ = 0;
517 }
518
Parker Schuh2a1447c2019-02-17 00:25:29 -0800519 return filtered;
520}
521
522} // namespace vision
523} // namespace y2019