TrackAruco.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 "TrackAruco.h"
23 
24 #include "cam/CamBase.h"
25 #include "feat/Feature.h"
26 #include "feat/FeatureDatabase.h"
28 
29 using namespace ov_core;
30 
32 
33  // Error check that we have all the data
34  if (message.sensor_ids.empty() || message.sensor_ids.size() != message.images.size() || message.images.size() != message.masks.size()) {
35  PRINT_ERROR(RED "[ERROR]: MESSAGE DATA SIZES DO NOT MATCH OR EMPTY!!!\n" RESET);
36  PRINT_ERROR(RED "[ERROR]: - message.sensor_ids.size() = %zu\n" RESET, message.sensor_ids.size());
37  PRINT_ERROR(RED "[ERROR]: - message.images.size() = %zu\n" RESET, message.images.size());
38  PRINT_ERROR(RED "[ERROR]: - message.masks.size() = %zu\n" RESET, message.masks.size());
39  std::exit(EXIT_FAILURE);
40  }
41 
42  // There is not such thing as stereo tracking for aruco
43  // Thus here we should just call the monocular version two times
44 #if ENABLE_ARUCO_TAGS
45  size_t num_images = message.images.size();
46  parallel_for_(cv::Range(0, (int)num_images), LambdaBody([&](const cv::Range &range) {
47  for (int i = range.start; i < range.end; i++) {
48  perform_tracking(message.timestamp, message.images.at(i), message.sensor_ids.at(i), message.masks.at(i));
49  }
50  }));
51 #else
52  PRINT_ERROR(RED "[ERROR]: you have not compiled with aruco tag support!!!\n" RESET);
53  std::exit(EXIT_FAILURE);
54 #endif
55 }
56 
57 #if ENABLE_ARUCO_TAGS
58 
59 void TrackAruco::perform_tracking(double timestamp, const cv::Mat &imgin, size_t cam_id, const cv::Mat &maskin) {
60 
61  // Start timing
62  rT1 = boost::posix_time::microsec_clock::local_time();
63 
64  // Lock this data feed for this camera
65  std::lock_guard<std::mutex> lck(mtx_feeds.at(cam_id));
66 
67  // Histogram equalize
68  cv::Mat img;
69  if (histogram_method == HistogramMethod::HISTOGRAM) {
70  cv::equalizeHist(imgin, img);
71  } else if (histogram_method == HistogramMethod::CLAHE) {
72  double eq_clip_limit = 10.0;
73  cv::Size eq_win_size = cv::Size(8, 8);
74  cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE(eq_clip_limit, eq_win_size);
75  clahe->apply(imgin, img);
76  } else {
77  img = imgin;
78  }
79 
80  // Clear the old data from the last timestep
81  ids_aruco[cam_id].clear();
82  corners[cam_id].clear();
83  rejects[cam_id].clear();
84 
85  // If we are downsizing, then downsize
86  cv::Mat img0;
87  if (do_downsizing) {
88  cv::pyrDown(img, img0, cv::Size(img.cols / 2, img.rows / 2));
89  } else {
90  img0 = img;
91  }
92 
93  //===================================================================================
94  //===================================================================================
95 
96  // Perform extraction
97  cv::aruco::detectMarkers(img0, aruco_dict, corners[cam_id], ids_aruco[cam_id], aruco_params, rejects[cam_id]);
98  rT2 = boost::posix_time::microsec_clock::local_time();
99 
100  //===================================================================================
101  //===================================================================================
102 
103  // If we downsized, scale all our u,v measurements by a factor of two
104  // Note: we do this so we can use these results for visulization later
105  // Note: and so that the uv added is in the same image size
106  if (do_downsizing) {
107  for (size_t i = 0; i < corners[cam_id].size(); i++) {
108  for (size_t j = 0; j < corners[cam_id].at(i).size(); j++) {
109  corners[cam_id].at(i).at(j).x *= 2;
110  corners[cam_id].at(i).at(j).y *= 2;
111  }
112  }
113  for (size_t i = 0; i < rejects[cam_id].size(); i++) {
114  for (size_t j = 0; j < rejects[cam_id].at(i).size(); j++) {
115  rejects[cam_id].at(i).at(j).x *= 2;
116  rejects[cam_id].at(i).at(j).y *= 2;
117  }
118  }
119  }
120 
121  //===================================================================================
122  //===================================================================================
123 
124  // ID vectors, of all currently tracked IDs
125  std::vector<size_t> ids_new;
126  std::vector<cv::KeyPoint> pts_new;
127 
128  // Append to our feature database this new information
129  for (size_t i = 0; i < ids_aruco[cam_id].size(); i++) {
130  // Skip if ID is greater then our max
131  if (ids_aruco[cam_id].at(i) > max_tag_id)
132  continue;
133  // Assert we have 4 points (we will only use one of them)
134  assert(corners[cam_id].at(i).size() == 4);
135  for (int n = 0; n < 4; n++) {
136  // Check if it is in the mask
137  // NOTE: mask has max value of 255 (white) if it should be
138  if (maskin.at<uint8_t>((int)corners[cam_id].at(i).at(n).y, (int)corners[cam_id].at(i).at(n).x) > 127)
139  continue;
140  // Try to undistort the point
141  cv::Point2f npt_l = camera_calib.at(cam_id)->undistort_cv(corners[cam_id].at(i).at(n));
142  // Append to the ids vector and database
143  size_t tmp_id = (size_t)ids_aruco[cam_id].at(i) + n * max_tag_id;
144  database->update_feature(tmp_id, timestamp, cam_id, corners[cam_id].at(i).at(n).x, corners[cam_id].at(i).at(n).y, npt_l.x, npt_l.y);
145  // Append to active tracked point list
146  cv::KeyPoint kpt;
147  kpt.pt = corners[cam_id].at(i).at(n);
148  ids_new.push_back(tmp_id);
149  pts_new.push_back(kpt);
150  }
151  }
152 
153  // Move forward in time
154  {
155  std::lock_guard<std::mutex> lckv(mtx_last_vars);
156  img_last[cam_id] = img;
157  img_mask_last[cam_id] = maskin;
158  ids_last[cam_id] = ids_new;
159  pts_last[cam_id] = pts_new;
160  }
161  rT3 = boost::posix_time::microsec_clock::local_time();
162 
163  // Timing information
164  PRINT_ALL("[TIME-ARUCO]: %.4f seconds for detection\n", (rT2 - rT1).total_microseconds() * 1e-6);
165  PRINT_ALL("[TIME-ARUCO]: %.4f seconds for feature DB update (%d features)\n", (rT3 - rT2).total_microseconds() * 1e-6,
166  (int)ids_new.size());
167  PRINT_ALL("[TIME-ARUCO]: %.4f seconds for total\n", (rT3 - rT1).total_microseconds() * 1e-6);
168 }
169 
170 void TrackAruco::display_active(cv::Mat &img_out, int r1, int g1, int b1, int r2, int g2, int b2, std::string overlay) {
171 
172  // Cache the images to prevent other threads from editing while we viz (which can be slow)
173  std::map<size_t, cv::Mat> img_last_cache, img_mask_last_cache;
174  {
175  std::lock_guard<std::mutex> lckv(mtx_last_vars);
176  img_last_cache = img_last;
177  img_mask_last_cache = img_mask_last;
178  }
179  auto ids_aruco_cache = ids_aruco;
180  auto corners_cache = corners;
181  auto rejects_cache = rejects;
182 
183  // Get the largest width and height
184  int max_width = -1;
185  int max_height = -1;
186  for (auto const &pair : img_last_cache) {
187  if (max_width < pair.second.cols)
188  max_width = pair.second.cols;
189  if (max_height < pair.second.rows)
190  max_height = pair.second.rows;
191  }
192 
193  // Return if we didn't have a last image
194  if (img_last_cache.empty() || max_width == -1 || max_height == -1)
195  return;
196 
197  // If the image is "small" thus we shoudl use smaller display codes
198  bool is_small = (std::min(max_width, max_height) < 400);
199 
200  // If the image is "new" then draw the images from scratch
201  // Otherwise, we grab the subset of the main image and draw on top of it
202  bool image_new = ((int)img_last_cache.size() * max_width != img_out.cols || max_height != img_out.rows);
203 
204  // If new, then resize the current image
205  if (image_new)
206  img_out = cv::Mat(max_height, (int)img_last_cache.size() * max_width, CV_8UC3, cv::Scalar(0, 0, 0));
207 
208  // Loop through each image, and draw
209  int index_cam = 0;
210  for (auto const &pair : img_last_cache) {
211  // select the subset of the image
212  cv::Mat img_temp;
213  if (image_new)
214  cv::cvtColor(img_last_cache[pair.first], img_temp, cv::COLOR_GRAY2RGB);
215  else
216  img_temp = img_out(cv::Rect(max_width * index_cam, 0, max_width, max_height));
217  // draw...
218  if (!ids_aruco_cache[pair.first].empty())
219  cv::aruco::drawDetectedMarkers(img_temp, corners_cache[pair.first], ids_aruco_cache[pair.first]);
220  if (!rejects_cache[pair.first].empty())
221  cv::aruco::drawDetectedMarkers(img_temp, rejects_cache[pair.first], cv::noArray(), cv::Scalar(100, 0, 255));
222  // Draw what camera this is
223  auto txtpt = (is_small) ? cv::Point(10, 30) : cv::Point(30, 60);
224  if (overlay == "") {
225  cv::putText(img_temp, "CAM:" + std::to_string((int)pair.first), txtpt, cv::FONT_HERSHEY_COMPLEX_SMALL, (is_small) ? 1.5 : 3.0,
226  cv::Scalar(0, 255, 0), 3);
227  } else {
228  cv::putText(img_temp, overlay, txtpt, cv::FONT_HERSHEY_COMPLEX_SMALL, (is_small) ? 1.5 : 3.0, cv::Scalar(0, 0, 255), 3);
229  }
230  // Overlay the mask
231  cv::Mat mask = cv::Mat::zeros(img_mask_last_cache[pair.first].rows, img_mask_last_cache[pair.first].cols, CV_8UC3);
232  mask.setTo(cv::Scalar(0, 0, 255), img_mask_last_cache[pair.first]);
233  cv::addWeighted(mask, 0.1, img_temp, 1.0, 0.0, img_temp);
234  // Replace the output image
235  img_temp.copyTo(img_out(cv::Rect(max_width * index_cam, 0, img_last_cache[pair.first].cols, img_last_cache[pair.first].rows)));
236  index_cam++;
237  }
238 }
239 
240 #endif
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
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
std::vector< cv::Mat > images
Raw image we have collected for each camera.
Definition: sensor_data.h:64
boost::posix_time::ptime rT2
Definition: TrackBase.h:195
std::unordered_map< size_t, std::vector< cv::KeyPoint > > pts_last
Last set of tracked points.
Definition: TrackBase.h:186
Struct for a collection of camera measurements.
Definition: sensor_data.h:55
#define RESET
Definition: colors.h:25
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
boost::posix_time::ptime rT3
Definition: TrackBase.h:195
std::vector< int > sensor_ids
Camera ids for each of the images collected.
Definition: sensor_data.h:61
HistogramMethod histogram_method
What histogram equalization method we should pre-process images with?
Definition: TrackBase.h:171
std::vector< cv::Mat > masks
Tracking masks for each camera we have.
Definition: sensor_data.h:67
void feed_new_camera(const CameraData &message) override
Process a new image.
Definition: TrackAruco.cpp:31
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
std::unordered_map< size_t, std::shared_ptr< CamBase > > camera_calib
Camera object which has all calibration in it.
Definition: TrackBase.h:156
#define RED
Definition: colors.h:27
Core algorithms for OpenVINS.
Definition: CamBase.h:30
std::shared_ptr< FeatureDatabase > database
Database with all our current features.
Definition: TrackBase.h:159
boost::posix_time::ptime rT1
Definition: TrackBase.h:195
std::mutex mtx_last_vars
Mutex for editing the *_last variables.
Definition: TrackBase.h:177
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
Author(s): Patrick Geneva , Kevin Eckenhoff , Guoquan Huang
autogenerated on Wed Jun 21 2023 03:05:36