test_tracking.cpp
Go to the documentation of this file.
1 /*
2  * OpenVINS: An Open Platform for Visual-Inertial Research
3  * Copyright (C) 2018-2023 Patrick Geneva
4  * Copyright (C) 2018-2023 Guoquan Huang
5  * Copyright (C) 2018-2023 OpenVINS Contributors
6  * Copyright (C) 2018-2019 Kevin Eckenhoff
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <cmath>
23 #include <deque>
24 #include <sstream>
25 #include <unistd.h>
26 #include <vector>
27 
28 #include <cv_bridge/cv_bridge.h>
29 #include <ros/ros.h>
30 #include <rosbag/bag.h>
31 #include <rosbag/view.h>
32 #include <sensor_msgs/Image.h>
33 
34 #include <opencv2/core/core.hpp>
35 #include <opencv2/highgui/highgui.hpp>
36 
37 #include "cam/CamRadtan.h"
38 #include "feat/Feature.h"
39 #include "feat/FeatureDatabase.h"
40 #include "track/TrackAruco.h"
41 #include "track/TrackDescriptor.h"
42 #include "track/TrackKLT.h"
44 #include "utils/print.h"
45 
46 using namespace ov_core;
47 
48 // Our feature extractor
50 
51 // FPS counter, and other statistics
52 // https://gamedev.stackexchange.com/a/83174
53 int frames = 0;
54 int num_lostfeats = 0;
55 int num_margfeats = 0;
56 int featslengths = 0;
57 int clone_states = 10;
58 std::deque<double> clonetimes;
60 
61 // How many cameras we will do visual tracking on (mono=1, stereo=2)
62 int max_cameras = 2;
63 
64 // Our master function for tracking
65 void handle_stereo(double time0, double time1, cv::Mat img0, cv::Mat img1);
66 
67 // Main function
68 int main(int argc, char **argv) {
69 
70  // Ensure we have a path, if the user passes it then we should use it
71  std::string config_path = "unset_path.txt";
72  if (argc > 1) {
73  config_path = argv[1];
74  }
75 
76  // Initialize this as a ROS node
77  ros::init(argc, argv, "test_tracking");
78  auto nh = std::make_shared<ros::NodeHandle>("~");
79  nh->param<std::string>("config_path", config_path, config_path);
80 
81  // Load parameters
82  auto parser = std::make_shared<ov_core::YamlParser>(config_path, false);
83  parser->set_node_handler(nh);
84 
85  // Verbosity
86  std::string verbosity = "DEBUG";
87  parser->parse_config("verbosity", verbosity);
89 
90  // Our camera topics (left and right stereo)
91  std::string topic_camera0, topic_camera1;
92  nh->param<std::string>("topic_camera0", topic_camera0, "/cam0/image_raw");
93  nh->param<std::string>("topic_camera1", topic_camera1, "/cam1/image_raw");
94  parser->parse_external("relative_config_imucam", "cam" + std::to_string(0), "rostopic", topic_camera0);
95  parser->parse_external("relative_config_imucam", "cam" + std::to_string(1), "rostopic", topic_camera1);
96 
97  // Location of the ROS bag we want to read in
98  std::string path_to_bag;
99  nh->param<std::string>("path_bag", path_to_bag, "/home/patrick/datasets/euroc_mav/V1_01_easy.bag");
100  // nh->param<std::string>("path_bag", path_to_bag, "/home/patrick/datasets/rpng_aruco/aruco_room_01.bag");
101  PRINT_INFO("ros bag path is: %s\n", path_to_bag.c_str());
102 
103  // Get our start location and how much of the bag we want to play
104  // Make the bag duration < 0 to just process to the end of the bag
105  double bag_start, bag_durr;
106  nh->param<double>("bag_start", bag_start, 0);
107  nh->param<double>("bag_durr", bag_durr, -1);
108 
109  //===================================================================================
110  //===================================================================================
111  //===================================================================================
112 
113  // This will globally set the thread count we will use
114  // -1 will reset to the system default threading (usually the num of cores)
115  cv::setNumThreads(4);
116 
117  // Parameters for our extractor
118  int num_pts = 200;
119  int num_aruco = 1024;
120  int fast_threshold = 20;
121  int grid_x = 5;
122  int grid_y = 3;
123  int min_px_dist = 10;
124  double knn_ratio = 0.70;
125  bool do_downsizing = false;
126  bool use_stereo = false;
127  parser->parse_config("max_cameras", max_cameras, false);
128  parser->parse_config("num_pts", num_pts, false);
129  parser->parse_config("num_aruco", num_aruco, false);
130  parser->parse_config("clone_states", clone_states, false);
131  parser->parse_config("fast_threshold", fast_threshold, false);
132  parser->parse_config("grid_x", grid_x, false);
133  parser->parse_config("grid_y", grid_y, false);
134  parser->parse_config("min_px_dist", min_px_dist, false);
135  parser->parse_config("knn_ratio", knn_ratio, false);
136  parser->parse_config("do_downsizing", do_downsizing, false);
137  parser->parse_config("use_stereo", use_stereo, false);
138 
139  // Histogram method
141  std::string histogram_method_str = "HISTOGRAM";
142  parser->parse_config("histogram_method", histogram_method_str, false);
143  if (histogram_method_str == "NONE") {
144  method = ov_core::TrackBase::NONE;
145  } else if (histogram_method_str == "HISTOGRAM") {
147  } else if (histogram_method_str == "CLAHE") {
148  method = ov_core::TrackBase::CLAHE;
149  } else {
150  printf(RED "invalid feature histogram specified:\n" RESET);
151  printf(RED "\t- NONE\n" RESET);
152  printf(RED "\t- HISTOGRAM\n" RESET);
153  printf(RED "\t- CLAHE\n" RESET);
154  std::exit(EXIT_FAILURE);
155  }
156 
157  // Debug print!
158  PRINT_DEBUG("max cameras: %d\n", max_cameras);
159  PRINT_DEBUG("max features: %d\n", num_pts);
160  PRINT_DEBUG("max aruco: %d\n", num_aruco);
161  PRINT_DEBUG("clone states: %d\n", clone_states);
162  PRINT_DEBUG("grid size: %d x %d\n", grid_x, grid_y);
163  PRINT_DEBUG("fast threshold: %d\n", fast_threshold);
164  PRINT_DEBUG("min pixel distance: %d\n", min_px_dist);
165  PRINT_DEBUG("downsize aruco image: %d\n", do_downsizing);
166  PRINT_DEBUG("stereo tracking: %d\n", use_stereo);
167 
168  // Fake camera info (we don't need this, as we are not using the normalized coordinates for anything)
169  std::unordered_map<size_t, std::shared_ptr<CamBase>> cameras;
170  for (int i = 0; i < 2; i++) {
171  Eigen::Matrix<double, 8, 1> cam0_calib;
172  cam0_calib << 1, 1, 0, 0, 0, 0, 0, 0;
173  std::shared_ptr<CamBase> camera_calib = std::make_shared<CamRadtan>(100, 100);
174  camera_calib->set_value(cam0_calib);
175  cameras.insert({i, camera_calib});
176  }
177 
178  // Lets make a feature extractor
179  extractor = new TrackKLT(cameras, num_pts, num_aruco, use_stereo, method, fast_threshold, grid_x, grid_y, min_px_dist);
180  // extractor = new TrackDescriptor(cameras, num_pts, num_aruco, use_stereo, method, fast_threshold, grid_x, grid_y, min_px_dist,
181  // knn_ratio);
182  // extractor = new TrackAruco(cameras, num_aruco, use_stereo, method, do_downsizing);
183 
184  //===================================================================================
185  //===================================================================================
186  //===================================================================================
187 
188  // Load rosbag here, and find messages we can play
189  rosbag::Bag bag;
190  bag.open(path_to_bag, rosbag::bagmode::Read);
191 
192  // We should load the bag as a view
193  // Here we go from beginning of the bag to the end of the bag
194  rosbag::View view_full;
195  rosbag::View view;
196 
197  // Start a few seconds in from the full view time
198  // If we have a negative duration then use the full bag length
199  view_full.addQuery(bag);
200  ros::Time time_init = view_full.getBeginTime();
201  time_init += ros::Duration(bag_start);
202  ros::Time time_finish = (bag_durr < 0) ? view_full.getEndTime() : time_init + ros::Duration(bag_durr);
203  PRINT_DEBUG("time start = %.6f\n", time_init.toSec());
204  PRINT_DEBUG("time end = %.6f\n", time_finish.toSec());
205  view.addQuery(bag, time_init, time_finish);
206 
207  // Check to make sure we have data to play
208  if (view.size() == 0) {
209  PRINT_ERROR(RED "No messages to play on specified topics. Exiting.\n" RESET);
210  ros::shutdown();
211  return EXIT_FAILURE;
212  }
213 
214  // Record the start time for our FPS counter
216 
217  // Our stereo pair we have
218  bool has_left = false;
219  bool has_right = false;
220  cv::Mat img0, img1;
221  double time0 = time_init.toSec();
222  double time1 = time_init.toSec();
223 
224  // Step through the rosbag
225  for (const rosbag::MessageInstance &m : view) {
226 
227  // If ros is wants us to stop, break out
228  if (!ros::ok())
229  break;
230 
231  // Handle LEFT camera
232  sensor_msgs::Image::ConstPtr s0 = m.instantiate<sensor_msgs::Image>();
233  if (s0 != nullptr && m.getTopic() == topic_camera0) {
234  // Get the image
236  try {
238  } catch (cv_bridge::Exception &e) {
239  PRINT_ERROR(RED "cv_bridge exception: %s\n" RESET, e.what());
240  continue;
241  }
242  // Save to our temp variable
243  has_left = true;
244  cv::equalizeHist(cv_ptr->image, img0);
245  // img0 = cv_ptr->image.clone();
246  time0 = cv_ptr->header.stamp.toSec();
247  }
248 
249  // Handle RIGHT camera
250  sensor_msgs::Image::ConstPtr s1 = m.instantiate<sensor_msgs::Image>();
251  if (s1 != nullptr && m.getTopic() == topic_camera1) {
252  // Get the image
254  try {
256  } catch (cv_bridge::Exception &e) {
257  PRINT_ERROR(RED "cv_bridge exception: %s\n" RESET, e.what());
258  continue;
259  }
260  // Save to our temp variable
261  has_right = true;
262  cv::equalizeHist(cv_ptr->image, img1);
263  // img1 = cv_ptr->image.clone();
264  time1 = cv_ptr->header.stamp.toSec();
265  }
266 
267  // If we have both left and right, then process
268  if (has_left && has_right) {
269  // process
270  handle_stereo(time0, time1, img0, img1);
271  // reset bools
272  has_left = false;
273  has_right = false;
274  }
275  }
276 
277  // Done!
278  return EXIT_SUCCESS;
279 }
280 
284 void handle_stereo(double time0, double time1, cv::Mat img0, cv::Mat img1) {
285 
286  // Animate our dynamic mask moving
287  // Very simple ball bounding around the screen example
288  cv::Mat mask = cv::Mat::zeros(cv::Size(img0.cols, img0.rows), CV_8UC1);
289  static cv::Point2f ball_center;
290  static cv::Point2f ball_velocity;
291  if (ball_velocity.x == 0 || ball_velocity.y == 0) {
292  ball_center.x = (float)img0.cols / 2.0f;
293  ball_center.y = (float)img0.rows / 2.0f;
294  ball_velocity.x = 2.5;
295  ball_velocity.y = 2.5;
296  }
297  ball_center += ball_velocity;
298  if (ball_center.x < 0 || (int)ball_center.x > img0.cols)
299  ball_velocity.x *= -1;
300  if (ball_center.y < 0 || (int)ball_center.y > img0.rows)
301  ball_velocity.y *= -1;
302  cv::circle(mask, ball_center, 100, cv::Scalar(255), cv::FILLED);
303 
304  // Process this new image
305  ov_core::CameraData message;
306  message.timestamp = time0;
307  message.sensor_ids.push_back(0);
308  message.images.push_back(img0);
309  message.masks.push_back(mask);
310  if (max_cameras == 2) {
311  message.sensor_ids.push_back(1);
312  message.images.push_back(img1);
313  message.masks.push_back(mask);
314  }
315  extractor->feed_new_camera(message);
316 
317  // Display the resulting tracks
318  cv::Mat img_active, img_history;
319  extractor->display_active(img_active, 255, 0, 0, 0, 0, 255);
320  extractor->display_history(img_history, 255, 255, 0, 255, 255, 255);
321 
322  // Show our image!
323  cv::imshow("Active Tracks", img_active);
324  cv::imshow("Track History", img_history);
325  cv::waitKey(1);
326 
327  // Get lost tracks
328  std::shared_ptr<FeatureDatabase> database = extractor->get_feature_database();
329  std::vector<std::shared_ptr<Feature>> feats_lost = database->features_not_containing_newer(time0);
330  num_lostfeats += feats_lost.size();
331 
332  // Mark theses feature pointers as deleted
333  for (size_t i = 0; i < feats_lost.size(); i++) {
334  // Total number of measurements
335  int total_meas = 0;
336  for (auto const &pair : feats_lost[i]->timestamps) {
337  total_meas += (int)pair.second.size();
338  }
339  // Update stats
340  featslengths += total_meas;
341  feats_lost[i]->to_delete = true;
342  }
343 
344  // Push back the current time, as a clone time
345  clonetimes.push_back(time0);
346 
347  // Marginalized features if we have reached 5 frame tracks
348  if ((int)clonetimes.size() >= clone_states) {
349  // Remove features that have reached their max track length
350  double margtime = clonetimes.at(0);
351  clonetimes.pop_front();
352  std::vector<std::shared_ptr<Feature>> feats_marg = database->features_containing(margtime);
353  num_margfeats += feats_marg.size();
354  // Delete theses feature pointers
355  for (size_t i = 0; i < feats_marg.size(); i++) {
356  feats_marg[i]->to_delete = true;
357  }
358  }
359 
360  // Tell the feature database to delete old features
361  database->cleanup();
362 
363  // Debug print out what our current processing speed it
364  // We want the FPS to be as high as possible
365  ros::Time time_curr = ros::Time::now();
366  // if (time_curr.toSec()-time_start.toSec() > 2) {
367  if (frames > 60) {
368  // Calculate the FPS
369  double fps = (double)frames / (time_curr.toSec() - time_start.toSec());
370  double lpf = (double)num_lostfeats / frames;
371  double fpf = (double)featslengths / num_lostfeats;
372  double mpf = (double)num_margfeats / frames;
373  // DEBUG PRINT OUT
374  PRINT_DEBUG("fps = %.2f | lost_feats/frame = %.2f | track_length/lost_feat = %.2f | marg_tracks/frame = %.2f\n", fps, lpf, fpf, mpf);
375  // Reset variables
376  frames = 0;
377  time_start = time_curr;
378  num_lostfeats = 0;
379  num_margfeats = 0;
380  featslengths = 0;
381  }
382  frames++;
383 }
FeatureDatabase.h
num_lostfeats
int num_lostfeats
Definition: test_tracking.cpp:54
rosbag::Bag
rosbag::View::size
uint32_t size()
featslengths
int featslengths
Definition: test_tracking.cpp:56
ov_core::TrackBase::HistogramMethod
HistogramMethod
Desired pre-processing image method.
Definition: TrackBase.h:78
boost::shared_ptr
ov_core::TrackBase::NONE
@ NONE
Definition: TrackBase.h:78
max_cameras
int max_cameras
Definition: test_tracking.cpp:62
ros::init
ROSCPP_DECL void init(const M_string &remappings, const std::string &name, uint32_t options=0)
ov_core::CameraData::images
std::vector< cv::Mat > images
Raw image we have collected for each camera.
Definition: sensor_data.h:64
rosbag::MessageInstance
extractor
TrackBase * extractor
Definition: test_tracking.cpp:49
ov_core::TrackBase::feed_new_camera
virtual void feed_new_camera(const CameraData &message)=0
Process a new image.
ros.h
PRINT_DEBUG
#define PRINT_DEBUG(x...)
Definition: print.h:97
Feature.h
num_margfeats
int num_margfeats
Definition: test_tracking.cpp:55
TimeBase< Time, Duration >::toSec
double toSec() const
ros::shutdown
ROSCPP_DECL void shutdown()
TrackKLT.h
ov_core::CameraData
Struct for a collection of camera measurements.
Definition: sensor_data.h:55
cv_bridge::Exception
CamRadtan.h
bag.h
PRINT_ERROR
#define PRINT_ERROR(x...)
Definition: print.h:100
ros::ok
ROSCPP_DECL bool ok()
rosbag::bagmode::Read
Read
frames
int frames
Definition: test_tracking.cpp:53
ov_core::CameraData::sensor_ids
std::vector< int > sensor_ids
Camera ids for each of the images collected.
Definition: sensor_data.h:61
ov_core::CameraData::timestamp
double timestamp
Timestamp of the reading.
Definition: sensor_data.h:58
ov_core::CameraData::masks
std::vector< cv::Mat > masks
Tracking masks for each camera we have.
Definition: sensor_data.h:67
ov_core::TrackBase::display_active
virtual void display_active(cv::Mat &img_out, int r1, int g1, int b1, int r2, int g2, int b2, std::string overlay="")
Shows features extracted in the last image.
Definition: TrackBase.cpp:43
ov_core::Printer::setPrintLevel
static void setPrintLevel(const std::string &level)
Set the print level to use for all future printing to stdout.
Definition: print.cpp:29
handle_stereo
void handle_stereo(double time0, double time1, cv::Mat img0, cv::Mat img1)
Definition: test_tracking.cpp:284
time_start
ros::Time time_start
Definition: test_tracking.cpp:59
print.h
main
int main(int argc, char **argv)
Definition: test_tracking.cpp:68
ov_core::TrackBase::CLAHE
@ CLAHE
Definition: TrackBase.h:78
RED
#define RED
Definition: colors.h:27
rosbag::View::addQuery
void addQuery(Bag const &bag, boost::function< bool(ConnectionInfo const *)> query, ros::Time const &start_time=ros::TIME_MIN, ros::Time const &end_time=ros::TIME_MAX)
view.h
opencv_yaml_parse.h
clonetimes
std::deque< double > clonetimes
Definition: test_tracking.cpp:58
TrackDescriptor.h
rosbag::View
ov_core::TrackBase::HISTOGRAM
@ HISTOGRAM
Definition: TrackBase.h:78
rosbag::View::getBeginTime
ros::Time getBeginTime()
ros::Time
sensor_msgs::image_encodings::MONO8
const std::string MONO8
PRINT_INFO
#define PRINT_INFO(x...)
Definition: print.h:98
cv_bridge.h
ov_core::TrackBase::get_feature_database
std::shared_ptr< FeatureDatabase > get_feature_database()
Get the feature database with all the track information.
Definition: TrackBase.h:123
RESET
#define RESET
Definition: colors.h:25
ov_core::TrackKLT
KLT tracking of features.
Definition: TrackKLT.h:39
ov_core::TrackBase::display_history
virtual void display_history(cv::Mat &img_out, int r1, int g1, int b1, int r2, int g2, int b2, std::vector< size_t > highlighted={}, std::string overlay="")
Shows a "trail" for each feature (i.e. its history)
Definition: TrackBase.cpp:119
cv_bridge::toCvShare
CvImageConstPtr toCvShare(const sensor_msgs::Image &source, const boost::shared_ptr< void const > &tracked_object, const std::string &encoding=std::string())
rosbag::View::getEndTime
ros::Time getEndTime()
rosbag::Bag::open
void open(std::string const &filename, uint32_t mode=bagmode::Read)
ros::Duration
clone_states
int clone_states
Definition: test_tracking.cpp:57
ov_core
Core algorithms for OpenVINS.
Definition: CamBase.h:30
ov_core::TrackBase
Visual feature tracking base class.
Definition: TrackBase.h:72
TrackAruco.h
ros::Time::now
static Time now()


ov_core
Author(s): Patrick Geneva , Kevin Eckenhoff , Guoquan Huang
autogenerated on Mon Jan 22 2024 03:08:17