From c892d5dc2e88c4e9499a634ef00fd78b6a051960 Mon Sep 17 00:00:00 2001 From: acpaquette <acp263@nau.edu> Date: Thu, 21 Feb 2019 15:28:31 -0700 Subject: [PATCH] Distortion Integration (#184) * Moved transverse distortion from line scanner into new file * Extracted radial destortion, and built associated tests * Reconstructed distortion functions return results * Corrected spelling, and removed commented out functions * Renamed all dpoint variables to distortionPoint * Removed computeUndistortedFocalPlaneCoordinates function * Renamed distortionPoint to undistortedPoint and distortedPoint where applicable * Removed doc string parameters * Added combined new distortion model * Half way through redefining parameters * Major update to distortion and coefficient storage * More debugging * More debugging * More debugging * debugging * debugging * Removed debugging messages and set the output value for remove distortion * Brought changes inline with dev * Updated isd parsing to handle either radial or transverse * Added Distortion Parse function * Split apply and remove distortion, and added the distortion type to each model state * Removed cmath * Removing prints, and other fixes * Updated how coefficients are parsed * Reverted notebook * Function namespace and parameter clean up * More function and parameter clean up * Reverted old code and updated distortion defaults * Small changes for distortion * Updated utilities and fixed failing test --- CMakeLists.txt | 4 +- include/usgscsm/Distortion.h | 31 +- include/usgscsm/UsgsAstroFrameSensorModel.h | 5 +- include/usgscsm/UsgsAstroLsSensorModel.h | 4 +- include/usgscsm/Utilities.h | 6 +- src/Distortion.cpp | 329 ++++++++++---------- src/UsgsAstroFrameSensorModel.cpp | 64 ++-- src/UsgsAstroLsSensorModel.cpp | 56 ++-- src/UsgsAstroPlugin.cpp | 1 - src/Utilities.cpp | 115 ++++--- tests/DistortionTests.cpp | 123 ++++---- tests/ISDParsingTests.cpp | 22 +- tests/PluginTests.cpp | 13 +- tests/data/empty.json | 1 + 14 files changed, 393 insertions(+), 381 deletions(-) create mode 100644 tests/data/empty.json diff --git a/CMakeLists.txt b/CMakeLists.txt index c987943..625de6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.10) project(usgscsm VERSION 0.0.1 DESCRIPTION "usgscsm library") - + include(GoogleTest) include(cmake/gtest.cmake) include(GNUInstallDirs) @@ -58,7 +58,7 @@ install(DIRECTORY ${USGSCSM_INCLUDE_DIRS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR # Optional build or link against CSM option (BUILD_TESTS "Build tests" ON) if(BUILD_TESTS) - + # Setup for GoogleTest find_package (Threads) diff --git a/include/usgscsm/Distortion.h b/include/usgscsm/Distortion.h index 6ae0d04..574a970 100644 --- a/include/usgscsm/Distortion.h +++ b/include/usgscsm/Distortion.h @@ -4,22 +4,27 @@ #include <vector> #include <math.h> #include <tuple> +#include <iostream> -// Transverse Distortion -std::tuple<double, double> removeDistortion(double dx, double dy, - const std::vector<double> &odtX, const std::vector<double> &odtY); - -std::vector<std::vector<double>> distortionJacobian(double x, double y, - const std::vector<double> &odtX, const std::vector<double> &odtY); +enum DistortionType { + RADIAL, + TRANSVERSE +}; -std::tuple<double, double> distortionFunction(double ux, double uy, - const std::vector<double> &odtX, const std::vector<double> &odtY); +// Transverse Distortion +void distortionJacobian(double x, double y, double *jacobian, + const std::vector<double> opticalDistCoeffs); -// Radial Distortion -std::tuple<double, double> removeDistortion(double inFocalPlaneX, double inFocalPlaneY, - const double opticalDistCoef[3], double tolerance = 1.0E-6); +void computeTransverseDistortion(double ux, double uy, double &dx, double &dy, + const std::vector<double> opticalDistCoeffs); -std::tuple<double, double> invertDistortion(double inFocalPlaneX, double inFocalPlaneY, - const double opticalDistCoef[3], double desiredPrecision, double tolerance = 1.0E-6); +void removeDistortion(double dx, double dy, double &ux, double &uy, + const std::vector<double> opticalDistCoeffs, + DistortionType distortionType, + const double tolerance = 1.0E-6); +void applyDistortion(double ux, double uy, double &dx, double &dy, + const std::vector<double> opticalDistCoeffs, + DistortionType distortionType, + const double desiredPrecision = 0, const double tolerance = 1.0E-6); #endif diff --git a/include/usgscsm/UsgsAstroFrameSensorModel.h b/include/usgscsm/UsgsAstroFrameSensorModel.h index 3a823e7..71c033f 100644 --- a/include/usgscsm/UsgsAstroFrameSensorModel.h +++ b/include/usgscsm/UsgsAstroFrameSensorModel.h @@ -9,6 +9,7 @@ #include "RasterGM.h" #include "CorrelationModel.h" #include "Distortion.h" +#include "Utilities.h" #include <json.hpp> using json = nlohmann::json; @@ -330,8 +331,8 @@ protected: std::vector<double> m_currentParameterCovariance; std::vector<csm::param::Type> m_parameterType; std::vector<double> m_noAdjustments; - std::vector<double> m_odtX; - std::vector<double> m_odtY; + DistortionType m_distortionType; + std::vector<double> m_opticalDistCoeffs; std::vector<double> m_transX; std::vector<double> m_transY; std::vector<double> m_spacecraftVelocity; diff --git a/include/usgscsm/UsgsAstroLsSensorModel.h b/include/usgscsm/UsgsAstroLsSensorModel.h index ceb9a15..5329ede 100644 --- a/include/usgscsm/UsgsAstroLsSensorModel.h +++ b/include/usgscsm/UsgsAstroLsSensorModel.h @@ -30,6 +30,7 @@ #include <RasterGM.h> #include <SettableEllipsoid.h> #include <CorrelationModel.h> +#include "Distortion.h" class UsgsAstroLsSensorModel : public csm::RasterGM, virtual public csm::SettableEllipsoid { @@ -78,7 +79,8 @@ public: int m_ikCode; double m_focalLength; double m_zDirection; - double m_opticalDistCoef[3]; + DistortionType m_distortionType; + std::vector<double> m_opticalDistCoeffs; double m_iTransS[3]; double m_iTransL[3]; double m_detectorSampleOrigin; diff --git a/include/usgscsm/Utilities.h b/include/usgscsm/Utilities.h index 49e84d5..719a724 100644 --- a/include/usgscsm/Utilities.h +++ b/include/usgscsm/Utilities.h @@ -1,6 +1,8 @@ #ifndef Utilities_h #define Utilities_h +#include "Distortion.h" + #include <vector> #include <math.h> #include <tuple> @@ -75,8 +77,8 @@ double getMinHeight(nlohmann::json isd, csm::WarningList *list=nullptr); double getMaxHeight(nlohmann::json isd, csm::WarningList *list=nullptr); double getSemiMajorRadius(nlohmann::json isd, csm::WarningList *list=nullptr); double getSemiMinorRadius(nlohmann::json isd, csm::WarningList *list=nullptr); -std::vector<double> getTransverseDistortionX(nlohmann::json isd, csm::WarningList *list=nullptr); -std::vector<double> getTransverseDistortionY(nlohmann::json isd, csm::WarningList *list=nullptr); +DistortionType getDistortionModel(nlohmann::json isd, csm::WarningList *list=nullptr); +std::vector<double> getDistortionCoeffs(nlohmann::json isd, csm::WarningList *list=nullptr); std::vector<double> getRadialDistortion(nlohmann::json isd, csm::WarningList *list=nullptr); std::vector<double> getSunPositions(nlohmann::json isd, csm::WarningList *list=nullptr); std::vector<double> getSensorPositions(nlohmann::json isd, csm::WarningList *list=nullptr); diff --git a/src/Distortion.cpp b/src/Distortion.cpp index 103aa5c..7a43ceb 100644 --- a/src/Distortion.cpp +++ b/src/Distortion.cpp @@ -1,82 +1,5 @@ #include "Distortion.h" -/** - * @brief Compute undistorted focal plane x/y. - * - * Computes undistorted focal plane (x,y) coordinates given a distorted focal plane (x,y) - * coordinate. The undistorted coordinates are solved for using the Newton-Raphson - * method for root-finding if the distortionFunction method is invoked. - * - * @param dx distorted focal plane x in millimeters - * @param dy distorted focal plane y in millimeters - * @param undistortedX The undistorted x coordinate, in millimeters. - * @param undistortedY The undistorted y coordinate, in millimeters. - * - * @return if the conversion was successful - * @todo Review the tolerance and maximum iterations of the root- - * finding algorithm. - * @todo Review the handling of non-convergence of the root-finding - * algorithm. - * @todo Add error handling for near-zero determinant. -*/ -std::tuple<double, double> removeDistortion(double dx, double dy, - const std::vector<double> &odtX, const std::vector<double> &odtY) { - // Solve the distortion equation using the Newton-Raphson method. - // Set the error tolerance to about one millionth of a NAC pixel. - const double tol = 1.4E-5; - - // The maximum number of iterations of the Newton-Raphson method. - const int maxTries = 60; - - double x; - double y; - std::tuple<double, double> undistortedPoint(dx, dy); - std::tuple<double, double> distortedPoint; - - // Initial guess at the root - x = dx; - y = dy; - - distortedPoint = distortionFunction(x, y, odtX, odtY); - - for (int count = 1; ((fabs(std::get<0>(distortedPoint)) +fabs(std::get<1>(distortedPoint))) > tol) && (count < maxTries); count++) { - - distortedPoint = distortionFunction(x, y, odtX, odtY); - - // fx = dx - fx; - // fy = dy - fy; - distortedPoint = std::make_tuple(dx - std::get<0>(distortedPoint), dy - std::get<1>(distortedPoint)); - - std::vector<std::vector<double>> jacobian; - - jacobian = distortionJacobian(x, y, odtX, odtY); - - // Jxx * Jyy - Jxy * Jyx - double determinant = jacobian[0][0] * jacobian[1][1] - jacobian[0][1] * jacobian[1][0]; - if (fabs(determinant) < 1E-6) { - undistortedPoint = std::make_tuple(x, y); - // - // Near-zero determinant. Add error handling here. - // - //-- Just break out and return with no convergence - return undistortedPoint; - } - - //x = x + (Jyy * fx - Jxy * fy) - x = x + (jacobian[1][1] * std::get<0>(distortedPoint) - jacobian[0][1] * std::get<1>(distortedPoint)) / determinant; - // y = y + (Jxx * fy - Jyx * fx) - y = y + (jacobian[0][0] * std::get<1>(distortedPoint) - jacobian[1][0] * std::get<0>(distortedPoint)) / determinant; - } - - if ( (fabs(std::get<0>(distortedPoint)) + fabs(std::get<1>(distortedPoint))) <= tol) { - // The method converged to a root. - undistortedPoint = std::make_tuple(x, y); - } - // Otherwise method did not converge to a root within the maximum - // number of iterations. Return with no distortion. - return undistortedPoint; -} - /** * @description Jacobian of the distortion function. The Jacobian was computed * algebraically from the function described in the distortionFunction @@ -92,8 +15,8 @@ std::tuple<double, double> removeDistortion(double dx, double dy, [1][0]: yx, [1][1]: yy */ -std::vector<std::vector<double>> distortionJacobian(double x, double y, - const std::vector<double> &odtX, const std::vector<double> &odtY) { +void distortionJacobian(double x, double y, double *jacobian, + const std::vector<double> opticalDistCoeffs) { double d_dx[10]; d_dx[0] = 0; @@ -118,21 +41,20 @@ std::vector<std::vector<double>> distortionJacobian(double x, double y, d_dy[8] = 2 * x * y; d_dy[9] = 3 * y * y; - std::vector<std::vector<double>> jacobian(2, std::vector<double>(2)); + jacobian[0] = 0; // xx + jacobian[1] = 0; // xy + jacobian[2] = 0; // yx + jacobian[3] = 0; // yy - jacobian[0][0] = 0; - jacobian[0][1] = 0; - jacobian[1][0] = 0; - jacobian[1][1] = 0; + int xPointer = 0; + int yPointer = opticalDistCoeffs.size() / 2; - for (int i = 0; i < 10; i++) { - jacobian[0][0] = jacobian[0][0] + d_dx[i] * odtX[i]; - jacobian[0][1] = jacobian[0][1] + d_dy[i] * odtX[i]; - jacobian[1][0] = jacobian[1][0] + d_dx[i] * odtY[i]; - jacobian[1][1] = jacobian[1][1] + d_dy[i] * odtY[i]; + for (int i = 0; i < 10; xPointer++, yPointer++, i++) { + jacobian[0] = jacobian[0] + d_dx[i] * opticalDistCoeffs[xPointer]; + jacobian[1] = jacobian[1] + d_dy[i] * opticalDistCoeffs[xPointer]; + jacobian[2] = jacobian[2] + d_dx[i] * opticalDistCoeffs[yPointer]; + jacobian[3] = jacobian[3] + d_dy[i] * opticalDistCoeffs[yPointer]; } - - return jacobian; } /** @@ -142,13 +64,12 @@ std::vector<std::vector<double>> distortionJacobian(double x, double y, * * @param ux Undistored x * @param uy Undistored y - * @param odtX opticalDistCoef In X - * @param odtY opticalDistCoef In Y + * @param opticalDistCoeffs For both X and Y coefficients * * @returns distortedPoint Newly adjusted focal plane coordinates as an x, y tuple */ -std::tuple<double, double> distortionFunction(double ux, double uy, - const std::vector<double> &odtX, const std::vector<double> &odtY) { +void computeTransverseDistortion(double ux, double uy, double &dx, double &dy, + const std::vector<double> opticalDistCoeffs) { double f[10]; f[0] = 1; @@ -162,93 +83,155 @@ std::tuple<double, double> distortionFunction(double ux, double uy, f[8] = ux * uy * uy; f[9] = uy * uy * uy; - std::tuple<double, double> distortedPoint(0.0, 0.0); - for (int i = 0; i < 10; i++) { - distortedPoint = std::make_tuple(std::get<0>(distortedPoint) + f[i] * odtX[i], - std::get<1>(distortedPoint) + f[i] * odtY[i]); - } + int xPointer = 0; + int yPointer = opticalDistCoeffs.size() / 2; - return distortedPoint; -} + dx = 0.0; + dy = 0.0; -/** - * @description Compute undistorted focal plane coordinate given a distorted - * coordinate set and the distortion coefficients - * - * @param inFocalPlaneX Distorted x - * @param inFocalPlaneY Distorted y - * @param opticalDistCoef distortion coefficients - * - * @returns undistortedPoint Newly adjusted focal plane coordinates as an x, y tuple - */ -std::tuple<double, double> removeDistortion(double inFocalPlaneX, double inFocalPlaneY, - const double opticalDistCoef[3], double tolerance) { - double rr = inFocalPlaneX * inFocalPlaneX + inFocalPlaneY * inFocalPlaneY; - std::tuple<double, double> undistortedPoint(inFocalPlaneX, inFocalPlaneY); - - if (rr > tolerance) - { - double dr = opticalDistCoef[0] + (rr * (opticalDistCoef[1] + rr * opticalDistCoef[2])); - undistortedPoint = std::make_tuple(inFocalPlaneX * (1.0 - dr), inFocalPlaneY * (1.0 - dr)); + for (int i = 0; i < 10; xPointer++, yPointer++, i++) { + dx = dx + f[i] * opticalDistCoeffs[xPointer]; + dy = dy + f[i] * opticalDistCoeffs[yPointer]; } - - return undistortedPoint; } -/** - * @description Compute undistorted focal plane coordinate given a distorted - * focal plane coordinate. This method works by iteratively adding distortion - * until the new distorted point, r, undistorts to within a tolerance of the - * original point, rp. - * - * @param inFocalPlaneX Distorted x - * @param inFocalPlaneY Distorted y - * @param opticalDistCoef Distortion coefficients - * @param desiredPrecision Convergence precision - * @param tolerance Tolerance of r^2 - * - * @returns undistortedPoint Newly adjusted focal plane coordinates as an x, y tuple - */ -std::tuple<double, double> invertDistortion(double inFocalPlaneX, double inFocalPlaneY, - const double opticalDistCoef[3], double desiredPrecision, double tolerance) { - double rp2 = (inFocalPlaneX * inFocalPlaneX) + - (inFocalPlaneY * inFocalPlaneY); - std::tuple<double, double> undistortedPoint; - - if (rp2 > tolerance) { - double rp = sqrt(rp2); - // Compute first fractional distortion using rp - double drOverR = opticalDistCoef[0] - + (rp2 * (opticalDistCoef[1] + (rp2 * opticalDistCoef[2]))); - // Compute first distorted point estimate, r - double r = rp + (drOverR * rp); - double r_prev, r2_prev; - int iteration = 0; - do { - // Don't get in an end-less loop. This algorithm should - // converge quickly. If not then we are probably way outside - // of the focal plane. Just set the distorted position to the - // undistorted position. Also, make sure the focal plane is less - // than 1km, it is unreasonable for it to grow larger than that. - if (iteration >= 15 || r > 1E9) { - drOverR = 0.0; - break; +void removeDistortion(double dx, double dy, double &ux, double &uy, + const std::vector<double> opticalDistCoeffs, + DistortionType distortionType, + const double tolerance) { + ux = dx; + uy = dy; + + switch (distortionType) { + // Compute undistorted focal plane coordinate given a distorted + // coordinate set and the distortion coefficients + case RADIAL: { + double rr = dx * dx + dy * dy; + + if (rr > tolerance) + { + double dr = opticalDistCoeffs[0] + (rr * (opticalDistCoeffs[1] + rr * opticalDistCoeffs[2])); + ux = dx * (1.0 - dr); + uy = dy * (1.0 - dr); + } + } + break; + // Computes undistorted focal plane (x,y) coordinates given a distorted focal plane (x,y) + // coordinate. The undistorted coordinates are solved for using the Newton-Raphson + // method for root-finding if the distortionFunction method is invoked. + case TRANSVERSE: { + // Solve the distortion equation using the Newton-Raphson method. + // Set the error tolerance to about one millionth of a NAC pixel. + // The maximum number of iterations of the Newton-Raphson method. + const int maxTries = 20; + + double x; + double y; + double fx; + double fy; + double jacobian[4]; + + // Initial guess at the root + x = dx; + y = dy; + + computeTransverseDistortion(x, y, fx, fy, opticalDistCoeffs); + + for (int count = 1; ((fabs(fx) +fabs(fy)) > tolerance) && (count < maxTries); count++) { + + computeTransverseDistortion(x, y, fx, fy, opticalDistCoeffs); + + fx = dx - fx; + fy = dy - fy; + + distortionJacobian(x, y, jacobian, opticalDistCoeffs); + + // Jxx * Jyy - Jxy * Jyx + double determinant = jacobian[0] * jacobian[3] - jacobian[1] * jacobian[2]; + if (fabs(determinant) < 1E-6) { + ux = x; + uy = y; + // + // Near-zero determinant. Add error handling here. + // + //-- Just break out and return with no convergence + return; + } + + x = x + (jacobian[3] * fx - jacobian[1] * fy) / determinant; + y = y + (jacobian[0] * fy - jacobian[2] * fx) / determinant; } - r_prev = r; - r2_prev = r * r; + if ((fabs(fx) + fabs(fy)) <= tolerance) { + // The method converged to a root. + ux = x; + uy = y; - // Compute new fractional distortion: - drOverR = opticalDistCoef[0] - + (r2_prev * (opticalDistCoef[1] + (r2_prev * opticalDistCoef[2]))); + return; + } + // Otherwise method did not converge to a root within the maximum + // number of iterations + } + break; + } +} - // Compute new estimate of r - r = rp + (drOverR * r_prev); - iteration++; +void applyDistortion(double ux, double uy, double &dx, double &dy, + const std::vector<double> opticalDistCoeffs, + DistortionType distortionType, + const double desiredPrecision, const double tolerance) +{ + dx = ux; + dy = uy; + + switch (distortionType) { + // Compute undistorted focal plane coordinate given a distorted + // focal plane coordinate. This case works by iteratively adding distortion + // until the new distorted point, r, undistorts to within a tolerance of the + // original point, rp. + case RADIAL: { + double rp2 = (ux * ux) + (uy * uy); + + if (rp2 > tolerance) { + double rp = sqrt(rp2); + // Compute first fractional distortion using rp + double drOverR = opticalDistCoeffs[0] + + (rp2 * (opticalDistCoeffs[1] + (rp2 * opticalDistCoeffs[2]))); + // Compute first distorted point estimate, r + double r = rp + (drOverR * rp); + double r_prev, r2_prev; + int iteration = 0; + do { + // Don't get in an end-less loop. This algorithm should + // converge quickly. If not then we are probably way outside + // of the focal plane. Just set the distorted position to the + // undistorted position. Also, make sure the focal plane is less + // than 1km, it is unreasonable for it to grow larger than that. + if (iteration >= 15 || r > 1E9) { + drOverR = 0.0; + break; + } + + r_prev = r; + r2_prev = r * r; + + // Compute new fractional distortion: + drOverR = opticalDistCoeffs[0] + + (r2_prev * (opticalDistCoeffs[1] + (r2_prev * opticalDistCoeffs[2]))); + + // Compute new estimate of r + r = rp + (drOverR * r_prev); + iteration++; + } + while (fabs(r * (1 - drOverR) - rp) > desiredPrecision); + dx = ux / (1.0 - drOverR); + dy = uy / (1.0 - drOverR); + } + } + break; + case TRANSVERSE: { + computeTransverseDistortion(ux, uy, dx, dy, opticalDistCoeffs); } - while (fabs(r * (1 - drOverR) - rp) > desiredPrecision); - undistortedPoint = std::make_tuple(inFocalPlaneX / (1.0 - drOverR), - inFocalPlaneY / (1.0 - drOverR)); + break; } - return undistortedPoint; } diff --git a/src/UsgsAstroFrameSensorModel.cpp b/src/UsgsAstroFrameSensorModel.cpp index 39a1523..843a236 100644 --- a/src/UsgsAstroFrameSensorModel.cpp +++ b/src/UsgsAstroFrameSensorModel.cpp @@ -9,9 +9,6 @@ #include <Error.h> #include <Version.h> -#include "Distortion.h" -#include "Utilities.h" - using json = nlohmann::json; using namespace std; @@ -64,8 +61,8 @@ void UsgsAstroFrameSensorModel::reset() { m_ccdCenter = std::vector<double>(2, 0.0); m_spacecraftVelocity = std::vector<double>(3, 0.0); m_sunPosition = std::vector<double>(3, 0.0); - m_odtX = std::vector<double>(10, 0.0); - m_odtY = std::vector<double>(10, 0.0); + m_distortionType = DistortionType::TRANSVERSE; + m_opticalDistCoeffs = std::vector<double>(20, 0.0); m_transX = std::vector<double>(3, 0.0); m_transY = std::vector<double>(3, 0.0); m_iTransS = std::vector<double>(3, 0.0); @@ -128,7 +125,7 @@ csm::ImageCoord UsgsAstroFrameSensorModel::groundToImage( // Camera rotation matrix double m[3][3]; - calcRotationMatrix(m, adjustments); + calcRotationMatrix(m,adjustments); // Sensor position double undistortedx, undistortedy, denom; @@ -137,14 +134,15 @@ csm::ImageCoord UsgsAstroFrameSensorModel::groundToImage( undistortedy = (f * (m[0][1] * xo + m[1][1] * yo + m[2][1] * zo)/denom) + m_linePp; // Apply the distortion to the line/sample location and then convert back to line/sample - std::tuple<double, double> distortedPoint; - distortedPoint = distortionFunction(undistortedx, undistortedy, m_odtX, m_odtY); + double distortedX, distortedY; + applyDistortion(undistortedx, undistortedy, distortedX, distortedY, + m_opticalDistCoeffs, m_distortionType); // Convert distorted mm into line/sample double sample, line; - sample = m_iTransS[0] + m_iTransS[1] * std::get<0>(distortedPoint) + m_iTransS[2] * std::get<1>(distortedPoint) + m_ccdCenter[1]; - line = m_iTransL[0] + m_iTransL[1] * std::get<0>(distortedPoint) + m_iTransL[2] * std::get<1>(distortedPoint) + m_ccdCenter[0]; + sample = m_iTransS[0] + m_iTransS[1] * distortedX + m_iTransS[2] * distortedY + m_ccdCenter[1]; + line = m_iTransL[0] + m_iTransL[1] * distortedX + m_iTransL[2] * distortedY + m_ccdCenter[0]; return csm::ImageCoord(line, sample); } @@ -195,13 +193,15 @@ csm::EcefCoord UsgsAstroFrameSensorModel::imageToGround(const csm::ImageCoord &i x_camera = m_transX[0] + m_transX[1] * (lo - line_center) + m_transX[2] * (so - sample_center); // Apply the distortion model (remove distortion) - std::tuple<double, double> undistortedPoint; - undistortedPoint = removeDistortion(x_camera, y_camera, m_odtX, m_odtY); + double undistortedX, undistortedY; + removeDistortion(x_camera, y_camera, undistortedX, undistortedY, + m_opticalDistCoeffs, + m_distortionType); // Now back from distorted mm to pixels - xl = m[0][0] * std::get<0>(undistortedPoint) + m[0][1] * std::get<1>(undistortedPoint) - m[0][2] * - m_focalLength; - yl = m[1][0] * std::get<0>(undistortedPoint) + m[1][1] * std::get<1>(undistortedPoint) - m[1][2] * - m_focalLength; - zl = m[2][0] * std::get<0>(undistortedPoint) + m[2][1] * std::get<1>(undistortedPoint) - m[2][2] * - m_focalLength; + xl = m[0][0] * undistortedX + m[0][1] * undistortedY - m[0][2] * - m_focalLength; + yl = m[1][0] * undistortedX + m[1][1] * undistortedY - m[1][2] * - m_focalLength; + zl = m[2][0] * undistortedX + m[2][1] * undistortedY - m[2][2] * - m_focalLength; double x, y, z; double xc, yc, zc; @@ -249,13 +249,16 @@ csm::EcefLocus UsgsAstroFrameSensorModel::imageToRemoteImagingLocus(const csm::I double focalPlaneY = m_transY[0] + m_transY[1] * row + m_transY[2] * col; // Distort - std::tuple<double, double> undistortedPoint; - undistortedPoint = removeDistortion(focalPlaneX, focalPlaneY, m_odtX, m_odtY); + double undistortedFocalPlaneX, undistortedFocalPlaneY; + removeDistortion(focalPlaneX, focalPlaneY, + undistortedFocalPlaneX, undistortedFocalPlaneY, + m_opticalDistCoeffs, + m_distortionType); // Get rotation matrix and transform to a body-fixed frame double m[3][3]; calcRotationMatrix(m); - std::vector<double> lookC { std::get<0>(undistortedPoint), std::get<1>(undistortedPoint), m_focalLength }; + std::vector<double> lookC { undistortedFocalPlaneX, undistortedFocalPlaneY, m_focalLength }; std::vector<double> lookB { m[0][0] * lookC[0] + m[0][1] * lookC[1] + m[0][2] * lookC[2], m[1][0] * lookC[0] + m[1][1] * lookC[1] + m[1][2] * lookC[2], @@ -636,8 +639,8 @@ std::string UsgsAstroFrameSensorModel::getModelState() const { {"m_samplePp", m_samplePp}, {"m_minElevation", m_minElevation}, {"m_maxElevation", m_maxElevation}, - {"m_odtX", m_odtX}, - {"m_odtY", m_odtY}, + {"m_distortionType", m_distortionType}, + {"m_opticalDistCoeffs", m_opticalDistCoeffs}, {"m_originalHalfLines", m_originalHalfLines}, {"m_originalHalfSamples", m_originalHalfSamples}, {"m_spacecraftName", m_spacecraftName}, @@ -671,8 +674,8 @@ bool UsgsAstroFrameSensorModel::isValidModelState(const std::string& stringState "m_ccdCenter", "m_spacecraftVelocity", "m_sunPosition", - "m_odtX", - "m_odtY", + "m_distortionType", + "m_opticalDistCoeffs", "m_transX", "m_transY", "m_iTransS", @@ -745,8 +748,8 @@ void UsgsAstroFrameSensorModel::replaceModelState(const std::string& stringState m_ccdCenter = state.at("m_ccdCenter").get<std::vector<double>>(); m_spacecraftVelocity = state.at("m_spacecraftVelocity").get<std::vector<double>>(); m_sunPosition = state.at("m_sunPosition").get<std::vector<double>>(); - m_odtX = state.at("m_odtX").get<std::vector<double>>(); - m_odtY = state.at("m_odtY").get<std::vector<double>>(); + m_distortionType = (DistortionType)state.at("m_distortionType").get<int>(); + m_opticalDistCoeffs = state.at("m_opticalDistCoeffs").get<std::vector<double>>(); m_transX = state.at("m_transX").get<std::vector<double>>(); m_transY = state.at("m_transY").get<std::vector<double>>(); m_iTransS = state.at("m_iTransS").get<std::vector<double>>(); @@ -793,7 +796,6 @@ void UsgsAstroFrameSensorModel::replaceModelState(const std::string& stringState std::string UsgsAstroFrameSensorModel::constructStateFromIsd(const std::string& jsonIsd, csm::WarningList* warnings) { - json isd = json::parse(jsonIsd); json state = {}; @@ -807,7 +809,6 @@ std::string UsgsAstroFrameSensorModel::constructStateFromIsd(const std::string& state["m_startingDetectorSample"] = getDetectorStartingSample(isd, parsingWarnings); state["m_startingDetectorLine"] = getDetectorStartingLine(isd, parsingWarnings); - // get focal length state["m_focalLength"] = getFocalLength(isd, parsingWarnings); state["m_focalLengthEpsilon"] = getFocalLengthEpsilon(isd, parsingWarnings); @@ -849,10 +850,9 @@ std::string UsgsAstroFrameSensorModel::constructStateFromIsd(const std::string& // sun position is not strictly necessary, but is required for getIlluminationDirection. state["m_sunPosition"] = getSunPositions(isd); - // get sensor_orientation quaternion std::vector<double> quaternion = getSensorOrientations(isd, parsingWarnings); - if (!quaternion.empty() && quaternion.size() != 4) { + if (quaternion.size() != 4) { parsingWarnings->push_back( csm::Warning( csm::Warning::DATA_NOT_AVAILABLE, @@ -866,11 +866,9 @@ std::string UsgsAstroFrameSensorModel::constructStateFromIsd(const std::string& state["m_currentParameterValue"][6] = quaternion[3]; } - // get optical_distortion - state["m_odtX"] = getTransverseDistortionX(isd, parsingWarnings); - state["m_odtY"] = getTransverseDistortionY(isd, parsingWarnings); - + state["m_distortionType"] = getDistortionModel(isd); + state["m_opticalDistCoeffs"] = getDistortionCoeffs(isd); // get detector_center state["m_ccdCenter"][0] = getDetectorCenterLine(isd, parsingWarnings); @@ -939,10 +937,10 @@ std::string UsgsAstroFrameSensorModel::constructStateFromIsd(const std::string& parsingWarnings = nullptr; return state.dump(); - } + csm::EcefCoord UsgsAstroFrameSensorModel::getReferencePoint() const { return m_referencePointXyz; } diff --git a/src/UsgsAstroLsSensorModel.cpp b/src/UsgsAstroLsSensorModel.cpp index c998736..ec3d030 100644 --- a/src/UsgsAstroLsSensorModel.cpp +++ b/src/UsgsAstroLsSensorModel.cpp @@ -77,7 +77,8 @@ const std::string UsgsAstroLsSensorModel::_STATE_KEYWORD[] = "m_ikCode", "m_focalLength", "m_zDirection", - "m_opticalDistCoef", + "m_distortionType", + "m_opticalDistCoeffs", "m_iTransS", "m_iTransL", "m_detectorSampleOrigin", @@ -151,9 +152,9 @@ void UsgsAstroLsSensorModel::replaceModelState(const std::string &stateString ) m_focalLength = j["m_focalLength"]; m_zDirection = j["m_zDirection"]; - + m_distortionType = (DistortionType)j["m_distortionType"].get<int>(); + m_opticalDistCoeffs = j["m_opticalDistCoeffs"].get<std::vector<double>>(); for (int i = 0; i < 3; i++) { - m_opticalDistCoef[i] = j["m_opticalDistCoef"][i]; m_iTransS[i] = j["m_iTransS"][i]; m_iTransL[i] = j["m_iTransL"][i]; } @@ -258,7 +259,8 @@ std::string UsgsAstroLsSensorModel::getModelState() const { state["m_ikCode"] = m_ikCode; state["m_focalLength"] = m_focalLength; state["m_zDirection"] = m_zDirection; - state["m_opticalDistCoef"] = std::vector<double>(m_opticalDistCoef, m_opticalDistCoef+3); + state["m_distortionType"] = m_distortionType; + state["m_opticalDistCoeffs"] = m_opticalDistCoeffs; state["m_iTransS"] = std::vector<double>(m_iTransS, m_iTransS+3); state["m_iTransL"] = std::vector<double>(m_iTransL, m_iTransL+3); state["m_detectorSampleOrigin"] = m_detectorSampleOrigin; @@ -323,9 +325,8 @@ void UsgsAstroLsSensorModel::reset() m_ikCode = -85600; // 17 m_focalLength = 350.0; // 18 m_zDirection = 1.0; // 19 - m_opticalDistCoef[0] = 0.0; // 20 - m_opticalDistCoef[1] = 0.0; // 20 - m_opticalDistCoef[2] = 0.0; // 20 + m_distortionType = DistortionType::RADIAL; + m_opticalDistCoeffs = std::vector<double>(3, 0.0); m_iTransS[0] = 0.0; // 21 m_iTransS[1] = 0.0; // 21 m_iTransS[2] = 150.0; // 21 @@ -353,13 +354,13 @@ void UsgsAstroLsSensorModel::reset() m_currentParameterValue.assign(NUM_PARAMETERS,0.0); m_parameterType.assign(NUM_PARAMETERS,csm::param::REAL); - m_referencePointXyz.x = 0.0; // 47 - m_referencePointXyz.y = 0.0; // 47 - m_referencePointXyz.z = 0.0; // 47 - m_gsd = 1.0; // 48 - m_flyingHeight = 1000.0; // 49 - m_halfSwath = 1000.0; // 50 - m_halfTime = 10.0; // 51 + m_referencePointXyz.x = 0.0; + m_referencePointXyz.y = 0.0; + m_referencePointXyz.z = 0.0; + m_gsd = 1.0; + m_flyingHeight = 1000.0; + m_halfSwath = 1000.0; + m_halfTime = 10.0; m_covariance = std::vector<double>(NUM_PARAMETERS * NUM_PARAMETERS,0.0); // 52 } @@ -1660,12 +1661,15 @@ void UsgsAstroLsSensorModel::losToEcf( computeDistortedFocalPlaneCoordinates(fractionalLine, sampleUSGSFull, m_detectorSampleOrigin, m_detectorLineOrigin, m_detectorSampleSumming, m_startingSample, m_iTransS, m_iTransL, natFocalPlane); // Remove lens distortion - std::tuple<double, double> undistortedPoint; - undistortedPoint = removeDistortion(std::get<0>(natFocalPlane), std::get<1>(natFocalPlane), m_opticalDistCoef); + double undistortedFocalPlaneX, undistortedFocalPlaneY; + removeDistortion(std::get<0>(natFocalPlane), std::get<1>(natFocalPlane), + undistortedFocalPlaneX, undistortedFocalPlaneY, + m_opticalDistCoeffs, + DistortionType::RADIAL); // Define imaging ray (look vector) in camera space double cameraLook[3]; - createCameraLookVector(std::get<0>(undistortedPoint), std::get<1>(undistortedPoint), m_zDirection, m_focalLength, cameraLook); + createCameraLookVector(undistortedFocalPlaneX, undistortedFocalPlaneY, m_zDirection, m_focalLength, cameraLook); // Apply attitude correction double attCorr[9]; @@ -2249,18 +2253,19 @@ csm::ImageCoord UsgsAstroLsSensorModel::computeViewingPixel( double lookScale = m_focalLength / adjustedLookZ; double focalX = adjustedLookX * lookScale; double focalY = adjustedLookY * lookScale; - std::tuple<double, double> undistortedPoint; + double distortedFocalX, distortedFocalY; // Invert distortion - undistortedPoint = invertDistortion(focalX, focalY, m_opticalDistCoef, desiredPrecision); + applyDistortion(focalX, focalY, distortedFocalX, distortedFocalY, + m_opticalDistCoeffs, m_distortionType, desiredPrecision); // Convert to detector line and sample double detectorLine = m_iTransL[0] - + m_iTransL[1] * std::get<0>(undistortedPoint) - + m_iTransL[2] * std::get<1>(undistortedPoint); + + m_iTransL[1] * distortedFocalX + + m_iTransL[2] * distortedFocalY; double detectorSample = m_iTransS[0] - + m_iTransS[1] * std::get<0>(undistortedPoint) - + m_iTransS[2] * std::get<1>(undistortedPoint); + + m_iTransS[1] * distortedFocalX + + m_iTransS[2] * distortedFocalY; // Convert to image sample line double line = detectorLine + m_detectorLineOrigin; @@ -2442,7 +2447,6 @@ double UsgsAstroLsSensorModel::determinant3x3(double mat[9]) const std::string UsgsAstroLsSensorModel::constructStateFromIsd(const std::string imageSupportData, csm::WarningList *warnings) const { - // Instantiate UsgsAstroLineScanner sensor model json isd = json::parse(imageSupportData); json state = {}; @@ -2468,6 +2472,9 @@ std::string UsgsAstroLsSensorModel::constructStateFromIsd(const std::string imag state["m_ikCode"] = 0; state["m_zDirection"] = 1; + state["m_distortionType"] = getDistortionModel(isd, parsingWarnings); + state["m_opticalDistCoeffs"] = getDistortionCoeffs(isd, parsingWarnings); + // Zero computed state values state["m_referencePointXyz"] = std::vector<double>(3, 0.0); @@ -2487,7 +2494,6 @@ std::string UsgsAstroLsSensorModel::constructStateFromIsd(const std::string imag state["m_detectorSampleSumming"] = getSampleSumming(isd, parsingWarnings); state["m_startingDetectorSample"] = getDetectorStartingSample(isd, parsingWarnings); state["m_startingDetectorLine"] = getDetectorStartingLine(isd, parsingWarnings); - state["m_opticalDistCoef"] = getRadialDistortion(isd, parsingWarnings); state["m_detectorSampleOrigin"] = getDetectorCenterSample(isd, parsingWarnings); state["m_detectorLineOrigin"] = getDetectorCenterLine(isd, parsingWarnings); diff --git a/src/UsgsAstroPlugin.cpp b/src/UsgsAstroPlugin.cpp index d731f6c..cf6e017 100644 --- a/src/UsgsAstroPlugin.cpp +++ b/src/UsgsAstroPlugin.cpp @@ -217,7 +217,6 @@ std::string UsgsAstroPlugin::convertISDToModelState(const csm::Isd &imageSupport csm::Model *UsgsAstroPlugin::constructModelFromISD(const csm::Isd &imageSupportDataOriginal, const std::string &modelName, csm::WarningList *warnings) const { - std::string stringIsd = loadImageSupportData(imageSupportDataOriginal); if (modelName == UsgsAstroFrameSensorModel::_SENSOR_MODEL_NAME) { diff --git a/src/Utilities.cpp b/src/Utilities.cpp index a8fc1cf..1812d66 100644 --- a/src/Utilities.cpp +++ b/src/Utilities.cpp @@ -551,56 +551,89 @@ double getSemiMinorRadius(json isd, csm::WarningList *list) { return radius; } -std::vector<double> getTransverseDistortionX(json isd, csm::WarningList *list) { - std::vector<double> coefficients; - try { - coefficients = isd.at("optical_distortion").at("transverse").at("x").get<std::vector<double>>(); - coefficients.resize(10, 0.0); +DistortionType getDistortionModel(json isd, csm::WarningList *list) { + json distoriton_subset = isd.at("optical_distortion"); + + json::iterator it = distoriton_subset.begin(); + + std::string distortion = (std::string)it.key(); + + if (distortion.compare("transverse") == 0) { + return DistortionType::TRANSVERSE; } - catch (...) { - if (list) { - list->push_back( - csm::Warning( - csm::Warning::DATA_NOT_AVAILABLE, - "Could not parse the transverse distortion model X coefficients.", - "Utilities::getTransverseDistortionX()")); - } + else if (distortion.compare("radial") == 0) { + return DistortionType::RADIAL; } - return coefficients; -} -std::vector<double> getTransverseDistortionY(json isd, csm::WarningList *list) { - std::vector<double> coefficients; - try { - coefficients = isd.at("optical_distortion").at("transverse").at("y").get<std::vector<double>>(); - coefficients.resize(10, 0.0); - } - catch (...) { - if (list) { - list->push_back( - csm::Warning( - csm::Warning::DATA_NOT_AVAILABLE, - "Could not parse the transverse distortion model Y coefficients.", - "Utilities::getTransverseDistortionY()")); - } + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the distortion model.", + "Utilities::getDistortionModel()")); } - return coefficients; + return DistortionType::TRANSVERSE; } -std::vector<double> getRadialDistortion(json isd, csm::WarningList *list) { +std::vector<double> getDistortionCoeffs(json isd, csm::WarningList *list) { std::vector<double> coefficients; - try { - coefficients = isd.at("optical_distortion").at("radial").at("coefficients").get<std::vector<double>>(); - } - catch (...) { - if (list) { - list->push_back( - csm::Warning( - csm::Warning::DATA_NOT_AVAILABLE, - "Could not parse the radial distortion model coefficients.", - "Utilities::getRadialDistortion()")); + + DistortionType distortion = getDistortionModel(isd); + + switch (distortion) { + case DistortionType::TRANSVERSE: { + try { + std::vector<double> coefficientsX, coefficientsY; + + coefficientsX = isd.at("optical_distortion").at("transverse").at("x").get<std::vector<double>>(); + coefficientsX.resize(10, 0.0); + + coefficientsY = isd.at("optical_distortion").at("transverse").at("y").get<std::vector<double>>(); + coefficientsY.resize(10, 0.0); + + coefficients = coefficientsX; + + coefficients.insert(coefficients.end(), coefficientsY.begin(), coefficientsY.end()); + return coefficients; + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse a set of transverse distortion model coefficients.", + "Utilities::getDistortion()")); + } + coefficients = std::vector<double>(20, 0.0); + } + } + break; + case DistortionType::RADIAL: { + try { + coefficients = isd.at("optical_distortion").at("radial").at("coefficients").get<std::vector<double>>(); + + return coefficients; + } + catch (...) { + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the radial distortion model coefficients.", + "Utilities::getDistortion()")); + } + coefficients = std::vector<double>(3, 0.0); + } } } + if (list) { + list->push_back( + csm::Warning( + csm::Warning::DATA_NOT_AVAILABLE, + "Could not parse the distortion model coefficients.", + "Utilities::getDistortion()")); + } + return coefficients; } diff --git a/tests/DistortionTests.cpp b/tests/DistortionTests.cpp index b21971e..26715ef 100644 --- a/tests/DistortionTests.cpp +++ b/tests/DistortionTests.cpp @@ -10,117 +10,114 @@ INSTANTIATE_TEST_CASE_P(JacobianTest,ImageCoordParameterizedTest, ::testing::Values(csm::ImageCoord(2.5,2.5),csm::ImageCoord(7.5,7.5))); TEST_P(ImageCoordParameterizedTest, JacobianTest) { - std::vector<double> odtX = {1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0}; - std::vector<double> odtY = {0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0}; + std::vector<double> transverseDistortionCoeffs = {1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0}; double Jxx,Jxy,Jyx,Jyy; csm::ImageCoord imagePt = GetParam(); - std::vector<std::vector<double>> jacobian; - jacobian = distortionJacobian(imagePt.samp, imagePt.line, odtX, odtY); + double jacobian[4]; + distortionJacobian(imagePt.samp, imagePt.line, jacobian, transverseDistortionCoeffs); // Jxx * Jyy - Jxy * Jyx - double determinant = fabs(jacobian[0][0] * jacobian[1][1] - jacobian[0][1] * jacobian[1][0]); + double determinant = fabs(jacobian[0] * jacobian[3] - jacobian[1] * jacobian[2]); EXPECT_GT(determinant,1e-3); } TEST(Transverse, Jacobian1) { csm::ImageCoord imagePt(7.5, 7.5); - std::vector<double> odtX = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0}; - std::vector<double> odtY = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,1.0}; + std::vector<double> transverseDistortionCoeffs = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0, + 0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,1.0}; - std::vector<std::vector<double>> jacobian; - jacobian = distortionJacobian(imagePt.samp, imagePt.line, odtX, odtY); + double jacobian[4]; + distortionJacobian(imagePt.samp, imagePt.line, jacobian, transverseDistortionCoeffs); - EXPECT_NEAR(jacobian[0][0],56.25,1e-8 ); - EXPECT_NEAR(jacobian[0][1],112.5,1e-8); - EXPECT_NEAR(jacobian[1][0],56.25,1e-8); - EXPECT_NEAR(jacobian[1][1],281.25,1e-8); + EXPECT_NEAR(jacobian[0],56.25,1e-8 ); + EXPECT_NEAR(jacobian[1],112.5,1e-8); + EXPECT_NEAR(jacobian[2],56.25,1e-8); + EXPECT_NEAR(jacobian[3],281.25,1e-8); } TEST(Transverse, distortMe_AlternatingOnes) { csm::ImageCoord imagePt(7.5, 7.5); - std::vector<double> odtX = {1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0}; - std::vector<double> odtY = {0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0}; + std::vector<double> transverseDistortionCoeffs = {1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0, + 0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0}; - double dx,dy; - std::tuple<double, double> distortedPoint; - distortedPoint = distortionFunction(imagePt.samp, imagePt.line, odtX, odtY); + double dx, dy; + computeTransverseDistortion(imagePt.samp, imagePt.line, dx, dy, transverseDistortionCoeffs); - EXPECT_NEAR(std::get<0>(distortedPoint),908.5,1e-8 ); - EXPECT_NEAR(std::get<1>(distortedPoint),963.75,1e-8); + EXPECT_NEAR(dx,908.5,1e-8 ); + EXPECT_NEAR(dy,963.75,1e-8); } TEST(Transverse, distortMe_AllCoefficientsOne) { csm::ImageCoord imagePt(7.5, 7.5); - std::vector<double> odtX = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; - std::vector<double> odtY = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; + std::vector<double> transverseDistortionCoeffs = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0, + 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; - double dx,dy; - std::tuple<double, double> distortedPoint; - distortedPoint = distortionFunction(imagePt.samp, imagePt.line, odtX, odtY); + double dx, dy; + computeTransverseDistortion(imagePt.samp, imagePt.line, dx, dy, transverseDistortionCoeffs); - EXPECT_NEAR(std::get<0>(distortedPoint),1872.25,1e-8 ); - EXPECT_NEAR(std::get<1>(distortedPoint),1872.25,1e-8); + EXPECT_NEAR(dx,1872.25,1e-8 ); + EXPECT_NEAR(dy,1872.25,1e-8); } TEST(transverse, removeDistortion1) { csm::ImageCoord imagePt(7.5, 7.5); + double ux, uy; - std::vector<double> odtX = {0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}; - std::vector<double> odtY = {0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}; + std::vector<double> transverseDistortionCoeffs = {0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0, + 0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}; - std::tuple<double, double> undistortedPoint; - undistortedPoint = removeDistortion(imagePt.samp, imagePt.line, odtX, odtY); + removeDistortion(imagePt.samp, imagePt.line, ux, uy, transverseDistortionCoeffs, DistortionType::TRANSVERSE); - EXPECT_NEAR(imagePt.samp,7.5,1e-8 ); - EXPECT_NEAR(imagePt.line,7.5,1e-8); - EXPECT_NEAR(imagePt.line,std::get<0>(undistortedPoint),1e-8); - EXPECT_NEAR(imagePt.samp,std::get<1>(undistortedPoint),1e-8); + EXPECT_NEAR(imagePt.samp, 7.5, 1e-8 ); + EXPECT_NEAR(imagePt.line, 7.5, 1e-8); + EXPECT_NEAR(imagePt.line, ux, 1e-8); + EXPECT_NEAR(imagePt.samp, uy, 1e-8); } TEST(transverse, removeDistortion_AllCoefficientsOne) { csm::ImageCoord imagePt(1872.25, 1872.25); + double ux, uy; - std::vector<double> odtX = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; - std::vector<double> odtY = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; + std::vector<double> transverseDistortionCoeffs = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0, + 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; - std::tuple<double, double> undistortedPoint; - undistortedPoint = removeDistortion(imagePt.samp, imagePt.line, odtX, odtY); + removeDistortion(imagePt.samp, imagePt.line, ux, uy, transverseDistortionCoeffs, DistortionType::TRANSVERSE); // The Jacobian is singular, so the setFocalPlane should break out of it's iteration and // returns the same distorted coordinates that were passed in. - EXPECT_NEAR(std::get<0>(undistortedPoint),imagePt.samp,1e-8 ); - EXPECT_NEAR(std::get<1>(undistortedPoint),imagePt.line,1e-8); + EXPECT_NEAR(ux, imagePt.samp,1e-8 ); + EXPECT_NEAR(uy, imagePt.line,1e-8); } TEST(transverse, removeDistortion_AlternatingOnes) { csm::ImageCoord imagePt(963.75, 908.5); + double ux, uy; - std::vector<double> odtX = {1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0}; - std::vector<double> odtY = {0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0}; + std::vector<double> transverseDistortionCoeffs = {1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0, + 0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0,0.0,1.0}; - std::tuple<double, double> undistortedPoint; - undistortedPoint = removeDistortion(imagePt.samp, imagePt.line, odtX, odtY); + removeDistortion(imagePt.samp, imagePt.line, ux, uy, transverseDistortionCoeffs, DistortionType::TRANSVERSE); - EXPECT_NEAR(std::get<0>(undistortedPoint),7.5,1e-8 ); - EXPECT_NEAR(std::get<1>(undistortedPoint),7.5,1e-8); + EXPECT_NEAR(ux, 7.5, 1e-8 ); + EXPECT_NEAR(uy, 7.5, 1e-8); } TEST(Radial, testRemoveDistortion) { csm::ImageCoord imagePt(0.0, 4.0); - double dx, dy; - double coeffs[3] = {0, 0, 0}; - std::tuple<double, double> undistortedPoint; + double ux, uy; + std::vector<double> coeffs = {0, 0, 0}; - undistortedPoint = removeDistortion(imagePt.samp, imagePt.line, coeffs); + removeDistortion(imagePt.samp, imagePt.line, ux, uy, coeffs, DistortionType::RADIAL); - EXPECT_NEAR(std::get<0>(undistortedPoint),4,1e-8); - EXPECT_NEAR(std::get<1>(undistortedPoint),0,1e-8); + EXPECT_NEAR(ux, 4, 1e-8); + EXPECT_NEAR(uy, 0, 1e-8); } // If coeffs are 0 then this will have the same result as removeDistortion @@ -130,13 +127,13 @@ TEST(Radial, testInverseDistortion){ double dx, dy; double desiredPrecision = 0.01; - double coeffs[3] = {0, 0, 0}; - std::tuple<double, double> undistortedPoint; + std::vector<double> coeffs = {0, 0, 0}; - undistortedPoint = invertDistortion(imagePt.samp, imagePt.line, coeffs, desiredPrecision); + applyDistortion(imagePt.samp, imagePt.line, dx, dy, coeffs, + DistortionType::RADIAL, desiredPrecision); - EXPECT_NEAR(std::get<0>(undistortedPoint),4,1e-8); - EXPECT_NEAR(std::get<1>(undistortedPoint),0,1e-8); + EXPECT_NEAR(dx,4,1e-8); + EXPECT_NEAR(dy,0,1e-8); } TEST(Radial, testInverseOnesCoeffs){ @@ -144,11 +141,11 @@ TEST(Radial, testInverseOnesCoeffs){ double dx, dy; double desiredPrecision = 0.01; - double coeffs[3] = {1, 1, 1}; - std::tuple<double, double> undistortedPoint; + std::vector<double> coeffs = {1, 1, 1}; - undistortedPoint = invertDistortion(imagePt.samp, imagePt.line, coeffs, desiredPrecision); + applyDistortion(imagePt.samp, imagePt.line, dx, dy, coeffs, + DistortionType::RADIAL, desiredPrecision); - EXPECT_NEAR(std::get<0>(undistortedPoint),4,1e-8); - EXPECT_NEAR(std::get<1>(undistortedPoint),0,1e-8); + EXPECT_NEAR(dx,4,1e-8); + EXPECT_NEAR(dy,0,1e-8); } diff --git a/tests/ISDParsingTests.cpp b/tests/ISDParsingTests.cpp index c2976e3..3c2c062 100644 --- a/tests/ISDParsingTests.cpp +++ b/tests/ISDParsingTests.cpp @@ -231,28 +231,18 @@ TEST(ISDParsing, SemiMinor) { EXPECT_EQ(1000, getSemiMinorRadius(isd)); } -TEST(ISDParsing, TransverseX) { +TEST(ISDParsing, TransverseDistortion) { json isd = { {"optical_distortion", { {"transverse", { + {"y", {-11, 21, 24}}, {"x", {-1, 2, 4}}}} } } }; - std::vector<double> coefficients = {-1, 2, 4, 0, 0, 0, 0, 0, 0, 0}; - EXPECT_EQ(coefficients, getTransverseDistortionX(isd)); -} - -TEST(ISDParsing, TransverseY) { - json isd = { - {"optical_distortion", { - {"transverse", { - {"y", {-11, 21, 24, 16, 20}}}} - } - } - }; - std::vector<double> coefficients = {-11, 21, 24, 16, 20, 0, 0, 0, 0, 0}; - EXPECT_EQ(coefficients, getTransverseDistortionY(isd)); + std::vector<double> coefficients = {-1, 2, 4, 0, 0, 0, 0, 0, 0, 0, + -11, 21, 24, 0, 0, 0, 0, 0, 0, 0}; + EXPECT_EQ(coefficients, getDistortionCoeffs(isd)); } TEST(ISDParsing, Radial) { @@ -264,7 +254,7 @@ TEST(ISDParsing, Radial) { } }; std::vector<double> coefficients = {0, 1, 2}; - EXPECT_EQ(coefficients, getRadialDistortion(isd)); + EXPECT_EQ(coefficients, getDistortionCoeffs(isd)); } TEST(ISDParsing, SunPosition) { diff --git a/tests/PluginTests.cpp b/tests/PluginTests.cpp index 795201a..f034b41 100644 --- a/tests/PluginTests.cpp +++ b/tests/PluginTests.cpp @@ -118,19 +118,17 @@ TEST_F(FrameIsdTest, ConstructValidCamera) { TEST_F(FrameIsdTest, ConstructInValidCamera) { UsgsAstroPlugin testPlugin; - isd.setFilename("data/constVelocityLineScan.img"); + isd.setFilename("data/empty.img"); csm::Model *cameraModel = NULL; - csm::WarningList *warnings = new csm::WarningList; try { testPlugin.constructModelFromISD( isd, "USGS_ASTRO_FRAME_SENSOR_MODEL", - warnings); + nullptr); FAIL() << "Expected an error"; } catch(csm::Error &e) { EXPECT_EQ(e.getError(), csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE); - EXPECT_FALSE(warnings->empty()); } catch(...) { FAIL() << "Expected csm SENSOR_MODEL_NOT_CONSTRUCTIBLE error"; @@ -138,9 +136,6 @@ TEST_F(FrameIsdTest, ConstructInValidCamera) { if (cameraModel) { delete cameraModel; } - if (warnings) { - delete warnings; - } } TEST_F(ConstVelLineScanIsdTest, Constructible) { @@ -184,13 +179,13 @@ TEST_F(ConstVelLineScanIsdTest, ConstructValidCamera) { TEST_F(ConstVelLineScanIsdTest, ConstructInValidCamera) { UsgsAstroPlugin testPlugin; - isd.setFilename("data/simpleFramerISD.img"); + isd.setFilename("data/empty.img"); csm::Model *cameraModel = NULL; try { testPlugin.constructModelFromISD( isd, "USGS_ASTRO_LINE_SCANNER_SENSOR_MODEL", - NULL); + nullptr); FAIL() << "Expected an error"; } diff --git a/tests/data/empty.json b/tests/data/empty.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/data/empty.json @@ -0,0 +1 @@ +{} -- GitLab