Hey everyone! I’m currently trying to calibrate my camera with 7 given images (see here) of a Charuco-Board following this example from the documentation. However, the result is so bad, that I think I got something fundamentally wrong here.
Can someone spot my mistake?
#include <iostream>
#include <vector>
#include <opencv2/calib3d.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect/charuco_detector.hpp>
using namespace std;
using namespace cv;
int main() {
// Define board parameters
int squaresX = 11;
int squaresY = 8;
float squareLength = 15.0f; // in mm
float markerLength = 11.0f; // in mm
// Create CharucoBoard and detector
aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_4X4_50);
aruco::CharucoBoard board(Size(squaresX, squaresY), squareLength, markerLength, dictionary);
aruco::CharucoParameters charucoParams;
aruco::DetectorParameters detectorParams;
aruco::CharucoDetector detector(board, charucoParams, detectorParams);
// Vectors to store the detected corner positions and ids
vector<Mat> allCharucoCorners, allCharucoIds;
vector<vector<Point2f>> allImagePoints;
vector<vector<Point3f>> allObjectPoints;
Size imageSize;
int successfulImages = 0;
// Process all calibration images
for (int i = 1; i <= 7; i++) {
string filename = "assets/iPhone/cal_0" + to_string(i) + ".png";
Mat image = imread(filename);
if (image.empty()) {
cerr << "Could not read image: " << filename << endl;
continue;
}
successfulImages++;
imageSize = image.size();
vector<int> markerIds;
vector<vector<Point2f>> markerCorners;
Mat currentCharucoCorners, currentCharucoIds;
// Detect ChArUco board
detector.detectBoard(image, currentCharucoCorners, currentCharucoIds);
if (currentCharucoCorners.total() > 3) {
vector<Point3f> currentObjectPoints;
vector<Point2f> currentImagePoints;
board.matchImagePoints(currentCharucoCorners, currentCharucoIds, currentObjectPoints, currentImagePoints);
// For debugging
Mat imageWithCorners = image.clone();
aruco::drawDetectedCornersCharuco(imageWithCorners, currentCharucoCorners, currentCharucoIds);
imshow("Detected Corners - Image " + to_string(i), imageWithCorners);
waitKey(500); // Display each image for 500ms
// End debugging
if (!currentImagePoints.empty() && !currentObjectPoints.empty()) {
allCharucoCorners.push_back(currentCharucoCorners);
allCharucoIds.push_back(currentCharucoIds);
allImagePoints.push_back(currentImagePoints);
allObjectPoints.push_back(currentObjectPoints);
}
}
}
cout << "Successfully processed " << successfulImages << " out of 6 images." << endl;
if (allCharucoCorners.size() < 4) {
cerr << "Not enough corners for calibration. Need at least 4 valid images." << endl;
return 0;
}
// Calibrate camera
Mat cameraMatrix, distCoeffs;
vector<Mat> rvecs, tvecs;
int calibrationFlags = 0;
double repError = calibrateCamera(allObjectPoints, allImagePoints, imageSize,
cameraMatrix, distCoeffs, rvecs, tvecs, calibrationFlags);
// Print calibration results
cout << "Reprojection error: " << repError << endl;
cout << "Camera matrix: " << endl << cameraMatrix << endl;
cout << "Distortion coefficients: " << endl << distCoeffs << endl;
// Undistort and display an image
string filename = "assets/iPhone/cal_01.png"; // Using the first image
Mat image = imread(filename);
if (image.empty()) {
cerr << "Could not read image for undistortion: " << filename << endl;
return -1;
}
Mat undistorted;
undistort(image, undistorted, cameraMatrix, distCoeffs);
// Display original and undistorted images
namedWindow("Original", WINDOW_NORMAL);
namedWindow("Undistorted", WINDOW_NORMAL);
imshow("Original", image);
imshow("Undistorted", undistorted);
cout << "Press any key to close the image windows..." << endl;
waitKey(0);
return 0;
}