/* * MIT License * * Copyright (c) PhotonVision * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "photon/simulation/SimCameraProperties.h" #include #include #include #include using namespace photon; void SimCameraProperties::SetCalibration(int width, int height, frc::Rotation2d fovDiag) { if (fovDiag.Degrees() < 1_deg || fovDiag.Degrees() > 179_deg) { fovDiag = frc::Rotation2d{ std::clamp(fovDiag.Degrees(), 1_deg, 179_deg)}; FRC_ReportError( frc::err::Error, "Requested invalid FOV! Clamping between (1, 179) degrees..."); } double resDiag = std::sqrt(width * width + height * height); double diagRatio = units::math::tan(fovDiag.Radians() / 2.0); frc::Rotation2d fovWidth{ units::radian_t{std::atan(diagRatio * (width / resDiag)) * 2}}; frc::Rotation2d fovHeight{ units::radian_t{std::atan(diagRatio * (height / resDiag)) * 2}}; Eigen::Matrix newDistCoeffs = Eigen::Matrix::Zero(); double cx = width / 2.0 - 0.5; double cy = height / 2.0 - 0.5; double fx = cx / units::math::tan(fovWidth.Radians() / 2.0); double fy = cy / units::math::tan(fovHeight.Radians() / 2.0); Eigen::Matrix newCamIntrinsics; newCamIntrinsics << fx, 0.0, cx, 0.0, fy, cy, 0.0, 0.0, 1.0; SetCalibration(width, height, newCamIntrinsics, newDistCoeffs); } void SimCameraProperties::SetCalibration( int width, int height, const Eigen::Matrix& newCamIntrinsics, const Eigen::Matrix& newDistCoeffs) { resWidth = width; resHeight = height; camIntrinsics = newCamIntrinsics; distCoeffs = newDistCoeffs; std::array p{ frc::Translation3d{1_m, frc::Rotation3d{0_rad, 0_rad, (GetPixelYaw(0) + frc::Rotation2d{units::radian_t{ -std::numbers::pi / 2.0}}) .Radians()}}, frc::Translation3d{1_m, frc::Rotation3d{0_rad, 0_rad, (GetPixelYaw(width) + frc::Rotation2d{units::radian_t{ std::numbers::pi / 2.0}}) .Radians()}}, frc::Translation3d{1_m, frc::Rotation3d{0_rad, (GetPixelPitch(0) + frc::Rotation2d{units::radian_t{ std::numbers::pi / 2.0}}) .Radians(), 0_rad}}, frc::Translation3d{1_m, frc::Rotation3d{0_rad, (GetPixelPitch(height) + frc::Rotation2d{units::radian_t{ -std::numbers::pi / 2.0}}) .Radians(), 0_rad}}, }; viewplanes.clear(); for (size_t i = 0; i < p.size(); i++) { viewplanes.emplace_back(Eigen::Matrix{ p[i].X().to(), p[i].Y().to(), p[i].Z().to()}); } } std::pair, std::optional> SimCameraProperties::GetVisibleLine(const RotTrlTransform3d& camRt, const frc::Translation3d& a, const frc::Translation3d& b) const { frc::Translation3d relA = camRt.Apply(a); frc::Translation3d relB = camRt.Apply(b); if (relA.X() <= 0_m && relB.X() <= 0_m) { return {std::nullopt, std::nullopt}; } Eigen::Matrix av{relA.X().to(), relA.Y().to(), relA.Z().to()}; Eigen::Matrix bv{relB.X().to(), relB.Y().to(), relB.Z().to()}; Eigen::Matrix abv = bv - av; bool aVisible = true; bool bVisible = true; for (size_t i = 0; i < viewplanes.size(); i++) { Eigen::Matrix normal = viewplanes[i]; double aVisibility = av.dot(normal); if (aVisibility < 0) { aVisible = false; } double bVisibility = bv.dot(normal); if (bVisibility < 0) { bVisible = false; } if (aVisibility <= 0 && bVisibility <= 0) { return {std::nullopt, std::nullopt}; } } if (aVisible && bVisible) { return {0, 1}; } std::array intersections{std::nan(""), std::nan(""), std::nan(""), std::nan("")}; std::vector>> ipts{ {std::nullopt, std::nullopt, std::nullopt, std::nullopt}}; for (size_t i = 0; i < viewplanes.size(); i++) { Eigen::Matrix normal = viewplanes[i]; Eigen::Matrix a_projn{}; a_projn = (av.dot(normal) / normal.dot(normal)) * normal; if (std::abs(abv.dot(normal)) < 1e-5) { continue; } intersections[i] = a_projn.dot(a_projn) / -(abv.dot(a_projn)); Eigen::Matrix apv{}; apv = intersections[i] * abv; Eigen::Matrix intersectpt{}; intersectpt = av + apv; ipts[i] = intersectpt; for (size_t j = 1; j < viewplanes.size(); j++) { int oi = (i + j) % viewplanes.size(); Eigen::Matrix onormal = viewplanes[oi]; if (intersectpt.dot(onormal) < 0) { intersections[i] = std::nan(""); ipts[i] = std::nullopt; break; } } if (!ipts[i]) { continue; } for (int j = i - 1; j >= 0; j--) { std::optional> oipt = ipts[j]; if (!oipt) { continue; } Eigen::Matrix diff{}; diff = oipt.value() - intersectpt; if (diff.cwiseAbs().maxCoeff() < 1e-4) { intersections[i] = std::nan(""); ipts[i] = std::nullopt; break; } } } double inter1 = std::nan(""); double inter2 = std::nan(""); for (double inter : intersections) { if (!std::isnan(inter)) { if (std::isnan(inter1)) { inter1 = inter; } else { inter2 = inter; } } } if (!std::isnan(inter2)) { double max = std::max(inter1, inter2); double min = std::min(inter1, inter2); if (aVisible) { min = 0; } if (bVisible) { max = 1; } return {min, max}; } else if (!std::isnan(inter1)) { if (aVisible) { return {0, inter1}; } if (bVisible) { return {inter1, 1}; } return {inter1, std::nullopt}; } else { return {std::nullopt, std::nullopt}; } }