blob: ba7abaf4a4ff14da68e43f5ecc5d4f8b2dc0ce76 [file] [log] [blame]
Jim Ostrowskiff7b3de2022-01-22 22:20:26 -08001#include "y2022/vision/camera_reader.h"
2
Henry Speiser3069ca92022-02-05 16:25:00 -08003#include <chrono>
Milind Upadhyay25610d22022-02-07 15:35:26 -08004#include <cmath>
Henry Speiser3069ca92022-02-05 16:25:00 -08005#include <thread>
Jim Ostrowskiff7b3de2022-01-22 22:20:26 -08006
Philipp Schrader790cb542023-07-05 21:06:52 -07007#include "opencv2/imgproc.hpp"
8
Jim Ostrowskiff7b3de2022-01-22 22:20:26 -08009#include "aos/events/event_loop.h"
Henry Speiser1f34eea2022-01-30 14:35:21 -080010#include "aos/events/shm_event_loop.h"
Jim Ostrowskiff7b3de2022-01-22 22:20:26 -080011#include "aos/flatbuffer_merge.h"
12#include "aos/network/team_number.h"
milind-u2f101fc2023-01-21 12:28:49 -080013#include "frc971/vision/calibration_generated.h"
Jim Ostrowskiff7b3de2022-01-22 22:20:26 -080014#include "frc971/vision/v4l2_reader.h"
15#include "frc971/vision/vision_generated.h"
milind-u92195982022-01-22 20:29:31 -080016#include "y2022/vision/blob_detector.h"
Jim Ostrowskiff7b3de2022-01-22 22:20:26 -080017
Henry Speiser1f34eea2022-01-30 14:35:21 -080018DEFINE_string(image_png, "", "A set of PNG images");
19
Jim Ostrowskiff7b3de2022-01-22 22:20:26 -080020namespace y2022 {
21namespace vision {
22
23using namespace frc971::vision;
24
Milind Upadhyaya31f0272022-04-03 13:55:22 -070025const calibration::CameraCalibration *CameraReader::FindCameraCalibration(
26 const calibration::CalibrationData *calibration_data,
27 std::string_view node_name, int team_number) {
Jim Ostrowski007e2ea2022-01-30 13:13:26 -080028 for (const calibration::CameraCalibration *candidate :
Milind Upadhyaya31f0272022-04-03 13:55:22 -070029 *calibration_data->camera_calibrations()) {
Jim Ostrowskiff7b3de2022-01-22 22:20:26 -080030 if (candidate->node_name()->string_view() != node_name) {
31 continue;
32 }
33 if (candidate->team_number() != team_number) {
34 continue;
35 }
36 return candidate;
37 }
38 LOG(FATAL) << ": Failed to find camera calibration for " << node_name
39 << " on " << team_number;
40}
41
Henry Speisere45e7a22022-02-04 23:17:01 -080042namespace {
Milind Upadhyayf61e1482022-02-11 20:42:55 -080043flatbuffers::Offset<flatbuffers::Vector<const Point *>> CvPointsToFbs(
44 const std::vector<cv::Point> &points,
45 aos::Sender<TargetEstimate>::Builder *builder) {
46 std::vector<Point> points_fbs;
47 for (auto p : points) {
48 points_fbs.push_back(Point{p.x, p.y});
49 }
50 return builder->fbb()->CreateVectorOfStructs(points_fbs);
51}
52
James Kuszmauld230d7a2022-03-06 15:00:43 -080053constexpr size_t kMaxBlobsForDebug = 100;
54
Henry Speisere45e7a22022-02-04 23:17:01 -080055// Converts a vector of cv::Point to PointT for the flatbuffer
56flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Blob>>>
57CvBlobsToFbs(const std::vector<std::vector<cv::Point>> &blobs,
Milind Upadhyayf61e1482022-02-11 20:42:55 -080058 aos::Sender<TargetEstimate>::Builder *builder) {
Henry Speisere45e7a22022-02-04 23:17:01 -080059 std::vector<flatbuffers::Offset<Blob>> blobs_fbs;
60 for (auto &blob : blobs) {
Milind Upadhyayf61e1482022-02-11 20:42:55 -080061 const auto points_offset = CvPointsToFbs(blob, builder);
62 auto blob_builder = builder->MakeBuilder<Blob>();
Henry Speisere45e7a22022-02-04 23:17:01 -080063 blob_builder.add_points(points_offset);
64 blobs_fbs.emplace_back(blob_builder.Finish());
James Kuszmauld230d7a2022-03-06 15:00:43 -080065 if (blobs_fbs.size() == kMaxBlobsForDebug) {
66 break;
67 }
Henry Speisere45e7a22022-02-04 23:17:01 -080068 }
Milind Upadhyayf61e1482022-02-11 20:42:55 -080069 return builder->fbb()->CreateVector(blobs_fbs.data(), blobs_fbs.size());
Henry Speisere45e7a22022-02-04 23:17:01 -080070}
71
72flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BlobStatsFbs>>>
73BlobStatsToFbs(const std::vector<BlobDetector::BlobStats> blob_stats,
Milind Upadhyayf61e1482022-02-11 20:42:55 -080074 aos::Sender<TargetEstimate>::Builder *builder) {
Henry Speisere45e7a22022-02-04 23:17:01 -080075 std::vector<flatbuffers::Offset<BlobStatsFbs>> stats_fbs;
76 for (auto &stats : blob_stats) {
77 // Make BlobStatsFbs builder then fill each field using the BlobStats
78 // struct, then you finish it and add it to stats_fbs.
Milind Upadhyayf61e1482022-02-11 20:42:55 -080079 auto stats_builder = builder->MakeBuilder<BlobStatsFbs>();
Henry Speisere45e7a22022-02-04 23:17:01 -080080 Point centroid_fbs = Point{stats.centroid.x, stats.centroid.y};
81 stats_builder.add_centroid(&centroid_fbs);
82 stats_builder.add_aspect_ratio(stats.aspect_ratio);
83 stats_builder.add_area(stats.area);
84 stats_builder.add_num_points(stats.num_points);
85
86 auto current_stats = stats_builder.Finish();
87 stats_fbs.emplace_back(current_stats);
88 }
Milind Upadhyayf61e1482022-02-11 20:42:55 -080089 return builder->fbb()->CreateVector(stats_fbs.data(), stats_fbs.size());
Henry Speisere45e7a22022-02-04 23:17:01 -080090}
91} // namespace
92
Milind Upadhyay3c1a5c02022-03-27 16:27:19 -070093void CameraReader::ProcessImage(cv::Mat image_mat_distorted,
Jim Ostrowski210765a2022-02-27 12:52:14 -080094 int64_t image_monotonic_timestamp_ns) {
Milind Upadhyaya31f0272022-04-03 13:55:22 -070095 cv::Mat image_mat = UndistortImage(image_mat_distorted, undistort_maps_);
Milind Upadhyay3c1a5c02022-03-27 16:27:19 -070096
Milind Upadhyay25610d22022-02-07 15:35:26 -080097 BlobDetector::BlobResult blob_result;
Milind Upadhyay25610d22022-02-07 15:35:26 -080098 BlobDetector::ExtractBlobs(image_mat, &blob_result);
milind-u92195982022-01-22 20:29:31 -080099 auto builder = target_estimate_sender_.MakeBuilder();
Milind Upadhyay25610d22022-02-07 15:35:26 -0800100 flatbuffers::Offset<BlobResultFbs> blob_result_offset;
Henry Speisere45e7a22022-02-04 23:17:01 -0800101 {
Milind Upadhyay25610d22022-02-07 15:35:26 -0800102 const auto filtered_blobs_offset =
Milind Upadhyayf61e1482022-02-11 20:42:55 -0800103 CvBlobsToFbs(blob_result.filtered_blobs, &builder);
Henry Speisere45e7a22022-02-04 23:17:01 -0800104 const auto unfiltered_blobs_offset =
Milind Upadhyayf61e1482022-02-11 20:42:55 -0800105 CvBlobsToFbs(blob_result.unfiltered_blobs, &builder);
Milind Upadhyay25610d22022-02-07 15:35:26 -0800106 const auto blob_stats_offset =
Milind Upadhyayf61e1482022-02-11 20:42:55 -0800107 BlobStatsToFbs(blob_result.blob_stats, &builder);
Milind Upadhyay8f38ad82022-03-03 10:06:18 -0800108 const auto filtered_stats_offset =
109 BlobStatsToFbs(blob_result.filtered_stats, &builder);
Milind Upadhyay25610d22022-02-07 15:35:26 -0800110 const Point centroid_fbs =
111 Point{blob_result.centroid.x, blob_result.centroid.y};
Henry Speisere45e7a22022-02-04 23:17:01 -0800112
Milind Upadhyay25610d22022-02-07 15:35:26 -0800113 auto blob_result_builder = builder.MakeBuilder<BlobResultFbs>();
Henry Speisere45e7a22022-02-04 23:17:01 -0800114 blob_result_builder.add_filtered_blobs(filtered_blobs_offset);
115 blob_result_builder.add_unfiltered_blobs(unfiltered_blobs_offset);
116 blob_result_builder.add_blob_stats(blob_stats_offset);
Milind Upadhyay8f38ad82022-03-03 10:06:18 -0800117 blob_result_builder.add_filtered_stats(filtered_stats_offset);
Henry Speisere45e7a22022-02-04 23:17:01 -0800118 blob_result_builder.add_centroid(&centroid_fbs);
119 blob_result_offset = blob_result_builder.Finish();
120 }
121
Milind Upadhyay8f38ad82022-03-03 10:06:18 -0800122 target_estimator_.Solve(blob_result.filtered_stats, std::nullopt);
Milind Upadhyayf61e1482022-02-11 20:42:55 -0800123
Milind Upadhyaye2f40d72022-02-24 13:55:53 -0800124 const auto camera_calibration_offset =
125 aos::RecursiveCopyFlatBuffer(camera_calibration_, builder.fbb());
126
Milind Upadhyayf61e1482022-02-11 20:42:55 -0800127 const auto rotation =
128 Rotation{target_estimator_.roll(), target_estimator_.pitch(),
129 target_estimator_.yaw()};
130
Henry Speisere45e7a22022-02-04 23:17:01 -0800131 auto target_estimate_builder = builder.MakeBuilder<TargetEstimate>();
Milind Upadhyayf61e1482022-02-11 20:42:55 -0800132
133 target_estimate_builder.add_distance(target_estimator_.distance());
134 target_estimate_builder.add_angle_to_target(
135 target_estimator_.angle_to_target());
136 target_estimate_builder.add_angle_to_camera(
137 target_estimator_.angle_to_camera());
138 target_estimate_builder.add_rotation_camera_hub(&rotation);
Milind Upadhyay14279de2022-02-26 16:07:53 -0800139 target_estimate_builder.add_confidence(target_estimator_.confidence());
Henry Speisere45e7a22022-02-04 23:17:01 -0800140 target_estimate_builder.add_blob_result(blob_result_offset);
Milind Upadhyaye2f40d72022-02-24 13:55:53 -0800141 target_estimate_builder.add_camera_calibration(camera_calibration_offset);
Jim Ostrowski210765a2022-02-27 12:52:14 -0800142 target_estimate_builder.add_image_monotonic_timestamp_ns(
143 image_monotonic_timestamp_ns);
Henry Speisere45e7a22022-02-04 23:17:01 -0800144 builder.CheckOk(builder.Send(target_estimate_builder.Finish()));
Jim Ostrowskiff7b3de2022-01-22 22:20:26 -0800145}
146
147void CameraReader::ReadImage() {
Henry Speiser1f34eea2022-01-30 14:35:21 -0800148 // Path is for reading from the Disk.
149 if (FLAGS_image_png != "") {
150 std::vector<cv::String> file_list;
151 cv::glob(FLAGS_image_png + "/*.png", file_list, false);
Henry Speiser1f34eea2022-01-30 14:35:21 -0800152 for (auto file : file_list) {
Henry Speiser3069ca92022-02-05 16:25:00 -0800153 // Sleep for 0.05 seconds in order to not reach the max number of messages
154 // that can be sent in a second.
155 std::this_thread::sleep_for(std::chrono::milliseconds(50));
Henry Speiser1f34eea2022-01-30 14:35:21 -0800156 LOG(INFO) << "Reading file " << file;
Milind Upadhyayec41e132022-02-05 17:14:05 -0800157 cv::Mat bgr_image = cv::imread(file.c_str());
Jim Ostrowskia045aee2022-02-27 15:08:22 -0800158 cv::Mat image_color_mat;
159 cv::cvtColor(bgr_image, image_color_mat, cv::COLOR_BGR2YUV);
160
161 // Convert YUV (3 channels) to YUYV (stacked format)
162 std::vector<uint8_t> yuyv;
163 for (int i = 0; i < image_color_mat.rows; i++) {
164 for (int j = 0; j < image_color_mat.cols; j++) {
165 // Always push a Y value
166 yuyv.emplace_back(image_color_mat.at<cv::Vec3b>(i, j)[0]);
167 if ((j % 2) == 0) {
168 // If column # is even, push a U value.
169 yuyv.emplace_back(image_color_mat.at<cv::Vec3b>(i, j)[1]);
170 } else {
171 // If column # is odd, push a V value.
172 yuyv.emplace_back(image_color_mat.at<cv::Vec3b>(i, j)[2]);
173 }
174 }
175 }
176
177 CHECK_EQ(static_cast<int>(yuyv.size()),
178 image_color_mat.rows * image_color_mat.cols * 2);
179
180 auto builder = image_sender_.MakeBuilder();
181 auto image_offset = builder.fbb()->CreateVector(yuyv);
182 auto image_builder = builder.MakeBuilder<CameraImage>();
183
Jim Ostrowski210765a2022-02-27 12:52:14 -0800184 int64_t timestamp =
185 aos::monotonic_clock::now().time_since_epoch().count();
Jim Ostrowskia045aee2022-02-27 15:08:22 -0800186
187 image_builder.add_rows(image_color_mat.rows);
188 image_builder.add_cols(image_color_mat.cols);
189 image_builder.add_data(image_offset);
190 image_builder.add_monotonic_timestamp_ns(timestamp);
191
Jim Ostrowski210765a2022-02-27 12:52:14 -0800192 ProcessImage(bgr_image, timestamp);
Jim Ostrowskia045aee2022-02-27 15:08:22 -0800193 builder.CheckOk(builder.Send(image_builder.Finish()));
Henry Speiser1f34eea2022-01-30 14:35:21 -0800194 }
195 event_loop_->Exit();
196 return;
197 }
198 // If we are not reading from the disk, we read the live camera stream.
Jim Ostrowskiff7b3de2022-01-22 22:20:26 -0800199 if (!reader_->ReadLatestImage()) {
200 read_image_timer_->Setup(event_loop_->monotonic_now() +
201 std::chrono::milliseconds(10));
202 return;
203 }
204
Henry Speiser1f34eea2022-01-30 14:35:21 -0800205 const CameraImage &image = reader_->LatestImage();
Henry Speiser1f34eea2022-01-30 14:35:21 -0800206
Jim Ostrowski210765a2022-02-27 12:52:14 -0800207 cv::Mat image_color_mat(cv::Size(image.cols(), image.rows()), CV_8UC2,
208 (void *)image.data()->data());
209 cv::Mat image_mat(cv::Size(image.cols(), image.rows()), CV_8UC3);
210 cv::cvtColor(image_color_mat, image_mat, cv::COLOR_YUV2BGR_YUYV);
Henry Speiser1f34eea2022-01-30 14:35:21 -0800211
Jim Ostrowski210765a2022-02-27 12:52:14 -0800212 ProcessImage(image_mat, image.monotonic_timestamp_ns());
Jim Ostrowskiff7b3de2022-01-22 22:20:26 -0800213
214 reader_->SendLatestImage();
215 read_image_timer_->Setup(event_loop_->monotonic_now());
Milind Upadhyayd67e9cf2022-03-13 13:56:57 -0700216
217 // Disable the LEDs based on localizer output
218 if (localizer_output_fetcher_.Fetch()) {
219 const auto node_name = event_loop_->node()->name()->string_view();
220 const size_t pi_number =
Milind Upadhyay0fd96542022-03-13 21:24:45 -0700221 std::atol(node_name.substr(node_name.size() - 1).data()) - 1;
Milind Upadhyayd67e9cf2022-03-13 13:56:57 -0700222
223 CHECK(localizer_output_fetcher_->has_led_outputs() &&
224 localizer_output_fetcher_->led_outputs()->size() > pi_number);
225
226 const LedOutput led_output =
227 localizer_output_fetcher_->led_outputs()->Get(pi_number);
228
229 if (led_output != prev_led_output_) {
230 gpio_disable_control_.GPIOWrite(led_output == LedOutput::OFF ? kGPIOHigh
231 : kGPIOLow);
232
233 prev_led_output_ = led_output;
234 }
235 }
Jim Ostrowskiff7b3de2022-01-22 22:20:26 -0800236}
237
238} // namespace vision
239} // namespace y2022