PhotonVision C++ dev-v2025.0.0-beta-8-2-gbd1c5c03
Loading...
Searching...
No Matches
OpenCVHelp.h
Go to the documentation of this file.
1/*
2 * Copyright (C) Photon Vision.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#pragma once
19
20#include <utility>
21#include <vector>
22
23#include <Eigen/Core>
24#include <opencv2/calib3d.hpp>
25#include <opencv2/core.hpp>
26#include <opencv2/imgproc.hpp>
27
28#include "RotTrlTransform3d.h"
29
30#define OPENCV_DISABLE_EIGEN_TENSOR_SUPPORT
31#include <opencv2/core/eigen.hpp>
34
36
37namespace photon {
38namespace OpenCVHelp {
39
40static frc::Rotation3d NWU_TO_EDN{
41 (Eigen::Matrix3d() << 0, -1, 0, 0, 0, -1, 1, 0, 0).finished()};
42static frc::Rotation3d EDN_TO_NWU{
43 (Eigen::Matrix3d() << 0, 0, 1, -1, 0, 0, 0, -1, 0).finished()};
44
45static std::vector<cv::Point2f> GetConvexHull(
46 const std::vector<cv::Point2f>& points) {
47 std::vector<int> outputHull{};
48 cv::convexHull(points, outputHull);
49 std::vector<cv::Point2f> convexPoints;
50 for (size_t i = 0; i < outputHull.size(); i++) {
51 convexPoints.push_back(points[outputHull[i]]);
52 }
53 return convexPoints;
54}
55
56[[maybe_unused]] static cv::RotatedRect GetMinAreaRect(
57 const std::vector<cv::Point2f>& points) {
58 return cv::minAreaRect(points);
59}
60
61static frc::Translation3d TranslationNWUtoEDN(const frc::Translation3d& trl) {
62 return trl.RotateBy(NWU_TO_EDN);
63}
64
65static frc::Rotation3d RotationNWUtoEDN(const frc::Rotation3d& rot) {
66 return -NWU_TO_EDN + (rot + NWU_TO_EDN);
67}
68
69static std::vector<cv::Point3f> TranslationToTVec(
70 const std::vector<frc::Translation3d>& translations) {
71 std::vector<cv::Point3f> retVal;
72 retVal.reserve(translations.size());
73 for (size_t i = 0; i < translations.size(); i++) {
74 frc::Translation3d trl = TranslationNWUtoEDN(translations[i]);
75 retVal.emplace_back(cv::Point3f{trl.X().to<float>(), trl.Y().to<float>(),
76 trl.Z().to<float>()});
77 }
78 return retVal;
79}
80
81static std::vector<cv::Point3f> RotationToRVec(
82 const frc::Rotation3d& rotation) {
83 std::vector<cv::Point3f> retVal{};
84 frc::Rotation3d rot = RotationNWUtoEDN(rotation);
85 retVal.emplace_back(cv::Point3d{
86 rot.GetQuaternion().ToRotationVector()(0),
87 rot.GetQuaternion().ToRotationVector()(1),
88 rot.GetQuaternion().ToRotationVector()(2),
89 });
90 return retVal;
91}
92
93[[maybe_unused]] static cv::Point2f AvgPoint(std::vector<cv::Point2f> points) {
94 if (points.size() == 0) {
95 return cv::Point2f{};
96 }
97 cv::reduce(points, points, 0, cv::REDUCE_AVG);
98 return points[0];
99}
100
101[[maybe_unused]] static std::vector<photon::TargetCorner> PointsToTargetCorners(
102 const std::vector<cv::Point2f>& points) {
103 std::vector<photon::TargetCorner> retVal;
104 retVal.reserve(points.size());
105 for (size_t i = 0; i < points.size(); i++) {
106 retVal.emplace_back(photon::TargetCorner{points[i].x, points[i].y});
107 }
108 return retVal;
109}
110
111[[maybe_unused]] static std::vector<std::pair<float, float>> PointsToCorners(
112 const std::vector<cv::Point2f>& points) {
113 std::vector<std::pair<float, float>> retVal;
114 retVal.reserve(points.size());
115 for (size_t i = 0; i < points.size(); i++) {
116 retVal.emplace_back(std::make_pair(points[i].x, points[i].y));
117 }
118 return retVal;
119}
120
121[[maybe_unused]] static std::vector<cv::Point2f> CornersToPoints(
122 const std::vector<std::pair<float, float>>& corners) {
123 std::vector<cv::Point2f> retVal;
124 retVal.reserve(corners.size());
125 for (size_t i = 0; i < corners.size(); i++) {
126 retVal.emplace_back(cv::Point2f{corners[i].first, corners[i].second});
127 }
128 return retVal;
129}
130
131[[maybe_unused]] static std::vector<cv::Point2f> CornersToPoints(
132 const std::vector<photon::TargetCorner>& corners) {
133 std::vector<cv::Point2f> retVal;
134 retVal.reserve(corners.size());
135 for (size_t i = 0; i < corners.size(); i++) {
136 retVal.emplace_back(cv::Point2f{static_cast<float>(corners[i].x),
137 static_cast<float>(corners[i].y)});
138 }
139 return retVal;
140}
141
142[[maybe_unused]] static cv::Rect GetBoundingRect(
143 const std::vector<cv::Point2f>& points) {
144 return cv::boundingRect(points);
145}
146
147[[maybe_unused]] static std::vector<cv::Point2f> ProjectPoints(
148 const Eigen::Matrix<double, 3, 3>& cameraMatrix,
149 const Eigen::Matrix<double, 8, 1>& distCoeffs,
150 const RotTrlTransform3d& camRt,
151 const std::vector<frc::Translation3d>& objectTranslations) {
152 std::vector<cv::Point3f> objectPoints = TranslationToTVec(objectTranslations);
153 std::vector<cv::Point3f> rvec = RotationToRVec(camRt.GetRotation());
154 std::vector<cv::Point3f> tvec = TranslationToTVec({camRt.GetTranslation()});
155 cv::Mat cameraMat(cameraMatrix.rows(), cameraMatrix.cols(), CV_64F);
156 cv::eigen2cv(cameraMatrix, cameraMat);
157 cv::Mat distCoeffsMat(distCoeffs.rows(), distCoeffs.cols(), CV_64F);
158 cv::eigen2cv(distCoeffs, distCoeffsMat);
159 std::vector<cv::Point2f> imagePoints{};
160 cv::projectPoints(objectPoints, rvec, tvec, cameraMat, distCoeffsMat,
161 imagePoints);
162 return imagePoints;
163}
164
165template <typename T>
166static std::vector<T> ReorderCircular(const std::vector<T> elements,
167 bool backwards, int shiftStart) {
168 size_t size = elements.size();
169 int dir = backwards ? -1 : 1;
170 std::vector<T> reordered{elements};
171 for (size_t i = 0; i < size; i++) {
172 int index = (i * dir + shiftStart * dir) % size;
173 if (index < 0) {
174 index = size + index;
175 }
176 reordered[i] = elements[index];
177 }
178 return reordered;
179}
180
181static frc::Translation3d TranslationEDNToNWU(const frc::Translation3d& trl) {
182 return trl.RotateBy(EDN_TO_NWU);
183}
184
185static frc::Rotation3d RotationEDNToNWU(const frc::Rotation3d& rot) {
186 return -EDN_TO_NWU + (rot + EDN_TO_NWU);
187}
188
189static frc::Translation3d TVecToTranslation(const cv::Mat& tvecInput) {
190 cv::Vec3f data{};
191 cv::Mat wrapped{tvecInput.rows, tvecInput.cols, CV_32F};
192 tvecInput.convertTo(wrapped, CV_32F);
193 data = wrapped.at<cv::Vec3f>(cv::Point{0, 0});
194 return TranslationEDNToNWU(frc::Translation3d{units::meter_t{data[0]},
195 units::meter_t{data[1]},
196 units::meter_t{data[2]}});
197}
198
199static frc::Rotation3d RVecToRotation(const cv::Mat& rvecInput) {
200 cv::Vec3f data{};
201 cv::Mat wrapped{rvecInput.rows, rvecInput.cols, CV_32F};
202 rvecInput.convertTo(wrapped, CV_32F);
203 data = wrapped.at<cv::Vec3f>(cv::Point{0, 0});
204 return RotationEDNToNWU(
205 frc::Rotation3d{Eigen::Vector3d{data[0], data[1], data[2]}});
206}
207
208[[maybe_unused]] static std::optional<photon::PnpResult> SolvePNP_Square(
209 const Eigen::Matrix<double, 3, 3>& cameraMatrix,
210 const Eigen::Matrix<double, 8, 1>& distCoeffs,
211 std::vector<frc::Translation3d> modelTrls,
212 std::vector<cv::Point2f> imagePoints) {
213 modelTrls = ReorderCircular(modelTrls, true, -1);
214 imagePoints = ReorderCircular(imagePoints, true, -1);
215 std::vector<cv::Point3f> objectMat = TranslationToTVec(modelTrls);
216 std::vector<cv::Mat> rvecs;
217 std::vector<cv::Mat> tvecs;
218 cv::Mat rvec = cv::Mat::zeros(3, 1, CV_32F);
219 cv::Mat tvec = cv::Mat::zeros(3, 1, CV_32F);
220 cv::Mat reprojectionError = cv::Mat::zeros(2, 1, CV_32F);
221
222 cv::Mat cameraMat(cameraMatrix.rows(), cameraMatrix.cols(), CV_32F);
223 cv::eigen2cv(cameraMatrix, cameraMat);
224 cv::Mat distCoeffsMat(distCoeffs.rows(), distCoeffs.cols(), CV_32F);
225 cv::eigen2cv(distCoeffs, distCoeffsMat);
226
227 cv::Vec2d errors{};
228 frc::Transform3d best{};
229 std::optional<frc::Transform3d> alt{std::nullopt};
230
231 for (int tries = 0; tries < 2; tries++) {
232 cv::solvePnPGeneric(objectMat, imagePoints, cameraMat, distCoeffsMat, rvecs,
233 tvecs, false, cv::SOLVEPNP_IPPE_SQUARE, rvec, tvec,
234 reprojectionError);
235
236 errors = reprojectionError.at<cv::Vec2f>(cv::Point{0, 0});
237 best = frc::Transform3d{TVecToTranslation(tvecs.at(0)),
238 RVecToRotation(rvecs[0])};
239
240 if (tvecs.size() > 1) {
241 alt = frc::Transform3d{TVecToTranslation(tvecs.at(1)),
242 RVecToRotation(rvecs[1])};
243 }
244
245 if (!std::isnan(errors[0])) {
246 break;
247 } else {
248 cv::Point2f pt = imagePoints[0];
249 pt.x -= 0.001f;
250 pt.y -= 0.001f;
251 imagePoints[0] = pt;
252 }
253 }
254
255 if (std::isnan(errors[0])) {
256 fmt::print("SolvePNP_Square failed!\n");
257 return std::nullopt;
258 }
259 if (alt) {
260 photon::PnpResult result;
261 result.best = best;
262 result.alt = alt.value();
263 result.ambiguity = errors[0] / errors[1];
264 result.bestReprojErr = errors[0];
265 result.altReprojErr = errors[1];
266 return result;
267 } else {
268 photon::PnpResult result;
269 result.best = best;
270 result.bestReprojErr = errors[0];
271 return result;
272 }
273}
274
275[[maybe_unused]] static std::optional<photon::PnpResult> SolvePNP_SQPNP(
276 const Eigen::Matrix<double, 3, 3>& cameraMatrix,
277 const Eigen::Matrix<double, 8, 1>& distCoeffs,
278 std::vector<frc::Translation3d> modelTrls,
279 std::vector<cv::Point2f> imagePoints) {
280 std::vector<cv::Point3f> objectMat = TranslationToTVec(modelTrls);
281 std::vector<cv::Mat> rvecs{};
282 std::vector<cv::Mat> tvecs{};
283 cv::Mat rvec = cv::Mat::zeros(3, 1, CV_32F);
284 cv::Mat tvec = cv::Mat::zeros(3, 1, CV_32F);
285 cv::Mat reprojectionError = cv::Mat::zeros(2, 1, CV_32F);
286
287 cv::Mat cameraMat(cameraMatrix.rows(), cameraMatrix.cols(), CV_64F);
288 cv::eigen2cv(cameraMatrix, cameraMat);
289 cv::Mat distCoeffsMat(distCoeffs.rows(), distCoeffs.cols(), CV_64F);
290 cv::eigen2cv(distCoeffs, distCoeffsMat);
291
292 float error = 0;
293 frc::Transform3d best{};
294
295 cv::solvePnPGeneric(objectMat, imagePoints, cameraMat, distCoeffsMat, rvecs,
296 tvecs, false, cv::SOLVEPNP_SQPNP, rvec, tvec,
297 reprojectionError);
298
299 error = reprojectionError.at<float>(cv::Point{0, 0});
300 best = frc::Transform3d{TVecToTranslation(tvecs.at(0)),
301 RVecToRotation(rvecs[0])};
302
303 if (std::isnan(error)) {
304 fmt::print("SolvePNP_Square failed!\n");
305 }
306 photon::PnpResult result;
307 result.best = best;
308 result.bestReprojErr = error;
309 return result;
310}
311} // namespace OpenCVHelp
312} // namespace photon
Definition RotTrlTransform3d.h:27
frc::Rotation3d GetRotation() const
Definition RotTrlTransform3d.h:56
frc::Translation3d GetTranslation() const
Definition RotTrlTransform3d.h:54
Definition TargetCorner.h:25
static std::vector< cv::Point3f > RotationToRVec(const frc::Rotation3d &rotation)
Definition OpenCVHelp.h:81
static cv::RotatedRect GetMinAreaRect(const std::vector< cv::Point2f > &points)
Definition OpenCVHelp.h:56
static frc::Rotation3d NWU_TO_EDN
Definition OpenCVHelp.h:40
static std::vector< photon::TargetCorner > PointsToTargetCorners(const std::vector< cv::Point2f > &points)
Definition OpenCVHelp.h:101
static cv::Point2f AvgPoint(std::vector< cv::Point2f > points)
Definition OpenCVHelp.h:93
static frc::Rotation3d RotationNWUtoEDN(const frc::Rotation3d &rot)
Definition OpenCVHelp.h:65
static frc::Rotation3d RVecToRotation(const cv::Mat &rvecInput)
Definition OpenCVHelp.h:199
static std::optional< photon::PnpResult > SolvePNP_SQPNP(const Eigen::Matrix< double, 3, 3 > &cameraMatrix, const Eigen::Matrix< double, 8, 1 > &distCoeffs, std::vector< frc::Translation3d > modelTrls, std::vector< cv::Point2f > imagePoints)
Definition OpenCVHelp.h:275
static frc::Translation3d TranslationEDNToNWU(const frc::Translation3d &trl)
Definition OpenCVHelp.h:181
static cv::Rect GetBoundingRect(const std::vector< cv::Point2f > &points)
Definition OpenCVHelp.h:142
static frc::Translation3d TranslationNWUtoEDN(const frc::Translation3d &trl)
Definition OpenCVHelp.h:61
static frc::Rotation3d RotationEDNToNWU(const frc::Rotation3d &rot)
Definition OpenCVHelp.h:185
static frc::Rotation3d EDN_TO_NWU
Definition OpenCVHelp.h:42
static std::vector< std::pair< float, float > > PointsToCorners(const std::vector< cv::Point2f > &points)
Definition OpenCVHelp.h:111
static std::vector< T > ReorderCircular(const std::vector< T > elements, bool backwards, int shiftStart)
Definition OpenCVHelp.h:166
static std::vector< cv::Point3f > TranslationToTVec(const std::vector< frc::Translation3d > &translations)
Definition OpenCVHelp.h:69
static frc::Translation3d TVecToTranslation(const cv::Mat &tvecInput)
Definition OpenCVHelp.h:189
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< frc::Translation3d > &objectTranslations)
Definition OpenCVHelp.h:147
static std::vector< cv::Point2f > GetConvexHull(const std::vector< cv::Point2f > &points)
Definition OpenCVHelp.h:45
static std::optional< photon::PnpResult > SolvePNP_Square(const Eigen::Matrix< double, 3, 3 > &cameraMatrix, const Eigen::Matrix< double, 8, 1 > &distCoeffs, std::vector< frc::Translation3d > modelTrls, std::vector< cv::Point2f > imagePoints)
Definition OpenCVHelp.h:208
static std::vector< cv::Point2f > CornersToPoints(const std::vector< std::pair< float, float > > &corners)
Definition OpenCVHelp.h:121
Definition VisionEstimation.h:32
double altReprojErr
Definition PnpResultStruct.h:33
double bestReprojErr
Definition PnpResultStruct.h:32
double ambiguity
Definition PnpResultStruct.h:34
frc::Transform3d alt
Definition PnpResultStruct.h:31
frc::Transform3d best
Definition PnpResultStruct.h:30
Definition PnpResult.h:29