Merge pull request #30176 from SavyaSanchi-Sharma:callibrateCam

calib: accept float64 points in calibrateCamera - #30176

Lets calibrateCamera, calibrateCameraRO, stereoCalibrate, initCameraMatrix2D, and registerCameras accept Point3d/Point2d (double) input in addition to Point3f/Point2f.  Adds a new test suite (float64Points) exercising each of those entry points with double-precision input and comparing accuracy against the float32 path.

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [ ] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Savya Sanchi Sharma
2026-10-09 18:38:25 +05:30
committed by GitHub
parent 731831782c
commit 1e21d6ed7d
3 changed files with 205 additions and 27 deletions

View File

@@ -564,7 +564,8 @@ CV_EXPORTS_W Mat initCameraMatrix2D( InputArrayOfArrays objectPoints,
pattern.
@param objectPoints In the new interface it is a vector of vectors of calibration pattern points in
the calibration pattern coordinate space (e.g. std::vector<std::vector<cv::Vec3f>>). The outer
the calibration pattern coordinate space (e.g. std::vector<std::vector<cv::Vec3f>> or
std::vector<std::vector<cv::Vec3d>>). Both single and double precision points are accepted. The outer
vector contains as many elements as the number of pattern views. If the same calibration pattern
is shown in each view and it is fully visible, all the vectors will be the same. Although, it is
possible to use partially occluded patterns or even different patterns in different views. Then,
@@ -573,7 +574,8 @@ XY coordinate plane (thus 0 in the Z-coordinate), if the used calibration patter
In the old interface all the vectors of object points from different views are concatenated
together.
@param imagePoints In the new interface it is a vector of vectors of the projections of calibration
pattern points (e.g. std::vector<std::vector<cv::Vec2f>>). imagePoints.size() and
pattern points (e.g. std::vector<std::vector<cv::Vec2f>> or std::vector<std::vector<cv::Vec2d>>).
Both single and double precision points are accepted. imagePoints.size() and
objectPoints.size(), and imagePoints[i].size() and objectPoints[i].size() for each i, must be equal,
respectively. In the old interface all the vectors of object points from different views are
concatenated together.
@@ -745,7 +747,8 @@ for details.
@param newObjPoints The updated output vector of calibration pattern points. The coordinates might
be scaled based on three fixed points. The returned coordinates are accurate only if the above
mentioned three fixed points are accurate. If not needed, noArray() can be passed in. This parameter
is ignored with standard calibration method.
is ignored with standard calibration method. The output has the same depth as objectPoints[0]
(CV_32F or CV_64F).
@param stdDeviationsIntrinsics Output vector of standard deviations estimated for intrinsic parameters.
See #calibrateCamera for details.
@param stdDeviationsExtrinsics Output vector of standard deviations estimated for extrinsic parameters.

View File

@@ -2741,65 +2741,60 @@ static void collectCalibrationData( InputArrayOfArrays objectPoints,
Mat objectPoint = objectPoints.getMat(i);
if (objectPoint.empty())
CV_Error(cv::Error::StsBadSize, "objectPoints should not contain empty vector of vectors of points");
int numberOfObjectPoints = objectPoint.checkVector(3, CV_32F, false);
int numberOfObjectPoints = std::max(objectPoint.checkVector(3, CV_32F, false), objectPoint.checkVector(3, CV_64F, false));
if (numberOfObjectPoints <= 0)
CV_Error(cv::Error::StsUnsupportedFormat, "objectPoints should contain vector of vectors of points of type Point3f");
CV_Error(cv::Error::StsUnsupportedFormat, "objectPoints should contain vector of vectors of points of type Point3f or Point3d");
Mat imagePoint1 = imagePoints1.getMat(i);
if (imagePoint1.empty())
CV_Error(cv::Error::StsBadSize, "imagePoints1 should not contain empty vector of vectors of points");
int numberOfImagePoints = imagePoint1.checkVector(2, CV_32F, false);
int numberOfImagePoints = std::max(imagePoint1.checkVector(2, CV_32F, false), imagePoint1.checkVector(2, CV_64F, false));
if (numberOfImagePoints <= 0)
CV_Error(cv::Error::StsUnsupportedFormat, "imagePoints1 should contain vector of vectors of points of type Point2f");
CV_Error(cv::Error::StsUnsupportedFormat, "imagePoints1 should contain vector of vectors of points of type Point2f or Point2d");
CV_CheckEQ(numberOfObjectPoints, numberOfImagePoints, "Number of object and image points must be equal");
total += numberOfObjectPoints;
}
npoints.create(1, (int)nimages, CV_32S);
objPt.create(1, (int)total, CV_32FC3);
imgPt1.create(1, (int)total, CV_32FC2);
Point2f* imgPtData2 = 0;
objPt.create(1, (int)total, CV_64FC3);
imgPt1.create(1, (int)total, CV_64FC2);
Mat imgPt2Mat;
Mat imgPt1Mat = imgPt1.getMat();
if (!imagePoints2.empty())
{
imgPt2.create(1, (int)total, CV_32FC2);
imgPtData2 = imgPt2.getMat().ptr<Point2f>();
imgPt2.create(1, (int)total, CV_64FC2);
imgPt2Mat = imgPt2.getMat();
}
Mat nPointsMat = npoints.getMat();
Mat objPtMat = objPt.getMat();
Point3f* objPtData = objPtMat.ptr<Point3f>();
Point2f* imgPtData1 = imgPt1.getMat().ptr<Point2f>();
for (int i = 0, j = 0; i < nimages; i++)
{
Mat objpt = objectPoints.getMat(i);
Mat imgpt1 = imagePoints1.getMat(i);
int numberOfObjectPoints = objpt.checkVector(3, CV_32F, false);
int numberOfObjectPoints = std::max(objpt.checkVector(3, CV_32F, false), objpt.checkVector(3, CV_64F, false));
if (!objpt.isContinuous())
objpt = objpt.clone();
if (!imgpt1.isContinuous())
imgpt1 = imgpt1.clone();
nPointsMat.at<int>(i) = numberOfObjectPoints;
for (int n = 0; n < numberOfObjectPoints; ++n)
{
objPtData[j + n] = objpt.ptr<Point3f>()[n];
imgPtData1[j + n] = imgpt1.ptr<Point2f>()[n];
}
Mat(1, numberOfObjectPoints, CV_MAKETYPE(objpt.depth(), 3), objpt.ptr())
.convertTo(objPtMat.colRange(j, j + numberOfObjectPoints), CV_64F);
Mat(1, numberOfObjectPoints, CV_MAKETYPE(imgpt1.depth(), 2), imgpt1.ptr())
.convertTo(imgPt1Mat.colRange(j, j + numberOfObjectPoints), CV_64F);
if (imgPtData2)
if (!imgPt2Mat.empty())
{
Mat imgpt2 = imagePoints2.getMat(i);
int numberOfImage2Points = imgpt2.checkVector(2, CV_32F, false);
int numberOfImage2Points = std::max(imgpt2.checkVector(2, CV_32F, false), imgpt2.checkVector(2, CV_64F, false));
CV_CheckEQ(numberOfObjectPoints, numberOfImage2Points, "Number of object and image(2) points must be equal");
if (!imgpt2.isContinuous())
imgpt2 = imgpt2.clone();
for (int n = 0; n < numberOfImage2Points; ++n)
{
imgPtData2[j + n] = imgpt2.ptr<Point2f>()[n];
}
Mat(1, numberOfImage2Points, CV_MAKETYPE(imgpt2.depth(), 2), imgpt2.ptr())
.convertTo(imgPt2Mat.colRange(j, j + numberOfImage2Points), CV_64F);
}
j += numberOfObjectPoints;
@@ -2994,7 +2989,7 @@ double calibrateCameraRO(InputArrayOfArrays _objectPoints,
int np = npoints.at<int>( 0 );
Mat newObjPt;
if( newobj_needed ) {
newObjPoints.create( 1, np, CV_32FC3 );
newObjPoints.create( 1, np, CV_MAKETYPE(_objectPoints.depth(0), 3) );
newObjPt = newObjPoints.getMat();
}

View File

@@ -43,6 +43,7 @@
#include "opencv2/stereo.hpp"
#include "opencv2/objdetect.hpp"
#include <cfloat>
#include "opencv2/ts/cuda_test.hpp"
namespace opencv_test { namespace {
@@ -2029,4 +2030,183 @@ TEST(Calib_CalibrateCamera, size4DistortionCoeffs)
EXPECT_LE(cv::norm(cv::Vec4d(D2), distCoeffs, NORM_INF), 1e-4);
}
static void makeFloat64Views(const Matx33d& K, const Vec3d& rStereo, const Vec3d& tStereo,
std::vector<std::vector<Point3d> >& objectPoints,
std::vector<std::vector<Point2d> >& imagePoints1,
std::vector<std::vector<Point2d> >& imagePoints2)
{
std::vector<Point3d> board;
for (int y = 0; y < 6; y++)
for (int x = 0; x < 9; x++)
board.push_back(Point3d(x * 0.025, y * 0.025, 0));
const Vec3d rvecs[] = { Vec3d(0.1, 0.2, 0), Vec3d(-0.2, 0.1, 0.1), Vec3d(0.3, -0.1, 0.05), Vec3d(0, -0.3, -0.1) };
const Vec3d tvecs[] = { Vec3d(-0.1, -0.07, 0.5), Vec3d(-0.08, -0.06, 0.6), Vec3d(-0.12, -0.05, 0.55), Vec3d(-0.1, -0.08, 0.45) };
for (int i = 0; i < 4; i++)
{
Vec3d r2, t2;
composeRT(rvecs[i], tvecs[i], rStereo, tStereo, r2, t2);
std::vector<Point2d> p1, p2;
projectPoints(board, rvecs[i], tvecs[i], K, noArray(), p1);
projectPoints(board, r2, t2, K, noArray(), p2);
objectPoints.push_back(board);
imagePoints1.push_back(p1);
imagePoints2.push_back(p2);
}
}
template <typename T, typename S>
static std::vector<std::vector<T> > toPointsOf(const std::vector<std::vector<S> >& src)
{
std::vector<std::vector<T> > dst(src.size());
for (size_t i = 0; i < src.size(); i++)
for (const S& p : src[i])
dst[i].push_back(T(p));
return dst;
}
template <typename T>
static Mat toExplicitDimsMat(const std::vector<T>& pts, int cn)
{
int sz[] = { 1, (int)pts.size(), cn };
return Mat(3, sz, CV_64F, (void*)pts.data()).clone();
}
TEST(Calib_CalibrateCamera, float64Points)
{
const Matx33d K(800, 0, 320, 0, 800, 240, 0, 0, 1);
std::vector<std::vector<Point3d> > obj;
std::vector<std::vector<Point2d> > img, img2;
makeFloat64Views(K, Vec3d::all(0), Vec3d(-0.1, 0, 0), obj, img, img2);
Mat K64, dist64, K32, dist32;
std::vector<Mat> rvecs, tvecs;
double rms64 = calibrateCamera(obj, img, Size(640, 480), K64, dist64, rvecs, tvecs);
double rms32 = calibrateCamera(toPointsOf<Point3f>(obj), toPointsOf<Point2f>(img),
Size(640, 480), K32, dist32, rvecs, tvecs);
EXPECT_LT(rms64, 1e-7);
EXPECT_MAT_NEAR(K64, Mat(K), 1e-6);
EXPECT_LT(rms32, 1e-3);
EXPECT_MAT_NEAR(K32, Mat(K), 1e-2);
}
TEST(Calib_CalibrateCamera, dims3PointArrays)
{
const Matx33d K(800, 0, 320, 0, 800, 240, 0, 0, 1);
std::vector<std::vector<Point3d> > obj;
std::vector<std::vector<Point2d> > img, img2;
makeFloat64Views(K, Vec3d::all(0), Vec3d(-0.1, 0, 0), obj, img, img2);
std::vector<Mat> objDims3(obj.size()), imgDims3(img.size());
for (size_t i = 0; i < obj.size(); i++)
{
objDims3[i] = toExplicitDimsMat(obj[i], 3);
imgDims3[i] = toExplicitDimsMat(img[i], 2);
ASSERT_EQ(objDims3[i].dims, 3);
ASSERT_EQ(objDims3[i].checkVector(3), (int)obj[i].size());
}
Mat Kref, distref, Kdims3, distdims3;
std::vector<Mat> rvecs, tvecs;
double rmsRef = calibrateCamera(obj, img, Size(640, 480), Kref, distref, rvecs, tvecs);
double rmsDims3 = calibrateCamera(objDims3, imgDims3, Size(640, 480), Kdims3, distdims3, rvecs, tvecs);
EXPECT_LT(rmsRef, 1e-7);
EXPECT_NEAR(rmsRef, rmsDims3, 1e-9);
EXPECT_MAT_NEAR(Kref, Kdims3, 1e-9);
EXPECT_MAT_NEAR(distref, distdims3, 1e-9);
}
TEST(Calib_CalibrateCameraRO, float64Points)
{
const Matx33d K(800, 0, 320, 0, 800, 240, 0, 0, 1);
std::vector<std::vector<Point3d> > obj;
std::vector<std::vector<Point2d> > img, img2;
makeFloat64Views(K, Vec3d::all(0), Vec3d(-0.1, 0, 0), obj, img, img2);
std::vector<std::vector<Point3f> > obj32 = toPointsOf<Point3f>(obj);
Mat K64, dist64, K32, dist32;
std::vector<Mat> rvecs, tvecs;
std::vector<Point3d> newObj64;
std::vector<Point3f> newObj32;
double rms64 = calibrateCameraRO(obj, img, Size(640, 480), 8, K64, dist64, rvecs, tvecs, newObj64);
double rms32 = calibrateCameraRO(obj32, toPointsOf<Point2f>(img), Size(640, 480), 8,
K32, dist32, rvecs, tvecs, newObj32);
EXPECT_LT(rms64, 1e-7);
EXPECT_MAT_NEAR(K64, Mat(K), 1e-6);
EXPECT_MAT_NEAR(Mat(newObj64).reshape(1), Mat(obj[0]).reshape(1), 1e-9);
EXPECT_LT(rms32, 1e-3);
EXPECT_MAT_NEAR(K32, Mat(K), 1e-2);
EXPECT_MAT_NEAR(Mat(newObj32).reshape(1), Mat(obj32[0]).reshape(1), 1e-4);
}
TEST(Calib_StereoCalibrate, float64Points)
{
const Matx33d K(800, 0, 320, 0, 800, 240, 0, 0, 1);
const Vec3d rStereo(0, 0.05, 0), tStereo(-0.1, 0, 0);
std::vector<std::vector<Point3d> > obj;
std::vector<std::vector<Point2d> > img1, img2;
makeFloat64Views(K, rStereo, tStereo, obj, img1, img2);
Mat Rgt;
Rodrigues(rStereo, Rgt);
Mat K1 = Mat(K).clone(), K2 = Mat(K).clone(), D1 = Mat::zeros(1, 5, CV_64F), D2 = Mat::zeros(1, 5, CV_64F);
Mat R64, T64, R32, T32;
double rms64 = stereoCalibrate(obj, img1, img2, K1, D1, K2, D2, Size(640, 480),
R64, T64, noArray(), noArray(), CALIB_FIX_INTRINSIC);
double rms32 = stereoCalibrate(toPointsOf<Point3f>(obj), toPointsOf<Point2f>(img1), toPointsOf<Point2f>(img2),
K1, D1, K2, D2, Size(640, 480),
R32, T32, noArray(), noArray(), CALIB_FIX_INTRINSIC);
EXPECT_LT(rms64, 1e-7);
EXPECT_MAT_NEAR(R64, Rgt, 1e-6);
EXPECT_MAT_NEAR(T64, Mat(tStereo), 1e-6);
EXPECT_LT(rms32, 1e-3);
EXPECT_MAT_NEAR(R32, Rgt, 1e-4);
EXPECT_MAT_NEAR(T32, Mat(tStereo), 1e-4);
}
TEST(Calib_InitCameraMatrix2D, float64Points)
{
const Matx33d K(800, 0, 319.5, 0, 800, 239.5, 0, 0, 1);
std::vector<std::vector<Point3d> > obj;
std::vector<std::vector<Point2d> > img, img2;
makeFloat64Views(K, Vec3d::all(0), Vec3d(-0.1, 0, 0), obj, img, img2);
Mat K64 = initCameraMatrix2D(obj, img, Size(640, 480));
Mat K32 = initCameraMatrix2D(toPointsOf<Point3f>(obj), toPointsOf<Point2f>(img), Size(640, 480));
EXPECT_MAT_NEAR(K64, Mat(K), 1e-4);
EXPECT_MAT_NEAR(K64, K32, 1e-2);
}
TEST(Calib_RegisterCameras, float64Points)
{
const Matx33d K(800, 0, 320, 0, 800, 240, 0, 0, 1);
const Vec3d rStereo(0, 0.05, 0), tStereo(-0.1, 0, 0);
std::vector<std::vector<Point3d> > obj;
std::vector<std::vector<Point2d> > img1, img2;
makeFloat64Views(K, rStereo, tStereo, obj, img1, img2);
Mat Rgt;
Rodrigues(rStereo, Rgt);
Mat D = Mat::zeros(1, 5, CV_64F);
Mat R64, T64, R32, T32, E, F, errs;
double rms64 = registerCameras(obj, obj, img1, img2, K, D, CALIB_MODEL_PINHOLE, K, D, CALIB_MODEL_PINHOLE,
R64, T64, E, F, errs);
std::vector<std::vector<Point3f> > obj32 = toPointsOf<Point3f>(obj);
double rms32 = registerCameras(obj32, obj32, toPointsOf<Point2f>(img1), toPointsOf<Point2f>(img2),
K, D, CALIB_MODEL_PINHOLE, K, D, CALIB_MODEL_PINHOLE,
R32, T32, E, F, errs);
EXPECT_LT(rms64, 1e-7);
EXPECT_MAT_NEAR(R64, Rgt, 1e-6);
EXPECT_MAT_NEAR(T64, Mat(tStereo), 1e-6);
EXPECT_LT(rms32, 1e-3);
EXPECT_MAT_NEAR(R32, Rgt, 1e-4);
EXPECT_MAT_NEAR(T32, Mat(tStereo), 1e-4);
}
}} // namespace