blob: 2bc41acf4d8cccc64b2260c42da8a503cbe03c30 [file] [log] [blame]
Parker Schuh2a1447c2019-02-17 00:25:29 -08001#include <Eigen/Dense>
2#include <iostream>
3
4#include "y2019/vision/target_finder.h"
5
6#include "aos/vision/blob/move_scale.h"
7#include "aos/vision/blob/stream_view.h"
8#include "aos/vision/blob/transpose.h"
9#include "aos/vision/debug/debug_framework.h"
10#include "aos/vision/math/vector.h"
Austin Schuh75921532019-03-09 18:46:34 -080011#include "gflags/gflags.h"
Parker Schuh2a1447c2019-02-17 00:25:29 -080012
13using aos::vision::ImageRange;
14using aos::vision::ImageFormat;
15using aos::vision::RangeImage;
16using aos::vision::AnalysisAllocator;
17using aos::vision::BlobList;
18using aos::vision::Vector;
19using aos::vision::Segment;
20using aos::vision::PixelRef;
21
Austin Schuh75921532019-03-09 18:46:34 -080022DEFINE_int32(camera, 10, "The camera to use the intrinsics for");
23
Parker Schuh2a1447c2019-02-17 00:25:29 -080024namespace y2019 {
25namespace vision {
26
27std::vector<PixelRef> GetNColors(size_t num_colors) {
28 std::vector<PixelRef> colors;
29 for (size_t i = 0; i < num_colors; ++i) {
30 int quadrent = i * 6 / num_colors;
31 uint8_t alpha = (256 * 6 * i - quadrent * num_colors * 256) / num_colors;
32 uint8_t inv_alpha = 255 - alpha;
33 switch (quadrent) {
34 case 0:
35 colors.push_back(PixelRef{255, alpha, 0});
36 break;
37 case 1:
38 colors.push_back(PixelRef{inv_alpha, 255, 0});
39 break;
40 case 2:
41 colors.push_back(PixelRef{0, 255, alpha});
42 break;
43 case 3:
44 colors.push_back(PixelRef{0, inv_alpha, 255});
45 break;
46 case 4:
47 colors.push_back(PixelRef{alpha, 0, 255});
48 break;
49 case 5:
50 colors.push_back(PixelRef{255, 0, inv_alpha});
51 break;
52 }
53 }
54 return colors;
55}
56
57class FilterHarness : public aos::vision::FilterHarness {
58 public:
Austin Schuh75921532019-03-09 18:46:34 -080059 FilterHarness() {
60 *(target_finder_.mutable_intrinsics()) = GetCamera(FLAGS_camera)->intrinsics;
61 }
Parker Schuh2a1447c2019-02-17 00:25:29 -080062 aos::vision::RangeImage Threshold(aos::vision::ImagePtr image) override {
Austin Schuh75921532019-03-09 18:46:34 -080063 return target_finder_.Threshold(image);
Parker Schuh2a1447c2019-02-17 00:25:29 -080064 }
65
66 void InstallViewer(aos::vision::BlobStreamViewer *viewer) override {
67 viewer_ = viewer;
Austin Schuh9e5ca2b2019-03-06 20:43:17 -080068 viewer_->SetScale(2.0);
Parker Schuh2a1447c2019-02-17 00:25:29 -080069 overlays_.push_back(&overlay_);
Austin Schuh75921532019-03-09 18:46:34 -080070 overlays_.push_back(target_finder_.GetOverlay());
Parker Schuh2a1447c2019-02-17 00:25:29 -080071 viewer_->view()->SetOverlays(&overlays_);
72 }
73
74 void DrawBlob(const RangeImage &blob, PixelRef color) {
75 if (viewer_) {
76 BlobList list;
77 list.push_back(blob);
78 viewer_->DrawBlobList(list, color);
79 }
80 }
81
82 bool HandleBlobs(BlobList imgs, ImageFormat fmt) override {
83 imgs_last_ = imgs;
84 fmt_last_ = fmt;
85 // reset for next drawing cycle
86 for (auto &overlay : overlays_) {
87 overlay->Reset();
88 }
89
90 if (draw_select_blob_ || draw_raw_poly_ || draw_components_ ||
91 draw_raw_target_ || draw_raw_IR_ || draw_results_) {
92 printf("_____ New Image _____\n");
93 }
94
95 // Remove bad blobs.
Austin Schuh75921532019-03-09 18:46:34 -080096 target_finder_.PreFilter(&imgs);
Parker Schuh2a1447c2019-02-17 00:25:29 -080097
98 // Find polygons from blobs.
99 std::vector<std::vector<Segment<2>>> raw_polys;
100 for (const RangeImage &blob : imgs) {
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800101 // Convert blobs to contours in the corrected space.
Austin Schuh75921532019-03-09 18:46:34 -0800102 ContourNode *contour = target_finder_.GetContour(blob);
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800103 if (draw_contours_) {
104 DrawContour(contour, {255, 0, 0});
105 }
Austin Schuhe5015972019-03-09 17:47:34 -0800106 const ::std::vector<::Eigen::Vector2f> unwarped_contour =
Austin Schuh75921532019-03-09 18:46:34 -0800107 target_finder_.UnWarpContour(contour);
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800108 if (draw_contours_) {
Austin Schuhe5015972019-03-09 17:47:34 -0800109 DrawContour(unwarped_contour, {0, 0, 255});
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800110 }
111
112 // Process to polygons.
Parker Schuh2a1447c2019-02-17 00:25:29 -0800113 std::vector<Segment<2>> polygon =
Austin Schuh75921532019-03-09 18:46:34 -0800114 target_finder_.FillPolygon(unwarped_contour, draw_raw_poly_);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800115 if (polygon.empty()) {
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800116 if (!draw_contours_) {
117 DrawBlob(blob, {255, 0, 0});
118 }
Parker Schuh2a1447c2019-02-17 00:25:29 -0800119 } else {
120 raw_polys.push_back(polygon);
121 if (draw_select_blob_) {
122 DrawBlob(blob, {0, 0, 255});
123 }
124 if (draw_raw_poly_) {
125 std::vector<PixelRef> colors = GetNColors(polygon.size());
126 std::vector<Vector<2>> corners;
Austin Schuhe5015972019-03-09 17:47:34 -0800127 for (size_t i = 0; i < polygon.size(); ++i) {
128 corners.push_back(
129 polygon[i].Intersect(polygon[(i + 1) % polygon.size()]));
Parker Schuh2a1447c2019-02-17 00:25:29 -0800130 }
131
Austin Schuhe5015972019-03-09 17:47:34 -0800132 for (size_t i = 0; i < polygon.size(); ++i) {
133 overlay_.AddLine(corners[i], corners[(i + 1) % polygon.size()],
134 colors[i]);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800135 }
136 }
137 }
138 }
139
140 // Calculate each component side of a possible target.
141 std::vector<TargetComponent> target_component_list =
Austin Schuh32ffac22019-03-09 22:42:02 -0800142 target_finder_.FillTargetComponentList(raw_polys, draw_components_);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800143 if (draw_components_) {
144 for (const TargetComponent &comp : target_component_list) {
145 DrawComponent(comp, {0, 255, 255}, {0, 255, 255}, {255, 0, 0},
146 {0, 0, 255});
147 }
148 }
149
150 // Put the compenents together into targets.
Austin Schuh75921532019-03-09 18:46:34 -0800151 std::vector<Target> target_list = target_finder_.FindTargetsFromComponents(
Parker Schuh2a1447c2019-02-17 00:25:29 -0800152 target_component_list, draw_raw_target_);
153 if (draw_raw_target_) {
154 for (const Target &target : target_list) {
155 DrawTarget(target);
156 }
157 }
158
159 // Use the solver to generate an intermediate version of our results.
160 std::vector<IntermediateResult> results;
161 for (const Target &target : target_list) {
Austin Schuh75921532019-03-09 18:46:34 -0800162 results.emplace_back(
163 target_finder_.ProcessTargetToResult(target, draw_raw_IR_));
Parker Schuh2a1447c2019-02-17 00:25:29 -0800164 if (draw_raw_IR_) DrawResult(results.back(), {255, 128, 0});
165 }
166
167 // Check that our current results match possible solutions.
Austin Schuh75921532019-03-09 18:46:34 -0800168 results = target_finder_.FilterResults(results, 0);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800169 if (draw_results_) {
170 for (const IntermediateResult &res : results) {
Parker Schuh2a1447c2019-02-17 00:25:29 -0800171 DrawTarget(res, {0, 255, 0});
172 }
173 }
174
175 // If the target list is not empty then we found a target.
176 return !results.empty();
177 }
178
179 std::function<void(uint32_t)> RegisterKeyPress() override {
180 return [this](uint32_t key) {
181 (void)key;
182 if (key == 'z') {
183 draw_results_ = !draw_results_;
184 } else if (key == 'x') {
185 draw_raw_IR_ = !draw_raw_IR_;
186 } else if (key == 'c') {
187 draw_raw_target_ = !draw_raw_target_;
188 } else if (key == 'v') {
189 draw_components_ = !draw_components_;
190 } else if (key == 'b') {
191 draw_raw_poly_ = !draw_raw_poly_;
192 } else if (key == 'n') {
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800193 draw_contours_ = !draw_contours_;
194 } else if (key == 'm') {
Parker Schuh2a1447c2019-02-17 00:25:29 -0800195 draw_select_blob_ = !draw_select_blob_;
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800196 } else if (key == 'h') {
197 printf("Key Mappings:\n");
198 printf(" z: Toggle drawing final target pose.\n");
199 printf(" x: Toggle drawing re-projected targets and print solver results.\n");
200 printf(" c: Toggle drawing proposed target groupings.\n");
201 printf(" v: Toggle drawing ordered target components.\n");
202 printf(" b: Toggle drawing proposed target components.\n");
203 printf(" n: Toggle drawing countours before and after warping.\n");
204 printf(" m: Toggle drawing raw blob data (may need to change image to toggle a redraw).\n");
205 printf(" h: Print this message.\n");
Ben Fredricksona8c3d552019-03-03 14:14:53 -0800206 printf(" a: May log camera image to /tmp/debug_viewer_jpeg_<#>.yuyv\n");
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800207 printf(" q: Exit the application.\n");
Parker Schuh2a1447c2019-02-17 00:25:29 -0800208 } else if (key == 'q') {
209 printf("User requested shutdown.\n");
210 exit(0);
211 }
212 HandleBlobs(imgs_last_, fmt_last_);
213 viewer_->Redraw();
214 };
215 }
216
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800217 void DrawContour(ContourNode *contour, PixelRef color) {
218 if (viewer_) {
219 for (ContourNode *node = contour; node->next != contour;) {
220 Vector<2> a(node->pt.x, node->pt.y);
221 Vector<2> b(node->next->pt.x, node->next->pt.y);
222 overlay_.AddLine(a, b, color);
223 node = node->next;
224 }
225 }
226 }
227
Austin Schuhe5015972019-03-09 17:47:34 -0800228 void DrawContour(const ::std::vector<::Eigen::Vector2f> &contour,
229 PixelRef color) {
230 if (viewer_) {
231 for (size_t i = 0; i < contour.size(); ++i) {
232 Vector<2> a(contour[i].x(), contour[i].y());
233 Vector<2> b(contour[(i + 1) % contour.size()].x(),
234 contour[(i + 1) % contour.size()].y());
235 overlay_.AddLine(a, b, color);
236 }
237 }
238 }
239
Parker Schuh2a1447c2019-02-17 00:25:29 -0800240 void DrawComponent(const TargetComponent &comp, PixelRef top_color,
241 PixelRef bot_color, PixelRef in_color,
242 PixelRef out_color) {
243 overlay_.AddLine(comp.top, comp.inside, top_color);
244 overlay_.AddLine(comp.bottom, comp.outside, bot_color);
245
246 overlay_.AddLine(comp.bottom, comp.inside, in_color);
247 overlay_.AddLine(comp.top, comp.outside, out_color);
248 }
249
250 void DrawTarget(const Target &target) {
251 Vector<2> leftTop = (target.left.top + target.left.inside) * 0.5;
252 Vector<2> rightTop = (target.right.top + target.right.inside) * 0.5;
253 overlay_.AddLine(leftTop, rightTop, {255, 215, 0});
254
255 Vector<2> leftBot = (target.left.bottom + target.left.outside) * 0.5;
256 Vector<2> rightBot = (target.right.bottom + target.right.outside) * 0.5;
257 overlay_.AddLine(leftBot, rightBot, {255, 215, 0});
258
259 overlay_.AddLine(leftTop, leftBot, {255, 215, 0});
260 overlay_.AddLine(rightTop, rightBot, {255, 215, 0});
261 }
262
263 void DrawResult(const IntermediateResult &result, PixelRef color) {
Austin Schuh75921532019-03-09 18:46:34 -0800264 Target target = Project(target_finder_.GetTemplateTarget(), intrinsics(),
265 result.extrinsics);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800266 DrawComponent(target.left, color, color, color, color);
267 DrawComponent(target.right, color, color, color, color);
268 }
269
270 void DrawTarget(const IntermediateResult &result, PixelRef color) {
Austin Schuh75921532019-03-09 18:46:34 -0800271 Target target = Project(target_finder_.GetTemplateTarget(), intrinsics(),
272 result.extrinsics);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800273 Segment<2> leftAx((target.left.top + target.left.inside) * 0.5,
274 (target.left.bottom + target.left.outside) * 0.5);
275 leftAx.Set(leftAx.A() * 0.9 + leftAx.B() * 0.1,
276 leftAx.B() * 0.9 + leftAx.A() * 0.1);
277 overlay_.AddLine(leftAx, color);
278
279 Segment<2> rightAx((target.right.top + target.right.inside) * 0.5,
280 (target.right.bottom + target.right.outside) * 0.5);
281 rightAx.Set(rightAx.A() * 0.9 + rightAx.B() * 0.1,
282 rightAx.B() * 0.9 + rightAx.A() * 0.1);
283 overlay_.AddLine(rightAx, color);
284
285 overlay_.AddLine(leftAx.A(), rightAx.A(), color);
286 overlay_.AddLine(leftAx.B(), rightAx.B(), color);
287 Vector<3> p1(0.0, 0.0, 100.0);
288
289 Vector<3> p2 =
290 Rotate(intrinsics().mount_angle, result.extrinsics.r1, 0.0, p1);
291 Vector<2> p3(p2.x(), p2.y());
292 overlay_.AddLine(leftAx.A(), p3 + leftAx.A(), {0, 255, 0});
293 overlay_.AddLine(leftAx.B(), p3 + leftAx.B(), {0, 255, 0});
294 overlay_.AddLine(rightAx.A(), p3 + rightAx.A(), {0, 255, 0});
295 overlay_.AddLine(rightAx.B(), p3 + rightAx.B(), {0, 255, 0});
296
297 overlay_.AddLine(p3 + leftAx.A(), p3 + leftAx.B(), {0, 255, 0});
298 overlay_.AddLine(p3 + leftAx.A(), p3 + rightAx.A(), {0, 255, 0});
299 overlay_.AddLine(p3 + rightAx.A(), p3 + rightAx.B(), {0, 255, 0});
300 overlay_.AddLine(p3 + leftAx.B(), p3 + rightAx.B(), {0, 255, 0});
301 }
302
Austin Schuh75921532019-03-09 18:46:34 -0800303 const IntrinsicParams &intrinsics() const {
304 return target_finder_.intrinsics();
305 }
Parker Schuh2a1447c2019-02-17 00:25:29 -0800306
307 private:
308 // implementation of the filter pipeline.
Austin Schuh75921532019-03-09 18:46:34 -0800309 TargetFinder target_finder_;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800310 aos::vision::BlobStreamViewer *viewer_ = nullptr;
311 aos::vision::PixelLinesOverlay overlay_;
312 std::vector<aos::vision::OverlayBase *> overlays_;
313 BlobList imgs_last_;
314 ImageFormat fmt_last_;
315 bool draw_select_blob_ = false;
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800316 bool draw_contours_ = false;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800317 bool draw_raw_poly_ = false;
318 bool draw_components_ = false;
319 bool draw_raw_target_ = false;
320 bool draw_raw_IR_ = false;
321 bool draw_results_ = true;
322};
323
324} // namespace vision
325} // namespace y2017
326
327int main(int argc, char **argv) {
328 y2019::vision::FilterHarness filter_harness;
Austin Schuh75921532019-03-09 18:46:34 -0800329 ::gflags::ParseCommandLineFlags(&argc, &argv, true);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800330 aos::vision::DebugFrameworkMain(argc, argv, &filter_harness,
331 aos::vision::CameraParams());
332}