35 #include <opencv2/highgui/highgui.hpp>
37 #include <opencv2/imgproc/imgproc.hpp>
46 static const auto CVCOLOR_RED = cv::Vec3b(0, 0, 255);
47 static const auto CVCOLOR_YELLOW = cv::Vec3b(0, 255, 255);
48 static const auto CVCOLOR_GREEN = cv::Vec3b(0, 255, 0);
49 static const auto CVCOLOR_BLUE = cv::Vec3b(255, 0, 0);
50 static const auto CVCOLOR_GRAY = cv::Vec3b(127, 127, 127);
51 static const auto CVCOLOR_BLACK = cv::Vec3b(0, 0, 0);
52 static const auto CVCOLOR_WHITE = cv::Vec3b(255, 255, 255);
53 static const auto CVCOLOR_VIOLET = cv::Vec3b(127, 0, 255);
55 static const auto DEBUG_WINDOW_NAME =
"OSVR Tracker Debug Window";
56 static const auto DEBUG_FRAME_STRIDE = 11;
57 TrackingDebugDisplay::TrackingDebugDisplay(ConfigParams
const ¶ms)
58 : m_enabled(params.debug), m_windowName(DEBUG_WINDOW_NAME),
59 m_debugStride(DEBUG_FRAME_STRIDE),
60 m_performingOptimization(params.performingOptimization) {
65 if (m_performingOptimization) {
66 m_mode = DebugDisplayMode::StatusWithAllReprojections;
68 std::cout <<
"\nVideo-based tracking debug windows help:\n";
70 <<
" - press 's' to show the detected blobs and the status of "
71 "recognized beacons (default)\n"
72 <<
" - press 'p' to show the same status view as above, but "
73 "with the (re)projected locations of even non-detected "
75 <<
" - press 'b' to show the labeled blobs and the "
76 "reprojected beacons\n"
77 <<
" - press 'i' to show the raw input image\n"
78 <<
" - press 't' to show the blob-detecting threshold image\n"
79 <<
" - press 'q' to quit the debug windows (tracker will "
80 "continue operation)\n"
85 void TrackingDebugDisplay::showDebugImage(cv::Mat
const &image,
88 image.copyTo(m_displayedFrame);
90 m_displayedFrame = image;
92 cv::imshow(m_windowName, m_displayedFrame);
95 void TrackingDebugDisplay::quitDebug() {
99 cv::destroyWindow(m_windowName);
110 return USING_INVERTED_LED_POSITION
113 : WindowCoordsPoint{loc};
116 inline Eigen::Vector2d pointToEigenVec(cv::Point2f pt) {
117 return Eigen::Vector2d(pt.x, pt.y);
119 inline cv::Point2f eigenVecToPoint(Eigen::Vector2d
const &vec) {
120 return cv::Point2f(vec.x(), vec.y());
127 explicit DebugImage(cv::Mat &im) : image_(im) {}
128 DebugImage(DebugImage
const &) =
delete;
129 DebugImage &operator=(DebugImage
const &) =
delete;
133 void drawLedCircle(Led
const &led,
bool filled, cv::Vec3b color) {
134 cv::circle(image_, led.getLocation(),
135 led.getMeasurement().diameter / 2.,
141 void drawLedLabel(OneBasedBeaconId
id, WindowCoordsPoint
const &loc,
142 cv::Vec3b color = CVCOLOR_GRAY,
double size = 0.5,
143 cv::Point2f offset = cv::Point2f(0, 0)) {
144 auto label = std::to_string(
id.value());
145 cv::putText(image_, label, loc.point + offset,
150 void drawLedLabel(OneBasedBeaconId
id, cv::Point2f location,
151 cv::Vec3b color = CVCOLOR_GRAY,
double size = 0.5,
152 cv::Point2f offset = cv::Point2f(0, 0)) {
153 drawLedLabel(
id, toWindowCoords(location), color,
size, offset);
158 void drawLedLabel(OneBasedBeaconId
id, Eigen::Vector2d
const &loc,
159 cv::Vec3b color = CVCOLOR_GRAY,
double size = 0.5,
160 cv::Point2f offset = cv::Point2f(0, 0)) {
161 drawLedLabel(
id, eigenVecToPoint(loc), color,
size, offset);
166 void drawLedLabel(Led
const &led, cv::Vec3b color = CVCOLOR_GRAY,
168 cv::Point2f offset = cv::Point2f(0, 0)) {
169 drawLedLabel(led.getOneBasedID(),
170 WindowCoordsPoint{led.getLocation()}, color,
size,
174 void drawStatusMessage(std::string message,
175 cv::Vec3b color = CVCOLOR_GREEN,
176 std::int16_t line = 1) {
177 static const auto LINE_OFFSET = 20.f;
178 cv::putText(image_, message, cv::Point2f(0, line * LINE_OFFSET),
179 cv::FONT_HERSHEY_SIMPLEX, 0.5,
cv::Scalar(color));
182 WindowCoordsPoint toWindowCoords(cv::Point2f loc)
const {
183 return USING_INVERTED_LED_POSITION
184 ? WindowCoordsPoint{cv::Point2f(image_.cols - loc.x,
185 image_.rows - loc.y)}
186 : WindowCoordsPoint{loc};
190 vecToWindowCoords(Eigen::Vector2d
const &loc)
const {
191 return toWindowCoords(eigenVecToPoint(loc));
194 cv::Mat &image() {
return image_; }
204 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
205 Reprojection(TrackedBodyTarget
const &target,
206 CameraParameters
const &camParams)
207 : m_body(target.getBody()),
208 m_xform3d(m_body.getState().getIsometry()),
209 m_offset(target.getTargetToBody()),
210 m_fl(camParams.focalLength()),
211 m_pp(camParams.eiPrincipalPoint()) {}
215 Eigen::Vector2d operator()(Eigen::Vector3d
const &bodyPoint) {
216 Eigen::Vector3d camPoints = m_xform3d * (bodyPoint + m_offset);
217 return (camPoints.head<2>() / camPoints[2]) * m_fl + m_pp;
221 TrackedBody
const &m_body;
222 Eigen::Isometry3d m_xform3d;
223 Eigen::Vector3d m_offset;
225 Eigen::Vector2d m_pp;
228 inline void drawOriginAxes(Reprojection &reproject, DebugImage &dbgImg,
229 double size = 0.02) {
230 auto drawLine = [&](Eigen::Vector3d
const &axis, cv::Vec3b color) {
231 auto beginWindowPoint =
232 dbgImg.vecToWindowCoords(reproject(axis * 0.5 *
size));
233 auto endWindowPoint =
234 dbgImg.vecToWindowCoords(reproject(axis * -0.5 * size));
235 cv::line(dbgImg.image(), beginWindowPoint.point,
239 drawLine(Eigen::Vector3d::UnitX(), CVCOLOR_RED);
240 drawLine(Eigen::Vector3d::UnitY(), CVCOLOR_GREEN);
241 drawLine(Eigen::Vector3d::UnitZ(), CVCOLOR_BLUE);
244 inline Eigen::Vector2d
245 getBeaconReprojection(Reprojection &reprojection,
246 TrackedBodyTarget
const &target,
247 ZeroBasedBeaconId beaconId) {
251 return reprojection(target.getBeaconAutocalibPosition(beaconId) -
252 target.getBeaconOffset());
254 return reprojection(target.getBeaconAutocalibPosition(beaconId));
258 cv::Mat TrackingDebugDisplay::createAnnotatedBlobImage(
259 TrackingSystem
const &tracking, CameraParameters
const &camParams,
260 cv::Mat
const &blobImage) {
262 blobImage.copyTo(output);
263 DebugImage img(output);
264 if (tracking.getNumBodies() == 0) {
266 img.drawStatusMessage(
267 "No tracked bodies registered, only showing detected blobs",
272 auto &body = tracking.getBody(BodyId{0});
274 auto targetPtr = body.getTarget(TargetId{0});
278 img.drawStatusMessage(
279 "No target registered on body 0, only showing detected blobs",
283 const auto labelOffset = cv::Point2f(1, 1);
284 const auto textSize = 0.5;
287 auto &leds = targetPtr->leds();
288 for (
auto &led : leds) {
289 img.drawLedLabel(led, CVCOLOR_RED, textSize, labelOffset);
292 if (targetPtr->hasPoseEstimate()) {
294 Reprojection reproject{*targetPtr, camParams};
296 auto numBeacons = targetPtr->getNumBeacons();
297 using size_type = decltype(numBeacons);
298 for (size_type i = 0; i < numBeacons; ++i) {
299 auto beaconId = ZeroBasedBeaconId(i);
300 Eigen::Vector2d imagePoint =
301 getBeaconReprojection(reproject, *targetPtr, beaconId);
302 img.drawLedLabel(makeOneBased(beaconId), imagePoint,
303 CVCOLOR_GREEN, textSize, labelOffset);
306 img.drawStatusMessage(
"No video tracker pose for this "
307 "target, so green reprojection "
314 cv::Mat TrackingDebugDisplay::createStatusImage(
315 TrackingSystem
const &tracking, CameraParameters
const &camParams,
316 cv::Mat
const &baseImage,
bool reprojectUnseenBeacons) {
319 DebugImage img(output);
320 baseImage.copyTo(output);
322 if (tracking.getNumBodies() == 0) {
324 img.drawStatusMessage(
"No tracked bodies registered, "
325 "showing raw input image - press "
331 auto &body = tracking.getBody(BodyId{0});
333 auto targetPtr = body.getTarget(TargetId{0});
337 img.drawStatusMessage(
"No target registered on body 0, "
338 "showing raw input image - press "
347 const auto textSize = 0.25;
348 const auto mainBeaconLabelColor = CVCOLOR_BLUE;
349 const auto baseBeaconLabelColor =
350 m_performingOptimization ? CVCOLOR_WHITE : CVCOLOR_BLACK;
352 auto gotPose = targetPtr->hasPoseEstimate();
353 auto numBeacons = targetPtr->getNumBeacons();
355 using BeaconIdContainer = std::vector<ZeroBasedBeaconId>;
357 auto drawnBeaconIds = BeaconIdContainer{};
358 auto recordBeaconAsDrawn = [&](ZeroBasedBeaconId id) {
359 drawnBeaconIds.push_back(
id);
364 auto drawUnidentifiedBlob = [&img](Led
const &led) {
366 img.drawLedCircle(led,
false, CVCOLOR_RED);
371 Reprojection reproject{*targetPtr, camParams};
374 drawOriginAxes(reproject, img);
376 for (
auto const &led : targetPtr->leds()) {
377 if (led.identified()) {
380 auto beaconId = led.getID();
381 recordBeaconAsDrawn(beaconId);
386 led.wasUsedLastFrame() ? CVCOLOR_GREEN : CVCOLOR_YELLOW;
387 img.drawLedCircle(led,
true, color);
394 img.drawLedLabel(led, baseBeaconLabelColor, textSize);
397 Eigen::Vector2d imagePoint =
398 getBeaconReprojection(reproject, *targetPtr, beaconId);
400 WindowCoordsPoint{eigenVecToPoint(imagePoint)};
401 img.drawLedLabel(makeOneBased(beaconId), imagePoint,
402 mainBeaconLabelColor, textSize);
404 drawUnidentifiedBlob(led);
407 if (reprojectUnseenBeacons) {
409 auto comparator = [](ZeroBasedBeaconId
const &a,
410 ZeroBasedBeaconId
const &b) {
411 return a.value() < b.value();
413 std::sort(begin(drawnBeaconIds), end(drawnBeaconIds),
415 auto haveDrawnBeaconAlready = [&](ZeroBasedBeaconId id) {
416 return std::binary_search(begin(drawnBeaconIds),
417 end(drawnBeaconIds),
id,
421 auto numBeacons = targetPtr->getNumBeacons();
422 for (UnderlyingBeaconIdType i = 0; i < numBeacons; ++i) {
423 auto beaconId = ZeroBasedBeaconId(i);
424 if (haveDrawnBeaconAlready(beaconId)) {
429 Eigen::Vector2d imagePoint =
430 getBeaconReprojection(reproject, *targetPtr, beaconId);
432 WindowCoordsPoint{eigenVecToPoint(imagePoint)};
433 img.drawLedLabel(makeOneBased(beaconId), imagePoint,
434 CVCOLOR_VIOLET, textSize);
439 for (
auto const &led : targetPtr->leds()) {
440 if (led.identified()) {
443 img.drawLedCircle(led,
false, CVCOLOR_YELLOW);
444 img.drawLedLabel(led, baseBeaconLabelColor, textSize);
446 drawUnidentifiedBlob(led);
462 m_debugStride.advance();
463 if (!m_debugStride) {
467 auto &blobEx = impl.blobExtractor;
470 case DebugDisplayMode::InputImage:
471 showDebugImage(impl.frame);
473 case DebugDisplayMode::Thresholding:
474 showDebugImage(blobEx->getDebugThresholdImage());
476 case DebugDisplayMode::Blobs:
478 createAnnotatedBlobImage(tracking, impl.
camParams,
479 blobEx->getDebugBlobImage()),
482 case DebugDisplayMode::Status:
484 createStatusImage(tracking, impl.
camParams, impl.frame),
false);
486 case DebugDisplayMode::StatusWithAllReprojections:
488 createStatusImage(tracking, impl.
camParams, impl.frame,
true),
494 int key = cv::waitKey(1) & 0xff;
495 if (m_performingOptimization) {
506 msg() <<
"'s' pressed - Switching to the status image in the "
509 m_mode = DebugDisplayMode::Status;
515 msg() <<
"'b' pressed - Switching to the blobs image in the "
518 m_mode = DebugDisplayMode::Blobs;
524 msg() <<
"'i' pressed - Switching to the input image in the "
527 m_mode = DebugDisplayMode::InputImage;
533 msg() <<
"'t' pressed - Switching to the thresholded image in "
536 m_mode = DebugDisplayMode::Thresholding;
541 msg() <<
"'p' pressed - Switching to the status image with all "
542 "reprojections in the debug window."
544 m_mode = DebugDisplayMode::StatusWithAllReprojections;
550 msg() <<
"'q' pressed - quitting the debug window." << std::endl;
560 msg() <<
"Toggling orientation usage to " << std::boolalpha
561 << newState << std::endl;
571 std::ostream &TrackingDebugDisplay::msg()
const {
572 return std::cout <<
"[Tracking Debug Display] ";
Private implementation structure for TrackingSystem.
void setUseIMU(bool useIMU)
ConfigParams const & getParams() const
detail::size< coerce_list< Ts...>> size
Get the size of a list (number of elements.)
IMUInputParams imu
IMU input-related parameters.
void triggerDisplay(TrackingSystem &tracking, TrackingSystem::Impl const &impl)
double Scalar
Common scalar type.
CameraParameters camParams
Cached copy of the last (undistorted) camera parameters to be used.