blob: b2b5a904a143bd9ae1c557d6c4c3803a624bbe27 [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();
Brian Silverman37b15b32019-03-10 13:30:18 -070014 return aos::vision::ThresholdImageWithFunction(
15 image, [&](aos::vision::PixelRef px) {
16 if (px.g > threshold_value && px.b > threshold_value &&
17 px.r > threshold_value) {
18 return true;
19 }
20 return false;
21 });
Parker Schuh2a1447c2019-02-17 00:25:29 -080022}
23
24// Filter blobs on size.
25void TargetFinder::PreFilter(BlobList *imgs) {
26 imgs->erase(
27 std::remove_if(imgs->begin(), imgs->end(),
28 [](RangeImage &img) {
29 // We can drop images with a small number of
30 // pixels, but images
31 // must be over 20px or the math will have issues.
32 return (img.npixels() < 100 || img.height() < 25);
33 }),
34 imgs->end());
35}
36
Austin Schuh7d2ef032019-03-10 14:59:34 -070037ContourNode *TargetFinder::GetContour(const RangeImage &blob) {
Ben Fredricksonf7b68522019-03-02 21:19:42 -080038 alloc_.reset();
39 return RangeImgToContour(blob, &alloc_);
40}
41
Ben Fredricksonec575822019-03-02 22:03:20 -080042// TODO(ben): These values will be moved into the constants.h file.
Ben Fredricksonf7b68522019-03-02 21:19:42 -080043namespace {
44
Austin Schuh7d2ef032019-03-10 14:59:34 -070045::Eigen::Vector2f AosVectorToEigenVector(Vector<2> in) {
46 return ::Eigen::Vector2f(in.x(), in.y());
47}
48
Ben Fredricksonec575822019-03-02 22:03:20 -080049constexpr double f_x = 481.4957;
50constexpr double c_x = 341.215;
51constexpr double f_y = 484.314;
52constexpr double c_y = 251.29;
Ben Fredricksonf7b68522019-03-02 21:19:42 -080053
Ben Fredricksonec575822019-03-02 22:03:20 -080054constexpr double f_x_prime = 363.1424;
55constexpr double c_x_prime = 337.9895;
56constexpr double f_y_prime = 366.4837;
57constexpr double c_y_prime = 240.0702;
Ben Fredricksonf7b68522019-03-02 21:19:42 -080058
Ben Fredricksonec575822019-03-02 22:03:20 -080059constexpr double k_1 = -0.2739;
60constexpr double k_2 = 0.01583;
61constexpr double k_3 = 0.04201;
Ben Fredricksonf7b68522019-03-02 21:19:42 -080062
63constexpr int iterations = 7;
64
65}
66
Austin Schuhe5015972019-03-09 17:47:34 -080067::Eigen::Vector2f UnWarpPoint(const Point point) {
Ben Fredricksonec575822019-03-02 22:03:20 -080068 const double x0 = ((double)point.x - c_x) / f_x;
69 const double y0 = ((double)point.y - c_y) / f_y;
Ben Fredricksonf7b68522019-03-02 21:19:42 -080070 double x = x0;
71 double y = y0;
72 for (int i = 0; i < iterations; i++) {
73 const double r_sqr = x * x + y * y;
74 const double coeff =
Ben Fredricksonec575822019-03-02 22:03:20 -080075 1.0 + r_sqr * (k_1 + k_2 * r_sqr * (1.0 + k_3 * r_sqr));
Ben Fredricksonf7b68522019-03-02 21:19:42 -080076 x = x0 / coeff;
77 y = y0 / coeff;
78 }
Austin Schuhe5015972019-03-09 17:47:34 -080079 const double nx = x * f_x_prime + c_x_prime;
80 const double ny = y * f_y_prime + c_y_prime;
81 return ::Eigen::Vector2f(nx, ny);
Ben Fredricksonf7b68522019-03-02 21:19:42 -080082}
83
Austin Schuhe5015972019-03-09 17:47:34 -080084::std::vector<::Eigen::Vector2f> TargetFinder::UnWarpContour(
85 ContourNode *start) const {
86 ::std::vector<::Eigen::Vector2f> result;
Ben Fredricksonf7b68522019-03-02 21:19:42 -080087 ContourNode *node = start;
88 while (node->next != start) {
Austin Schuhe5015972019-03-09 17:47:34 -080089 result.push_back(UnWarpPoint(node->pt));
Ben Fredricksonf7b68522019-03-02 21:19:42 -080090 node = node->next;
91 }
Austin Schuhe5015972019-03-09 17:47:34 -080092 result.push_back(UnWarpPoint(node->pt));
93 return result;
Ben Fredricksonf7b68522019-03-02 21:19:42 -080094}
95
Parker Schuh2a1447c2019-02-17 00:25:29 -080096// TODO: Try hierarchical merge for this.
97// Convert blobs into polygons.
Austin Schuh6e56faf2019-03-10 14:04:57 -070098Polygon TargetFinder::FindPolygon(::std::vector<::Eigen::Vector2f> &&contour,
99 bool verbose) {
Parker Schuh2a1447c2019-02-17 00:25:29 -0800100 if (verbose) printf("Process Polygon.\n");
Parker Schuh2a1447c2019-02-17 00:25:29 -0800101
Austin Schuhe5015972019-03-09 17:47:34 -0800102 ::std::vector<::Eigen::Vector2f> slopes;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800103
104 // Collect all slopes from the contour.
Austin Schuhe5015972019-03-09 17:47:34 -0800105 ::Eigen::Vector2f previous_point = contour[0];
106 for (size_t i = 0; i < contour.size(); ++i) {
107 ::Eigen::Vector2f next_point = contour[(i + 1) % contour.size()];
Parker Schuh2a1447c2019-02-17 00:25:29 -0800108
Austin Schuhe5015972019-03-09 17:47:34 -0800109 slopes.push_back(next_point - previous_point);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800110
Austin Schuhe5015972019-03-09 17:47:34 -0800111 previous_point = next_point;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800112 }
113
Austin Schuhe5015972019-03-09 17:47:34 -0800114 const int num_points = slopes.size();
115 auto get_pt = [&slopes, num_points](int i) {
116 return slopes[(i + num_points * 2) % num_points];
Austin Schuh335eef12019-03-02 17:04:17 -0800117 };
Parker Schuh2a1447c2019-02-17 00:25:29 -0800118
Austin Schuh6a484962019-03-09 21:51:27 -0800119 // Bigger objects should be more filtered. Filter roughly proportional to the
120 // perimeter of the object.
121 const int range = slopes.size() / 50;
122 if (verbose) printf("Corner range: %d.\n", range);
123
Austin Schuhe5015972019-03-09 17:47:34 -0800124 ::std::vector<::Eigen::Vector2f> filtered_slopes = slopes;
Austin Schuh335eef12019-03-02 17:04:17 -0800125 // Three box filter makith a guassian?
126 // Run gaussian filter over the slopes 3 times. That'll get us pretty close
127 // to running a gausian over it.
128 for (int k = 0; k < 3; ++k) {
Austin Schuh6a484962019-03-09 21:51:27 -0800129 const int window_size = ::std::max(2, range);
Austin Schuhe5015972019-03-09 17:47:34 -0800130 for (size_t i = 0; i < slopes.size(); ++i) {
131 ::Eigen::Vector2f a = ::Eigen::Vector2f::Zero();
Parker Schuh2a1447c2019-02-17 00:25:29 -0800132 for (int j = -window_size; j <= window_size; ++j) {
Austin Schuhe5015972019-03-09 17:47:34 -0800133 ::Eigen::Vector2f p = get_pt(j + i);
134 a += p;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800135 }
Austin Schuhe5015972019-03-09 17:47:34 -0800136 a /= (window_size * 2 + 1);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800137
Austin Schuhe5015972019-03-09 17:47:34 -0800138 filtered_slopes[i] = a;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800139 }
Austin Schuhe5015972019-03-09 17:47:34 -0800140 slopes = filtered_slopes;
Austin Schuh335eef12019-03-02 17:04:17 -0800141 }
Austin Schuh6a484962019-03-09 21:51:27 -0800142 if (verbose) printf("Point count: %zu.\n", slopes.size());
Parker Schuh2a1447c2019-02-17 00:25:29 -0800143
Austin Schuh6a484962019-03-09 21:51:27 -0800144 ::std::vector<float> corner_metric(slopes.size(), 0.0);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800145
Austin Schuh6a484962019-03-09 21:51:27 -0800146 for (size_t i = 0; i < slopes.size(); ++i) {
147 const ::Eigen::Vector2f a = get_pt(i - ::std::max(3, range));
148 const ::Eigen::Vector2f b = get_pt(i + ::std::max(3, range));
149 corner_metric[i] = (a - b).squaredNorm();
150 }
151
152 // We want to find the Nth highest peaks.
153 // Clever algorithm: Find the highest point. Then, walk forwards and
154 // backwards to find the next valley each direction which is over x% lower
155 // than the peak.
156 // We want to ignore those points in the future. Set them to 0.
157 // Repeat until we've found the Nth highest peak.
Parker Schuh2a1447c2019-02-17 00:25:29 -0800158
159 // Find all centers of corners.
Austin Schuhe5015972019-03-09 17:47:34 -0800160 // Because they round, multiple slopes may be a corner.
161 ::std::vector<size_t> edges;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800162
Austin Schuh6a484962019-03-09 21:51:27 -0800163 constexpr float peak_acceptance_ratio = 0.16;
164 constexpr float valley_ratio = 0.75;
165
166 float highest_peak_value = 0.0;
167
168 // Nth higest points.
Austin Schuh32ffac22019-03-09 22:42:02 -0800169 while (edges.size() < 5) {
Austin Schuh6a484962019-03-09 21:51:27 -0800170 const ::std::vector<float>::iterator max_element =
171 ::std::max_element(corner_metric.begin(), corner_metric.end());
172 const size_t highest_index =
173 ::std::distance(corner_metric.begin(), max_element);
174 const float max_value = *max_element;
Austin Schuh32ffac22019-03-09 22:42:02 -0800175 if (edges.size() == 0) {
Austin Schuh6a484962019-03-09 21:51:27 -0800176 highest_peak_value = max_value;
177 }
Austin Schuh32ffac22019-03-09 22:42:02 -0800178 if (max_value < highest_peak_value * peak_acceptance_ratio &&
179 edges.size() == 4) {
Austin Schuh6a484962019-03-09 21:51:27 -0800180 if (verbose)
181 printf("Rejecting index: %zu, %f (%f %%)\n", highest_index, max_value,
182 max_value / highest_peak_value);
183 break;
184 }
185 const float valley_value = max_value * valley_ratio;
186
187 if (verbose)
188 printf("Highest index: %zu, %f (%f %%)\n", highest_index, max_value,
189 max_value / highest_peak_value);
190
Austin Schuh32ffac22019-03-09 22:42:02 -0800191 bool foothill = false;
Austin Schuh6a484962019-03-09 21:51:27 -0800192 {
193 float min_value = max_value;
194 size_t fwd_index = (highest_index + 1) % corner_metric.size();
195 while (true) {
196 const float current_value = corner_metric[fwd_index];
Austin Schuh32ffac22019-03-09 22:42:02 -0800197
198 if (current_value == -1.0) {
199 if (min_value >= valley_value) {
200 if (verbose) printf("Foothill\n");
201 foothill = true;
202 }
203 break;
204 }
205
Austin Schuh6a484962019-03-09 21:51:27 -0800206 min_value = ::std::min(current_value, min_value);
207
Austin Schuh32ffac22019-03-09 22:42:02 -0800208 if (min_value < valley_value && current_value > min_value) {
Austin Schuh6a484962019-03-09 21:51:27 -0800209 break;
210 }
211 // Kill!!!
Austin Schuh32ffac22019-03-09 22:42:02 -0800212 corner_metric[fwd_index] = -1.0;
Austin Schuh6a484962019-03-09 21:51:27 -0800213
214 fwd_index = (fwd_index + 1) % corner_metric.size();
Parker Schuh2a1447c2019-02-17 00:25:29 -0800215 }
216 }
Austin Schuh6a484962019-03-09 21:51:27 -0800217
218 {
219 float min_value = max_value;
220 size_t rev_index =
221 (highest_index - 1 + corner_metric.size()) % corner_metric.size();
222 while (true) {
223 const float current_value = corner_metric[rev_index];
Austin Schuh32ffac22019-03-09 22:42:02 -0800224
225 if (current_value == -1.0) {
226 if (min_value >= valley_value) {
227 if (verbose) printf("Foothill\n");
228 foothill = true;
229 }
230 break;
231 }
232
Austin Schuh6a484962019-03-09 21:51:27 -0800233 min_value = ::std::min(current_value, min_value);
234
Austin Schuh32ffac22019-03-09 22:42:02 -0800235 if (min_value < valley_value && current_value > min_value) {
Austin Schuh6a484962019-03-09 21:51:27 -0800236 break;
237 }
238 // Kill!!!
Austin Schuh32ffac22019-03-09 22:42:02 -0800239 corner_metric[rev_index] = -1.0;
Austin Schuh6a484962019-03-09 21:51:27 -0800240
241 rev_index =
242 (rev_index - 1 + corner_metric.size()) % corner_metric.size();
243 }
Parker Schuh2a1447c2019-02-17 00:25:29 -0800244 }
Austin Schuh6a484962019-03-09 21:51:27 -0800245
Austin Schuh32ffac22019-03-09 22:42:02 -0800246 *max_element = -1.0;
247 if (!foothill) {
248 edges.push_back(highest_index);
249 }
Parker Schuh2a1447c2019-02-17 00:25:29 -0800250 }
251
Austin Schuh6a484962019-03-09 21:51:27 -0800252 ::std::sort(edges.begin(), edges.end());
Parker Schuh2a1447c2019-02-17 00:25:29 -0800253
254 if (verbose) printf("Edge Count (%zu).\n", edges.size());
255
Parker Schuh2a1447c2019-02-17 00:25:29 -0800256 // Run best-fits over each line segment.
Austin Schuh6e56faf2019-03-10 14:04:57 -0700257 Polygon polygon;
Austin Schuh6a484962019-03-09 21:51:27 -0800258 if (edges.size() >= 3) {
Austin Schuhe5015972019-03-09 17:47:34 -0800259 for (size_t i = 0; i < edges.size(); ++i) {
260 // Include the corners in both line fits.
261 const size_t segment_start_index = edges[i];
262 const size_t segment_end_index =
263 (edges[(i + 1) % edges.size()] + 1) % contour.size();
Parker Schuh2a1447c2019-02-17 00:25:29 -0800264 float mx = 0.0;
265 float my = 0.0;
266 int n = 0;
Austin Schuhe5015972019-03-09 17:47:34 -0800267 for (size_t j = segment_start_index; j != segment_end_index;
268 (j = (j + 1) % contour.size())) {
269 mx += contour[j].x();
270 my += contour[j].y();
Parker Schuh2a1447c2019-02-17 00:25:29 -0800271 ++n;
272 // (x - [x] / N) ** 2 = [x * x] - 2 * [x] * [x] / N + [x] * [x] / N / N;
273 }
274 mx /= n;
275 my /= n;
276
277 float xx = 0.0;
278 float xy = 0.0;
279 float yy = 0.0;
Austin Schuhe5015972019-03-09 17:47:34 -0800280 for (size_t j = segment_start_index; j != segment_end_index;
281 (j = (j + 1) % contour.size())) {
282 const float x = contour[j].x() - mx;
283 const float y = contour[j].y() - my;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800284 xx += x * x;
285 xy += x * y;
286 yy += y * y;
287 }
288
289 // TODO: Extract common to hierarchical merge.
Austin Schuh335eef12019-03-02 17:04:17 -0800290 const float neg_b_over_2 = (xx + yy) / 2.0;
291 const float c = (xx * yy - xy * xy);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800292
Austin Schuh335eef12019-03-02 17:04:17 -0800293 const float sqr = sqrt(neg_b_over_2 * neg_b_over_2 - c);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800294
295 {
Austin Schuh335eef12019-03-02 17:04:17 -0800296 const float lam = neg_b_over_2 + sqr;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800297 float x = xy;
298 float y = lam - xx;
299
Austin Schuh335eef12019-03-02 17:04:17 -0800300 const float norm = hypot(x, y);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800301 x /= norm;
302 y /= norm;
303
Austin Schuh6e56faf2019-03-10 14:04:57 -0700304 polygon.segments.push_back(
Parker Schuh2a1447c2019-02-17 00:25:29 -0800305 Segment<2>(Vector<2>(mx, my), Vector<2>(mx + x, my + y)));
306 }
307
308 /* Characteristic polynomial
309 1 lam^2 - (xx + yy) lam + (xx * yy - xy * xy) = 0
310
311 [a b]
312 [c d]
313
314 // covariance matrix.
315 [xx xy] [nx]
316 [xy yy] [ny]
317 */
318 }
319 }
Austin Schuh6e56faf2019-03-10 14:04:57 -0700320 if (verbose) printf("Poly Count (%zu).\n", polygon.segments.size());
321 polygon.contour = ::std::move(contour);
322 return polygon;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800323}
324
325// Convert segments into target components (left or right)
Austin Schuh6e56faf2019-03-10 14:04:57 -0700326::std::vector<TargetComponent> TargetFinder::FillTargetComponentList(
327 const ::std::vector<Polygon> &seg_list, bool verbose) {
328 ::std::vector<TargetComponent> list;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800329 TargetComponent new_target;
Austin Schuh7d2ef032019-03-10 14:59:34 -0700330 for (const Polygon &polygon : seg_list) {
Parker Schuh2a1447c2019-02-17 00:25:29 -0800331 // Reject missized pollygons for now. Maybe rectify them here in the future;
Austin Schuh7d2ef032019-03-10 14:59:34 -0700332 if (polygon.segments.size() != 4) {
Austin Schuh9f859ca2019-03-06 20:46:01 -0800333 continue;
334 }
Austin Schuh32ffac22019-03-09 22:42:02 -0800335 ::std::vector<Vector<2>> corners;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800336 for (size_t i = 0; i < 4; ++i) {
Austin Schuh7d2ef032019-03-10 14:59:34 -0700337 Vector<2> corner =
338 polygon.segments[i].Intersect(polygon.segments[(i + 1) % 4]);
Austin Schuh9f859ca2019-03-06 20:46:01 -0800339 if (::std::isnan(corner.x()) || ::std::isnan(corner.y())) {
340 break;
341 }
342 corners.push_back(corner);
343 }
344 if (corners.size() != 4) {
345 continue;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800346 }
347
348 // Select the closest two points. Short side of the rectangle.
349 double min_dist = -1;
Austin Schuh32ffac22019-03-09 22:42:02 -0800350 ::std::pair<size_t, size_t> closest;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800351 for (size_t i = 0; i < 4; ++i) {
352 size_t next = (i + 1) % 4;
353 double nd = corners[i].SquaredDistanceTo(corners[next]);
354 if (min_dist == -1 || nd < min_dist) {
355 min_dist = nd;
356 closest.first = i;
357 closest.second = next;
358 }
359 }
360
361 // Verify our top is above the bottom.
362 size_t bot_index = closest.first;
363 size_t top_index = (closest.first + 2) % 4;
364 if (corners[top_index].y() < corners[bot_index].y()) {
365 closest.first = top_index;
366 closest.second = (top_index + 1) % 4;
367 }
368
369 // Find the major axis.
370 size_t far_first = (closest.first + 2) % 4;
371 size_t far_second = (closest.second + 2) % 4;
372 Segment<2> major_axis(
373 (corners[closest.first] + corners[closest.second]) * 0.5,
374 (corners[far_first] + corners[far_second]) * 0.5);
375 if (major_axis.AsVector().AngleToZero() > M_PI / 180.0 * 120.0 ||
376 major_axis.AsVector().AngleToZero() < M_PI / 180.0 * 60.0) {
377 // Target is angled way too much, drop it.
378 continue;
379 }
380
381 // organize the top points.
382 Vector<2> topA = corners[closest.first] - major_axis.B();
383 new_target.major_axis = major_axis;
384 if (major_axis.AsVector().AngleToZero() > M_PI / 2.0) {
385 // We have a left target since we are leaning positive.
386 new_target.is_right = false;
387 if (topA.AngleTo(major_axis.AsVector()) > 0.0) {
388 // And our A point is left of the major axis.
389 new_target.inside = corners[closest.second];
390 new_target.top = corners[closest.first];
391 } else {
392 // our A point is to the right of the major axis.
393 new_target.inside = corners[closest.first];
394 new_target.top = corners[closest.second];
395 }
396 } else {
397 // We have a right target since we are leaning negative.
398 new_target.is_right = true;
399 if (topA.AngleTo(major_axis.AsVector()) > 0.0) {
400 // And our A point is left of the major axis.
401 new_target.inside = corners[closest.first];
402 new_target.top = corners[closest.second];
403 } else {
404 // our A point is to the right of the major axis.
405 new_target.inside = corners[closest.second];
406 new_target.top = corners[closest.first];
407 }
408 }
409
410 // organize the top points.
411 Vector<2> botA = corners[far_first] - major_axis.A();
412 if (major_axis.AsVector().AngleToZero() > M_PI / 2.0) {
413 // We have a right target since we are leaning positive.
414 if (botA.AngleTo(major_axis.AsVector()) < M_PI) {
415 // And our A point is left of the major axis.
416 new_target.outside = corners[far_second];
417 new_target.bottom = corners[far_first];
418 } else {
419 // our A point is to the right of the major axis.
420 new_target.outside = corners[far_first];
421 new_target.bottom = corners[far_second];
422 }
423 } else {
424 // We have a left target since we are leaning negative.
425 if (botA.AngleTo(major_axis.AsVector()) < M_PI) {
426 // And our A point is left of the major axis.
427 new_target.outside = corners[far_first];
428 new_target.bottom = corners[far_second];
429 } else {
430 // our A point is to the right of the major axis.
431 new_target.outside = corners[far_second];
432 new_target.bottom = corners[far_first];
433 }
434 }
435
Austin Schuh7d2ef032019-03-10 14:59:34 -0700436 // Take the vector which points from the bottom to the top of the target
437 // along the outside edge.
438 const ::Eigen::Vector2f outer_edge_vector =
439 AosVectorToEigenVector(new_target.top - new_target.outside);
440 // Now, dot each point in the perimeter along this vector. The one with the
441 // smallest component will be the one closest to the bottom along this
442 // direction vector.
443 ::Eigen::Vector2f smallest_point = polygon.contour[0];
444 float smallest_value = outer_edge_vector.transpose() * smallest_point;
445 for (const ::Eigen::Vector2f point : polygon.contour) {
446 const float current_value = outer_edge_vector.transpose() * point;
447 if (current_value < smallest_value) {
448 smallest_value = current_value;
449 smallest_point = point;
450 }
451 }
452
453 // This piece of the target should be ready now.
454 new_target.bottom_point = smallest_point;
455 if (verbose) {
456 printf("Lowest point in the blob is (%f, %f)\n", smallest_point.x(),
457 smallest_point.y());
458 }
459
Parker Schuh2a1447c2019-02-17 00:25:29 -0800460 // This piece of the target should be ready now.
461 list.emplace_back(new_target);
Austin Schuh7d2ef032019-03-10 14:59:34 -0700462
Austin Schuh32ffac22019-03-09 22:42:02 -0800463 if (verbose) printf("Happy with a target\n");
Parker Schuh2a1447c2019-02-17 00:25:29 -0800464 }
465
466 return list;
467}
468
469// Match components into targets.
470std::vector<Target> TargetFinder::FindTargetsFromComponents(
471 const std::vector<TargetComponent> component_list, bool verbose) {
472 std::vector<Target> target_list;
473 using namespace aos::vision;
474 if (component_list.size() < 2) {
475 // We don't enough parts for a target.
476 return target_list;
477 }
478
479 for (size_t i = 0; i < component_list.size(); i++) {
480 const TargetComponent &a = component_list[i];
481 for (size_t j = 0; j < i; j++) {
482 bool target_valid = false;
483 Target new_target;
484 const TargetComponent &b = component_list[j];
485
Parker Schuh2a1447c2019-02-17 00:25:29 -0800486 if (a.is_right && !b.is_right) {
487 if (a.top.x() > b.top.x()) {
488 new_target.right = a;
489 new_target.left = b;
490 target_valid = true;
491 }
492 } else if (!a.is_right && b.is_right) {
493 if (b.top.x() > a.top.x()) {
494 new_target.right = b;
495 new_target.left = a;
496 target_valid = true;
497 }
Alex Perrybac3d3f2019-03-10 14:26:51 -0700498 } else if (verbose) {
499 printf("Found same side components: %s.\n",
500 a.is_right ? "right" : "left");
Parker Schuh2a1447c2019-02-17 00:25:29 -0800501 }
502 if (target_valid) {
503 target_list.emplace_back(new_target);
504 }
505 }
506 }
507 if (verbose) printf("Possible Target: %zu.\n", target_list.size());
508 return target_list;
509}
510
511std::vector<IntermediateResult> TargetFinder::FilterResults(
Alex Perrybac3d3f2019-03-10 14:26:51 -0700512 const std::vector<IntermediateResult> &results, uint64_t print_rate,
513 bool verbose) {
Parker Schuh2a1447c2019-02-17 00:25:29 -0800514 std::vector<IntermediateResult> filtered;
515 for (const IntermediateResult &res : results) {
Alex Perrybac3d3f2019-03-10 14:26:51 -0700516 // Based on a linear regression between error and distance to target.
517 // Closer targets can have a higher error because they are bigger.
518 double acceptable_error = std::max(2 * (21 - 12 * res.extrinsics.z), 50.0);
519 if (res.solver_error < acceptable_error) {
520 if (verbose) {
521 printf("Using an 8 point solve: %f < %f \n", res.solver_error,
522 acceptable_error);
523 }
Parker Schuh2a1447c2019-02-17 00:25:29 -0800524 filtered.emplace_back(res);
Alex Perrybac3d3f2019-03-10 14:26:51 -0700525 } else if (res.backup_solver_error < acceptable_error) {
526 if (verbose) {
527 printf("Using a 4 point solve: %f < %f \n", res.backup_solver_error,
528 acceptable_error);
529 }
530 IntermediateResult backup;
531 backup.extrinsics = res.backup_extrinsics;
532 backup.solver_error= res.backup_solver_error;
533 filtered.emplace_back(backup);
534 } else if (verbose) {
535 printf("Rejecting a target with errors: (%f, %f) > %f \n",
536 res.solver_error, res.backup_solver_error, acceptable_error);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800537 }
538 }
Ben Fredricksona8c3d552019-03-03 14:14:53 -0800539 frame_count_++;
540 if (!filtered.empty()) {
541 valid_result_count_++;
542 }
543 if (print_rate > 0 && frame_count_ > print_rate) {
544 LOG(INFO, "Found (%zu / %zu)(%.2f) targets.\n", valid_result_count_,
545 frame_count_, (double)valid_result_count_ / (double)frame_count_);
546 frame_count_ = 0;
547 valid_result_count_ = 0;
548 }
549
Parker Schuh2a1447c2019-02-17 00:25:29 -0800550 return filtered;
551}
552
553} // namespace vision
554} // namespace y2019