Skip to content

Commit 52fa2c5

Browse files
authored
Fix SolvePnPMethod enum values to match native OpenCV5 numbering (#2081)
* Fix SolvePnPMethod enum values to match native OpenCV5 numbering The geometry module removed the broken DLS/UPnP methods (both just fell back to EPnP) and renumbered the remaining SOLVEPNP_* constants, but the managed SolvePnPMethod enum still carried the old OpenCV4 values. As a result AP3P/IPPE silently ran the wrong native algorithm, and IPPE_SQUARE/SQPNP were rejected outright since they fell outside the native flag range. Also drops the obsolete SolvePnPFlags alias enum, which is no longer needed now that OpenCvSharp5 doesn't guarantee source compatibility with the 4.x API. Fixes #2080 * Address CodeRabbit feedback on #2081 - Rename SolvePnPMethod.IPPE_SQUARE to IppeSquare: once an acronym is glued to another word to form a compound member name, this repo's enum-naming convention PascalCases the whole compound rather than leaving part of it in caps (see .github/copilot-instructions.md). - Use a positive-Z tvec in the new synthetic poses so the projected points sit in front of the camera instead of behind it. - Strengthen SolvePnPTestByArrayMethods/IppeSquare to reproject with the recovered pose and assert it matches the input points, instead of only asserting the call doesn't throw. Confirmed this actually matters: with the old AP3P/IPPE values, SolvePnP silently dispatches to a different native solver that still completes without throwing, so a throws-only assertion doesn't catch it - the reprojection check does. * Rename IppeSquare to IPPESquare to match existing acronym+word precedent CodeRabbit's suggested PascalCase compound (IppeSquare) follows the letter of the naming convention doc, but AKAZEDescriptorType already has an established precedent for this exact shape (acronym + plain word) that keeps the acronym in caps: KAZEUpright/MLDBUpright, not KazeUpright/MldbUpright. Match that existing style instead so IPPE (the bare enum member) and the compound built from it stay visually related.
1 parent 5d08d9c commit 52fa2c5

2 files changed

Lines changed: 77 additions & 52 deletions

File tree

src/OpenCvSharp/Modules/geometry/Enum/SolvePnPFlags.cs renamed to src/OpenCvSharp/Modules/geometry/Enum/SolvePnPMethod.cs

Lines changed: 4 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -27,32 +27,19 @@ public enum SolvePnPMethod
2727
/// </summary>
2828
P3P = 2,
2929

30-
/// <summary>
31-
/// **Broken implementation. Using this flag will fallback to EPnP**.
32-
/// Joel A. Hesch and Stergios I. Roumeliotis. "A Direct Least-Squares (DLS) Method for PnP"
33-
/// </summary>
34-
DLS = 3,
35-
36-
/// <summary>
37-
/// **Broken implementation. Using this flag will fallback to EPnP.**
38-
/// Method is based on the paper of A.Penate-Sanchez, J.Andrade-Cetto, F.Moreno-Noguer.
39-
/// "Exhaustive Linearization for Robust Camera Pose and Focal Length Estimation"
40-
/// </summary>
41-
UPNP = 4,
42-
4330
/// <summary>
4431
/// Method is based on the paper of T. Ke, S. Roumeliotis
4532
/// "An Efficient Algebraic Solution to the Perspective-Three-Point Problem"
4633
/// In this case the function requires exactly four object and image points.
4734
/// </summary>
48-
AP3P = 5,
35+
AP3P = 3,
4936

5037
/// <summary>
5138
/// Method is based on the paper of T. Collins and A. Bartoli.
5239
/// "Infinitesimal Plane-Based Pose Estimation".
5340
/// This method requires coplanar object points.
5441
/// </summary>
55-
IPPE = 6,
42+
IPPE = 4,
5643

5744
/// <summary>
5845
/// Method is based on the paper of Toby Collins and Adrien Bartoli.
@@ -63,46 +50,11 @@ public enum SolvePnPMethod
6350
/// - point 2: [squareLength / 2, -squareLength / 2, 0]
6451
/// - point 3: [-squareLength / 2, -squareLength / 2, 0]
6552
/// </summary>
66-
IPPE_SQUARE = 7,
53+
IPPESquare = 5,
6754

6855
/// <summary>
6956
/// Method is based on the paper "A Consistently Fast and Globally Optimal Solution to the
7057
/// Perspective-n-Point Problem" by G. Terzakis and M. Lourakis. It requires 3 or more points.
7158
/// </summary>
72-
SQPNP = 8,
73-
}
74-
75-
/// <summary>
76-
/// Obsolete: Use SolvePnPMethod instead. This enum is kept for backward compatibility.
77-
/// </summary>
78-
[Obsolete("Use SolvePnPMethod instead", true)]
79-
public enum SolvePnPFlags
80-
{
81-
/// <summary>
82-
/// Iterative method is based on Levenberg-Marquardt optimization.
83-
/// In this case the function finds such a pose that minimizes reprojection error,
84-
/// that is the sum of squared distances between the observed projections imagePoints and the projected (using projectPoints() ) objectPoints .
85-
/// </summary>
86-
Iterative = 0,
87-
88-
/// <summary>
89-
/// Method has been introduced by F.Moreno-Noguer, V.Lepetit and P.Fua in the paper “EPnP: Efficient Perspective-n-Point Camera Pose Estimation”.
90-
/// </summary>
91-
EPNP = 1,
92-
93-
/// <summary>
94-
/// Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang“Complete Solution Classification for
95-
/// the Perspective-Three-Point Problem”. In this case the function requires exactly four object and image points.
96-
/// </summary>
97-
P3P = 2,
98-
99-
/// <summary>
100-
/// Joel A. Hesch and Stergios I. Roumeliotis. "A Direct Least-Squares (DLS) Method for PnP"
101-
/// </summary>
102-
DLS = 3,
103-
104-
/// <summary>
105-
/// A.Penate-Sanchez, J.Andrade-Cetto, F.Moreno-Noguer. "Exhaustive Linearization for Robust Camera Pose and Focal Length Estimation"
106-
/// </summary>
107-
UPNP = 4,
59+
SQPNP = 6,
10860
}

