blob: 537e20995255a05d9e84877fd973046096c4ae60 [file] [log] [blame]
Brian Silverman58899fd2019-03-24 11:03:11 -07001#include "y2019/vision/target_finder.h"
2
Alex Perry24319272019-04-07 12:31:29 -07003#include <condition_variable>
Parker Schuh2a1447c2019-02-17 00:25:29 -08004#include <fstream>
Alex Perry24319272019-04-07 12:31:29 -07005#include <mutex>
Alex Perryb375df72019-04-07 12:31:21 -07006#include <random>
Alex Perry24319272019-04-07 12:31:29 -07007#include <thread>
Parker Schuh2a1447c2019-02-17 00:25:29 -08008
Parker Schuh2a1447c2019-02-17 00:25:29 -08009#include "aos/vision/blob/codec.h"
10#include "aos/vision/blob/find_blob.h"
11#include "aos/vision/events/socket_types.h"
12#include "aos/vision/events/udp.h"
Parker Schuh5e8e3a52019-02-24 13:36:19 -080013#include "y2019/jevois/camera/image_stream.h"
14#include "y2019/jevois/camera/reader.h"
Parker Schuh2a1447c2019-02-17 00:25:29 -080015#include "y2019/jevois/serial.h"
Brian Silvermance4825f2019-02-17 18:28:39 -080016#include "y2019/jevois/structures.h"
17#include "y2019/jevois/uart.h"
Austin Schuh4e2629d2019-03-28 14:44:37 -070018#include "y2019/vision/image_writer.h"
Brian Silverman58899fd2019-03-24 11:03:11 -070019
20// This has to be last to preserve compatibility with other headers using AOS
21// logging.
22#include "glog/logging.h"
Parker Schuh2a1447c2019-02-17 00:25:29 -080023
24using ::aos::events::DataSocket;
25using ::aos::events::RXUdpSocket;
26using ::aos::events::TCPServer;
27using ::aos::vision::DataRef;
28using ::aos::vision::Int32Codec;
29using ::aos::monotonic_clock;
30using ::y2019::jevois::open_via_terminos;
31using aos::vision::Segment;
32
Parker Schuh5e8e3a52019-02-24 13:36:19 -080033class CameraStream : public ::y2019::camera::ImageStreamEvent {
Parker Schuh2a1447c2019-02-17 00:25:29 -080034 public:
35 CameraStream(::aos::vision::CameraParams params, const ::std::string &fname)
36 : ImageStreamEvent(fname, params) {}
37
38 void ProcessImage(DataRef data, monotonic_clock::time_point monotonic_now) {
Brian Silverman58899fd2019-03-24 11:03:11 -070039 LOG(INFO) << "got frame: " << data.size();
Parker Schuh2a1447c2019-02-17 00:25:29 -080040
Parker Schuh5e8e3a52019-02-24 13:36:19 -080041 if (on_frame_) on_frame_(data, monotonic_now);
Parker Schuh2a1447c2019-02-17 00:25:29 -080042 }
43
Parker Schuh5e8e3a52019-02-24 13:36:19 -080044 void set_on_frame(const std::function<
45 void(DataRef, monotonic_clock::time_point)> &on_frame) {
46 on_frame_ = on_frame;
47 }
48
49 private:
50 std::function<void(DataRef, monotonic_clock::time_point)> on_frame_;
Parker Schuh2a1447c2019-02-17 00:25:29 -080051};
52
53int open_terminos(const char *tty_name) { return open_via_terminos(tty_name); }
54
55std::string GetFileContents(const std::string &filename) {
56 std::ifstream in(filename, std::ios::in | std::ios::binary);
57 if (in) {
58 std::string contents;
59 in.seekg(0, std::ios::end);
60 contents.resize(in.tellg());
61 in.seekg(0, std::ios::beg);
62 in.read(&contents[0], contents.size());
63 in.close();
64 return (contents);
65 }
66 fprintf(stderr, "Could not read file: %s\n", filename.c_str());
67 exit(-1);
68}
69
Parker Schuh5e8e3a52019-02-24 13:36:19 -080070using aos::vision::ImageRange;
71using aos::vision::RangeImage;
72using aos::vision::ImageFormat;
Alex Perry24319272019-04-07 12:31:29 -070073using y2019::vision::TargetFinder;
74using y2019::vision::IntermediateResult;
75using y2019::vision::Target;
76
77class TargetProcessPool {
78 public:
79 // The number of threads we'll use.
80 static constexpr int kThreads = 4;
81
82 TargetProcessPool(TargetFinder *finder);
83 ~TargetProcessPool();
84
85 std::vector<IntermediateResult> Process(std::vector<const Target *> &&inputs,
86 bool verbose);
87
88 private:
89 // The main function for a thread.
90 void RunThread();
91
92 std::array<std::thread, kThreads> threads_;
93 // Coordinates access to results_/inputs_ and coordinates with
94 // condition_variable_.
95 std::mutex mutex_;
96 // Signals changes to results_/inputs_ and quit_.
97 std::condition_variable condition_variable_;
98 bool quit_ = false;
99
100 bool verbose_ = false;
101 std::vector<const Target *> inputs_;
102 std::vector<IntermediateResult> results_;
103
104 TargetFinder *const finder_;
105};
106
107TargetProcessPool::TargetProcessPool(TargetFinder *finder) : finder_(finder) {
108 for (int i = 0; i < kThreads; ++i) {
109 threads_[i] = std::thread([this]() { RunThread(); });
110 }
111}
112
113TargetProcessPool::~TargetProcessPool() {
114 {
115 std::unique_lock<std::mutex> locker(mutex_);
116 quit_ = true;
117 condition_variable_.notify_all();
118 }
119 for (int i = 0; i < kThreads; ++i) {
120 threads_[i].join();
121 }
122}
123
124std::vector<IntermediateResult> TargetProcessPool::Process(
125 std::vector<const Target *> &&inputs, bool verbose) {
126 inputs_ = std::move(inputs);
127 results_.clear();
128 verbose_ = verbose;
129 const size_t number_targets = inputs_.size();
130 {
131 std::unique_lock<std::mutex> locker(mutex_);
132 condition_variable_.notify_all();
133 while (results_.size() < number_targets) {
134 condition_variable_.wait(locker);
135 }
136 }
137 return std::move(results_);
138}
139
140void TargetProcessPool::RunThread() {
141 while (true) {
142 const Target *my_input;
143 {
144 std::unique_lock<std::mutex> locker(mutex_);
145 while (inputs_.empty()) {
146 if (quit_) {
147 return;
148 }
149 condition_variable_.wait(locker);
150 }
151 my_input = inputs_.back();
152 inputs_.pop_back();
153 }
154 IntermediateResult my_output =
155 finder_->ProcessTargetToResult(*my_input, false);
156 {
157 std::unique_lock<std::mutex> locker(mutex_);
158 results_.emplace_back(std::move(my_output));
159 condition_variable_.notify_all();
160 }
161 }
162}
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800163
Parker Schuh2a1447c2019-02-17 00:25:29 -0800164int main(int argc, char **argv) {
165 (void)argc;
166 (void)argv;
167 using namespace y2019::vision;
Brian Silvermane9924fd2019-03-02 15:20:42 -0800168 using frc971::jevois::CameraCommand;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800169 // gflags::ParseCommandLineFlags(&argc, &argv, false);
Brian Silverman58899fd2019-03-24 11:03:11 -0700170 FLAGS_logtostderr = true;
171 google::InitGoogleLogging(argv[0]);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800172
173 int itsDev = open_terminos("/dev/ttyS0");
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800174 frc971::jevois::CobsPacketizer<frc971::jevois::uart_to_camera_size()> cobs;
175 // Uncomment these to printf directly to stdout to get debug info...
176 // dup2(itsDev, 1);
177 // dup2(itsDev, 2);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800178
Alex Perry24319272019-04-07 12:31:29 -0700179 TargetFinder finder;
180 TargetProcessPool process_pool(&finder);
Austin Schuh4e2629d2019-03-28 14:44:37 -0700181 ImageWriter writer;
182 uint32_t image_count = 0;
183 bool log_images = false;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800184
Parker Schuh2a1447c2019-02-17 00:25:29 -0800185 aos::vision::CameraParams params0;
Ben Fredricksona8c3d552019-03-03 14:14:53 -0800186 params0.set_exposure(50);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800187 params0.set_brightness(40);
188 params0.set_width(640);
Alex Perry24319272019-04-07 12:31:29 -0700189 params0.set_fps(25);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800190 params0.set_height(480);
191
Brian Silverman20b57772019-03-23 22:02:49 -0700192 aos::vision::FastYuyvYPooledThresholder thresholder;
193
Alex Perryb375df72019-04-07 12:31:21 -0700194 // A source of psuedorandom numbers which gives different numbers each time we
195 // need to drop targets.
196 std::minstd_rand random_engine;
197
Parker Schuh2a1447c2019-02-17 00:25:29 -0800198 ::std::unique_ptr<CameraStream> camera0(
199 new CameraStream(params0, "/dev/video0"));
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800200 camera0->set_on_frame([&](DataRef data,
201 monotonic_clock::time_point monotonic_now) {
Parker Schuh2a1447c2019-02-17 00:25:29 -0800202 aos::vision::ImageFormat fmt{640, 480};
Brian Silverman20b57772019-03-23 22:02:49 -0700203 aos::vision::BlobList imgs =
204 aos::vision::FindBlobs(thresholder.Threshold(fmt, data.data(), 120));
Alex Perry24319272019-04-07 12:31:29 -0700205 finder.PreFilter(&imgs);
Brian Silverman58899fd2019-03-24 11:03:11 -0700206 LOG(INFO) << "Blobs: " << imgs.size();
Parker Schuh2a1447c2019-02-17 00:25:29 -0800207
Austin Schuh32ffac22019-03-09 22:42:02 -0800208 constexpr bool verbose = false;
Austin Schuh6e56faf2019-03-10 14:04:57 -0700209 ::std::vector<Polygon> raw_polys;
Parker Schuh2a1447c2019-02-17 00:25:29 -0800210 for (const RangeImage &blob : imgs) {
Ben Fredricksonf7b68522019-03-02 21:19:42 -0800211 // Convert blobs to contours in the corrected space.
Alex Perry24319272019-04-07 12:31:29 -0700212 ContourNode *contour = finder.GetContour(blob);
Austin Schuh6e56faf2019-03-10 14:04:57 -0700213 ::std::vector<::Eigen::Vector2f> unwarped_contour =
Alex Perry24319272019-04-07 12:31:29 -0700214 finder.UnWarpContour(contour);
Austin Schuh6e56faf2019-03-10 14:04:57 -0700215 const Polygon polygon =
Alex Perry24319272019-04-07 12:31:29 -0700216 finder.FindPolygon(::std::move(unwarped_contour), verbose);
Austin Schuh6e56faf2019-03-10 14:04:57 -0700217 if (!polygon.segments.empty()) {
Parker Schuh2a1447c2019-02-17 00:25:29 -0800218 raw_polys.push_back(polygon);
219 }
220 }
Brian Silverman58899fd2019-03-24 11:03:11 -0700221 LOG(INFO) << "Polygons: " << raw_polys.size();
Parker Schuh2a1447c2019-02-17 00:25:29 -0800222
223 // Calculate each component side of a possible target.
Austin Schuh32ffac22019-03-09 22:42:02 -0800224 ::std::vector<TargetComponent> target_component_list =
Alex Perry24319272019-04-07 12:31:29 -0700225 finder.FillTargetComponentList(raw_polys, verbose);
Brian Silverman58899fd2019-03-24 11:03:11 -0700226 LOG(INFO) << "Components: " << target_component_list.size();
Parker Schuh2a1447c2019-02-17 00:25:29 -0800227
228 // Put the compenents together into targets.
Austin Schuh32ffac22019-03-09 22:42:02 -0800229 ::std::vector<Target> target_list =
Alex Perry24319272019-04-07 12:31:29 -0700230 finder.FindTargetsFromComponents(target_component_list, verbose);
Alex Perryb375df72019-04-07 12:31:21 -0700231 static constexpr size_t kMaximumPotentialTargets = 8;
232 LOG(INFO) << "Potential Targets (will filter to "
233 << kMaximumPotentialTargets << "): " << target_list.size();
234
235 // A list of all the indices into target_list which we're going to actually
236 // use.
237 std::vector<int> target_list_indices;
238 target_list_indices.resize(target_list.size());
239 for (size_t i = 0; i < target_list.size(); ++i) {
240 target_list_indices[i] = i;
241 }
242 // Drop random elements until we get sufficiently few of them. We drop
243 // different elements each time to ensure we will see different valid
244 // targets on successive frames, which provides more useful information to
245 // the localization.
246 while (target_list_indices.size() > kMaximumPotentialTargets) {
247 std::uniform_int_distribution<size_t> distribution(
248 0, target_list_indices.size() - 1);
249 const size_t index = distribution(random_engine);
250 target_list_indices.erase(target_list_indices.begin() + index);
251 }
Parker Schuh2a1447c2019-02-17 00:25:29 -0800252
253 // Use the solver to generate an intermediate version of our results.
Alex Perry24319272019-04-07 12:31:29 -0700254 std::vector<const Target *> inputs;
Alex Perryb375df72019-04-07 12:31:21 -0700255 for (size_t index : target_list_indices) {
Alex Perry24319272019-04-07 12:31:29 -0700256 inputs.push_back(&target_list[index]);
Parker Schuh2a1447c2019-02-17 00:25:29 -0800257 }
Alex Perry24319272019-04-07 12:31:29 -0700258 std::vector<IntermediateResult> results =
259 process_pool.Process(std::move(inputs), verbose);
Brian Silverman58899fd2019-03-24 11:03:11 -0700260 LOG(INFO) << "Raw Results: " << results.size();
Parker Schuh2a1447c2019-02-17 00:25:29 -0800261
Alex Perry24319272019-04-07 12:31:29 -0700262 results = finder.FilterResults(results, 30, verbose);
Brian Silverman58899fd2019-03-24 11:03:11 -0700263 LOG(INFO) << "Results: " << results.size();
Brian Silvermance4825f2019-02-17 18:28:39 -0800264
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800265 // TODO: Select top 3 (randomly?)
266
Brian Silvermanc41fb862019-03-02 21:14:46 -0800267 frc971::jevois::CameraFrame frame{};
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800268
269 for (size_t i = 0; i < results.size() && i < frame.targets.max_size();
270 ++i) {
271 const auto &result = results[i].extrinsics;
272 frame.targets.push_back(frc971::jevois::Target{
273 static_cast<float>(result.z), static_cast<float>(result.y),
274 static_cast<float>(result.r2), static_cast<float>(result.r1)});
275 }
276
277 frame.age = std::chrono::duration_cast<frc971::jevois::camera_duration>(
278 aos::monotonic_clock::now() - monotonic_now);
279
Brian Silverman86891e52019-03-23 22:02:52 -0700280 // If we succeed in writing our delimiter, then write out the rest of
281 // the frame. If not, no point in continuing.
Brian Silvermance4825f2019-02-17 18:28:39 -0800282 if (write(itsDev, "\0", 1) == 1) {
Brian Silvermance4825f2019-02-17 18:28:39 -0800283 const auto serialized_frame = frc971::jevois::UartPackToTeensy(frame);
284 // We don't really care if this succeeds or not. If it fails for some
285 // reason, we'll just try again with the next frame, and the other end
286 // will find the new packet just fine.
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800287 ssize_t n =
288 write(itsDev, serialized_frame.data(), serialized_frame.size());
289
290 if (n != (ssize_t)serialized_frame.size()) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700291 LOG(INFO) << "Some problem happened";
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800292 }
Brian Silvermance4825f2019-02-17 18:28:39 -0800293 }
Austin Schuh4e2629d2019-03-28 14:44:37 -0700294
295 if (log_images) {
296 if ((image_count % 5) == 0) {
297 writer.WriteImage(data);
298 }
299 ++image_count;
300 }
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800301 });
Parker Schuh2a1447c2019-02-17 00:25:29 -0800302
303 aos::events::EpollLoop loop;
304
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800305 while (true) {
306 std::this_thread::sleep_for(std::chrono::milliseconds(1));
Parker Schuh2a1447c2019-02-17 00:25:29 -0800307 camera0->ReadEvent();
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800308
309 {
310 constexpr size_t kBufferSize = frc971::jevois::uart_to_teensy_size();
311 char data[kBufferSize];
312 ssize_t n = read(itsDev, &data[0], kBufferSize);
313 if (n >= 1) {
314 cobs.ParseData(gsl::span<const char>(&data[0], n));
315 auto packet = cobs.received_packet();
316 if (!packet.empty()) {
317 auto calibration_question =
318 frc971::jevois::UartUnpackToCamera(packet);
319 if (calibration_question) {
320 const auto &calibration = *calibration_question;
Alex Perry24319272019-04-07 12:31:29 -0700321 IntrinsicParams *intrinsics = finder.mutable_intrinsics();
Alex Perry3bf1bee2019-02-23 20:01:15 -0800322 intrinsics->mount_angle = calibration.calibration(0, 0);
323 intrinsics->focal_length = calibration.calibration(0, 1);
324 intrinsics->barrel_mount = calibration.calibration(0, 2);
325
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800326 switch (calibration.camera_command) {
Brian Silvermane9924fd2019-03-02 15:20:42 -0800327 case CameraCommand::kNormal:
Brian Silvermanbac77542019-03-03 13:57:00 -0800328 case CameraCommand::kAs:
Austin Schuh4e2629d2019-03-28 14:44:37 -0700329 log_images = false;
330 break;
331 case CameraCommand::kLog:
332 log_images = true;
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800333 break;
Brian Silvermane9924fd2019-03-02 15:20:42 -0800334 case CameraCommand::kUsb:
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800335 return 0;
Brian Silvermane9924fd2019-03-02 15:20:42 -0800336 case CameraCommand::kCameraPassthrough:
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800337 return system("touch /tmp/do_not_export_sd_card");
338 }
339 } else {
Brian Silverman58899fd2019-03-24 11:03:11 -0700340 fprintf(stderr, "bad frame\n");
Parker Schuh5e8e3a52019-02-24 13:36:19 -0800341 }
342 cobs.clear_received_packet();
343 }
344 }
345 }
Parker Schuh2a1447c2019-02-17 00:25:29 -0800346 }
347
348 // TODO: Fix event loop on jevois:
349 // loop.Add(camera0.get());
350 // loop.Run();
351}