blob: 5da2dbe07e1106ff2c1aad1df5042fcd0fda5b32 [file] [log] [blame]
Jim Ostrowskic560cbe2020-03-07 00:29:30 -08001#include <map>
Jim Ostrowski23eb6582020-03-04 23:15:32 -08002#include <opencv2/calib3d.hpp>
3#include <opencv2/features2d.hpp>
4#include <opencv2/highgui/highgui.hpp>
5#include <opencv2/imgproc.hpp>
Jim Ostrowski56c09322021-10-23 16:17:21 -07006#include <random>
Jim Ostrowski23eb6582020-03-04 23:15:32 -08007
8#include "aos/events/shm_event_loop.h"
9#include "aos/init.h"
Jim Ostrowskibaa43692020-03-08 16:25:10 -070010#include "aos/time/time.h"
Jim Ostrowskic560cbe2020-03-07 00:29:30 -080011#include "y2020/vision/sift/sift_generated.h"
Jim Ostrowski23eb6582020-03-04 23:15:32 -080012#include "y2020/vision/vision_generated.h"
13
14DEFINE_string(config, "config.json", "Path to the config file to use.");
Jim Ostrowski56c09322021-10-23 16:17:21 -070015DEFINE_bool(show_features, true, "Show the SIFT features that matched.");
Austin Schuh69b2f792020-03-15 14:31:55 -070016DEFINE_string(channel, "/camera", "Channel name for the image.");
Jim Ostrowski23eb6582020-03-04 23:15:32 -080017
18namespace frc971 {
19namespace vision {
20namespace {
21
Jim Ostrowski834dddf2021-04-10 14:40:19 -070022aos::Fetcher<CameraImage> image_fetcher;
23aos::Fetcher<sift::ImageMatchResult> match_fetcher;
Jim Ostrowski56c09322021-10-23 16:17:21 -070024cv::Mat palette_;
Jim Ostrowski834dddf2021-04-10 14:40:19 -070025
26bool DisplayLoop() {
Jim Ostrowski56c09322021-10-23 16:17:21 -070027 // Try to get target match data
28 int64_t match_timestamp = 0;
29 const sift::ImageMatchResult *match;
Jim Ostrowski834dddf2021-04-10 14:40:19 -070030 if (match_fetcher.Fetch()) {
Jim Ostrowski56c09322021-10-23 16:17:21 -070031 match = match_fetcher.get();
Jim Ostrowski834dddf2021-04-10 14:40:19 -070032 CHECK(match != nullptr) << "Got null when trying to fetch match result";
33
Jim Ostrowski56c09322021-10-23 16:17:21 -070034 match_timestamp = match->image_monotonic_timestamp_ns();
35 if (match->camera_poses() != nullptr && match->camera_poses()->size() > 0) {
36 VLOG(2) << "Got matches for timestamp " << match_timestamp << "\n";
37 }
38 } else {
39 VLOG(2) << "Didn't get match this cycle";
40 }
41
42 int64_t image_timestamp = 0;
43 bool matching_image_found = false;
44 const CameraImage *image;
45 while (!matching_image_found) {
46 // Read next image
47 if (!image_fetcher.FetchNext()) {
48 VLOG(2) << "Couldn't fetch next image";
49 return true;
50 }
51
52 image = image_fetcher.get();
53 CHECK(image != nullptr) << "Couldn't read image";
54 image_timestamp = image->monotonic_timestamp_ns();
55 VLOG(2) << "Got image at timestamp: " << image_timestamp;
56
57 if (match_timestamp != 0 && image_timestamp == match_timestamp) {
58 matching_image_found = true;
59 } else if (image_timestamp > match_timestamp) {
60 LOG(INFO) << "Image timestamp went past match_timestamp";
61 return true;
Jim Ostrowski834dddf2021-04-10 14:40:19 -070062 }
63 }
64
Jim Ostrowski834dddf2021-04-10 14:40:19 -070065 // Create color image:
66 cv::Mat image_color_mat(cv::Size(image->cols(), image->rows()), CV_8UC2,
67 (void *)image->data()->data());
68 cv::Mat rgb_image(cv::Size(image->cols(), image->rows()), CV_8UC3);
69 cv::cvtColor(image_color_mat, rgb_image, CV_YUV2BGR_YUYV);
70
Jim Ostrowski56c09322021-10-23 16:17:21 -070071 if (matching_image_found) {
72 // Draw whatever matches we have
73 if (match->camera_poses() != nullptr && match->camera_poses()->size() > 0) {
74 // Draw target point in image
75 float x = match->camera_poses()->Get(0)->query_target_point_x();
76 float y = match->camera_poses()->Get(0)->query_target_point_y();
77 float radius = match->camera_poses()->Get(0)->query_target_point_radius();
78 cv::circle(rgb_image, cv::Point2f(x, y), radius, cv::Scalar(0, 255, 0),
79 5);
80 }
81
82 if (FLAGS_show_features && match->image_matches() != nullptr &&
83 match->image_matches()->size() > 0) {
84 // Iterate through matches and draw matched keypoints
85 for (uint model_match_ind = 0;
86 model_match_ind < match->image_matches()->size();
87 model_match_ind++) {
88 auto match_list =
89 match->image_matches()->Get(model_match_ind)->matches();
90 if (match_list != nullptr && match_list->size() > 0) {
91 int train_image_ind =
92 match->image_matches()->Get(model_match_ind)->train_image();
93 VLOG(2) << "Got " << match_list->size() << " matches to model "
94 << train_image_ind;
95
96 // Picking color from palette and drawing
97 auto color = palette_.at<cv::Vec3b>(train_image_ind % palette_.cols);
98 LOG(INFO) << "Using color " << color;
99 for (uint i = 0; i < match_list->size(); i++) {
100 uint query_feature_ind = match_list->Get(i)->query_feature();
101 float kp_x = match->features()->Get(query_feature_ind)->x();
102 float kp_y = match->features()->Get(query_feature_ind)->y();
103 cv::circle(rgb_image, cv::Point2f(kp_x, kp_y), 5, color, 2);
104 }
105 }
106 }
107 }
Jim Ostrowski834dddf2021-04-10 14:40:19 -0700108 }
109
110 cv::imshow("Display", rgb_image);
111 int keystroke = cv::waitKey(1);
112 if ((keystroke & 0xFF) == static_cast<int>('c')) {
113 // Convert again, to get clean image
114 cv::cvtColor(image_color_mat, rgb_image, CV_YUV2BGR_YUYV);
115 std::stringstream name;
116 name << "capture-" << aos::realtime_clock::now() << ".png";
117 cv::imwrite(name.str(), rgb_image);
118 LOG(INFO) << "Saved image file: " << name.str();
119 } else if ((keystroke & 0xFF) == static_cast<int>('q')) {
120 return false;
121 }
122 return true;
123}
124
125void ViewerMain() {
Jim Ostrowski56c09322021-10-23 16:17:21 -0700126 // Create random color palette for distinguishing multiple models
127 uchar colors[5][3] = {
128 {0, 0, 255}, {0, 165, 255}, {0, 255, 255}, {255, 0, 0}, {128, 0, 128}};
129 palette_ = cv::Mat(3, 5, CV_8U, &colors);
Jim Ostrowskic560cbe2020-03-07 00:29:30 -0800130
Jim Ostrowski23eb6582020-03-04 23:15:32 -0800131 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
132 aos::configuration::ReadConfig(FLAGS_config);
133
134 aos::ShmEventLoop event_loop(&config.message());
135
Jim Ostrowski834dddf2021-04-10 14:40:19 -0700136 image_fetcher = event_loop.MakeFetcher<CameraImage>(FLAGS_channel);
Jim Ostrowski23eb6582020-03-04 23:15:32 -0800137
Jim Ostrowski56c09322021-10-23 16:17:21 -0700138 // If we want to show the features, we have to use the detailed channel
139 std::string result_channel = FLAGS_channel;
140 if (FLAGS_show_features) {
141 result_channel += "/detailed";
142 }
143 match_fetcher =
144 event_loop.MakeFetcher<sift::ImageMatchResult>(result_channel);
Jim Ostrowskibaa43692020-03-08 16:25:10 -0700145
Jim Ostrowski834dddf2021-04-10 14:40:19 -0700146 // Run the display loop
147 event_loop.AddPhasedLoop(
148 [&event_loop](int) {
149 if (!DisplayLoop()) {
150 LOG(INFO) << "Calling event_loop Exit";
151 event_loop.Exit();
152 };
153 },
154 ::std::chrono::milliseconds(100));
Jim Ostrowski23eb6582020-03-04 23:15:32 -0800155
156 event_loop.Run();
157}
158
159} // namespace
160} // namespace vision
161} // namespace frc971
162
163// Quick and lightweight grayscale viewer for images
164int main(int argc, char **argv) {
165 aos::InitGoogle(&argc, &argv);
166 frc971::vision::ViewerMain();
167}