diff --git a/src/OpenCvSharp/Modules/geometry/Enum/SolvePnPFlags.cs b/src/OpenCvSharp/Modules/geometry/Enum/SolvePnPMethod.cs similarity index 56% rename from src/OpenCvSharp/Modules/geometry/Enum/SolvePnPFlags.cs rename to src/OpenCvSharp/Modules/geometry/Enum/SolvePnPMethod.cs index 261162399..98073c11d 100644 --- a/src/OpenCvSharp/Modules/geometry/Enum/SolvePnPFlags.cs +++ b/src/OpenCvSharp/Modules/geometry/Enum/SolvePnPMethod.cs @@ -27,32 +27,19 @@ public enum SolvePnPMethod /// P3P = 2, - /// - /// **Broken implementation. Using this flag will fallback to EPnP**. - /// Joel A. Hesch and Stergios I. Roumeliotis. "A Direct Least-Squares (DLS) Method for PnP" - /// - DLS = 3, - - /// - /// **Broken implementation. Using this flag will fallback to EPnP.** - /// Method is based on the paper of A.Penate-Sanchez, J.Andrade-Cetto, F.Moreno-Noguer. - /// "Exhaustive Linearization for Robust Camera Pose and Focal Length Estimation" - /// - UPNP = 4, - /// /// Method is based on the paper of T. Ke, S. Roumeliotis /// "An Efficient Algebraic Solution to the Perspective-Three-Point Problem" /// In this case the function requires exactly four object and image points. /// - AP3P = 5, + AP3P = 3, /// /// Method is based on the paper of T. Collins and A. Bartoli. /// "Infinitesimal Plane-Based Pose Estimation". /// This method requires coplanar object points. /// - IPPE = 6, + IPPE = 4, /// /// Method is based on the paper of Toby Collins and Adrien Bartoli. @@ -63,46 +50,11 @@ public enum SolvePnPMethod /// - point 2: [squareLength / 2, -squareLength / 2, 0] /// - point 3: [-squareLength / 2, -squareLength / 2, 0] /// - IPPE_SQUARE = 7, + IPPESquare = 5, /// /// Method is based on the paper "A Consistently Fast and Globally Optimal Solution to the /// Perspective-n-Point Problem" by G. Terzakis and M. Lourakis. It requires 3 or more points. /// - SQPNP = 8, -} - -/// -/// Obsolete: Use SolvePnPMethod instead. This enum is kept for backward compatibility. -/// -[Obsolete("Use SolvePnPMethod instead", true)] -public enum SolvePnPFlags -{ - /// - /// Iterative method is based on Levenberg-Marquardt optimization. - /// In this case the function finds such a pose that minimizes reprojection error, - /// that is the sum of squared distances between the observed projections imagePoints and the projected (using projectPoints() ) objectPoints . - /// - Iterative = 0, - - /// - /// Method has been introduced by F.Moreno-Noguer, V.Lepetit and P.Fua in the paper “EPnP: Efficient Perspective-n-Point Camera Pose Estimation”. - /// - EPNP = 1, - - /// - /// Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang“Complete Solution Classification for - /// the Perspective-Three-Point Problem”. In this case the function requires exactly four object and image points. - /// - P3P = 2, - - /// - /// Joel A. Hesch and Stergios I. Roumeliotis. "A Direct Least-Squares (DLS) Method for PnP" - /// - DLS = 3, - - /// - /// A.Penate-Sanchez, J.Andrade-Cetto, F.Moreno-Noguer. "Exhaustive Linearization for Robust Camera Pose and Focal Length Estimation" - /// - UPNP = 4, + SQPNP = 6, } diff --git a/test/OpenCvSharp.Tests/calib3d/Calib3dTest.cs b/test/OpenCvSharp.Tests/calib3d/Calib3dTest.cs index 68a09d27e..bf779ce2b 100644 --- a/test/OpenCvSharp.Tests/calib3d/Calib3dTest.cs +++ b/test/OpenCvSharp.Tests/calib3d/Calib3dTest.cs @@ -481,6 +481,79 @@ public void SolvePnPTestByArray(bool useExtrinsicGuess) Cv2.SolvePnP(objPts, imgPts, cameraMatrix, dist, ref rvec, ref tvec, useExtrinsicGuess: useExtrinsicGuess); } + [Theory] + [InlineData(SolvePnPMethod.P3P)] + [InlineData(SolvePnPMethod.AP3P)] + [InlineData(SolvePnPMethod.IPPE)] + [InlineData(SolvePnPMethod.SQPNP)] + public void SolvePnPTestByArrayMethods(SolvePnPMethod method) + { + var rvec = new double[] { 3, 0, 0 }; + var tvec = new double[] { 0, 0, 10 }; + var cameraMatrix = new double[,] + { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 } + }; + var dist = new double[] { 0, 0, 0, 0, 0 }; + + var objPts = new[] + { + new Point3f(0,0,1), + new Point3f(1,0,1), + new Point3f(0,1,1), + new Point3f(1,1,1) + }; + + Cv2.ProjectPoints(objPts, rvec, tvec, cameraMatrix, dist, out var imgPts, out _); + + Cv2.SolvePnP(objPts, imgPts, cameraMatrix, dist, ref rvec, ref tvec, flags: method); + + // Verify the recovered pose actually reprojects onto imgPts (rather than just "didn't throw"): + // a stale/misdirected enum value can silently dispatch to a different native solver that + // still runs to completion without throwing, but produces a wrong pose. + Cv2.ProjectPoints(objPts, rvec, tvec, cameraMatrix, dist, out var reprojected, out _); + for (var i = 0; i < imgPts.Length; i++) + { + Assert.Equal(imgPts[i].X, reprojected[i].X, 3); + Assert.Equal(imgPts[i].Y, reprojected[i].Y, 3); + } + } + + [Fact] + public void SolvePnPTestByArrayIPPESquare() + { + var rvec = new double[] { 3, 0, 0 }; + var tvec = new double[] { 0, 0, 10 }; + var cameraMatrix = new double[,] + { + { 1, 0, 0 }, + { 0, 1, 0 }, + { 0, 0, 1 } + }; + var dist = new double[] { 0, 0, 0, 0, 0 }; + + var objPts = new[] + { + new Point3f(-0.5f, 0.5f, 0), + new Point3f(0.5f, 0.5f, 0), + new Point3f(0.5f, -0.5f, 0), + new Point3f(-0.5f, -0.5f, 0) + }; + + Cv2.ProjectPoints(objPts, rvec, tvec, cameraMatrix, dist, out var imgPts, out _); + + Cv2.SolvePnP(objPts, imgPts, cameraMatrix, dist, ref rvec, ref tvec, flags: SolvePnPMethod.IPPESquare); + + Cv2.ProjectPoints(objPts, rvec, tvec, cameraMatrix, dist, out var reprojected, out _); + for (var i = 0; i < imgPts.Length; i++) + { + Assert.Equal(imgPts[i].X, reprojected[i].X, 3); + Assert.Equal(imgPts[i].Y, reprojected[i].Y, 3); + } + } + [Fact] public void SolvePnPTestByMat() {