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