blob: ff08f0107721d8fb925f449115cebdf285836cc5 [file] [log] [blame]
Jim Ostrowskic560cbe2020-03-07 00:29:30 -08001#include <map>
Philipp Schrader790cb542023-07-05 21:06:52 -07002#include <random>
3
Jim Ostrowski23eb6582020-03-04 23:15:32 -08004#include <opencv2/calib3d.hpp>
5#include <opencv2/features2d.hpp>
6#include <opencv2/highgui/highgui.hpp>
7#include <opencv2/imgproc.hpp>
8
9#include "aos/events/shm_event_loop.h"
10#include "aos/init.h"
Jim Ostrowskibaa43692020-03-08 16:25:10 -070011#include "aos/time/time.h"
Jim Ostrowski977850f2022-01-22 21:04:22 -080012#include "frc971/vision/v4l2_reader.h"
13#include "frc971/vision/vision_generated.h"
Jim Ostrowskic560cbe2020-03-07 00:29:30 -080014#include "y2020/vision/sift/sift_generated.h"
Jim Ostrowski23eb6582020-03-04 23:15:32 -080015
Austin Schuhc5fa6d92022-02-25 14:36:28 -080016DEFINE_string(config, "aos_config.json", "Path to the config file to use.");
Jim Ostrowski56c09322021-10-23 16:17:21 -070017DEFINE_bool(show_features, true, "Show the SIFT features that matched.");
Austin Schuh69b2f792020-03-15 14:31:55 -070018DEFINE_string(channel, "/camera", "Channel name for the image.");
Jim Ostrowski23eb6582020-03-04 23:15:32 -080019
Austin Schuh57763122021-11-06 20:49:18 -070020DEFINE_string(capture, "",
21 "If set, capture a single image and save it to this filename.");
22
Jim Ostrowski23eb6582020-03-04 23:15:32 -080023namespace frc971 {
24namespace vision {
25namespace {
26
Jim Ostrowski834dddf2021-04-10 14:40:19 -070027aos::Fetcher<CameraImage> image_fetcher;
28aos::Fetcher<sift::ImageMatchResult> match_fetcher;
Jim Ostrowski56c09322021-10-23 16:17:21 -070029cv::Mat palette_;
Jim Ostrowski834dddf2021-04-10 14:40:19 -070030
31bool DisplayLoop() {
Jim Ostrowski56c09322021-10-23 16:17:21 -070032 // Try to get target match data
33 int64_t match_timestamp = 0;
34 const sift::ImageMatchResult *match;
Jim Ostrowski834dddf2021-04-10 14:40:19 -070035 if (match_fetcher.Fetch()) {
Jim Ostrowski56c09322021-10-23 16:17:21 -070036 match = match_fetcher.get();
Jim Ostrowski834dddf2021-04-10 14:40:19 -070037 CHECK(match != nullptr) << "Got null when trying to fetch match result";
38
Jim Ostrowski56c09322021-10-23 16:17:21 -070039 match_timestamp = match->image_monotonic_timestamp_ns();
40 if (match->camera_poses() != nullptr && match->camera_poses()->size() > 0) {
41 VLOG(2) << "Got matches for timestamp " << match_timestamp << "\n";
42 }
43 } else {
44 VLOG(2) << "Didn't get match this cycle";
45 }
46
47 int64_t image_timestamp = 0;
48 bool matching_image_found = false;
49 const CameraImage *image;
50 while (!matching_image_found) {
51 // Read next image
52 if (!image_fetcher.FetchNext()) {
53 VLOG(2) << "Couldn't fetch next image";
54 return true;
55 }
56
57 image = image_fetcher.get();
58 CHECK(image != nullptr) << "Couldn't read image";
59 image_timestamp = image->monotonic_timestamp_ns();
60 VLOG(2) << "Got image at timestamp: " << image_timestamp;
61
62 if (match_timestamp != 0 && image_timestamp == match_timestamp) {
63 matching_image_found = true;
64 } else if (image_timestamp > match_timestamp) {
65 LOG(INFO) << "Image timestamp went past match_timestamp";
66 return true;
Jim Ostrowski834dddf2021-04-10 14:40:19 -070067 }
68 }
69
Jim Ostrowski834dddf2021-04-10 14:40:19 -070070 // Create color image:
71 cv::Mat image_color_mat(cv::Size(image->cols(), image->rows()), CV_8UC2,
72 (void *)image->data()->data());
73 cv::Mat rgb_image(cv::Size(image->cols(), image->rows()), CV_8UC3);
Brian Silverman4c7235a2021-11-17 19:04:37 -080074 cv::cvtColor(image_color_mat, rgb_image, cv::COLOR_YUV2BGR_YUYV);
Jim Ostrowski834dddf2021-04-10 14:40:19 -070075
Austin Schuh57763122021-11-06 20:49:18 -070076 if (!FLAGS_capture.empty()) {
77 cv::imwrite(FLAGS_capture, rgb_image);
78 return false;
79 }
80
Jim Ostrowski56c09322021-10-23 16:17:21 -070081 if (matching_image_found) {
82 // Draw whatever matches we have
83 if (match->camera_poses() != nullptr && match->camera_poses()->size() > 0) {
84 // Draw target point in image
85 float x = match->camera_poses()->Get(0)->query_target_point_x();
86 float y = match->camera_poses()->Get(0)->query_target_point_y();
87 float radius = match->camera_poses()->Get(0)->query_target_point_radius();
88 cv::circle(rgb_image, cv::Point2f(x, y), radius, cv::Scalar(0, 255, 0),
89 5);
90 }
91
92 if (FLAGS_show_features && match->image_matches() != nullptr &&
93 match->image_matches()->size() > 0) {
94 // Iterate through matches and draw matched keypoints
95 for (uint model_match_ind = 0;
96 model_match_ind < match->image_matches()->size();
97 model_match_ind++) {
98 auto match_list =
99 match->image_matches()->Get(model_match_ind)->matches();
100 if (match_list != nullptr && match_list->size() > 0) {
101 int train_image_ind =
102 match->image_matches()->Get(model_match_ind)->train_image();
103 VLOG(2) << "Got " << match_list->size() << " matches to model "
104 << train_image_ind;
105
106 // Picking color from palette and drawing
107 auto color = palette_.at<cv::Vec3b>(train_image_ind % palette_.cols);
108 LOG(INFO) << "Using color " << color;
109 for (uint i = 0; i < match_list->size(); i++) {
110 uint query_feature_ind = match_list->Get(i)->query_feature();
111 float kp_x = match->features()->Get(query_feature_ind)->x();
112 float kp_y = match->features()->Get(query_feature_ind)->y();
113 cv::circle(rgb_image, cv::Point2f(kp_x, kp_y), 5, color, 2);
114 }
115 }
116 }
117 }
Jim Ostrowski834dddf2021-04-10 14:40:19 -0700118 }
119
120 cv::imshow("Display", rgb_image);
121 int keystroke = cv::waitKey(1);
122 if ((keystroke & 0xFF) == static_cast<int>('c')) {
123 // Convert again, to get clean image
Brian Silverman4c7235a2021-11-17 19:04:37 -0800124 cv::cvtColor(image_color_mat, rgb_image, cv::COLOR_YUV2BGR_YUYV);
Jim Ostrowski834dddf2021-04-10 14:40:19 -0700125 std::stringstream name;
126 name << "capture-" << aos::realtime_clock::now() << ".png";
127 cv::imwrite(name.str(), rgb_image);
128 LOG(INFO) << "Saved image file: " << name.str();
129 } else if ((keystroke & 0xFF) == static_cast<int>('q')) {
130 return false;
131 }
132 return true;
133}
134
135void ViewerMain() {
Jim Ostrowski56c09322021-10-23 16:17:21 -0700136 // Create random color palette for distinguishing multiple models
137 uchar colors[5][3] = {
138 {0, 0, 255}, {0, 165, 255}, {0, 255, 255}, {255, 0, 0}, {128, 0, 128}};
139 palette_ = cv::Mat(3, 5, CV_8U, &colors);
Jim Ostrowskic560cbe2020-03-07 00:29:30 -0800140
Jim Ostrowski23eb6582020-03-04 23:15:32 -0800141 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
142 aos::configuration::ReadConfig(FLAGS_config);
143
144 aos::ShmEventLoop event_loop(&config.message());
145
Jim Ostrowski834dddf2021-04-10 14:40:19 -0700146 image_fetcher = event_loop.MakeFetcher<CameraImage>(FLAGS_channel);
Jim Ostrowski23eb6582020-03-04 23:15:32 -0800147
Jim Ostrowski56c09322021-10-23 16:17:21 -0700148 // If we want to show the features, we have to use the detailed channel
149 std::string result_channel = FLAGS_channel;
150 if (FLAGS_show_features) {
151 result_channel += "/detailed";
152 }
153 match_fetcher =
154 event_loop.MakeFetcher<sift::ImageMatchResult>(result_channel);
Jim Ostrowskibaa43692020-03-08 16:25:10 -0700155
Jim Ostrowski834dddf2021-04-10 14:40:19 -0700156 // Run the display loop
157 event_loop.AddPhasedLoop(
158 [&event_loop](int) {
159 if (!DisplayLoop()) {
160 LOG(INFO) << "Calling event_loop Exit";
161 event_loop.Exit();
162 };
163 },
164 ::std::chrono::milliseconds(100));
Jim Ostrowski23eb6582020-03-04 23:15:32 -0800165
166 event_loop.Run();
Austin Schuh57763122021-11-06 20:49:18 -0700167
168 image_fetcher = aos::Fetcher<CameraImage>();
169 match_fetcher = aos::Fetcher<sift::ImageMatchResult>();
Jim Ostrowski23eb6582020-03-04 23:15:32 -0800170}
171
172} // namespace
173} // namespace vision
174} // namespace frc971
175
176// Quick and lightweight grayscale viewer for images
177int main(int argc, char **argv) {
178 aos::InitGoogle(&argc, &argv);
179 frc971::vision::ViewerMain();
180}