PhotonVision C++ v2027.0.0-alpha-2
Loading...
Searching...
No Matches
VideoSimUtil.h
Go to the documentation of this file.
1/*
2 * MIT License
3 *
4 * Copyright (c) PhotonVision
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25#pragma once
26
27#include <algorithm>
28#include <numeric>
29#include <string>
30#include <unordered_map>
31#include <utility>
32#include <vector>
33
34#include <opencv2/core.hpp>
35#include <opencv2/imgcodecs.hpp>
36#include <opencv2/objdetect.hpp>
37#include <wpi/apriltag/AprilTag.hpp>
38#include <wpi/cs/CvSource.hpp>
39#include <wpi/units/length.hpp>
40
42
43namespace mathutil {
44template <typename T>
45int sgn(T val) {
46 return (T(0) < val) - (val < T(0));
47}
48} // namespace mathutil
49
50namespace photon {
51namespace VideoSimUtil {
52// Tag IDs start at 0, this should be set to 1 greater than the maximum tag ID
53// required
54static constexpr int kNumTags36h11 = 40;
55
56static constexpr wpi::units::meter_t fieldLength{16.54175_m};
57static constexpr wpi::units::meter_t fieldWidth{8.0137_m};
58
59static cv::Mat Get36h11TagImage(int id) {
60 wpi::util::RawFrame frame;
61 wpi::apriltag::AprilTag::Generate36h11AprilTagImage(&frame, id);
62 cv::Mat markerImage{frame.height, frame.width, CV_8UC1, frame.data,
63 static_cast<size_t>(frame.stride)};
64 cv::Mat markerClone = markerImage.clone();
65 return markerClone;
66}
67
68static std::unordered_map<int, cv::Mat> LoadAprilTagImages() {
69 std::unordered_map<int, cv::Mat> retVal{};
70 for (int i = 0; i < kNumTags36h11; i++) {
71 cv::Mat tagImage = Get36h11TagImage(i);
72 retVal[i] = tagImage;
73 }
74 return retVal;
75}
76
77/**
78 * Gets the points representing the corners of this image. Because image pixels
79 * are accessed through a cv::Mat, the point (0,0) actually represents the
80 * center of the top-left pixel and not the actual top-left corner.
81 *
82 * <p>Order of corners returned is: [BL, BR, TR, TL]
83 *
84 * @param size Size of image
85 * @return The corners
86 */
87static std::vector<cv::Point2f> GetImageCorners(const cv::Size& size) {
88 std::vector<cv::Point2f> retVal{};
89 retVal.emplace_back(cv::Point2f{-0.5f, size.height - 0.5f});
90 retVal.emplace_back(cv::Point2f{size.width - 0.5f, size.height - 0.5f});
91 retVal.emplace_back(cv::Point2f{size.width - 0.5f, -0.5f});
92 retVal.emplace_back(cv::Point2f{-0.5f, -0.5f});
93 return retVal;
94}
95
96/**
97 * Gets the points representing the marker(black square) corners.
98 *
99 * @param scale The scale of the tag image (10*scale x 10*scale image)
100 * @return The points
101 */
102static std::vector<cv::Point2f> Get36h11MarkerPts(int scale) {
103 cv::Rect2f roi36h11{cv::Point2f{1, 1}, cv::Point2f{8, 8}};
104 roi36h11.x *= scale;
105 roi36h11.y *= scale;
106 roi36h11.width *= scale;
107 roi36h11.height *= scale;
108 std::vector<cv::Point2f> pts = GetImageCorners(roi36h11.size());
109 for (size_t i = 0; i < pts.size(); i++) {
110 cv::Point2f pt = pts[i];
111 pts[i] = cv::Point2f{roi36h11.tl().x + pt.x, roi36h11.tl().y + pt.y};
112 }
113 return pts;
114}
115
116/**
117 * Gets the points representing the marker(black square) corners.
118 *
119 * @return The points
120 */
121static std::vector<cv::Point2f> Get36h11MarkerPts() {
122 return Get36h11MarkerPts(1);
123}
124
125static const std::unordered_map<int, cv::Mat> kTag36h11Images =
127static const std::vector<cv::Point2f> kTag36h11MarkPts = Get36h11MarkerPts();
128
129/** Updates the properties of this cs::CvSource video stream with the given
130 * camera properties. */
131[[maybe_unused]] static void UpdateVideoProp(wpi::cs::CvSource& video,
132 const SimCameraProperties& prop) {
133 video.SetResolution(prop.GetResWidth(), prop.GetResHeight());
134 video.SetFPS(prop.GetFPS().to<int>());
135}
136/**
137 * Warps the image of a specific 36h11 AprilTag onto the destination image at
138 * the given points.
139 *
140 * @param tagId The id of the specific tag to warp onto the destination image
141 * @param dstPoints Points(4) in destination image where the tag marker(black
142 * square) corners should be warped onto.
143 * @param antialiasing If antialiasing should be performed by automatically
144 * supersampling/interpolating the warped image. This should be used if
145 * better stream quality is desired or target detection is being done on the
146 * stream, but can hurt performance.
147 * @param destination The destination image to place the warped tag image onto.
148 */
149[[maybe_unused]] static void Warp165h5TagImage(
150 int tagId, const std::vector<cv::Point2f>& dstPoints, bool antialiasing,
151 cv::Mat& destination) {
152 if (!kTag36h11Images.contains(tagId)) {
153 return;
154 }
155 cv::Mat tagImage = kTag36h11Images.at(tagId);
156 std::vector<cv::Point2f> tagPoints{kTag36h11MarkPts};
157 std::vector<cv::Point2f> tagImageCorners{GetImageCorners(tagImage.size())};
158 std::vector<cv::Point2f> dstPointMat = dstPoints;
159 cv::Rect boundingRect = cv::boundingRect(dstPointMat);
160 cv::Mat perspecTrf = cv::getPerspectiveTransform(tagPoints, dstPointMat);
161 std::vector<cv::Point2f> extremeCorners{};
162 cv::perspectiveTransform(tagImageCorners, extremeCorners, perspecTrf);
163 boundingRect = cv::boundingRect(extremeCorners);
164
165 double warpedContourArea = cv::contourArea(extremeCorners);
166 double warpedTagUpscale =
167 std::sqrt(warpedContourArea) / std::sqrt(tagImage.size().area());
168 int warpStrat = cv::INTER_NEAREST;
169
170 int supersampling = 6;
171 supersampling = static_cast<int>(std::ceil(supersampling / warpedTagUpscale));
172 supersampling = std::max(std::min(supersampling, 10), 1);
173
174 cv::Mat scaledTagImage{};
175 if (warpedTagUpscale > 2.0) {
176 warpStrat = cv::INTER_LINEAR;
177 int scaleFactor = static_cast<int>(warpedTagUpscale / 3.0) + 2;
178 scaleFactor = std::max(std::min(scaleFactor, 40), 1);
179 scaleFactor *= supersampling;
180 cv::resize(tagImage, scaledTagImage, cv::Size{}, scaleFactor, scaleFactor,
181 cv::INTER_NEAREST);
182 tagPoints = Get36h11MarkerPts(scaleFactor);
183 } else {
184 scaledTagImage = tagImage;
185 }
186
187 boundingRect.x -= 1;
188 boundingRect.y -= 1;
189 boundingRect.width += 2;
190 boundingRect.height += 2;
191 if (boundingRect.x < 0) {
192 boundingRect.width += boundingRect.x;
193 boundingRect.x = 0;
194 }
195 if (boundingRect.y < 0) {
196 boundingRect.height += boundingRect.y;
197 boundingRect.y = 0;
198 }
199 boundingRect.width =
200 std::min(destination.size().width - boundingRect.x, boundingRect.width);
201 boundingRect.height =
202 std::min(destination.size().height - boundingRect.y, boundingRect.height);
203 if (boundingRect.width <= 0 || boundingRect.height <= 0) {
204 return;
205 }
206
207 std::vector<cv::Point2f> scaledDstPts{};
208 if (supersampling > 1) {
209 cv::multiply(dstPointMat,
210 cv::Scalar{static_cast<double>(supersampling),
211 static_cast<double>(supersampling)},
212 scaledDstPts);
213 boundingRect.x *= supersampling;
214 boundingRect.y *= supersampling;
215 boundingRect.width *= supersampling;
216 boundingRect.height *= supersampling;
217 } else {
218 scaledDstPts = dstPointMat;
219 }
220
221 cv::subtract(scaledDstPts,
222 cv::Scalar{static_cast<double>(boundingRect.tl().x),
223 static_cast<double>(boundingRect.tl().y)},
224 scaledDstPts);
225 perspecTrf = cv::getPerspectiveTransform(tagPoints, scaledDstPts);
226
227 cv::Mat tempRoi{};
228 cv::warpPerspective(scaledTagImage, tempRoi, perspecTrf, boundingRect.size(),
229 warpStrat);
230
231 if (supersampling > 1) {
232 boundingRect.x /= supersampling;
233 boundingRect.y /= supersampling;
234 boundingRect.width /= supersampling;
235 boundingRect.height /= supersampling;
236 cv::resize(tempRoi, tempRoi, boundingRect.size(), 0, 0, cv::INTER_AREA);
237 }
238
239 cv::Mat tempMask{cv::Mat::zeros(tempRoi.size(), CV_8UC1)};
240 cv::subtract(extremeCorners,
241 cv::Scalar{static_cast<float>(boundingRect.tl().x),
242 static_cast<float>(boundingRect.tl().y)},
243 extremeCorners);
244 cv::Point2f tempCenter{};
245 tempCenter.x =
246 std::accumulate(extremeCorners.begin(), extremeCorners.end(), 0.0,
247 [extremeCorners](float acc, const cv::Point2f& p2) {
248 return acc + p2.x / extremeCorners.size();
249 });
250 tempCenter.y =
251 std::accumulate(extremeCorners.begin(), extremeCorners.end(), 0.0,
252 [extremeCorners](float acc, const cv::Point2f& p2) {
253 return acc + p2.y / extremeCorners.size();
254 });
255
256 for (auto& corner : extremeCorners) {
257 float xDiff = corner.x - tempCenter.x;
258 float yDiff = corner.y - tempCenter.y;
259 xDiff += 1 * mathutil::sgn(xDiff);
260 yDiff += 1 * mathutil::sgn(yDiff);
261 corner = cv::Point2f{tempCenter.x + xDiff, tempCenter.y + yDiff};
262 }
263
264 std::vector<cv::Point> extremeCornerInt{extremeCorners.begin(),
265 extremeCorners.end()};
266 cv::fillConvexPoly(tempMask, extremeCornerInt, cv::Scalar{255});
267
268 cv::copyTo(tempRoi, destination(boundingRect), tempMask);
269}
270
271/**
272 * Given a line thickness in a 640x480 image, try to scale to the given
273 * destination image resolution.
274 *
275 * @param thickness480p A hypothetical line thickness in a 640x480 image
276 * @param destination The destination image to scale to
277 * @return Scaled thickness which cannot be less than 1
278 */
279static double GetScaledThickness(double thickness480p,
280 const cv::Mat& destination) {
281 double scaleX = destination.size().width / 640.0;
282 double scaleY = destination.size().height / 480.0;
283 double minScale = std::min(scaleX, scaleY);
284 return std::max(thickness480p * minScale, 1.0);
285}
286
287/**
288 * Draw a filled ellipse in the destination image.
289 *
290 * @param dstPoints The points in the destination image representing the
291 * rectangle in which the ellipse is inscribed.
292 * @param color The color of the ellipse. This is a scalar with BGR values
293 * (0-255)
294 * @param destination The destination image to draw onto. The image should be in
295 * the BGR color space.
296 */
297[[maybe_unused]] static void DrawInscribedEllipse(
298 const std::vector<cv::Point2f>& dstPoints, const cv::Scalar& color,
299 cv::Mat& destination) {
300 cv::RotatedRect rect = OpenCVHelp::GetMinAreaRect(dstPoints);
301 cv::ellipse(destination, rect, color, -1, cv::LINE_AA);
302}
303
304static void DrawPoly(const std::vector<cv::Point2f>& dstPoints, int thickness,
305 const cv::Scalar& color, bool isClosed,
306 cv::Mat& destination) {
307 std::vector<cv::Point> intDstPoints{dstPoints.begin(), dstPoints.end()};
308 std::vector<std::vector<cv::Point>> listOfListOfPoints;
309 listOfListOfPoints.emplace_back(intDstPoints);
310 if (thickness > 0) {
311 cv::polylines(destination, listOfListOfPoints, isClosed, color, thickness,
312 cv::LINE_AA);
313 } else {
314 cv::fillPoly(destination, listOfListOfPoints, color, cv::LINE_AA);
315 }
316}
317
318/**
319 * Draws a contour around the given points and text of the id onto the
320 * destination image.
321 *
322 * @param id Fiducial ID number to draw
323 * @param dstPoints Points representing the four corners of the tag marker(black
324 * square) in the destination image.
325 * @param destination The destination image to draw onto. The image should be in
326 * the BGR color space.
327 */
328[[maybe_unused]] static void DrawTagDetection(
329 int id, const std::vector<cv::Point2f>& dstPoints, cv::Mat& destination) {
330 double thickness = GetScaledThickness(1, destination);
331 DrawPoly(dstPoints, static_cast<int>(thickness), cv::Scalar{0, 0, 255}, true,
332 destination);
333 cv::Rect2d rect{cv::boundingRect(dstPoints)};
334 cv::Point2d textPt{rect.x + rect.width, rect.y};
335 textPt.x += thickness;
336 textPt.y += thickness;
337 cv::putText(destination, std::to_string(id), textPt, cv::FONT_HERSHEY_PLAIN,
338 1.5 * thickness, cv::Scalar{0, 200, 0},
339 static_cast<int>(thickness), cv::LINE_AA);
340}
341
342/**
343 * The translations used to draw the field side walls and driver station walls.
344 * It is a vector of vectors because the translations are not all connected.
345 */
346static std::vector<std::vector<wpi::math::Translation3d>> GetFieldWallLines() {
347 std::vector<std::vector<wpi::math::Translation3d>> list;
348
349 const wpi::units::meter_t sideHt = 19.5_in;
350 const wpi::units::meter_t driveHt = 35_in;
351 const wpi::units::meter_t topHt = 78_in;
352
353 // field floor
354 list.emplace_back(std::vector<wpi::math::Translation3d>{
355 wpi::math::Translation3d{0_m, 0_m, 0_m},
356 wpi::math::Translation3d{fieldLength, 0_m, 0_m},
357 wpi::math::Translation3d{fieldLength, fieldWidth, 0_m},
358 wpi::math::Translation3d{0_m, fieldWidth, 0_m},
359 wpi::math::Translation3d{0_m, 0_m, 0_m}});
360
361 // right side wall
362 list.emplace_back(std::vector<wpi::math::Translation3d>{
363 wpi::math::Translation3d{0_m, 0_m, 0_m},
364 wpi::math::Translation3d{0_m, 0_m, sideHt},
365 wpi::math::Translation3d{fieldLength, 0_m, sideHt},
366 wpi::math::Translation3d{fieldLength, 0_m, 0_m}});
367
368 // red driverstation
369 list.emplace_back(std::vector<wpi::math::Translation3d>{
370 wpi::math::Translation3d{fieldLength, 0_m, sideHt},
371 wpi::math::Translation3d{fieldLength, 0_m, topHt},
372 wpi::math::Translation3d{fieldLength, fieldWidth, topHt},
373 wpi::math::Translation3d{fieldLength, fieldWidth, sideHt},
374 });
375 list.emplace_back(std::vector<wpi::math::Translation3d>{
376 wpi::math::Translation3d{fieldLength, 0_m, driveHt},
377 wpi::math::Translation3d{fieldLength, fieldWidth, driveHt}});
378
379 // left side wall
380 list.emplace_back(std::vector<wpi::math::Translation3d>{
381 wpi::math::Translation3d{0_m, fieldWidth, 0_m},
382 wpi::math::Translation3d{0_m, fieldWidth, sideHt},
383 wpi::math::Translation3d{fieldLength, fieldWidth, sideHt},
384 wpi::math::Translation3d{fieldLength, fieldWidth, 0_m}});
385
386 // blue driverstation
387 list.emplace_back(std::vector<wpi::math::Translation3d>{
388 wpi::math::Translation3d{0_m, 0_m, sideHt},
389 wpi::math::Translation3d{0_m, 0_m, topHt},
390 wpi::math::Translation3d{0_m, fieldWidth, topHt},
391 wpi::math::Translation3d{0_m, fieldWidth, sideHt},
392 });
393 list.emplace_back(std::vector<wpi::math::Translation3d>{
394 wpi::math::Translation3d{0_m, 0_m, driveHt},
395 wpi::math::Translation3d{0_m, fieldWidth, driveHt}});
396
397 return list;
398}
399
400/**
401 * The translations used to draw the field floor subdivisions (not the floor
402 * outline). It is a vector of vectors because the translations are not all
403 * connected.
404 *
405 * @param subdivisions How many "subdivisions" along the width/length of the
406 * floor. E.g. 3 subdivisions would mean 2 lines along the length and 2 lines
407 * along the width creating a 3x3 "grid".
408 */
409static std::vector<std::vector<wpi::math::Translation3d>> GetFieldFloorLines(
410 int subdivisions) {
411 std::vector<std::vector<wpi::math::Translation3d>> list;
412 const wpi::units::meter_t subLength = fieldLength / subdivisions;
413 const wpi::units::meter_t subWidth = fieldWidth / subdivisions;
414
415 for (int i = 0; i < subdivisions; i++) {
416 list.emplace_back(std::vector<wpi::math::Translation3d>{
417 wpi::math::Translation3d{0_m, subWidth * (i + 1), 0_m},
418 wpi::math::Translation3d{fieldLength, subWidth * (i + 1), 0_m}});
419 list.emplace_back(std::vector<wpi::math::Translation3d>{
420 wpi::math::Translation3d{subLength * (i + 1), 0_m, 0_m},
421 wpi::math::Translation3d{subLength * (i + 1), fieldWidth, 0_m}});
422 }
423 return list;
424}
425
426/**
427 * Convert 3D lines represented by the given series of translations into a
428 * polygon(s) in the camera's image.
429 *
430 * @param camRt The change in basis from world coordinates to camera
431 * coordinates. See RotTrlTransform3d#makeRelativeTo(Pose3d).
432 * @param prop The simulated camera's properties.
433 * @param trls A sequential series of translations defining the polygon to be
434 * drawn.
435 * @param resolution Resolution as a fraction(0 - 1) of the video frame's
436 * diagonal length in pixels. Line segments will be subdivided if they exceed
437 * this resolution.
438 * @param isClosed If the final translation should also draw a line to the first
439 * translation.
440 * @param destination The destination image that is being drawn to.
441 * @return A list of polygons(which are an array of points)
442 */
443static std::vector<std::vector<cv::Point2f>> PolyFrom3dLines(
444 const RotTrlTransform3d& camRt, const SimCameraProperties& prop,
445 const std::vector<wpi::math::Translation3d>& trls, double resolution,
446 bool isClosed, cv::Mat& destination) {
447 resolution = std::hypot(destination.size().height, destination.size().width) *
448 resolution;
449 std::vector<wpi::math::Translation3d> pts{trls};
450 if (isClosed) {
451 pts.emplace_back(pts[0]);
452 }
453 std::vector<std::vector<cv::Point2f>> polyPointList{};
454
455 for (size_t i = 0; i < pts.size() - 1; i++) {
456 wpi::math::Translation3d pta = pts[i];
457 wpi::math::Translation3d ptb = pts[i + 1];
458
459 std::pair<std::optional<double>, std::optional<double>> inter =
460 prop.GetVisibleLine(camRt, pta, ptb);
461 if (!inter.second) {
462 continue;
463 }
464
465 double inter1 = inter.first.value();
466 double inter2 = inter.second.value();
467 wpi::math::Translation3d baseDelta = ptb - pta;
468 wpi::math::Translation3d old_pta = pta;
469 if (inter1 > 0) {
470 pta = old_pta + baseDelta * inter1;
471 }
472 if (inter2 < 1) {
473 ptb = old_pta + baseDelta * inter2;
474 }
475 baseDelta = ptb - pta;
476
477 std::vector<cv::Point2f> poly = OpenCVHelp::ProjectPoints(
478 prop.GetIntrinsics(), prop.GetDistCoeffs(), camRt, {pta, ptb});
479 cv::Point2d pxa = poly[0];
480 cv::Point2d pxb = poly[1];
481
482 double pxDist = std::hypot(pxb.x - pxa.x, pxb.y - pxa.y);
483 int subdivisions = static_cast<int>(pxDist / resolution);
484 wpi::math::Translation3d subDelta = baseDelta / (subdivisions + 1);
485 std::vector<wpi::math::Translation3d> subPts{};
486 for (int j = 0; j < subdivisions; j++) {
487 subPts.emplace_back(pta + (subDelta * (j + 1)));
488 }
489 if (subPts.size() > 0) {
490 std::vector<cv::Point2f> toAdd = OpenCVHelp::ProjectPoints(
491 prop.GetIntrinsics(), prop.GetDistCoeffs(), camRt, subPts);
492 poly.insert(poly.begin() + 1, toAdd.begin(), toAdd.end());
493 }
494
495 polyPointList.emplace_back(poly);
496 }
497
498 return polyPointList;
499}
500
501/**
502 * Draw a wireframe of the field to the given image.
503 *
504 * @param camRt The change in basis from world coordinates to camera
505 * coordinates. See RotTrlTransform3d#makeRelativeTo(wpi::math::Pose3d).
506 * @param prop The simulated camera's properties.
507 * @param resolution Resolution as a fraction(0 - 1) of the video frame's
508 * diagonal length in pixels. Line segments will be subdivided if they exceed
509 * this resolution.
510 * @param wallThickness Thickness of the lines used for drawing the field walls
511 * in pixels. This is scaled by #getScaledThickness(double, cv::Mat).
512 * @param wallColor Color of the lines used for drawing the field walls.
513 * @param floorSubdivisions A NxN "grid" is created from the floor where this
514 * parameter is N, which defines the floor lines.
515 * @param floorThickness Thickness of the lines used for drawing the field floor
516 * grid in pixels. This is scaled by #getScaledThickness(double, cv::Mat).
517 * @param floorColor Color of the lines used for drawing the field floor grid.
518 * @param destination The destination image to draw to.
519 */
520[[maybe_unused]] static void DrawFieldWireFrame(
521 const RotTrlTransform3d& camRt, const SimCameraProperties& prop,
522 double resolution, double wallThickness, const cv::Scalar& wallColor,
523 int floorSubdivisions, double floorThickness, const cv::Scalar& floorColor,
524 cv::Mat& destination) {
525 for (const auto& trls : GetFieldFloorLines(floorSubdivisions)) {
526 auto polys =
527 PolyFrom3dLines(camRt, prop, trls, resolution, false, destination);
528 for (const auto& poly : polys) {
529 DrawPoly(poly,
530 static_cast<int>(
531 std::round(GetScaledThickness(floorThickness, destination))),
532 floorColor, false, destination);
533 }
534 }
535 for (const auto& trls : GetFieldWallLines()) {
536 auto polys =
537 PolyFrom3dLines(camRt, prop, trls, resolution, false, destination);
538 for (const auto& poly : polys) {
539 DrawPoly(poly,
540 static_cast<int>(
541 std::round(GetScaledThickness(wallThickness, destination))),
542 wallColor, false, destination);
543 }
544 }
545}
546} // namespace VideoSimUtil
547} // namespace photon
Definition RotTrlTransform3d.h:27
Calibration and performance values for this camera.
Definition SimCameraProperties.h:56
Eigen::Matrix< double, 8, 1 > GetDistCoeffs() const
Returns the camera calibration's distortion coefficients, in OPENCV8 form.
Definition SimCameraProperties.h:157
wpi::units::hertz_t GetFPS() const
Gets the FPS of the simulated camera.
Definition SimCameraProperties.h:164
int GetResHeight() const
Gets the height of the simulated camera image.
Definition SimCameraProperties.h:136
int GetResWidth() const
Gets the width of the simulated camera image.
Definition SimCameraProperties.h:129
std::pair< std::optional< double >, std::optional< double > > GetVisibleLine(const RotTrlTransform3d &camRt, const wpi::math::Translation3d &a, const wpi::math::Translation3d &b) const
Determines where the line segment defined by the two given translations intersects the camera's frust...
Eigen::Matrix< double, 3, 3 > GetIntrinsics() const
Definition SimCameraProperties.h:149
Definition VideoSimUtil.h:43
int sgn(T val)
Definition VideoSimUtil.h:45
static cv::RotatedRect GetMinAreaRect(const std::vector< cv::Point2f > &points)
Definition OpenCVHelp.h:55
static std::vector< cv::Point2f > ProjectPoints(const Eigen::Matrix< double, 3, 3 > &cameraMatrix, const Eigen::Matrix< double, 8, 1 > &distCoeffs, const RotTrlTransform3d &camRt, const std::vector< wpi::math::Translation3d > &objectTranslations)
Definition OpenCVHelp.h:148
static std::vector< cv::Point2f > Get36h11MarkerPts()
Gets the points representing the marker(black square) corners.
Definition VideoSimUtil.h:121
static void DrawFieldWireFrame(const RotTrlTransform3d &camRt, const SimCameraProperties &prop, double resolution, double wallThickness, const cv::Scalar &wallColor, int floorSubdivisions, double floorThickness, const cv::Scalar &floorColor, cv::Mat &destination)
Draw a wireframe of the field to the given image.
Definition VideoSimUtil.h:520
static cv::Mat Get36h11TagImage(int id)
Definition VideoSimUtil.h:59
static void DrawTagDetection(int id, const std::vector< cv::Point2f > &dstPoints, cv::Mat &destination)
Draws a contour around the given points and text of the id onto the destination image.
Definition VideoSimUtil.h:328
static std::vector< std::vector< wpi::math::Translation3d > > GetFieldFloorLines(int subdivisions)
The translations used to draw the field floor subdivisions (not the floor outline).
Definition VideoSimUtil.h:409
static std::unordered_map< int, cv::Mat > LoadAprilTagImages()
Definition VideoSimUtil.h:68
static void Warp165h5TagImage(int tagId, const std::vector< cv::Point2f > &dstPoints, bool antialiasing, cv::Mat &destination)
Warps the image of a specific 36h11 AprilTag onto the destination image at the given points.
Definition VideoSimUtil.h:149
static std::vector< std::vector< wpi::math::Translation3d > > GetFieldWallLines()
The translations used to draw the field side walls and driver station walls.
Definition VideoSimUtil.h:346
static std::vector< cv::Point2f > GetImageCorners(const cv::Size &size)
Gets the points representing the corners of this image.
Definition VideoSimUtil.h:87
static double GetScaledThickness(double thickness480p, const cv::Mat &destination)
Given a line thickness in a 640x480 image, try to scale to the given destination image resolution.
Definition VideoSimUtil.h:279
static void DrawPoly(const std::vector< cv::Point2f > &dstPoints, int thickness, const cv::Scalar &color, bool isClosed, cv::Mat &destination)
Definition VideoSimUtil.h:304
static constexpr wpi::units::meter_t fieldLength
Definition VideoSimUtil.h:56
static const std::vector< cv::Point2f > kTag36h11MarkPts
Definition VideoSimUtil.h:127
static void DrawInscribedEllipse(const std::vector< cv::Point2f > &dstPoints, const cv::Scalar &color, cv::Mat &destination)
Draw a filled ellipse in the destination image.
Definition VideoSimUtil.h:297
static constexpr int kNumTags36h11
Definition VideoSimUtil.h:54
static std::vector< std::vector< cv::Point2f > > PolyFrom3dLines(const RotTrlTransform3d &camRt, const SimCameraProperties &prop, const std::vector< wpi::math::Translation3d > &trls, double resolution, bool isClosed, cv::Mat &destination)
Convert 3D lines represented by the given series of translations into a polygon(s) in the camera's im...
Definition VideoSimUtil.h:443
static void UpdateVideoProp(wpi::cs::CvSource &video, const SimCameraProperties &prop)
Updates the properties of this cs::CvSource video stream with the given camera properties.
Definition VideoSimUtil.h:131
static const std::unordered_map< int, cv::Mat > kTag36h11Images
Definition VideoSimUtil.h:125
static constexpr wpi::units::meter_t fieldWidth
Definition VideoSimUtil.h:57
Definition VisionEstimation.h:30