TrackBase.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 "TrackBase.h"
23 
24 #include "cam/CamBase.h"
25 #include "feat/Feature.h"
26 #include "feat/FeatureDatabase.h"
27 
28 using namespace ov_core;
29 
30 TrackBase::TrackBase(std::unordered_map<size_t, std::shared_ptr<CamBase>> cameras, int numfeats, int numaruco, bool stereo,
31  HistogramMethod histmethod)
32  : camera_calib(cameras), database(new FeatureDatabase()), num_features(numfeats), use_stereo(stereo), histogram_method(histmethod) {
33  // Our current feature ID should be larger then the number of aruco tags we have (each has 4 corners)
34  currid = 4 * (size_t)numaruco + 1;
35  // Create our mutex array based on the number of cameras we have
36  // See https://stackoverflow.com/a/24170141/7718197
37  if (mtx_feeds.empty() || mtx_feeds.size() != camera_calib.size()) {
38  std::vector<std::mutex> list(camera_calib.size());
39  mtx_feeds.swap(list);
40  }
41 }
42 
43 void TrackBase::display_active(cv::Mat &img_out, int r1, int g1, int b1, int r2, int g2, int b2, std::string overlay) {
44 
45  // Cache the images to prevent other threads from editing while we viz (which can be slow)
46  std::map<size_t, cv::Mat> img_last_cache, img_mask_last_cache;
47  std::unordered_map<size_t, std::vector<cv::KeyPoint>> pts_last_cache;
48  {
49  std::lock_guard<std::mutex> lckv(mtx_last_vars);
50  img_last_cache = img_last;
51  img_mask_last_cache = img_mask_last;
52  pts_last_cache = pts_last;
53  }
54 
55  // Get the largest width and height
56  int max_width = -1;
57  int max_height = -1;
58  for (auto const &pair : img_last_cache) {
59  if (max_width < pair.second.cols)
60  max_width = pair.second.cols;
61  if (max_height < pair.second.rows)
62  max_height = pair.second.rows;
63  }
64 
65  // Return if we didn't have a last image
66  if (img_last_cache.empty() || max_width == -1 || max_height == -1)
67  return;
68 
69  // If the image is "small" thus we should use smaller display codes
70  bool is_small = (std::min(max_width, max_height) < 400);
71 
72  // If the image is "new" then draw the images from scratch
73  // Otherwise, we grab the subset of the main image and draw on top of it
74  bool image_new = ((int)img_last_cache.size() * max_width != img_out.cols || max_height != img_out.rows);
75 
76  // If new, then resize the current image
77  if (image_new)
78  img_out = cv::Mat(max_height, (int)img_last_cache.size() * max_width, CV_8UC3, cv::Scalar(0, 0, 0));
79 
80  // Loop through each image, and draw
81  int index_cam = 0;
82  for (auto const &pair : img_last_cache) {
83  // select the subset of the image
84  cv::Mat img_temp;
85  if (image_new)
86  cv::cvtColor(img_last_cache[pair.first], img_temp, cv::COLOR_GRAY2RGB);
87  else
88  img_temp = img_out(cv::Rect(max_width * index_cam, 0, max_width, max_height));
89  // draw, loop through all keypoints
90  for (size_t i = 0; i < pts_last_cache[pair.first].size(); i++) {
91  // Get bounding pts for our boxes
92  cv::Point2f pt_l = pts_last_cache[pair.first].at(i).pt;
93  // Draw the extracted points and ID
94  cv::circle(img_temp, pt_l, (is_small) ? 1 : 2, cv::Scalar(r1, g1, b1), cv::FILLED);
95  // cv::putText(img_out, std::to_string(ids_left_last.at(i)), pt_l, cv::FONT_HERSHEY_SIMPLEX,0.5,cv::Scalar(0,0,255),1,cv::LINE_AA);
96  // Draw rectangle around the point
97  cv::Point2f pt_l_top = cv::Point2f(pt_l.x - 3, pt_l.y - 3);
98  cv::Point2f pt_l_bot = cv::Point2f(pt_l.x + 3, pt_l.y + 3);
99  cv::rectangle(img_temp, pt_l_top, pt_l_bot, cv::Scalar(r2, g2, b2), 1);
100  }
101  // Draw what camera this is
102  auto txtpt = (is_small) ? cv::Point(10, 30) : cv::Point(30, 60);
103  if (overlay == "") {
104  cv::putText(img_temp, "CAM:" + std::to_string((int)pair.first), txtpt, cv::FONT_HERSHEY_COMPLEX_SMALL, (is_small) ? 1.5 : 3.0,
105  cv::Scalar(0, 255, 0), 3);
106  } else {
107  cv::putText(img_temp, overlay, txtpt, cv::FONT_HERSHEY_COMPLEX_SMALL, (is_small) ? 1.5 : 3.0, cv::Scalar(0, 0, 255), 3);
108  }
109  // Overlay the mask
110  cv::Mat mask = cv::Mat::zeros(img_mask_last_cache[pair.first].rows, img_mask_last_cache[pair.first].cols, CV_8UC3);
111  mask.setTo(cv::Scalar(0, 0, 255), img_mask_last_cache[pair.first]);
112  cv::addWeighted(mask, 0.1, img_temp, 1.0, 0.0, img_temp);
113  // Replace the output image
114  img_temp.copyTo(img_out(cv::Rect(max_width * index_cam, 0, img_last_cache[pair.first].cols, img_last_cache[pair.first].rows)));
115  index_cam++;
116  }
117 }
118 
119 void TrackBase::display_history(cv::Mat &img_out, int r1, int g1, int b1, int r2, int g2, int b2, std::vector<size_t> highlighted,
120  std::string overlay) {
121 
122  // Cache the images to prevent other threads from editing while we viz (which can be slow)
123  std::map<size_t, cv::Mat> img_last_cache, img_mask_last_cache;
124  std::unordered_map<size_t, std::vector<cv::KeyPoint>> pts_last_cache;
125  std::unordered_map<size_t, std::vector<size_t>> ids_last_cache;
126  {
127  std::lock_guard<std::mutex> lckv(mtx_last_vars);
128  img_last_cache = img_last;
129  img_mask_last_cache = img_mask_last;
130  pts_last_cache = pts_last;
131  ids_last_cache = ids_last;
132  }
133 
134  // Get the largest width and height
135  int max_width = -1;
136  int max_height = -1;
137  for (auto const &pair : img_last_cache) {
138  if (max_width < pair.second.cols)
139  max_width = pair.second.cols;
140  if (max_height < pair.second.rows)
141  max_height = pair.second.rows;
142  }
143 
144  // Return if we didn't have a last image
145  if (img_last_cache.empty() || max_width == -1 || max_height == -1)
146  return;
147 
148  // If the image is "small" thus we shoudl use smaller display codes
149  bool is_small = (std::min(max_width, max_height) < 400);
150 
151  // If the image is "new" then draw the images from scratch
152  // Otherwise, we grab the subset of the main image and draw on top of it
153  bool image_new = ((int)img_last_cache.size() * max_width != img_out.cols || max_height != img_out.rows);
154 
155  // If new, then resize the current image
156  if (image_new)
157  img_out = cv::Mat(max_height, (int)img_last_cache.size() * max_width, CV_8UC3, cv::Scalar(0, 0, 0));
158 
159  // Max tracks to show (otherwise it clutters up the screen)
160  size_t maxtracks = 50;
161 
162  // Loop through each image, and draw
163  int index_cam = 0;
164  for (auto const &pair : img_last_cache) {
165  // select the subset of the image
166  cv::Mat img_temp;
167  if (image_new)
168  cv::cvtColor(img_last_cache[pair.first], img_temp, cv::COLOR_GRAY2RGB);
169  else
170  img_temp = img_out(cv::Rect(max_width * index_cam, 0, max_width, max_height));
171  // draw, loop through all keypoints
172  for (size_t i = 0; i < ids_last_cache[pair.first].size(); i++) {
173  // If a highlighted point, then put a nice box around it
174  if (std::find(highlighted.begin(), highlighted.end(), ids_last_cache[pair.first].at(i)) != highlighted.end()) {
175  cv::Point2f pt_c = pts_last_cache[pair.first].at(i).pt;
176  cv::Point2f pt_l_top = cv::Point2f(pt_c.x - ((is_small) ? 3 : 5), pt_c.y - ((is_small) ? 3 : 5));
177  cv::Point2f pt_l_bot = cv::Point2f(pt_c.x + ((is_small) ? 3 : 5), pt_c.y + ((is_small) ? 3 : 5));
178  cv::rectangle(img_temp, pt_l_top, pt_l_bot, cv::Scalar(0, 255, 0), 1);
179  cv::circle(img_temp, pt_c, (is_small) ? 1 : 2, cv::Scalar(0, 255, 0), cv::FILLED);
180  }
181  // Get the feature from the database
182  Feature feat;
183  if (!database->get_feature_clone(ids_last_cache[pair.first].at(i), feat))
184  continue;
185  if (feat.uvs.empty() || feat.uvs[pair.first].empty() || feat.to_delete)
186  continue;
187  // Draw the history of this point (start at the last inserted one)
188  for (size_t z = feat.uvs[pair.first].size() - 1; z > 0; z--) {
189  // Check if we have reached the max
190  if (feat.uvs[pair.first].size() - z > maxtracks)
191  break;
192  // Calculate what color we are drawing in
193  bool is_stereo = (feat.uvs.size() > 1);
194  int color_r = (is_stereo ? b2 : r2) - (int)(1.0 * (is_stereo ? b1 : r1) / feat.uvs[pair.first].size() * z);
195  int color_g = (is_stereo ? r2 : g2) - (int)(1.0 * (is_stereo ? r1 : g1) / feat.uvs[pair.first].size() * z);
196  int color_b = (is_stereo ? g2 : b2) - (int)(1.0 * (is_stereo ? g1 : b1) / feat.uvs[pair.first].size() * z);
197  // Draw current point
198  cv::Point2f pt_c(feat.uvs[pair.first].at(z)(0), feat.uvs[pair.first].at(z)(1));
199  cv::circle(img_temp, pt_c, (is_small) ? 1 : 2, cv::Scalar(color_r, color_g, color_b), cv::FILLED);
200  // If there is a next point, then display the line from this point to the next
201  if (z + 1 < feat.uvs[pair.first].size()) {
202  cv::Point2f pt_n(feat.uvs[pair.first].at(z + 1)(0), feat.uvs[pair.first].at(z + 1)(1));
203  cv::line(img_temp, pt_c, pt_n, cv::Scalar(color_r, color_g, color_b));
204  }
205  // If the first point, display the ID
206  if (z == feat.uvs[pair.first].size() - 1) {
207  // cv::putText(img_out0, std::to_string(feat->featid), pt_c, cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1,
208  // cv::LINE_AA); cv::circle(img_out0, pt_c, 2, cv::Scalar(color,color,255), CV_FILLED);
209  }
210  }
211  }
212  // Draw what camera this is
213  auto txtpt = (is_small) ? cv::Point(10, 30) : cv::Point(30, 60);
214  if (overlay == "") {
215  cv::putText(img_temp, "CAM:" + std::to_string((int)pair.first), txtpt, cv::FONT_HERSHEY_COMPLEX_SMALL, (is_small) ? 1.5 : 3.0,
216  cv::Scalar(0, 255, 0), 3);
217  } else {
218  cv::putText(img_temp, overlay, txtpt, cv::FONT_HERSHEY_COMPLEX_SMALL, (is_small) ? 1.5 : 3.0, cv::Scalar(0, 0, 255), 3);
219  }
220  // Overlay the mask
221  cv::Mat mask = cv::Mat::zeros(img_mask_last_cache[pair.first].rows, img_mask_last_cache[pair.first].cols, CV_8UC3);
222  mask.setTo(cv::Scalar(0, 0, 255), img_mask_last_cache[pair.first]);
223  cv::addWeighted(mask, 0.1, img_temp, 1.0, 0.0, img_temp);
224  // Replace the output image
225  img_temp.copyTo(img_out(cv::Rect(max_width * index_cam, 0, img_last_cache[pair.first].cols, img_last_cache[pair.first].rows)));
226  index_cam++;
227  }
228 }
229 
230 void TrackBase::change_feat_id(size_t id_old, size_t id_new) {
231 
232  // If found in db then replace
233  if (database->get_internal_data().find(id_old) != database->get_internal_data().end()) {
234  std::shared_ptr<Feature> feat = database->get_internal_data().at(id_old);
235  database->get_internal_data().erase(id_old);
236  feat->featid = id_new;
237  database->get_internal_data().insert({id_new, feat});
238  }
239 
240  // Update current track IDs
241  for (auto &cam_ids_pair : ids_last) {
242  for (size_t i = 0; i < cam_ids_pair.second.size(); i++) {
243  if (cam_ids_pair.second.at(i) == id_old) {
244  ids_last.at(cam_ids_pair.first).at(i) = id_new;
245  }
246  }
247  }
248 }
FeatureDatabase.h
ov_core::TrackBase::mtx_feeds
std::vector< std::mutex > mtx_feeds
Mutexs for our last set of image storage (img_last, pts_last, and ids_last)
Definition: TrackBase.h:174
ov_core::TrackBase::currid
std::atomic< size_t > currid
Master ID for this tracker (atomic to allow for multi-threading)
Definition: TrackBase.h:192
ov_core::TrackBase::database
std::shared_ptr< FeatureDatabase > database
Database with all our current features.
Definition: TrackBase.h:159
ov_core::TrackBase::img_mask_last
std::map< size_t, cv::Mat > img_mask_last
Last set of images (use map so all trackers render in the same order)
Definition: TrackBase.h:183
ov_core::TrackBase::mtx_last_vars
std::mutex mtx_last_vars
Mutex for editing the *_last variables.
Definition: TrackBase.h:177
ov_core::TrackBase::HistogramMethod
HistogramMethod
Desired pre-processing image method.
Definition: TrackBase.h:78
TrackBase.h
Feature.h
ov_core::Feature::uvs
std::unordered_map< size_t, std::vector< Eigen::VectorXf > > uvs
UV coordinates that this feature has been seen from (mapped by camera ID)
Definition: Feature.h:49
ov_core::TrackBase::TrackBase
TrackBase(std::unordered_map< size_t, std::shared_ptr< CamBase >> cameras, int numfeats, int numaruco, bool stereo, HistogramMethod histmethod)
Public constructor with configuration variables.
Definition: TrackBase.cpp:30
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::TrackBase::ids_last
std::unordered_map< size_t, std::vector< size_t > > ids_last
Set of IDs of each current feature in the database.
Definition: TrackBase.h:189
CamBase.h
ov_core::Feature
Sparse feature class used to collect measurements.
Definition: Feature.h:39
ov_core::TrackBase::change_feat_id
void change_feat_id(size_t id_old, size_t id_new)
Changes the ID of an actively tracked feature to another one.
Definition: TrackBase.cpp:230
ov_core::Feature::to_delete
bool to_delete
If this feature should be deleted.
Definition: Feature.h:46
ov_core::FeatureDatabase
Database containing features we are currently tracking.
Definition: FeatureDatabase.h:54
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
ov_core::TrackBase::pts_last
std::unordered_map< size_t, std::vector< cv::KeyPoint > > pts_last
Last set of tracked points.
Definition: TrackBase.h:186
ov_core::TrackBase::img_last
std::map< size_t, cv::Mat > img_last
Last set of images (use map so all trackers render in the same order)
Definition: TrackBase.h:180
ov_core::TrackBase::camera_calib
std::unordered_map< size_t, std::shared_ptr< CamBase > > camera_calib
Camera object which has all calibration in it.
Definition: TrackBase.h:156
ov_core
Core algorithms for OpenVINS.
Definition: CamBase.h:30


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