blob: 1e6e0494829175eb6be54b0ea2fbc5878ef70eb4 [file] [log] [blame]
Parker Schuh24ee58d2017-03-11 16:13:23 -08001#include <sys/stat.h>
Tyler Chatowbf0609c2021-07-31 16:13:27 -07002
3#include <cstdio>
4#include <cstdlib>
Parker Schuhd497ed62017-03-04 20:11:58 -08005#include <fstream>
6#include <iostream>
7#include <memory>
8#include <thread>
9#include <vector>
10
Philipp Schrader790cb542023-07-05 21:06:52 -070011#include <google/protobuf/io/zero_copy_stream_impl.h>
12#include <google/protobuf/text_format.h>
13
John Park33858a32018-09-28 23:05:48 -070014#include "aos/logging/implementations.h"
15#include "aos/logging/logging.h"
16#include "aos/time/time.h"
Parker Schuhd497ed62017-03-04 20:11:58 -080017#include "aos/vision/blob/codec.h"
18#include "aos/vision/blob/find_blob.h"
19#include "aos/vision/blob/range_image.h"
20#include "aos/vision/blob/threshold.h"
21#include "aos/vision/events/socket_types.h"
22#include "aos/vision/events/udp.h"
23#include "aos/vision/image/image_stream.h"
24#include "aos/vision/image/jpeg_routines.h"
25#include "aos/vision/image/reader.h"
26#include "y2017/vision/target_finder.h"
27#include "y2017/vision/vision_config.pb.h"
28#include "y2017/vision/vision_result.pb.h"
29
Stephan Pleinesf63bde82024-01-13 15:59:33 -080030namespace y2017::vision {
Parker Schuhd497ed62017-03-04 20:11:58 -080031
Parker Schuhd497ed62017-03-04 20:11:58 -080032using aos::events::DataSocket;
Tyler Chatowbf0609c2021-07-31 16:13:27 -070033using aos::events::TCPServer;
34using aos::events::TXUdpSocket;
35using aos::vision::DataRef;
Parker Schuhd497ed62017-03-04 20:11:58 -080036using aos::vision::ImageFormat;
37using aos::vision::ImageStreamEvent;
Tyler Chatowbf0609c2021-07-31 16:13:27 -070038using aos::vision::ImageValue;
39using aos::vision::Int32Codec;
40using aos::vision::Int64Codec;
Parker Schuhd497ed62017-03-04 20:11:58 -080041
42int64_t Nanos(aos::monotonic_clock::duration time_diff) {
43 return std::chrono::duration_cast<std::chrono::nanoseconds>(time_diff)
44 .count();
45}
46
47int64_t NowNanos() {
48 return Nanos(aos::monotonic_clock::now().time_since_epoch());
49}
50
51inline bool FileExist(const std::string &name) {
52 struct stat buffer;
53 return (stat(name.c_str(), &buffer) == 0);
54}
55
56class BlobLog {
57 public:
58 explicit BlobLog(const char *prefix, const char *extension) {
59 int index = 0;
60 while (true) {
61 std::string file = prefix + std::to_string(index) + extension;
62 if (FileExist(file)) {
63 index++;
64 } else {
65 printf("Logging to file (%s)\n", file.c_str());
66 ofst_.open(file);
67 assert(ofst_.is_open());
68 break;
69 }
70 }
71 }
72
73 ~BlobLog() { ofst_.close(); }
74
75 void WriteLogEntry(DataRef data) { ofst_.write(&data[0], data.size()); }
76
77 private:
78 std::ofstream ofst_;
79};
80
Parker Schuhd497ed62017-03-04 20:11:58 -080081class ImageSender : public ImageStreamEvent {
82 public:
Parker Schuh24ee58d2017-03-11 16:13:23 -080083 ImageSender(aos::vision::CameraParams params, GameSpecific game_cfg,
Parker Schuhd497ed62017-03-04 20:11:58 -080084 const std::string &fname, const std::string &ipadder, int port)
85 : ImageStreamEvent(fname, params),
86 game_cfg_(game_cfg),
87 udp_serv_(ipadder, 8080),
88 tcp_serv_(port),
89 log_("./logging/blob_record_", ".dat") {}
90
91 void WriteAndSendBlob(ImageFormat fmt, int64_t timestamp,
92 aos::vision::BlobList blobl) {
93 // Write blob to file for logging.
94 int blob_size = CalculateSize(blobl);
95 int tmp_size = blob_size + sizeof(int32_t) * 3 + sizeof(uint64_t);
96 char *buf;
97 std::string blob_data;
98 blob_data.resize(tmp_size, 0);
99 {
100 buf = Int32Codec::Write(&blob_data[0], tmp_size);
101 buf = Int64Codec::Write(buf, timestamp);
102 buf = Int32Codec::Write(buf, fmt.w);
103 buf = Int32Codec::Write(buf, fmt.h);
104 SerializeBlob(blobl, buf);
105 }
106 log_.WriteLogEntry(blob_data);
107
108 // Send the blob back to the debug-viewer
109 tcp_serv_.Broadcast([&](DataSocket *client) { client->Emit(blob_data); });
110 }
111
112 void ProcessImage(DataRef data, aos::monotonic_clock::time_point tp) {
113 using namespace aos::vision;
114 int64_t timestamp = std::chrono::duration_cast<std::chrono::nanoseconds>(
115 tp.time_since_epoch())
116 .count();
117 DecodeJpeg(data, &image_);
118 ImageFormat fmt = image_.fmt();
119
120 RangeImage rimg = finder_.Threshold(image_.get());
121 BlobList blobl = aos::vision::FindBlobs(rimg);
122
123 // Remove bad blobs before we log.
124 finder_.PreFilter(blobl);
125
126 // Write to the logi and stream to the debug-viewer.
127 WriteAndSendBlob(fmt, timestamp, blobl);
128
129 // Calculate each component.
130 std::vector<TargetComponent> target_component_list =
131 finder_.FillTargetComponentList(blobl);
132
133 // Put the compenents together into targets and pick the best.
134 y2017::vision::Target final_target;
135 bool found_target =
136 finder_.FindTargetFromComponents(target_component_list, &final_target);
137
138 std::string dat;
139 VisionResult result;
140 result.set_image_timestamp(timestamp);
141 result.set_send_timestamp(NowNanos());
142 if (found_target) {
143 result.mutable_target()->set_x(final_target.screen_coord.x());
144 result.mutable_target()->set_y(final_target.screen_coord.y());
145 }
146 // always send data
147 if (result.SerializeToString(&dat)) {
148 if (print_once_) {
149 printf("Beginning data streaming camera...\n");
150 print_once_ = false;
151 }
152
153 // Send only target over udp.
154 udp_serv_.Send(dat.data(), dat.size());
155 }
156 }
157
158 TCPServer<DataSocket> *GetTCPServ() { return &tcp_serv_; }
159
160 // Configuration related to the game.
161 GameSpecific game_cfg_;
162
163 // target selction code.
164 TargetFinder finder_;
165
166 // print when we start
167 bool print_once_ = true;
168
169 // udp socket on which to send to robot
170 TXUdpSocket udp_serv_;
171
172 // tcp socket on which to debug to laptop
173 TCPServer<DataSocket> tcp_serv_;
174
175 ImageValue image_;
176 BlobLog log_;
177 aos::monotonic_clock::time_point tstart;
178
179 private:
180};
181
Parker Schuh24ee58d2017-03-11 16:13:23 -0800182void RunCamera(aos::vision::CameraParams settings, GameSpecific game_cfg,
Parker Schuhd497ed62017-03-04 20:11:58 -0800183 const std::string &device, const std::string &ip_addr,
184 int port) {
185 printf("Creating camera (%dx%d).\n", settings.width(), settings.height());
Parker Schuh24ee58d2017-03-11 16:13:23 -0800186 ImageSender strm(settings, game_cfg, device, ip_addr, port);
Parker Schuhd497ed62017-03-04 20:11:58 -0800187
188 aos::events::EpollLoop loop;
189 loop.Add(strm.GetTCPServ());
190 loop.Add(&strm);
191 printf("Running Camera\n");
192 loop.Run();
193}
194
195bool ReadConfiguration(const std::string &file_name, VisionConfig *cfg) {
196 using namespace google::protobuf::io;
197 using namespace google::protobuf;
198 if (cfg == nullptr) {
199 return false;
200 }
201
202 // fd will close when it goes out of scope.
203 std::ifstream fd(file_name);
204 if (!fd.is_open()) {
205 fprintf(stderr, "File (%s) not found.\n", file_name.c_str());
206 return false;
207 }
208 IstreamInputStream is(&fd);
209 if (!TextFormat::Parse(&is, cfg)) {
210 fprintf(stderr, "Unable to parse file (%s).\n", file_name.c_str());
211 return false;
212 }
213
214 return true;
215}
216
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800217} // namespace y2017::vision
Parker Schuhd497ed62017-03-04 20:11:58 -0800218
219int main(int, char **) {
220 using namespace y2017::vision;
221
Parker Schuhd497ed62017-03-04 20:11:58 -0800222 VisionConfig cfg;
223 if (ReadConfiguration("ConfigFile.pb.ascii", &cfg)) {
224 if (cfg.robot_configs().count("Laptop") != 1) {
225 fprintf(stderr, "Could not find config key.\n");
226 return -1;
227 }
228 const RobotConfig &rbt = cfg.robot_configs().at("Laptop");
229 RunCamera(cfg.camera_params(), cfg.game_params(), rbt.camera_device_path(),
230 rbt.roborio_ipaddr(), rbt.port());
231 }
232
233 return EXIT_SUCCESS;
234}