39#include <opencv2/calib3d.hpp>
40#include <opencv2/core.hpp>
41#include <opencv2/core/utility.hpp>
42#include <opencv2/highgui.hpp>
43#include <opencv2/imgcodecs.hpp>
44#include <opencv2/imgproc.hpp>
45#include <opencv2/opencv.hpp>
46#include <opencv2/videoio.hpp>
48#include "Image/ByteImage.h"
49#include "Image/ImageProcessor.h"
50#include "Image/IplImageAdaptor.h"
70 computeReprojectionErrors(
const std::vector<std::vector<cv::Point3f>>& objectPoints,
71 const std::vector<std::vector<cv::Point2f>>& imagePoints,
72 const std::vector<cv::Mat>& rvecs,
73 const std::vector<cv::Mat>& tvecs,
74 const cv::Mat& cameraMatrix,
75 const cv::Mat& distCoeffs,
76 std::vector<float>& perViewErrors)
78 std::vector<cv::Point2f> imagePoints2;
79 size_t totalPoints = 0;
80 double totalErr = 0, err;
81 perViewErrors.resize(objectPoints.size());
83 for (
size_t i = 0; i < objectPoints.size(); ++i)
86 objectPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2);
87 err = cv::norm(imagePoints[i], imagePoints2, cv::NORM_L2);
89 size_t n = objectPoints[i].size();
90 perViewErrors[i] = (
float)std::sqrt(err * err / n);
91 totalErr += err * err;
95 return std::sqrt(totalErr / totalPoints);
99 calcBoardCornerPositions(cv::Size boardSize,
101 std::vector<cv::Point3f>& corners,
110 for (
int i = 0; i < boardSize.height; ++i)
111 for (
int j = 0; j < boardSize.width; ++j)
113 corners.push_back(cv::Point3f(j * squareSize, i * squareSize, 0));
118 for (
int i = 0; i < boardSize.height; i++)
119 for (
int j = 0; j < boardSize.width; j++)
122 cv::Point3f((2 * j + i % 2) * squareSize, i * squareSize, 0));
133 cv::Mat& cameraMatrix,
135 std::vector<std::vector<cv::Point2f>> imagePoints,
136 std::vector<cv::Mat>& rvecs,
137 std::vector<cv::Mat>& tvecs,
138 std::vector<float>& reprojErrs,
142 cameraMatrix = cv::Mat::eye(3, 3, CV_64F);
143 if (
s.flag & cv::CALIB_FIX_ASPECT_RATIO)
145 cameraMatrix.at<
double>(0, 0) =
s.aspectRatio;
148 distCoeffs = cv::Mat::zeros(8, 1, CV_64F);
150 std::vector<std::vector<cv::Point3f>> objectPoints(1);
151 calcBoardCornerPositions(
s.boardSize,
s.squareSize, objectPoints[0],
s.calibrationPattern);
153 objectPoints.resize(imagePoints.size(), objectPoints[0]);
158 rms = cv::calibrateCamera(
159 objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs,
s.flag);
161 ARMARX_INFO <<
"Re-projection error reported by calibrateCamera: " << rms;
163 bool ok = cv::checkRange(cameraMatrix) && cv::checkRange(distCoeffs);
165 totalAvgErr = computeReprojectionErrors(
166 objectPoints, imagePoints, rvecs, tvecs, cameraMatrix, distCoeffs, reprojErrs);
174 cv::Mat& cameraMatrix,
176 const std::vector<cv::Mat>& rvecs,
177 const std::vector<cv::Mat>& tvecs,
178 const std::vector<float>& reprojErrs,
179 const std::vector<std::vector<cv::Point2f>>& imagePoints,
182 cv::FileStorage fs(
s.outputFileName, cv::FileStorage::WRITE);
186 struct tm* t2 = localtime(&tm);
188 strftime(buf,
sizeof(buf),
"%c", t2);
190 fs <<
"calibration_time" << buf;
192 if (!rvecs.empty() || !reprojErrs.empty())
194 fs <<
"nr_of_frames" << (int)std::max(rvecs.size(), reprojErrs.size());
196 fs <<
"image_width" << imageSize.width;
197 fs <<
"image_height" << imageSize.height;
198 fs <<
"board_width" <<
s.boardSize.width;
199 fs <<
"board_height" <<
s.boardSize.height;
200 fs <<
"square_size" <<
s.squareSize;
202 if (
s.flag & cv::CALIB_FIX_ASPECT_RATIO)
204 fs <<
"fix_aspect_ratio" <<
s.aspectRatio;
209 std::stringstream flagsStringStream;
211 flagsStringStream <<
"flags:"
212 << (
s.flag & cv::CALIB_USE_INTRINSIC_GUESS ?
" +use_intrinsic_guess"
214 << (
s.flag & cv::CALIB_FIX_ASPECT_RATIO ?
" +fix_aspectRatio" :
"")
215 << (
s.flag & cv::CALIB_FIX_PRINCIPAL_POINT ?
" +fix_principal_point"
217 << (
s.flag & cv::CALIB_ZERO_TANGENT_DIST ?
" +zero_tangent_dist" :
"")
218 << (
s.flag & cv::CALIB_FIX_K1 ?
" +fix_k1" :
"")
219 << (
s.flag & cv::CALIB_FIX_K2 ?
" +fix_k2" :
"")
220 << (
s.flag & cv::CALIB_FIX_K3 ?
" +fix_k3" :
"")
221 << (
s.flag & cv::CALIB_FIX_K4 ?
" +fix_k4" :
"")
222 << (
s.flag & cv::CALIB_FIX_K5 ?
" +fix_k5" :
"")
223 << (
s.flag & cv::CALIB_FIX_K6 ?
" +fix_k6" :
"");
225 fs.writeComment(flagsStringStream.str());
228 fs <<
"flags" <<
s.flag;
230 fs <<
"camera_matrix" << cameraMatrix;
231 fs <<
"distortion_coefficients" << distCoeffs;
233 fs <<
"avg_reprojection_error" << totalAvgErr;
234 if (
s.writeExtrinsics && !reprojErrs.empty())
236 fs <<
"per_view_reprojection_errors" << cv::Mat(reprojErrs);
239 if (
s.writeExtrinsics && !rvecs.empty() && !tvecs.empty())
241 CV_Assert(rvecs[0].type() == tvecs[0].type());
242 cv::Mat bigmat((
int)rvecs.size(), 6, CV_MAKETYPE(rvecs[0].type(), 1));
243 bool needReshapeR = rvecs[0].depth() != 1 ? true :
false;
244 bool needReshapeT = tvecs[0].depth() != 1 ? true :
false;
246 for (
size_t i = 0; i < rvecs.size(); i++)
248 cv::Mat r = bigmat(cv::Range(
int(i),
int(i + 1)), cv::Range(0, 3));
249 cv::Mat t = bigmat(cv::Range(
int(i),
int(i + 1)), cv::Range(3, 6));
253 rvecs[i].reshape(1, 1).copyTo(r);
258 CV_Assert(rvecs[i].rows == 3 && rvecs[i].cols == 1);
264 tvecs[i].reshape(1, 1).copyTo(t);
268 CV_Assert(tvecs[i].rows == 3 && tvecs[i].cols == 1);
273 "a set of 6-tuples (rotation vector + translation vector) for each view");
274 fs <<
"extrinsic_parameters" << bigmat;
277 if (
s.writePoints && !imagePoints.empty())
279 cv::Mat imagePtMat((
int)imagePoints.size(), (
int)imagePoints[0].size(), CV_32FC2);
280 for (
size_t i = 0; i < imagePoints.size(); i++)
282 cv::Mat r = imagePtMat.row(
int(i)).reshape(2, imagePtMat.cols);
283 cv::Mat imgpti(imagePoints[i]);
286 fs <<
"image_points" << imagePtMat;
293 cv::Mat& cameraMatrix,
295 std::vector<std::vector<cv::Point2f>> imagePoints)
297 std::vector<cv::Mat> rvecs, tvecs;
298 std::vector<float> reprojErrs;
299 double totalAvgErr = 0;
301 bool ok = runCalibration(s,
310 ARMARX_INFO << (
ok ?
"Calibration succeeded" :
"Calibration failed")
311 <<
". avg re projection error = " << totalAvgErr;
340 waitingTimeBetweenCaptures =
341 IceUtil::Time::milliSeconds(
getProperty<int>(
"WaitingIntervalBetweenImages"));
343 if (image_side ==
"left")
347 else if (image_side ==
"right")
353 throw std::runtime_error(
"ImageToUse must be either left or right");
362 StereoCalibrationInterfacePrx calibrationProvider =
363 StereoCalibrationInterfacePrx::checkedCast(imageProviderInfo.
proxy);
364 auto cam = calibrationProvider->getStereoCalibration();
365 auto calib = imageID == 0 ? cam.calibrationLeft : cam.calibrationRight;
366 auto focal_lengths = calib.cameraParam.focalLength;
368 cameraImages =
new CByteImage*[2];
373 getProperty(m_settings.boardSize.width,
"NumberOfColumns");
374 getProperty(m_settings.boardSize.height,
"NumberOfRows");
378 getProperty(m_settings.squareSize,
"PatternSquareSize");
380 getProperty(m_settings.nrFrames,
"NumberOfImages");
384 m_settings.aspectRatio = focal_lengths.at(0) / focal_lengths.at(1);
388 m_settings.aspectRatio = 0;
391 m_settings.writePoints =
true;
393 m_settings.writeExtrinsics =
false;
395 m_settings.flipVertical =
false;
397 m_settings.outputFileName = m_outputFileName;
399 getProperty(m_settings.useK3ToK6,
"UseAdditionalParams");
401 m_settings.validate();
402 if (not m_settings.goodInput)
404 throw std::runtime_error(
"Invalid input detected. Application stopping.");
407 ARMARX_INFO <<
"Board info: " << m_settings.boardSize.width <<
"x"
408 << m_settings.boardSize.height <<
" squares with square size of "
409 << m_settings.squareSize;
415 delete cameraImages[0];
416 delete cameraImages[1];
417 delete[] cameraImages;
435 if (IceUtil::Time::now() - timeOfLastCapture < waitingTimeBetweenCaptures)
440 ARMARX_INFO <<
"Capturing image for calibration #" << imagePoints.size();
442 timeOfLastCapture = IceUtil::Time::now();
446 if (m_mode == CAPTURING && imagePoints.size() >= (
size_t)m_settings.nrFrames)
449 if (runCalibrationAndSave(m_settings, imageSize, cameraMatrix, distCoeffs, imagePoints))
456 imageSize = view.size();
457 if (m_settings.flipVertical)
459 cv::flip(view, view, 0);
463 std::vector<cv::Point2f> pointBuf;
467 int chessBoardFlags =
468 cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE | cv::CALIB_CB_FAST_CHECK;
470 switch (m_settings.calibrationPattern)
474 found = cv::findChessboardCorners(
475 view, m_settings.boardSize, pointBuf, chessBoardFlags);
478 found = cv::findCirclesGrid(view, m_settings.boardSize, pointBuf);
481 found = cv::findCirclesGrid(
482 view, m_settings.boardSize, pointBuf, cv::CALIB_CB_ASYMMETRIC_GRID);
497 cv::cvtColor(view, viewGray, cv::COLOR_BGR2GRAY);
503 cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 30, 0.1));
506 if (m_mode == CAPTURING)
508 imagePoints.push_back(pointBuf);
512 cv::drawChessboardCorners(view, m_settings.boardSize, cv::Mat(pointBuf), found);
Property< PropertyType > getProperty(const std::string &name)
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
static const std::string default_name
void onConnectImageProcessor() override
Implement this method in the ImageProcessor in order execute parts when the component is fully initia...
void onExitImageProcessor() override
Exit the ImapeProcessor component.
void process() override
Process the vision component.
void onInitImageProcessor() override
Setup the vision component.
void onDisconnectImageProcessor() override
Implement this method in the ImageProcessor in order execute parts when the component looses network ...
void usingImageProvider(std::string name)
Registers a delayed topic subscription and a delayed provider proxy retrieval which all will be avail...
bool waitForImages(int milliseconds=1000)
Wait for new images.
ImageProviderInfo getImageProvider(std::string name, ImageType destinationImageType=eRgb, bool waitForProxy=false)
Select an ImageProvider.
int getImages(CByteImage **ppImages)
Poll images from provider.
ImageProviderInterfacePrx proxy
proxy to image provider
@ ASYMMETRIC_CIRCLES_GRID
#define ARMARX_INFO
The normal logging level.
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
double s(double t, double s0, double v0, double a0, double j)
constexpr auto n() noexcept
void convert(const CByteImage &in, cv::Mat &out)
Converts an IVT CByteImage to OpenCV's BGR Mat.