blob: 3e29085d455a5cc6849dbd7c538489855d13221a [file] [log] [blame]
Parker Schuh2cd173d2017-01-28 00:12:01 -08001#include <stdio.h>
2#include <stdlib.h>
Parker Schuh24ee58d2017-03-11 16:13:23 -08003#include <sys/stat.h>
Parker Schuh2cd173d2017-01-28 00:12:01 -08004#include <fstream>
5#include <iostream>
6#include <memory>
7#include <thread>
8#include <vector>
9
John Park33858a32018-09-28 23:05:48 -070010#include "aos/logging/implementations.h"
11#include "aos/logging/logging.h"
12#include "aos/time/time.h"
Parker Schuh2cd173d2017-01-28 00:12:01 -080013#include "aos/vision/events/socket_types.h"
14#include "aos/vision/events/udp.h"
15#include "aos/vision/image/image_stream.h"
16#include "aos/vision/image/jpeg_routines.h"
17#include "aos/vision/image/reader.h"
18#include "y2016/vision/blob_filters.h"
19#include "y2016/vision/stereo_geometry.h"
20#include "y2016/vision/vision_data.pb.h"
21
22namespace y2016 {
23namespace vision {
24using aos::vision::ImageStreamEvent;
25using aos::vision::DataRef;
26using aos::events::TCPServer;
27using aos::vision::BlobLRef;
28using aos::vision::Vector;
29using aos::vision::Int32Codec;
30using aos::vision::BlobList;
31using aos::vision::RangeImage;
32using aos::vision::PixelRef;
33using aos::vision::ImageValue;
34using aos::vision::HistogramBlobFilter;
35using aos::vision::CornerFinder;
36using aos::vision::Int64Codec;
37using aos::events::TXUdpSocket;
38using aos::events::DataSocket;
39using aos::vision::ImageFormat;
40
Parker Schuh24ee58d2017-03-11 16:13:23 -080041::aos::vision::CameraParams GetCameraParams(const Calibration &calibration) {
42 ::aos::vision::CameraParams params;
43 params.set_width(calibration.camera_image_width());
44 params.set_height(calibration.camera_image_height());
45 params.set_exposure(calibration.camera_exposure());
46 params.set_brightness(calibration.camera_brightness());
47 params.set_gain(calibration.camera_gain());
48 params.set_fps(calibration.camera_fps());
49 return params;
Parker Schuh2cd173d2017-01-28 00:12:01 -080050}
51
52int64_t Nanos(aos::monotonic_clock::duration time_diff) {
53 return std::chrono::duration_cast<std::chrono::nanoseconds>(time_diff)
54 .count();
55}
56
57int64_t NowNanos() {
58 return Nanos(aos::monotonic_clock::now().time_since_epoch());
59}
60
61inline bool FileExist(const std::string &name) {
62 struct stat buffer;
63 return (stat(name.c_str(), &buffer) == 0);
64}
65
66class ImageSender : public ImageStreamEvent {
67 public:
Parker Schuh24ee58d2017-03-11 16:13:23 -080068 ImageSender(int camera_index, aos::vision::CameraParams params,
Parker Schuh2cd173d2017-01-28 00:12:01 -080069 const std::string &fname, const std::string &ipadder, int port)
70 : ImageStreamEvent(fname, params),
71 camera_index_(camera_index),
72 udp_serv_(ipadder, 8080),
73 tcp_serv_(port),
Parker Schuh24ee58d2017-03-11 16:13:23 -080074 blob_filt_(ImageFormat(params.width(), params.height()), 40, 750,
75 250000),
Parker Schuh2cd173d2017-01-28 00:12:01 -080076 finder_(0.25, 35) {
77 int index = 0;
78 while (true) {
79 std::string file = "./logging/blob_record_" + std::to_string(index) +
80 "_" + std::to_string(camera_index_) + ".dat";
81 if (FileExist(file)) {
82 index++;
83 } else {
84 printf("Logging to file (%s)\n", file.c_str());
85 ofst_.open(file);
86 assert(ofst_.is_open());
87 break;
88 }
89 }
90 }
91
92 ~ImageSender() { ofst_.close(); }
93
94 void ProcessImage(DataRef data, aos::monotonic_clock::time_point tp) {
95 int64_t timestamp = std::chrono::duration_cast<std::chrono::nanoseconds>(
96 tp.time_since_epoch())
97 .count();
98 DecodeJpeg(data, &image_);
99 auto fmt = image_.fmt();
100
Brian Silverman37b15b32019-03-10 13:30:18 -0700101 RangeImage rimg = ThresholdImageWithFunction(
102 image_.get(), [](PixelRef px) { return (px.g > 88); });
Parker Schuh2cd173d2017-01-28 00:12:01 -0800103
104 // flip the right image as this camera is mount backward
105 if (camera_index_ == 0) {
106 rimg.Flip(fmt.w, fmt.h);
107 }
108
109 BlobList blobl = aos::vision::FindBlobs(rimg);
110 auto whatever = blob_filt_.FilterBlobs(blobl);
111
112 VisionData target;
113 target.set_camera_index(camera_index_);
114 target.set_image_timestamp(timestamp);
115
116 if (!whatever.empty()) {
117 std::vector<std::pair<Vector<2>, Vector<2>>> corners =
118 finder_.Find(whatever);
119
120 if (!corners.empty()) {
121 for (int i = 0; i < (int)corners.size(); i++) {
122 Target *pos = target.add_target();
123 pos->set_left_corner_x(corners[i].first.x());
124 pos->set_left_corner_y(corners[i].first.y());
125 pos->set_right_corner_x(corners[i].second.x());
126 pos->set_right_corner_y(corners[i].second.y());
127 }
128 }
129 }
130 target.set_send_timestamp(NowNanos());
131
132 // always send data
133 std::string dat;
134 if (target.SerializeToString(&dat)) {
135 if (print_once_) {
136 printf("Beginning data streaming camera %d...\n", camera_index_);
137 print_once_ = false;
138 }
139
140 // Send only target over udp.
141 udp_serv_.Send(dat.data(), dat.size());
142 }
143
144 // Write blob to file for logging.
145 int blob_size = CalculateSize(blobl);
146 int tmp_size = blob_size + sizeof(int32_t) + sizeof(uint64_t);
147 char *buf;
148 blob_data_.resize(tmp_size, 0);
149 {
150 buf = Int32Codec::Write(&blob_data_[0], tmp_size);
151 buf = Int64Codec::Write(buf, timestamp);
152 SerializeBlob(blobl, buf);
153 }
154 ofst_.write(&blob_data_[0], tmp_size);
155
156 // Add blob debug
157 bool debug = true;
158 if (debug) {
159 target.set_raw(buf, blob_size);
160 if (target.SerializeToString(&dat)) {
161 tcp_serv_.Broadcast([&](DataSocket *client) { client->Emit(dat); });
162 }
163 }
164
165 bool timing = false;
166 if (timing) {
167 if (n_time > 0) {
168 auto now = aos::monotonic_clock::now();
169 printf("%g, %g\n",
170 (((double)Nanos(now - tstart)) / (double)(n_time)) / 1e6,
171 (double)Nanos(now - tp) / 1e6);
172 } else {
173 tstart = aos::monotonic_clock::now();
174 }
175 ++n_time;
176 }
177 }
178
179 TCPServer<DataSocket> *GetTCPServ() { return &tcp_serv_; }
180
181 // print when we start
182 bool print_once_ = true;
183
184 // left or right camera
185 int camera_index_;
186
187 // udp socket on which to send to robot
188 TXUdpSocket udp_serv_;
189
190 // tcp socket on which to debug to laptop
191 TCPServer<DataSocket> tcp_serv_;
192
193 // our blob processing object
194 HistogramBlobFilter blob_filt_;
195
196 // corner finder to align aiming
197 CornerFinder finder_;
198
199 ImageValue image_;
200 std::string blob_data_;
201 std::ofstream ofst_;
202 aos::monotonic_clock::time_point tstart;
203 int n_time = 0;
204
205 private:
206};
207
Parker Schuh24ee58d2017-03-11 16:13:23 -0800208void RunCamera(int camera_index, aos::vision::CameraParams params,
Parker Schuh2cd173d2017-01-28 00:12:01 -0800209 const std::string &device, const std::string &ip_addr,
210 int port) {
Parker Schuh24ee58d2017-03-11 16:13:23 -0800211 printf("Creating camera %d (%d, %d).\n", camera_index, params.width(),
212 params.height());
Parker Schuh2cd173d2017-01-28 00:12:01 -0800213 ImageSender strm(camera_index, params, device, ip_addr, port);
214
215 aos::events::EpollLoop loop;
216 loop.Add(strm.GetTCPServ());
217 loop.Add(&strm);
218 printf("Running Camera (%d)\n", camera_index);
219 loop.Run();
220}
221
222} // namespace vision
223} // namespace y2016
224
225int main(int, char **) {
226 using namespace y2016::vision;
227 StereoGeometry stereo("./stereo_rig.calib");
228 ::aos::logging::Init();
229 ::aos::logging::AddImplementation(
230 new ::aos::logging::StreamLogImplementation(stdout));
231 std::thread cam0([stereo]() {
232 RunCamera(0, GetCameraParams(stereo.calibration()),
233 stereo.calibration().right_camera_name(),
Parker Schuhef47dbf2017-03-04 16:59:30 -0800234 stereo.calibration().roborio_ip_addr(), 8080);
Parker Schuh2cd173d2017-01-28 00:12:01 -0800235 });
236 std::thread cam1([stereo]() {
237 RunCamera(1, GetCameraParams(stereo.calibration()),
238 stereo.calibration().left_camera_name(),
Parker Schuhef47dbf2017-03-04 16:59:30 -0800239 stereo.calibration().roborio_ip_addr(), 8080);
Parker Schuh2cd173d2017-01-28 00:12:01 -0800240 });
241 cam0.join();
242 cam1.join();
243
244 return EXIT_SUCCESS;
245}