PhotonVision C++ v2027.0.0-alpha-2
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>
32
35
36namespace photon {
37namespace OpenCVHelp {
38
39static wpi::math::Rotation3d NWU_TO_EDN{
40 (Eigen::Matrix3d() << 0, -1, 0, 0, 0, -1, 1, 0, 0).finished()};
41static wpi::math::Rotation3d EDN_TO_NWU{
42 (Eigen::Matrix3d() << 0, 0, 1, -1, 0, 0, 0, -1, 0).finished()};
43
44[[maybe_unused]] static std::vector<cv::Point2f> GetConvexHull(
45 const std::vector<cv::Point2f>& points) {
46 std::vector<int> outputHull{};
47 cv::convexHull(points, outputHull);
48 std::vector<cv::Point2f> convexPoints;
49 for (size_t i = 0; i < outputHull.size(); i++) {
50 convexPoints.push_back(points[outputHull[i]]);
51 }
52 return convexPoints;
53}
54
55[[maybe_unused]] static cv::RotatedRect GetMinAreaRect(
56 const std::vector<cv::Point2f>& points) {
57 return cv::minAreaRect(points);
58}
59
60static wpi::math::Translation3d TranslationNWUtoEDN(
61 const wpi::math::Translation3d& trl) {
62 return trl.RotateBy(NWU_TO_EDN);
63}
64
65static wpi::math::Rotation3d RotationNWUtoEDN(
66 const wpi::math::Rotation3d& rot) {
67 return NWU_TO_EDN.Inverse().RotateBy(rot.RotateBy(NWU_TO_EDN));
68}
69
70static std::vector<cv::Point3f> TranslationToTVec(
71 const std::vector<wpi::math::Translation3d>& translations) {
72 std::vector<cv::Point3f> retVal;
73 retVal.reserve(translations.size());
74 for (size_t i = 0; i < translations.size(); i++) {
75 wpi::math::Translation3d trl = TranslationNWUtoEDN(translations[i]);
76 retVal.emplace_back(cv::Point3f{trl.X().to<float>(), trl.Y().to<float>(),
77 trl.Z().to<float>()});
78 }
79 return retVal;
80}
81
82static std::vector<cv::Point3f> RotationToRVec(
83 const wpi::math::Rotation3d& rotation) {
84 std::vector<cv::Point3f> retVal{};
85 wpi::math::Rotation3d rot = RotationNWUtoEDN(rotation);
86 retVal.emplace_back(cv::Point3d{
87 rot.GetQuaternion().ToRotationVector()(0),
88 rot.GetQuaternion().ToRotationVector()(1),
89 rot.GetQuaternion().ToRotationVector()(2),
90 });
91 return retVal;
92}
93
94[[maybe_unused]] static cv::Point2f AvgPoint(std::vector<cv::Point2f> points) {
95 if (points.size() == 0) {
96 return cv::Point2f{};
97 }
98 cv::reduce(points, points, 0, cv::REDUCE_AVG);
99 return points[0];
100}
101
102[[maybe_unused]] static std::vector<photon::TargetCorner> PointsToTargetCorners(
103 const std::vector<cv::Point2f>& points) {
104 std::vector<photon::TargetCorner> retVal;
105 retVal.reserve(points.size());
106 for (size_t i = 0; i < points.size(); i++) {
107 retVal.emplace_back(photon::TargetCorner{points[i].x, points[i].y});
108 }
109 return retVal;
110}
111
112[[maybe_unused]] static std::vector<std::pair<float, float>> PointsToCorners(
113 const std::vector<cv::Point2f>& points) {
114 std::vector<std::pair<float, float>> retVal;
115 retVal.reserve(points.size());
116 for (size_t i = 0; i < points.size(); i++) {
117 retVal.emplace_back(std::make_pair(points[i].x, points[i].y));
118 }
119 return retVal;
120}
121
122[[maybe_unused]] static std::vector<cv::Point2f> CornersToPoints(
123 const std::vector<std::pair<float, float>>& corners) {
124 std::vector<cv::Point2f> retVal;
125 retVal.reserve(corners.size());
126 for (size_t i = 0; i < corners.size(); i++) {
127 retVal.emplace_back(cv::Point2f{corners[i].first, corners[i].second});
128 }
129 return retVal;
130}
131
132[[maybe_unused]] static std::vector<cv::Point2f> CornersToPoints(
133 const std::vector<photon::TargetCorner>& corners) {
134 std::vector<cv::Point2f> retVal;
135 retVal.reserve(corners.size());
136 for (size_t i = 0; i < corners.size(); i++) {
137 retVal.emplace_back(cv::Point2f{static_cast<float>(corners[i].x),
138 static_cast<float>(corners[i].y)});
139 }
140 return retVal;
141}
142
143[[maybe_unused]] static cv::Rect GetBoundingRect(
144 const std::vector<cv::Point2f>& points) {
145 return cv::boundingRect(points);
146}
147
148[[maybe_unused]] static std::vector<cv::Point2f> ProjectPoints(
149 const Eigen::Matrix<double, 3, 3>& cameraMatrix,
150 const Eigen::Matrix<double, 8, 1>& distCoeffs,
151 const RotTrlTransform3d& camRt,
152 const std::vector<wpi::math::Translation3d>& objectTranslations) {
153 std::vector<cv::Point3f> objectPoints = TranslationToTVec(objectTranslations);
154 std::vector<cv::Point3f> rvec = RotationToRVec(camRt.GetRotation());
155 std::vector<cv::Point3f> tvec = TranslationToTVec({camRt.GetTranslation()});
156 cv::Mat cameraMat(cameraMatrix.rows(), cameraMatrix.cols(), CV_64F);
157 cv::eigen2cv(cameraMatrix, cameraMat);
158 cv::Mat distCoeffsMat(distCoeffs.rows(), distCoeffs.cols(), CV_64F);
159 cv::eigen2cv(distCoeffs, distCoeffsMat);
160 std::vector<cv::Point2f> imagePoints{};
161 cv::projectPoints(objectPoints, rvec, tvec, cameraMat, distCoeffsMat,
162 imagePoints);
163 return imagePoints;
164}
165
166template <typename T>
167static std::vector<T> ReorderCircular(const std::vector<T> elements,
168 bool backwards, int shiftStart) {
169 size_t size = elements.size();
170 int dir = backwards ? -1 : 1;
171 std::vector<T> reordered{elements};
172 for (size_t i = 0; i < size; i++) {
173 int index = (i * dir + shiftStart * dir) % size;
174 if (index < 0) {
175 index = size + index;
176 }
177 reordered[i] = elements[index];
178 }
179 return reordered;
180}
181
182static wpi::math::Translation3d TranslationEDNToNWU(
183 const wpi::math::Translation3d& trl) {
184 return trl.RotateBy(EDN_TO_NWU);
185}
186
187static wpi::math::Rotation3d RotationEDNToNWU(
188 const wpi::math::Rotation3d& rot) {
189 return EDN_TO_NWU.Inverse().RotateBy(rot.RotateBy(EDN_TO_NWU));
190}
191
192static wpi::math::Translation3d TVecToTranslation(const cv::Mat& tvecInput) {
193 cv::Vec3f data{};
194 cv::Mat wrapped{tvecInput.rows, tvecInput.cols, CV_32F};
195 tvecInput.convertTo(wrapped, CV_32F);
196 data = wrapped.at<cv::Vec3f>(cv::Point{0, 0});
197 return TranslationEDNToNWU(wpi::math::Translation3d{
198 wpi::units::meter_t{data[0]}, wpi::units::meter_t{data[1]},
199 wpi::units::meter_t{data[2]}});
200}
201
202static wpi::math::Rotation3d RVecToRotation(const cv::Mat& rvecInput) {
203 cv::Vec3f data{};
204 cv::Mat wrapped{rvecInput.rows, rvecInput.cols, CV_32F};
205 rvecInput.convertTo(wrapped, CV_32F);
206 data = wrapped.at<cv::Vec3f>(cv::Point{0, 0});
207 return RotationEDNToNWU(
208 wpi::math::Rotation3d{Eigen::Vector3d{data[0], data[1], data[2]}});
209}
210
211[[maybe_unused]] static std::optional<photon::PnpResult> SolvePNP_Square(
212 const Eigen::Matrix<double, 3, 3>& cameraMatrix,
213 const Eigen::Matrix<double, 8, 1>& distCoeffs,
214 std::vector<wpi::math::Translation3d> modelTrls,
215 std::vector<cv::Point2f> imagePoints) {
216 modelTrls = ReorderCircular(modelTrls, true, -1);
217 imagePoints = ReorderCircular(imagePoints, true, -1);
218 std::vector<cv::Point3f> objectMat = TranslationToTVec(modelTrls);
219 std::vector<cv::Mat> rvecs;
220 std::vector<cv::Mat> tvecs;
221 cv::Mat rvec = cv::Mat::zeros(3, 1, CV_32F);
222 cv::Mat tvec = cv::Mat::zeros(3, 1, CV_32F);
223 cv::Mat reprojectionError = cv::Mat::zeros(2, 1, CV_32F);
224
225 cv::Mat cameraMat(cameraMatrix.rows(), cameraMatrix.cols(), CV_32F);
226 cv::eigen2cv(cameraMatrix, cameraMat);
227 cv::Mat distCoeffsMat(distCoeffs.rows(), distCoeffs.cols(), CV_32F);
228 cv::eigen2cv(distCoeffs, distCoeffsMat);
229
230 cv::Vec2d errors{};
231 wpi::math::Transform3d best{};
232 std::optional<wpi::math::Transform3d> alt{std::nullopt};
233
234 for (int tries = 0; tries < 2; tries++) {
235 cv::solvePnPGeneric(objectMat, imagePoints, cameraMat, distCoeffsMat, rvecs,
236 tvecs, false, cv::SOLVEPNP_IPPE_SQUARE, rvec, tvec,
237 reprojectionError);
238
239 errors = reprojectionError.at<cv::Vec2f>(cv::Point{0, 0});
240 best = wpi::math::Transform3d{TVecToTranslation(tvecs.at(0)),
241 RVecToRotation(rvecs[0])};
242
243 if (tvecs.size() > 1) {
244 alt = wpi::math::Transform3d{TVecToTranslation(tvecs.at(1)),
245 RVecToRotation(rvecs[1])};
246 }
247
248 if (!std::isnan(errors[0])) {
249 break;
250 } else {
251 cv::Point2f pt = imagePoints[0];
252 pt.x -= 0.001f;
253 pt.y -= 0.001f;
254 imagePoints[0] = pt;
255 }
256 }
257
258 if (std::isnan(errors[0])) {
259 fmt::print("SolvePNP_Square failed!\n");
260 return std::nullopt;
261 }
262 if (alt) {
263 photon::PnpResult result;
264 result.best = best;
265 result.alt = alt.value();
266 result.ambiguity = errors[0] / errors[1];
267 result.bestReprojErr = errors[0];
268 result.altReprojErr = errors[1];
269 return result;
270 } else {
271 photon::PnpResult result;
272 result.best = best;
273 result.bestReprojErr = errors[0];
274 return result;
275 }
276}
277
278[[maybe_unused]] static std::optional<photon::PnpResult> SolvePNP_SQPNP(
279 const Eigen::Matrix<double, 3, 3>& cameraMatrix,
280 const Eigen::Matrix<double, 8, 1>& distCoeffs,
281 std::vector<wpi::math::Translation3d> modelTrls,
282 std::vector<cv::Point2f> imagePoints) {
283 std::vector<cv::Point3f> objectMat = TranslationToTVec(modelTrls);
284 std::vector<cv::Mat> rvecs{};
285 std::vector<cv::Mat> tvecs{};
286 cv::Mat rvec = cv::Mat::zeros(3, 1, CV_32F);
287 cv::Mat tvec = cv::Mat::zeros(3, 1, CV_32F);
288 cv::Mat reprojectionError = cv::Mat::zeros(2, 1, CV_32F);
289
290 cv::Mat cameraMat(cameraMatrix.rows(), cameraMatrix.cols(), CV_64F);
291 cv::eigen2cv(cameraMatrix, cameraMat);
292 cv::Mat distCoeffsMat(distCoeffs.rows(), distCoeffs.cols(), CV_64F);
293 cv::eigen2cv(distCoeffs, distCoeffsMat);
294
295 float error = 0;
296 wpi::math::Transform3d best{};
297
298 cv::solvePnPGeneric(objectMat, imagePoints, cameraMat, distCoeffsMat, rvecs,
299 tvecs, false, cv::SOLVEPNP_SQPNP, rvec, tvec,
300 reprojectionError);
301
302 error = reprojectionError.at<float>(cv::Point{0, 0});
303 best = wpi::math::Transform3d{TVecToTranslation(tvecs.at(0)),
304 RVecToRotation(rvecs[0])};
305
306 if (std::isnan(error)) {
307 fmt::print("SolvePNP_Square failed!\n");
308 }
309 photon::PnpResult result;
310 result.best = best;
311 result.bestReprojErr = error;
312 return result;
313}
314} // namespace OpenCVHelp
315} // namespace photon
Definition RotTrlTransform3d.h:27
wpi::math::Translation3d GetTranslation() const
Definition RotTrlTransform3d.h:59
wpi::math::Rotation3d GetRotation() const
Definition RotTrlTransform3d.h:61
Definition TargetCorner.h:25
static cv::RotatedRect GetMinAreaRect(const std::vector< cv::Point2f > &points)
Definition OpenCVHelp.h:55
static std::vector< cv::Point3f > TranslationToTVec(const std::vector< wpi::math::Translation3d > &translations)
Definition OpenCVHelp.h:70
static wpi::math::Translation3d TVecToTranslation(const cv::Mat &tvecInput)
Definition OpenCVHelp.h:192
static std::vector< photon::TargetCorner > PointsToTargetCorners(const std::vector< cv::Point2f > &points)
Definition OpenCVHelp.h:102
static wpi::math::Rotation3d RotationNWUtoEDN(const wpi::math::Rotation3d &rot)
Definition OpenCVHelp.h:65
static cv::Point2f AvgPoint(std::vector< cv::Point2f > points)
Definition OpenCVHelp.h:94
static std::optional< photon::PnpResult > SolvePNP_SQPNP(const Eigen::Matrix< double, 3, 3 > &cameraMatrix, const Eigen::Matrix< double, 8, 1 > &distCoeffs, std::vector< wpi::math::Translation3d > modelTrls, std::vector< cv::Point2f > imagePoints)
Definition OpenCVHelp.h:278
static std::vector< cv::Point3f > RotationToRVec(const wpi::math::Rotation3d &rotation)
Definition OpenCVHelp.h:82
static wpi::math::Translation3d TranslationEDNToNWU(const wpi::math::Translation3d &trl)
Definition OpenCVHelp.h:182
static cv::Rect GetBoundingRect(const std::vector< cv::Point2f > &points)
Definition OpenCVHelp.h:143
static std::optional< photon::PnpResult > SolvePNP_Square(const Eigen::Matrix< double, 3, 3 > &cameraMatrix, const Eigen::Matrix< double, 8, 1 > &distCoeffs, std::vector< wpi::math::Translation3d > modelTrls, std::vector< cv::Point2f > imagePoints)
Definition OpenCVHelp.h:211
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 wpi::math::Translation3d TranslationNWUtoEDN(const wpi::math::Translation3d &trl)
Definition OpenCVHelp.h:60
static wpi::math::Rotation3d EDN_TO_NWU
Definition OpenCVHelp.h:41
static std::vector< std::pair< float, float > > PointsToCorners(const std::vector< cv::Point2f > &points)
Definition OpenCVHelp.h:112
static std::vector< T > ReorderCircular(const std::vector< T > elements, bool backwards, int shiftStart)
Definition OpenCVHelp.h:167
static wpi::math::Rotation3d RVecToRotation(const cv::Mat &rvecInput)
Definition OpenCVHelp.h:202
static wpi::math::Rotation3d NWU_TO_EDN
Definition OpenCVHelp.h:39
static std::vector< cv::Point2f > GetConvexHull(const std::vector< cv::Point2f > &points)
Definition OpenCVHelp.h:44
static wpi::math::Rotation3d RotationEDNToNWU(const wpi::math::Rotation3d &rot)
Definition OpenCVHelp.h:187
static std::vector< cv::Point2f > CornersToPoints(const std::vector< std::pair< float, float > > &corners)
Definition OpenCVHelp.h:122
Definition VisionEstimation.h:30
double altReprojErr
Definition PnpResultStruct.h:40
double bestReprojErr
Definition PnpResultStruct.h:39
double ambiguity
Definition PnpResultStruct.h:41
wpi::math::Transform3d alt
Definition PnpResultStruct.h:38
wpi::math::Transform3d best
Definition PnpResultStruct.h:37
Definition PnpResult.h:26