test/OpenCvSharp.Tests/calib3d/Calib3dTest.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,79 @@ public void SolvePnPTestByArray(bool useExtrinsicGuess)
481481
Cv2.SolvePnP(objPts, imgPts, cameraMatrix, dist, ref rvec, ref tvec, useExtrinsicGuess: useExtrinsicGuess);
482482
}
483483

484+
[Theory]
485+
[InlineData(SolvePnPMethod.P3P)]
486+
[InlineData(SolvePnPMethod.AP3P)]
487+
[InlineData(SolvePnPMethod.IPPE)]
488+
[InlineData(SolvePnPMethod.SQPNP)]
489+
public void SolvePnPTestByArrayMethods(SolvePnPMethod method)
490+
{
491+
var rvec = new double[] { 3, 0, 0 };
492+
var tvec = new double[] { 0, 0, 10 };
493+
var cameraMatrix = new double[,]
494+
{
495+
{ 1, 0, 0 },
496+
{ 0, 1, 0 },
497+
{ 0, 0, 1 }
498+
};
499+
var dist = new double[] { 0, 0, 0, 0, 0 };
500+
501+
var objPts = new[]
502+
{
503+
new Point3f(0,0,1),
504+
new Point3f(1,0,1),
505+
new Point3f(0,1,1),
506+
new Point3f(1,1,1)
507+
};
508+
509+
Cv2.ProjectPoints(objPts, rvec, tvec, cameraMatrix, dist, out var imgPts, out _);
510+
511+
Cv2.SolvePnP(objPts, imgPts, cameraMatrix, dist, ref rvec, ref tvec, flags: method);
512+
513+
// Verify the recovered pose actually reprojects onto imgPts (rather than just "didn't throw"):
514+
// a stale/misdirected enum value can silently dispatch to a different native solver that
515+
// still runs to completion without throwing, but produces a wrong pose.
516+
Cv2.ProjectPoints(objPts, rvec, tvec, cameraMatrix, dist, out var reprojected, out _);
517+
for (var i = 0; i < imgPts.Length; i++)
518+
{
519+
Assert.Equal(imgPts[i].X, reprojected[i].X, 3);
520+
Assert.Equal(imgPts[i].Y, reprojected[i].Y, 3);
521+
}
522+
}
523+
524+
[Fact]
525+
public void SolvePnPTestByArrayIPPESquare()
526+
{
527+
var rvec = new double[] { 3, 0, 0 };
528+
var tvec = new double[] { 0, 0, 10 };
529+
var cameraMatrix = new double[,]
530+
{
531+
{ 1, 0, 0 },
532+
{ 0, 1, 0 },
533+
{ 0, 0, 1 }
534+
};
535+
var dist = new double[] { 0, 0, 0, 0, 0 };
536+
537+
var objPts = new[]
538+
{
539+
new Point3f(-0.5f, 0.5f, 0),
540+
new Point3f(0.5f, 0.5f, 0),
541+
new Point3f(0.5f, -0.5f, 0),
542+
new Point3f(-0.5f, -0.5f, 0)
543+
};
544+
545+
Cv2.ProjectPoints(objPts, rvec, tvec, cameraMatrix, dist, out var imgPts, out _);
546+
547+
Cv2.SolvePnP(objPts, imgPts, cameraMatrix, dist, ref rvec, ref tvec, flags: SolvePnPMethod.IPPESquare);
548+
549+
Cv2.ProjectPoints(objPts, rvec, tvec, cameraMatrix, dist, out var reprojected, out _);
550+
for (var i = 0; i < imgPts.Length; i++)
551+
{
552+
Assert.Equal(imgPts[i].X, reprojected[i].X, 3);
553+
Assert.Equal(imgPts[i].Y, reprojected[i].Y, 3);
554+
}
555+
}
556+
484557
[Fact]
485558
public void SolvePnPTestByMat()
486559
{

0 commit comments

Comments
 (0)