1 #include "opencv2/calib3d/calib3d.hpp"
2 #include "opencv2/imgproc/imgproc.hpp"
3 #include "opencv2/highgui/highgui.hpp"
16 printf( "\nThis code generates an artificial camera and artificial chessboard images,\n"
17 "and then calibrates. It is basically test code for calibration that shows\n"
18 "how to package calibration points and then calibrate the camera.\n"
20 "./calibration_artificial\n\n");
25 /* copy of class defines int tests/cv/chessboardgenerator.h */
26 class ChessBoardGenerator
31 size_t squareEdgePointsNum;
35 int rendererResolutionMultiplier;
37 ChessBoardGenerator(const Size& patternSize = Size(8, 6));
38 Mat operator()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, vector<Point2f>& corners) const;
39 Size cornersSize() const;
41 void generateEdge(const Point3f& p1, const Point3f& p2, vector<Point3f>& out) const;
42 Mat generageChessBoard(const Mat& bg, const Mat& camMat, const Mat& distCoeffs,
43 const Point3f& zero, const Point3f& pb1, const Point3f& pb2,
44 float sqWidth, float sqHeight, const vector<Point3f>& whole, vector<Point2f>& corners) const;
45 void generateBasis(Point3f& pb1, Point3f& pb2) const;
46 Point3f generateChessBoardCenter(const Mat& camMat, const Size& imgSize) const;
53 const Size imgSize(800, 600);
54 const Size brdSize(8, 7);
55 const size_t brds_num = 20;
57 template<class T> ostream& operator<<(ostream& out, const Mat_<T>& mat)
59 for(int j = 0; j < mat.rows; ++j)
60 for(int i = 0; i < mat.cols; ++i)
61 out << mat(j, i) << " ";
70 cout << "Initializing background...";
71 Mat background(imgSize, CV_8UC3);
72 randu(background, Scalar::all(32), Scalar::all(255));
73 GaussianBlur(background, background, Size(5, 5), 2);
74 cout << "Done" << endl;
76 cout << "Initializing chess board generator...";
77 ChessBoardGenerator cbg(brdSize);
78 cbg.rendererResolutionMultiplier = 4;
79 cout << "Done" << endl;
82 Mat_<double> camMat(3, 3);
83 camMat << 300., 0., background.cols/2., 0, 300., background.rows/2., 0., 0., 1.;
85 Mat_<double> distCoeffs(1, 5);
86 distCoeffs << 1.2, 0.2, 0., 0., 0.;
88 cout << "Generating chessboards...";
89 vector<Mat> boards(brds_num);
91 for(size_t i = 0; i < brds_num; ++i)
92 cout << (boards[i] = cbg(background, camMat, distCoeffs, tmp), i) << " ";
93 cout << "Done" << endl;
95 vector<Point3f> chessboard3D;
96 for(int j = 0; j < cbg.cornersSize().height; ++j)
97 for(int i = 0; i < cbg.cornersSize().width; ++i)
98 chessboard3D.push_back(Point3i(i, j, 0));
101 vector< vector<Point3f> > objectPoints;
102 vector< vector<Point2f> > imagePoints;
104 cout << endl << "Finding chessboards' corners...";
105 for(size_t i = 0; i < brds_num; ++i)
108 namedWindow("Current chessboard"); imshow("Current chessboard", boards[i]); waitKey(100);
109 bool found = findChessboardCorners(boards[i], cbg.cornersSize(), tmp);
112 imagePoints.push_back(tmp);
113 objectPoints.push_back(chessboard3D);
117 cout<< "-not-found ";
119 drawChessboardCorners(boards[i], cbg.cornersSize(), Mat(tmp), found);
120 imshow("Current chessboard", boards[i]); waitKey(1000);
122 cout << "Done" << endl;
127 vector<Mat> rvecs, tvecs;
129 cout << "Calibrating...";
130 double rep_err = calibrateCamera(objectPoints, imagePoints, imgSize, camMat_est, distCoeffs_est, rvecs, tvecs);
131 cout << "Done" << endl;
133 cout << endl << "Average Reprojection error: " << rep_err/brds_num/cbg.cornersSize().area() << endl;
134 cout << "==================================" << endl;
135 cout << "Original camera matrix:\n" << camMat << endl;
136 cout << "Original distCoeffs:\n" << distCoeffs << endl;
137 cout << "==================================" << endl;
138 cout << "Estimated camera matrix:\n" << (Mat_<double>&)camMat_est << endl;
139 cout << "Estimated distCoeffs:\n" << (Mat_<double>&)distCoeffs_est << endl;
145 /////////////////////////////////////////////////////////////////////////////////////////////////
146 /////////////////////////////////////////////////////////////////////////////////////////////////
147 /////////////////////////////////////////////////////////////////////////////////////////////////
149 // Copy of tests/cv/src/chessboardgenerator code. Just do not want to add dependency.
152 ChessBoardGenerator::ChessBoardGenerator(const Size& _patternSize) : sensorWidth(32), sensorHeight(24),
153 squareEdgePointsNum(200), min_cos(std::sqrt(2.f)*0.5f), cov(0.5),
154 patternSize(_patternSize), rendererResolutionMultiplier(4), tvec(Mat::zeros(1, 3, CV_32F))
156 Rodrigues(Mat::eye(3, 3, CV_32F), rvec);
159 void cv::ChessBoardGenerator::generateEdge(const Point3f& p1, const Point3f& p2, vector<Point3f>& out) const
161 Point3f step = (p2 - p1) * (1.f/squareEdgePointsNum);
162 for(size_t n = 0; n < squareEdgePointsNum; ++n)
163 out.push_back( p1 + step * (float)n);
166 Size cv::ChessBoardGenerator::cornersSize() const
168 return Size(patternSize.width-1, patternSize.height-1);
174 Mult(int mult) : m((float)mult) {}
175 Point2f operator()(const Point2f& p)const { return p * m; }
178 void cv::ChessBoardGenerator::generateBasis(Point3f& pb1, Point3f& pb2) const
185 n[0] = rng.uniform(-1.f, 1.f);
186 n[1] = rng.uniform(-1.f, 1.f);
187 n[2] = rng.uniform(-1.f, 1.f);
188 float len = (float)norm(n);
193 if (fabs(n[2]) > min_cos)
197 Vec3f n_temp = n; n_temp[0] += 100;
198 Vec3f b1 = n.cross(n_temp);
199 Vec3f b2 = n.cross(b1);
200 float len_b1 = (float)norm(b1);
201 float len_b2 = (float)norm(b2);
203 pb1 = Point3f(b1[0]/len_b1, b1[1]/len_b1, b1[2]/len_b1);
204 pb2 = Point3f(b2[0]/len_b1, b2[1]/len_b2, b2[2]/len_b2);
207 Mat cv::ChessBoardGenerator::generageChessBoard(const Mat& bg, const Mat& camMat, const Mat& distCoeffs,
208 const Point3f& zero, const Point3f& pb1, const Point3f& pb2,
209 float sqWidth, float sqHeight, const vector<Point3f>& whole,
210 vector<Point2f>& corners) const
212 vector< vector<Point> > squares_black;
213 for(int i = 0; i < patternSize.width; ++i)
214 for(int j = 0; j < patternSize.height; ++j)
215 if ( (i % 2 == 0 && j % 2 == 0) || (i % 2 != 0 && j % 2 != 0) )
217 vector<Point3f> pts_square3d;
218 vector<Point2f> pts_square2d;
220 Point3f p1 = zero + (i + 0) * sqWidth * pb1 + (j + 0) * sqHeight * pb2;
221 Point3f p2 = zero + (i + 1) * sqWidth * pb1 + (j + 0) * sqHeight * pb2;
222 Point3f p3 = zero + (i + 1) * sqWidth * pb1 + (j + 1) * sqHeight * pb2;
223 Point3f p4 = zero + (i + 0) * sqWidth * pb1 + (j + 1) * sqHeight * pb2;
224 generateEdge(p1, p2, pts_square3d);
225 generateEdge(p2, p3, pts_square3d);
226 generateEdge(p3, p4, pts_square3d);
227 generateEdge(p4, p1, pts_square3d);
229 projectPoints( Mat(pts_square3d), rvec, tvec, camMat, distCoeffs, pts_square2d);
230 squares_black.resize(squares_black.size() + 1);
231 vector<Point2f> temp;
232 approxPolyDP(Mat(pts_square2d), temp, 1.0, true);
233 transform(temp.begin(), temp.end(), back_inserter(squares_black.back()), Mult(rendererResolutionMultiplier));
236 /* calculate corners */
237 vector<Point3f> corners3d;
238 for(int j = 0; j < patternSize.height - 1; ++j)
239 for(int i = 0; i < patternSize.width - 1; ++i)
240 corners3d.push_back(zero + (i + 1) * sqWidth * pb1 + (j + 1) * sqHeight * pb2);
242 projectPoints( Mat(corners3d), rvec, tvec, camMat, distCoeffs, corners);
244 vector<Point3f> whole3d;
245 vector<Point2f> whole2d;
246 generateEdge(whole[0], whole[1], whole3d);
247 generateEdge(whole[1], whole[2], whole3d);
248 generateEdge(whole[2], whole[3], whole3d);
249 generateEdge(whole[3], whole[0], whole3d);
250 projectPoints( Mat(whole3d), rvec, tvec, camMat, distCoeffs, whole2d);
251 vector<Point2f> temp_whole2d;
252 approxPolyDP(Mat(whole2d), temp_whole2d, 1.0, true);
254 vector< vector<Point > > whole_contour(1);
255 transform(temp_whole2d.begin(), temp_whole2d.end(),
256 back_inserter(whole_contour.front()), Mult(rendererResolutionMultiplier));
259 if (rendererResolutionMultiplier == 1)
262 drawContours(result, whole_contour, -1, Scalar::all(255), FILLED, LINE_AA);
263 drawContours(result, squares_black, -1, Scalar::all(0), FILLED, LINE_AA);
268 resize(bg, tmp, bg.size() * rendererResolutionMultiplier);
269 drawContours(tmp, whole_contour, -1, Scalar::all(255), FILLED, LINE_AA);
270 drawContours(tmp, squares_black, -1, Scalar::all(0), FILLED, LINE_AA);
271 resize(tmp, result, bg.size(), 0, 0, INTER_AREA);
276 Mat cv::ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, vector<Point2f>& corners) const
278 cov = std::min(cov, 0.8);
279 double fovx, fovy, focalLen;
280 Point2d principalPoint;
282 calibrationMatrixValues( camMat, bg.size(), sensorWidth, sensorHeight,
283 fovx, fovy, focalLen, principalPoint, aspect);
287 float d1 = static_cast<float>(rng.uniform(0.1, 10.0));
288 float ah = static_cast<float>(rng.uniform(-fovx/2 * cov, fovx/2 * cov) * CV_PI / 180);
289 float av = static_cast<float>(rng.uniform(-fovy/2 * cov, fovy/2 * cov) * CV_PI / 180);
297 generateBasis(pb1, pb2);
299 float cbHalfWidth = static_cast<float>(norm(p) * sin( std::min(fovx, fovy) * 0.5 * CV_PI / 180));
300 float cbHalfHeight = cbHalfWidth * patternSize.height / patternSize.width;
302 vector<Point3f> pts3d(4);
303 vector<Point2f> pts2d(4);
306 pts3d[0] = p + pb1 * cbHalfWidth + cbHalfHeight * pb2;
307 pts3d[1] = p + pb1 * cbHalfWidth - cbHalfHeight * pb2;
308 pts3d[2] = p - pb1 * cbHalfWidth - cbHalfHeight * pb2;
309 pts3d[3] = p - pb1 * cbHalfWidth + cbHalfHeight * pb2;
311 /* can remake with better perf */
312 projectPoints( Mat(pts3d), rvec, tvec, camMat, distCoeffs, pts2d);
314 bool inrect1 = pts2d[0].x < bg.cols && pts2d[0].y < bg.rows && pts2d[0].x > 0 && pts2d[0].y > 0;
315 bool inrect2 = pts2d[1].x < bg.cols && pts2d[1].y < bg.rows && pts2d[1].x > 0 && pts2d[1].y > 0;
316 bool inrect3 = pts2d[2].x < bg.cols && pts2d[2].y < bg.rows && pts2d[2].x > 0 && pts2d[2].y > 0;
317 bool inrect4 = pts2d[3].x < bg.cols && pts2d[3].y < bg.rows && pts2d[3].x > 0 && pts2d[3].y > 0;
319 if ( inrect1 && inrect2 && inrect3 && inrect4)
323 cbHalfHeight = cbHalfWidth * patternSize.height / patternSize.width;
326 cbHalfWidth *= static_cast<float>(patternSize.width)/(patternSize.width + 1);
327 cbHalfHeight *= static_cast<float>(patternSize.height)/(patternSize.height + 1);
329 Point3f zero = p - pb1 * cbHalfWidth - cbHalfHeight * pb2;
330 float sqWidth = 2 * cbHalfWidth/patternSize.width;
331 float sqHeight = 2 * cbHalfHeight/patternSize.height;
333 return generageChessBoard(bg, camMat, distCoeffs, zero, pb1, pb2, sqWidth, sqHeight, pts3d, corners);