RobotsIO
Loading...
Searching...
No Matches
Camera.cpp
1/*
2 * Copyright (C) 2019 Istituto Italiano di Tecnologia (IIT)
3 *
4 * This software may be modified and distributed under the terms of the
5 * BSD 3-Clause license. See the accompanying LICENSE file for details.
6 */
7
8#include <utility>
9// #ifdef _OPENMP
10// #include <omp.h>
11// #endif
12
13#include <RobotsIO/Camera/Camera.h>
14#include <RobotsIO/Camera/CameraDeprojectionMatrix.h>
15#include <RobotsIO/Utils/FileToDepth.h>
16#include <RobotsIO/Utils/Parameters.h>
17
18#include <opencv2/core/eigen.hpp>
19
20#include <iomanip>
21#include <iostream>
22
23using namespace Eigen;
24using namespace RobotsIO::Camera;
25using namespace RobotsIO::Utils;
26
27
29{}
30
31
32Camera::~Camera()
33{}
34
35
36bool Camera::status() const
37{
38 return status_;
39}
40
41
42bool Camera::reset()
43{
44 if (is_offline())
45 frame_index_ = -1;
46
47 status_ = true;
48
49 return true;
50}
51
52
53std::pair<bool, MatrixXd> Camera::deprojection_matrix() const
54{
55 if (!deprojection_matrix_initialized_)
56 return std::make_pair(false, MatrixXd());
57
58 return std::make_pair(true, deprojection_matrix_);
59}
60
61
62std::pair<bool, CameraParameters> Camera::parameters() const
63{
64 if (!(parameters_.initialized()))
65 return std::make_pair(false, CameraParameters());
66
67 return std::make_pair(true, parameters_);
68}
69
70
71std::pair<bool, Eigen::MatrixXd> Camera::point_cloud
72(
73 const bool& blocking,
74 const double& maximum_depth,
75 const bool& use_root_frame,
76 const bool& enable_colors
77)
78{
79 /* Get rgb, if required. */
80 bool valid_rgb = false;
81 cv::Mat rgb;
82 if (enable_colors)
83 {
84 std::tie(valid_rgb, rgb) = this->rgb(blocking);
85 if (!valid_rgb)
86 return std::make_pair(false, MatrixXd());
87 }
88
89 /* Get depth. */
90 bool valid_depth = false;
91 MatrixXf depth;
92 std::tie(valid_depth, depth) = this->depth(blocking);
93 if (!valid_depth)
94 return std::make_pair(false, MatrixXd());
95
96
97 /* Get pose, if required. */
98 bool valid_pose = false;
100 if (use_root_frame)
101 {
102 std::tie(valid_pose, camera_pose) = this->pose(blocking);
103 if (!valid_pose)
104 return std::make_pair(false, MatrixXd());
105 }
106
107 /* Find 3D points having positive and less than max_depth_ depth. */
108 MatrixXi valid_points(parameters_.height(), parameters_.width());
109// #pragma omp parallel for collapse(2)
110 for (std::size_t v = 0; v < parameters_.height(); v++)
111 {
112 for (std::size_t u = 0; u < parameters_.width(); u++)
113 {
114 valid_points(v, u) = 0;
115
116 float depth_u_v = depth(v, u);
117
118 if ((depth_u_v > 0) && (depth_u_v < maximum_depth))
119 valid_points(v, u) = 1;
120 }
121 }
122 const std::size_t number_valids = valid_points.sum();
123
124 if (number_valids == 0)
125 return std::make_pair(false, MatrixXd());
126
127 /* Get deprojection matrix. */
128 bool valid_deprojection_matrix = false;
129 MatrixXd deprojection_matrix;
130 std::tie(valid_deprojection_matrix, deprojection_matrix) = this->deprojection_matrix();
131 if (!valid_deprojection_matrix)
132 return std::make_pair(false, MatrixXd());
133
134 /* Store points in the output matrix. */
135 const std::size_t number_rows = enable_colors ? 6 : 3;
136 MatrixXd cloud(number_rows, number_valids);
137 std::size_t counter = 0;
138 for (std::size_t v = 0; v < parameters_.height(); v++)
139 for (std::size_t u = 0; u < parameters_.width(); u++)
140 {
141 if (valid_points(v, u) == 1)
142 {
143 /* Set 3D point. */
144 cloud.col(counter).head<3>() = deprojection_matrix.col(u * parameters_.height() + v) * depth(v, u);
145
146 if (enable_colors)
147 {
148 /* Set RGB channels. */
149 cv::Vec3b cv_color = rgb.at<cv::Vec3b>(cv::Point2d(u, v));
150 cloud.col(counter)(3) = cv_color[2];
151 cloud.col(counter)(4) = cv_color[1];
152 cloud.col(counter)(5) = cv_color[0];
153 }
154 counter++;
155 }
156 }
157
158 /* Express taking into account the camera pose, if required. */
159 if (use_root_frame)
160 cloud.topRows<3>() = camera_pose * cloud.topRows<3>().colwise().homogeneous();
161
162 return std::make_pair(true, cloud);
163}
164
165
166std::pair<bool, double> Camera::time_stamp_rgb() const
167{
168 return std::make_pair(false, 0.0);
169}
170
171
172std::pair<bool, double> Camera::time_stamp_depth() const
173{
174 return std::make_pair(false, 0.0);
175}
176
177
178std::pair<bool, VectorXd> Camera::auxiliary_data(const bool& blocking)
179{
180 return std::make_pair(false, VectorXd());
181}
182
183
184std::size_t Camera::auxiliary_data_size() const
185{
186 return 0;
187}
188
189
190std::int32_t Camera::frame_index() const
191{
192 if (is_offline())
193 return frame_index_ + dataset_parameters_.index_offset();
194
195 return -1;
196}
197
198
199bool Camera::is_offline() const
200{
201 return offline_mode_;
202}
203
204
205bool Camera::set_frame_index(const std::int32_t& index)
206{
207 if (int(index - dataset_parameters_.index_offset()) < 0)
208 frame_index_ = -1;
209 else
210 frame_index_ = index - dataset_parameters_.index_offset();
211
212 return true;
213}
214
215
216bool Camera::step_frame()
217{
218 if (is_offline())
219 {
220 /* Probes for parameters output. */
221 if (is_probe("camera_parameters_output"))
222 get_probe("camera_parameters_output").set_data(parameters_.parameters());
223
224 if (is_probe("dataset_parameters_output"))
225 get_probe("dataset_parameters_output").set_data(dataset_parameters_.parameters());
226
227 frame_index_++;
228
229 if ((frame_index_ + 1) > number_frames_)
230 {
231 status_ = false;
232
233 return false;
234 }
235 }
236
237 return true;
238}
239
240
241bool Camera::log_frame(const bool& log_depth)
242{
243 /* Get rgb image. */
244 bool valid_rgb = false;
245 cv::Mat rgb_image;
246 std::tie(valid_rgb, rgb_image) = rgb(true);
247 if (!valid_rgb)
248 return false;
249
250 /* TODO: complete implementation. */
251 /* Get depth image. */
252 bool valid_depth = false;
253 MatrixXf depth;
254 if (log_depth)
255 {}
256
257 /* Get camera pose .*/
258 bool valid_pose = false;
260 std::tie(valid_pose, camera_pose) = pose(true);
261 if (!valid_pose)
262 return false;
263
264 /* Get auxiliary data. */
265 bool is_aux_data = false;
266 VectorXd aux_data;
267 std::tie(is_aux_data, aux_data) = auxiliary_data(true);
268
269 /* Eigen precision format .*/
270 IOFormat full_precision(FullPrecision);
271
272 /* Save frame .*/
273 AngleAxisd angle_axis(camera_pose.rotation());
274 VectorXd angle(1);
275 angle(0) = angle_axis.angle();
276
277 if (valid_rgb)
278 cv::imwrite(log_path_ + "rgb_" + std::to_string(log_index_) + "." + dataset_parameters_.rgb_format(), rgb_image);
279 if (valid_depth)
280 ;
281 log_ << log_index_ << " "
282 << camera_pose.translation().transpose().format(full_precision) << " "
283 << angle_axis.axis().transpose().format(full_precision) << " "
284 << angle.format(full_precision);
285
286 if (is_aux_data)
287 log_ << " " << aux_data.transpose().format(full_precision);
288
289 log_ << std::endl;
290
291 log_index_++;
292
293 return true;
294}
295
296
297bool Camera::start_log(const std::string& path)
298{
299 log_path_ = path;
300 if (log_path_.back() != '/')
301 log_path_ += "/";
302
303 log_.open(log_path_ + "data.txt");
304
305 log_index_ = 0;
306
307 return log_.is_open();
308}
309
310
311bool Camera::stop_log()
312{
313 log_.close();
314
315 return !log_.fail();
316}
317
318
319bool Camera::initialize()
320{
321 bool ok = true;
322
323 /* Cache the deprojection matrix once for all. */
325
326 /* If offline mode, load data from file. */
327 if (is_offline())
328 {
329 bool valid_data = false;
330 std::tie(valid_data, data_) = load_data();
331 if (!valid_data)
332 throw(std::runtime_error(log_name_ + "::initialize. Cannot load offline data from " + dataset_parameters_.path()));
333 }
334
335 return ok;
336}
337
338
340{
341 if (!parameters_.initialized())
342 throw(std::runtime_error(log_name_ + "::reset. Camera parameters not initialized. Did you initialize the class member 'parameters_' in the derived class?."));
343
344 // Evaluate deprojection matrix
345 deprojection_matrix_ = RobotsIO::Camera::deprojection_matrix(parameters_);
346
347 deprojection_matrix_initialized_ = true;
348
349 return true;
350}
351
352
354(
355 const std::string& data_path,
356 const std::size_t& width,
357 const std::size_t& height,
358 const double& fx,
359 const double& cx,
360 const double& fy,
361 const double& cy
362) :
363 offline_mode_(true)
364{
365 /* Set intrinsic parameters. */
366 parameters_.width(width);
367 parameters_.height(height);
368 parameters_.fx(fx);
369 parameters_.cx(cx);
370 parameters_.fy(fy);
371 parameters_.cy(cy);
372 parameters_.initialized(true);
373
374 /* Set dataset parameters. */
375 dataset_parameters_.path(data_path);
376
377 /* Fix data path. */
378 if (dataset_parameters_.path().back() != '/')
379 dataset_parameters_.path(dataset_parameters_.path() + '/');
380
381 /* Log parameters. */
382 std::cout << log_name_ + "::ctor. Camera parameters:" << std::endl;
383 std::cout << log_name_ + " - width: " << parameters_.width() << std::endl;
384 std::cout << log_name_ + " - height: " << parameters_.height() << std::endl;
385 std::cout << log_name_ + " - fx: " << parameters_.fx() << std::endl;
386 std::cout << log_name_ + " - fy: " << parameters_.fy() << std::endl;
387 std::cout << log_name_ + " - cx: " << parameters_.cx() << std::endl;
388 std::cout << log_name_ + " - cy: " << parameters_.cy() << std::endl;
389}
390
391
392std::pair<bool, MatrixXf> Camera::depth_offline()
393{
394 if (!status())
395 return std::make_pair(false, MatrixXf());
396
397 const std::string file_name = dataset_parameters_.path() + dataset_parameters_.depth_prefix() + compose_index(frame_index() + depth_offset_) + "." + dataset_parameters_.depth_format();
398
399 MatrixXf float_image;
400 bool valid_image = false;
401 std::tie(valid_image, float_image) = file_to_depth(file_name);
402 if (!valid_image)
403 return std::make_pair(false, MatrixXf());
404
405 /* Resize image. */
406 MatrixXf depth;
407 bool is_resize = false;
408 if ((parameters_.width() != 0) && (parameters_.height() != 0))
409 {
410 if ((float_image.cols() > parameters_.width()) && (float_image.rows() > parameters_.height()))
411 {
412 if ((float_image.cols() % parameters_.width() == 0) && ((float_image.rows() % parameters_.height() == 0)))
413 {
414 std::size_t ratio = float_image.cols() / parameters_.width();
415 if (ratio == (float_image.rows() / parameters_.height()))
416 {
417 depth.resize(parameters_.height(), parameters_.width());
418 for (std::size_t i = 0; i < float_image.rows(); i += ratio)
419 for (std::size_t j = 0; j < float_image.cols(); j += ratio)
420 depth(i / ratio, j / ratio) = float_image(i, j);
421
422 is_resize = true;
423 }
424 }
425 }
426 }
427
428 if (!is_resize)
429 depth = float_image;
430
431 /* Probe for depth output. */
432 cv::Mat depth_cv;
433 cv::eigen2cv(depth, depth_cv);
434 if (is_probe("depth_output"))
435 get_probe("depth_output").set_data(depth_cv);
436
437 return std::make_pair(true, depth);
438}
439
440
441std::pair<bool, Transform<double, 3, Affine>> Camera::pose_offline()
442{
443 if (!status())
444 return std::make_pair(false, Transform<double, 3, Affine>());
445
446 if (dataset_parameters_.data_available())
447 {
448 VectorXd data = data_.col(frame_index_);
449
450 Vector3d position = data.segment<3>(2);
451 VectorXd axis_angle = data.segment<4>(2 + 3);
452 AngleAxisd angle_axis(axis_angle(3), axis_angle.head<3>());
453
455 pose = Translation<double, 3>(position);
456 pose.rotate(angle_axis);
457
458 /* Probe for pose output. */
459 if (is_probe("pose_output"))
460 get_probe("pose_output").set_data(pose);
461
462 return std::make_pair(true, pose);
463 }
464
465 return std::make_pair(true, Transform<double, 3, Affine>::Identity());
466}
467
468
469std::pair<bool, cv::Mat> Camera::rgb_offline()
470{
471 if (!status())
472 return std::make_pair(false, cv::Mat());
473
474 const std::string file_name = dataset_parameters_.path() + dataset_parameters_.rgb_prefix() + compose_index(frame_index() + rgb_offset_) + "." + dataset_parameters_.rgb_format();
475 cv::Mat image = cv::imread(file_name, cv::IMREAD_COLOR);
476
477 if (image.empty())
478 {
479 std::cout << log_name_ << "::rgb_offline. Warning: frame " << file_name << " is empty!" << std::endl;
480 return std::make_pair(false, cv::Mat());
481 }
482 if ((parameters_.width() != 0) && (parameters_.height() != 0))
483 cv::resize(image, image, cv::Size(parameters_.width(), parameters_.height()));
484
485 /* Probe for rgb output. */
486 if (is_probe("rgb_output"))
487 get_probe("rgb_output").set_data(image);
488
489 return std::make_pair(true, image);
490}
491
492
493std::pair<bool, double> Camera::time_stamp_rgb_offline() const
494{
495 if (status() && dataset_parameters_.data_available())
496 {
497 VectorXd data = data_.col(frame_index_ + rgb_offset_);
498
499 return std::make_pair(true, data(0));
500 }
501
502 return std::make_pair(false, 0.0);
503}
504
505
506std::pair<bool, double> Camera::time_stamp_depth_offline() const
507{
508 if (status() && dataset_parameters_.data_available())
509 {
510 VectorXd data = data_.col(frame_index_ + depth_offset_);
511
512 return std::make_pair(true, data(1));
513 }
514
515 return std::make_pair(false, 0.0);
516}
517
518
519std::pair<bool, VectorXd> Camera::auxiliary_data_offline()
520{
521 if (status() && dataset_parameters_.data_available())
522 {
523 VectorXd data = data_.col(frame_index_);
524
525 if (auxiliary_data_size() == 0)
526 return std::make_pair(false, VectorXd());
527
528 /* Probe for auxiliary data output. */
529 if (is_probe("auxiliary_data_output"))
530 get_probe("auxiliary_data__output").set_data(data);
531
532 return std::make_pair(true, data.segment(dataset_parameters_.standard_data_offset(), auxiliary_data_size()));
533 }
534
535 return std::make_pair(false, VectorXd());
536}
537
538
539std::string Camera::compose_index(const std::size_t& index)
540{
541 std::ostringstream ss;
542 ss << std::setw(dataset_parameters_.heading_zeros()) << std::setfill('0') << index;
543 return ss.str();
544}
545
546
547std::pair<bool, MatrixXd> Camera::load_data()
548{
549 MatrixXd data;
550 const std::string file_name = dataset_parameters_.path() + dataset_parameters_.data_prefix() + "data." + dataset_parameters_.data_format();
551 const std::size_t num_fields = dataset_parameters_.standard_data_offset() + auxiliary_data_size();
552
553 std::ifstream istrm(file_name);
554 if (!istrm.is_open())
555 {
556 std::cout << log_name_ + "::read_data_from_file. Error: failed to open " << file_name << std::endl;
557
558 return std::make_pair(false, MatrixXd(0,0));
559 }
560
561 std::vector<std::string> istrm_strings;
562 std::string line;
563 while (std::getline(istrm, line))
564 {
565 istrm_strings.push_back(line);
566 }
567
568 dataset_parameters_.data_available(true);
569
570 data.resize(num_fields, istrm_strings.size());
571 std::size_t found_lines = 0;
572 for (auto line : istrm_strings)
573 {
574 std::size_t found_fields = 0;
575 std::string number_str;
576 std::istringstream iss(line);
577
578 while (iss >> number_str)
579 {
580 if (found_fields > num_fields)
581 {
582 std::cout << log_name_ + "::read_data_from_file. Error: malformed input file " << file_name << std::endl;
583 std::cout << log_name_ + "::read_data_from_file. Found more than expected fields. Skipping content parsing." << std::endl;
584 dataset_parameters_.data_available(false);
585 number_frames_ = data.cols();
586 return std::make_pair(true, data);
587 }
588
589 try
590 {
591 std::size_t index = (num_fields * found_lines) + found_fields;
592 *(data.data() + index) = std::stod(number_str);
593 }
594 catch (std::invalid_argument)
595 {
596 std::cout << log_name_ + "::read_data_from_file. Error: malformed input file " << file_name << std::endl;
597 std::cout << log_name_ + "::read_data_from_file. Found unexpected fields. Skipping content parsing." << std::endl;
598 dataset_parameters_.data_available(false);
599 number_frames_ = data.cols();
600 return std::make_pair(true, data);
601 }
602
603 found_fields++;
604 }
605
606 if (found_fields != num_fields)
607 {
608 std::cout << log_name_ + "::read_data_from_file. Error: malformed input file " << file_name << std::endl;
609 std::cout << log_name_ + "::read_data_from_file. Found less than expected fields. Skipping content parsing." << std::endl;
610 dataset_parameters_.data_available(false);
611 number_frames_ = data.cols();
612 return std::make_pair(true, data);
613 }
614 found_lines++;
615 }
616
617 istrm.close();
618
619 number_frames_ = data.cols();
620
621 if (dataset_parameters_.data_available())
622 {
623 // If timestamp data is available, try to synchronize rgb and depth frames.
624 double timestamp_rgb_0 = data.col(0)(0);
625 VectorXd timestamps_depth = data.row(1);
626 VectorXd delta_rgb_depth = (timestamps_depth.array() - timestamp_rgb_0).abs();
627 delta_rgb_depth.minCoeff(&depth_offset_);
628
629 double timestamp_depth_0 = data.col(0)(1);
630 VectorXd timestamps_rgb = data.row(0);
631 VectorXd delta_depth_rgb = (timestamps_rgb.array() - timestamp_depth_0).abs();
632 delta_depth_rgb.minCoeff(&rgb_offset_);
633
634 if (depth_offset_ > rgb_offset_)
635 {
636 rgb_offset_ = 0;
637 number_frames_ -= depth_offset_;
638 std::cout << log_name_ + "::read_data_from_file. RGB stream is " << depth_offset_ << " frames ahead of the depth stream.";
639 }
640 else
641 {
642 depth_offset_ = 0;
643 number_frames_ -= rgb_offset_;
644 std::cout << log_name_ + "::read_data_from_file. Depth stream is " << rgb_offset_ << " frames ahead of the RGB stream.";
645 }
646
647 std::cout << " Streams have been re-synchronized." << std::endl;
648 }
649
650 return std::make_pair(true, data);
651}
virtual std::pair< bool, Eigen::MatrixXd > deprojection_matrix() const
Definition: Camera.cpp:53
virtual std::pair< bool, Eigen::MatrixXf > depth_offline()
Definition: Camera.cpp:392
virtual bool evaluate_deprojection_matrix()
Definition: Camera.cpp:339
virtual bool log_frame(const bool &log_depth=false)
Definition: Camera.cpp:241
virtual std::int32_t frame_index() const
Definition: Camera.cpp:190
virtual std::pair< bool, Eigen::VectorXd > auxiliary_data_offline()
Definition: Camera.cpp:519
const std::string log_name_
Definition: Camera.h:174
virtual std::pair< bool, Eigen::MatrixXf > depth(const bool &blocking)=0
virtual std::pair< bool, Eigen::VectorXd > auxiliary_data(const bool &blocking)
Definition: Camera.cpp:178
const Parameters * parameters() const
Definition: Parameters.cpp:67