blob: ef503e2d8b38ddeb4fdf60153c784b4e8046aa94 [file] [log] [blame]
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -08001#include <map>
2#include <opencv2/calib3d.hpp>
3#include <opencv2/features2d.hpp>
4#include <opencv2/highgui/highgui.hpp>
5#include <opencv2/imgproc.hpp>
6#include <random>
7
8#include "aos/events/shm_event_loop.h"
9#include "aos/init.h"
10#include "aos/time/time.h"
Jim Ostrowski977850f2022-01-22 21:04:22 -080011#include "frc971/vision/vision_generated.h"
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080012#include "y2022/vision/blob_detector.h"
Henry Speisere45e7a22022-02-04 23:17:01 -080013#include "y2022/vision/target_estimate_generated.h"
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080014
15DEFINE_string(capture, "",
16 "If set, capture a single image and save it to this filename.");
17DEFINE_string(channel, "/camera", "Channel name for the image.");
Austin Schuhc5fa6d92022-02-25 14:36:28 -080018DEFINE_string(config, "aos_config.json", "Path to the config file to use.");
Jim Ostrowski5caf81c2022-01-30 21:02:19 -080019DEFINE_string(png_dir, "", "Path to a set of images to display.");
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080020DEFINE_bool(show_features, true, "Show the blobs.");
21
22namespace y2022 {
23namespace vision {
24namespace {
25
Jim Ostrowski210765a2022-02-27 12:52:14 -080026using namespace frc971::vision;
27
28std::map<int64_t, BlobDetector::BlobResult> target_est_map;
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080029aos::Fetcher<frc971::vision::CameraImage> image_fetcher;
Henry Speisere45e7a22022-02-04 23:17:01 -080030aos::Fetcher<y2022::vision::TargetEstimate> target_estimate_fetcher;
31
32std::vector<std::vector<cv::Point>> FbsToCvBlobs(
33 const flatbuffers::Vector<flatbuffers::Offset<Blob>> &blobs_fbs) {
34 std::vector<std::vector<cv::Point>> blobs;
35 for (const auto blob : blobs_fbs) {
36 std::vector<cv::Point> points;
37 for (const Point *point : *blob->points()) {
38 points.emplace_back(cv::Point{point->x(), point->y()});
39 }
40 blobs.emplace_back(points);
41 }
42 return blobs;
43}
44
45std::vector<BlobDetector::BlobStats> FbsToBlobStats(
46 const flatbuffers::Vector<flatbuffers::Offset<BlobStatsFbs>>
47 &blob_stats_fbs) {
48 std::vector<BlobDetector::BlobStats> blob_stats;
49 for (const auto stats_fbs : blob_stats_fbs) {
50 cv::Point centroid{stats_fbs->centroid()->x(), stats_fbs->centroid()->y()};
51 blob_stats.emplace_back(BlobDetector::BlobStats{
52 centroid, stats_fbs->aspect_ratio(), stats_fbs->area(),
53 static_cast<size_t>(stats_fbs->num_points())});
54 }
55 return blob_stats;
56}
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080057
58bool DisplayLoop() {
Jim Ostrowski210765a2022-02-27 12:52:14 -080059 int64_t target_timestamp = 0;
60 if (target_estimate_fetcher.Fetch()) {
61 const TargetEstimate *target_est = target_estimate_fetcher.get();
62 CHECK(target_est != nullptr)
63 << "Got null when trying to fetch target estimate";
64
65 target_timestamp = target_est->image_monotonic_timestamp_ns();
66 if (target_est->blob_result()->filtered_blobs()->size() > 0) {
67 VLOG(2) << "Got blobs for timestamp " << target_est << "\n";
68 }
69 // Store the TargetEstimate data so we can match timestamp with image
70 target_est_map[target_timestamp] = BlobDetector::BlobResult(
71 {cv::Mat(), FbsToCvBlobs(*target_est->blob_result()->filtered_blobs()),
72 FbsToCvBlobs(*target_est->blob_result()->unfiltered_blobs()),
73 FbsToBlobStats(*target_est->blob_result()->blob_stats()),
74 cv::Point{target_est->blob_result()->centroid()->x(),
75 target_est->blob_result()->centroid()->y()}});
76 // Only keep last 10 matches
77 while (target_est_map.size() > 10u) {
78 target_est_map.erase(target_est_map.begin());
79 }
80 }
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080081 int64_t image_timestamp = 0;
Jim Ostrowski5caf81c2022-01-30 21:02:19 -080082 if (!image_fetcher.Fetch()) {
Jim Ostrowski210765a2022-02-27 12:52:14 -080083 VLOG(2) << "Couldn't fetch image";
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080084 return true;
85 }
Jim Ostrowski210765a2022-02-27 12:52:14 -080086 const CameraImage *image = image_fetcher.get();
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080087 CHECK(image != nullptr) << "Couldn't read image";
88 image_timestamp = image->monotonic_timestamp_ns();
89 VLOG(2) << "Got image at timestamp: " << image_timestamp;
90
91 // Create color image:
92 cv::Mat image_color_mat(cv::Size(image->cols(), image->rows()), CV_8UC2,
93 (void *)image->data()->data());
Milind Upadhyayec41e132022-02-05 17:14:05 -080094 cv::Mat bgr_image(cv::Size(image->cols(), image->rows()), CV_8UC3);
Milind Upadhyay40522942022-02-19 15:30:31 -080095 cv::cvtColor(image_color_mat, bgr_image, cv::COLOR_YUV2BGR_YUYV);
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080096
97 if (!FLAGS_capture.empty()) {
Milind Upadhyayec41e132022-02-05 17:14:05 -080098 cv::imwrite(FLAGS_capture, bgr_image);
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080099 return false;
100 }
101
Jim Ostrowski210765a2022-02-27 12:52:14 -0800102 auto target_est_it = target_est_map.find(image_timestamp);
103 if (target_est_it != target_est_map.end()) {
104 LOG(INFO) << image->monotonic_timestamp_ns() << ": # unfiltered blobs: "
105 << target_est_it->second.unfiltered_blobs.size()
106 << "; # filtered blobs: "
107 << target_est_it->second.filtered_blobs.size();
Henry Speisere45e7a22022-02-04 23:17:01 -0800108
Jim Ostrowski210765a2022-02-27 12:52:14 -0800109 cv::Mat ret_image =
110 cv::Mat::zeros(cv::Size(image->cols(), image->rows()), CV_8UC3);
111 BlobDetector::DrawBlobs(ret_image, target_est_it->second.filtered_blobs,
112 target_est_it->second.unfiltered_blobs,
113 target_est_it->second.blob_stats,
114 target_est_it->second.centroid);
Jim Ostrowski46a78382022-02-06 14:05:58 -0800115 cv::imshow("blobs", ret_image);
116 }
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -0800117
Milind Upadhyayec41e132022-02-05 17:14:05 -0800118 cv::imshow("image", bgr_image);
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -0800119
120 int keystroke = cv::waitKey(1);
121 if ((keystroke & 0xFF) == static_cast<int>('c')) {
122 // Convert again, to get clean image
Milind Upadhyayec41e132022-02-05 17:14:05 -0800123 cv::cvtColor(image_color_mat, bgr_image, cv::COLOR_YUV2BGR_YUYV);
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -0800124 std::stringstream name;
125 name << "capture-" << aos::realtime_clock::now() << ".png";
Milind Upadhyayec41e132022-02-05 17:14:05 -0800126 cv::imwrite(name.str(), bgr_image);
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -0800127 LOG(INFO) << "Saved image file: " << name.str();
128 } else if ((keystroke & 0xFF) == static_cast<int>('q')) {
129 return false;
130 }
131 return true;
132}
133
134void ViewerMain() {
135 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
136 aos::configuration::ReadConfig(FLAGS_config);
137
138 aos::ShmEventLoop event_loop(&config.message());
139
140 image_fetcher =
141 event_loop.MakeFetcher<frc971::vision::CameraImage>(FLAGS_channel);
142
Jim Ostrowski46a78382022-02-06 14:05:58 -0800143 target_estimate_fetcher =
144 event_loop.MakeFetcher<y2022::vision::TargetEstimate>(FLAGS_channel);
145
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -0800146 // 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));
155
156 event_loop.Run();
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -0800157}
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -0800158} // namespace
159} // namespace vision
160} // namespace y2022
161
162// Quick and lightweight viewer for images
163int main(int argc, char **argv) {
164 aos::InitGoogle(&argc, &argv);
Henry Speisere45e7a22022-02-04 23:17:01 -0800165 y2022::vision::ViewerMain();
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -0800166}