blob: 7c13d1b4eafbac02b3955190e75f4219ad2d6a3e [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.");
18DEFINE_string(config, "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
26aos::Fetcher<frc971::vision::CameraImage> image_fetcher;
Henry Speisere45e7a22022-02-04 23:17:01 -080027aos::Fetcher<y2022::vision::TargetEstimate> target_estimate_fetcher;
28
29std::vector<std::vector<cv::Point>> FbsToCvBlobs(
30 const flatbuffers::Vector<flatbuffers::Offset<Blob>> &blobs_fbs) {
31 std::vector<std::vector<cv::Point>> blobs;
32 for (const auto blob : blobs_fbs) {
33 std::vector<cv::Point> points;
34 for (const Point *point : *blob->points()) {
35 points.emplace_back(cv::Point{point->x(), point->y()});
36 }
37 blobs.emplace_back(points);
38 }
39 return blobs;
40}
41
42std::vector<BlobDetector::BlobStats> FbsToBlobStats(
43 const flatbuffers::Vector<flatbuffers::Offset<BlobStatsFbs>>
44 &blob_stats_fbs) {
45 std::vector<BlobDetector::BlobStats> blob_stats;
46 for (const auto stats_fbs : blob_stats_fbs) {
47 cv::Point centroid{stats_fbs->centroid()->x(), stats_fbs->centroid()->y()};
48 blob_stats.emplace_back(BlobDetector::BlobStats{
49 centroid, stats_fbs->aspect_ratio(), stats_fbs->area(),
50 static_cast<size_t>(stats_fbs->num_points())});
51 }
52 return blob_stats;
53}
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080054
55bool DisplayLoop() {
56 int64_t image_timestamp = 0;
57 const frc971::vision::CameraImage *image;
58 // Read next image
Jim Ostrowski5caf81c2022-01-30 21:02:19 -080059 if (!image_fetcher.Fetch()) {
60 LOG(INFO) << "Couldn't fetch image";
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080061 return true;
62 }
63
64 image = image_fetcher.get();
65 CHECK(image != nullptr) << "Couldn't read image";
66 image_timestamp = image->monotonic_timestamp_ns();
67 VLOG(2) << "Got image at timestamp: " << image_timestamp;
68
Henry Speisere45e7a22022-02-04 23:17:01 -080069 // TODO(Milind) Store the target estimates and match them by timestamp to make
70 // sure we're getting the right one.
71 CHECK(target_estimate_fetcher.FetchNext());
72 const TargetEstimate *target = target_estimate_fetcher.get();
73
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080074 // Create color image:
75 cv::Mat image_color_mat(cv::Size(image->cols(), image->rows()), CV_8UC2,
76 (void *)image->data()->data());
77 cv::Mat rgb_image(cv::Size(image->cols(), image->rows()), CV_8UC3);
78 cv::cvtColor(image_color_mat, rgb_image, cv::COLOR_YUV2BGR_YUYV);
79
80 if (!FLAGS_capture.empty()) {
81 cv::imwrite(FLAGS_capture, rgb_image);
82 return false;
83 }
84
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080085 LOG(INFO) << image->monotonic_timestamp_ns()
Henry Speisere45e7a22022-02-04 23:17:01 -080086 << ": # blobs: " << target->blob_result()->filtered_blobs()->size();
87
88 cv::Mat ret_image;
89 BlobDetector::DrawBlobs(
90 ret_image, FbsToCvBlobs(*target->blob_result()->filtered_blobs()),
91 FbsToCvBlobs(*target->blob_result()->unfiltered_blobs()),
92 FbsToBlobStats(*target->blob_result()->blob_stats()),
93 cv::Point{target->blob_result()->centroid()->x(),
94 target->blob_result()->centroid()->y()});
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080095
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -080096 cv::imshow("image", rgb_image);
97 cv::imshow("blobs", ret_image);
98
99 int keystroke = cv::waitKey(1);
100 if ((keystroke & 0xFF) == static_cast<int>('c')) {
101 // Convert again, to get clean image
102 cv::cvtColor(image_color_mat, rgb_image, cv::COLOR_YUV2BGR_YUYV);
103 std::stringstream name;
104 name << "capture-" << aos::realtime_clock::now() << ".png";
105 cv::imwrite(name.str(), rgb_image);
106 LOG(INFO) << "Saved image file: " << name.str();
107 } else if ((keystroke & 0xFF) == static_cast<int>('q')) {
108 return false;
109 }
110 return true;
111}
112
113void ViewerMain() {
114 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
115 aos::configuration::ReadConfig(FLAGS_config);
116
117 aos::ShmEventLoop event_loop(&config.message());
118
119 image_fetcher =
120 event_loop.MakeFetcher<frc971::vision::CameraImage>(FLAGS_channel);
121
122 // Run the display loop
123 event_loop.AddPhasedLoop(
124 [&event_loop](int) {
125 if (!DisplayLoop()) {
126 LOG(INFO) << "Calling event_loop Exit";
127 event_loop.Exit();
128 };
129 },
130 ::std::chrono::milliseconds(100));
131
132 event_loop.Run();
133
134 image_fetcher = aos::Fetcher<frc971::vision::CameraImage>();
135}
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -0800136} // namespace
137} // namespace vision
138} // namespace y2022
139
140// Quick and lightweight viewer for images
141int main(int argc, char **argv) {
142 aos::InitGoogle(&argc, &argv);
Henry Speisere45e7a22022-02-04 23:17:01 -0800143 y2022::vision::ViewerMain();
Jim Ostrowskiff0f5e42022-01-22 01:35:31 -0800144}