41#include <opencv2/calib3d.hpp>
42#include <opencv2/core.hpp>
43#include <opencv2/core/utility.hpp>
44#include <opencv2/highgui.hpp>
45#include <opencv2/imgcodecs.hpp>
46#include <opencv2/imgproc.hpp>
47#include <opencv2/opencv.hpp>
48#include <opencv2/videoio.hpp>
50#include "Image/ByteImage.h"
51#include "Image/ImageProcessor.h"
52#include "Image/IplImageAdaptor.h"
72 computeReprojectionErrors(
const std::vector<std::vector<cv::Point3f>>& objectPoints,
73 const std::vector<std::vector<cv::Point2f>>& imagePoints,
74 const std::vector<cv::Mat>& rvecs,
75 const std::vector<cv::Mat>& tvecs,
76 const cv::Mat& cameraMatrix,
77 const cv::Mat& distCoeffs,
78 std::vector<float>& perViewErrors)
80 std::vector<cv::Point2f> imagePoints2;
81 size_t totalPoints = 0;
82 double totalErr = 0, err;
83 perViewErrors.resize(objectPoints.size());
85 for (
size_t i = 0; i < objectPoints.size(); ++i)
88 objectPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2);
89 err = cv::norm(imagePoints[i], imagePoints2, cv::NORM_L2);
91 size_t n = objectPoints[i].size();
92 perViewErrors[i] = (
float)std::sqrt(err * err / n);
93 totalErr += err * err;
97 return std::sqrt(totalErr / totalPoints);
101 calcBoardCornerPositions(cv::Size boardSize,
103 std::vector<cv::Point3f>& corners,
112 for (
int i = 0; i < boardSize.height; ++i)
113 for (
int j = 0; j < boardSize.width; ++j)
115 corners.push_back(cv::Point3f(j * squareSize, i * squareSize, 0));
120 for (
int i = 0; i < boardSize.height; i++)
121 for (
int j = 0; j < boardSize.width; j++)
124 cv::Point3f((2 * j + i % 2) * squareSize, i * squareSize, 0));
135 cv::Mat& cameraMatrix,
137 std::vector<std::vector<cv::Point2f>> imagePoints,
138 std::vector<cv::Mat>& rvecs,
139 std::vector<cv::Mat>& tvecs,
140 std::vector<float>& reprojErrs,
144 cameraMatrix = cv::Mat::eye(3, 3, CV_64F);
145 if (
s.flag & cv::CALIB_FIX_ASPECT_RATIO)
147 cameraMatrix.at<
double>(0, 0) =
s.aspectRatio;
150 distCoeffs = cv::Mat::zeros(8, 1, CV_64F);
152 std::vector<std::vector<cv::Point3f>> objectPoints(1);
153 calcBoardCornerPositions(
s.boardSize,
s.squareSize, objectPoints[0],
s.calibrationPattern);
155 objectPoints.resize(imagePoints.size(), objectPoints[0]);
160 rms = cv::calibrateCamera(
161 objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs,
s.flag);
163 ARMARX_INFO <<
"Re-projection error reported by calibrateCamera: " << rms;
165 bool ok = cv::checkRange(cameraMatrix) && cv::checkRange(distCoeffs);
167 totalAvgErr = computeReprojectionErrors(
168 objectPoints, imagePoints, rvecs, tvecs, cameraMatrix, distCoeffs, reprojErrs);
176 cv::Mat& cameraMatrix,
178 const std::vector<cv::Mat>& rvecs,
179 const std::vector<cv::Mat>& tvecs,
180 const std::vector<float>& reprojErrs,
181 const std::vector<std::vector<cv::Point2f>>& imagePoints,
184 cv::FileStorage fs(
s.outputFileName, cv::FileStorage::WRITE);
188 struct tm* t2 = localtime(&tm);
190 strftime(buf,
sizeof(buf),
"%c", t2);
192 fs <<
"calibration_time" << buf;
194 if (!rvecs.empty() || !reprojErrs.empty())
196 fs <<
"nr_of_frames" << (int)std::max(rvecs.size(), reprojErrs.size());
198 fs <<
"image_width" << imageSize.width;
199 fs <<
"image_height" << imageSize.height;
200 fs <<
"board_width" <<
s.boardSize.width;
201 fs <<
"board_height" <<
s.boardSize.height;
202 fs <<
"square_size" <<
s.squareSize;
204 if (
s.flag & cv::CALIB_FIX_ASPECT_RATIO)
206 fs <<
"fix_aspect_ratio" <<
s.aspectRatio;
211 std::stringstream flagsStringStream;
213 flagsStringStream <<
"flags:"
214 << (
s.flag & cv::CALIB_USE_INTRINSIC_GUESS ?
" +use_intrinsic_guess"
216 << (
s.flag & cv::CALIB_FIX_ASPECT_RATIO ?
" +fix_aspectRatio" :
"")
217 << (
s.flag & cv::CALIB_FIX_PRINCIPAL_POINT ?
" +fix_principal_point"
219 << (
s.flag & cv::CALIB_ZERO_TANGENT_DIST ?
" +zero_tangent_dist" :
"")
220 << (
s.flag & cv::CALIB_FIX_K1 ?
" +fix_k1" :
"")
221 << (
s.flag & cv::CALIB_FIX_K2 ?
" +fix_k2" :
"")
222 << (
s.flag & cv::CALIB_FIX_K3 ?
" +fix_k3" :
"")
223 << (
s.flag & cv::CALIB_FIX_K4 ?
" +fix_k4" :
"")
224 << (
s.flag & cv::CALIB_FIX_K5 ?
" +fix_k5" :
"")
225 << (
s.flag & cv::CALIB_FIX_K6 ?
" +fix_k6" :
"");
227 fs.writeComment(flagsStringStream.str());
230 fs <<
"flags" <<
s.flag;
232 fs <<
"camera_matrix" << cameraMatrix;
233 fs <<
"distortion_coefficients" << distCoeffs;
235 fs <<
"avg_reprojection_error" << totalAvgErr;
236 if (
s.writeExtrinsics && !reprojErrs.empty())
238 fs <<
"per_view_reprojection_errors" << cv::Mat(reprojErrs);
241 if (
s.writeExtrinsics && !rvecs.empty() && !tvecs.empty())
243 CV_Assert(rvecs[0].type() == tvecs[0].type());
244 cv::Mat bigmat((
int)rvecs.size(), 6, CV_MAKETYPE(rvecs[0].type(), 1));
245 bool needReshapeR = rvecs[0].depth() != 1 ? true :
false;
246 bool needReshapeT = tvecs[0].depth() != 1 ? true :
false;
248 for (
size_t i = 0; i < rvecs.size(); i++)
250 cv::Mat r = bigmat(cv::Range(
int(i),
int(i + 1)), cv::Range(0, 3));
251 cv::Mat t = bigmat(cv::Range(
int(i),
int(i + 1)), cv::Range(3, 6));
255 rvecs[i].reshape(1, 1).copyTo(r);
260 CV_Assert(rvecs[i].rows == 3 && rvecs[i].cols == 1);
266 tvecs[i].reshape(1, 1).copyTo(t);
270 CV_Assert(tvecs[i].rows == 3 && tvecs[i].cols == 1);
275 "a set of 6-tuples (rotation vector + translation vector) for each view");
276 fs <<
"extrinsic_parameters" << bigmat;
279 if (
s.writePoints && !imagePoints.empty())
281 cv::Mat imagePtMat((
int)imagePoints.size(), (
int)imagePoints[0].size(), CV_32FC2);
282 for (
size_t i = 0; i < imagePoints.size(); i++)
284 cv::Mat r = imagePtMat.row(
int(i)).reshape(2, imagePtMat.cols);
285 cv::Mat imgpti(imagePoints[i]);
288 fs <<
"image_points" << imagePtMat;
295 cv::Mat& cameraMatrix,
297 std::vector<std::vector<cv::Point2f>> imagePoints)
299 std::vector<cv::Mat> rvecs, tvecs;
300 std::vector<float> reprojErrs;
301 double totalAvgErr = 0;
303 bool ok = runCalibration(s,
312 ARMARX_INFO << (
ok ?
"Calibration succeeded" :
"Calibration failed")
313 <<
". avg re projection error = " << totalAvgErr;
342 waitingTimeBetweenCaptures =
343 IceUtil::Time::milliSeconds(
getProperty<int>(
"WaitingIntervalBetweenImages"));
345 if (image_side ==
"left")
349 else if (image_side ==
"right")
355 throw std::runtime_error(
"ImageToUse must be either left or right");
364 StereoCalibrationInterfacePrx calibrationProvider =
365 StereoCalibrationInterfacePrx::checkedCast(imageProviderInfo.
proxy);
366 auto cam = calibrationProvider->getStereoCalibration();
367 auto calib = imageID == 0 ? cam.calibrationLeft : cam.calibrationRight;
368 auto focal_lengths = calib.cameraParam.focalLength;
370 cameraImages =
new CByteImage*[2];
375 getProperty(m_settings.boardSize.width,
"NumberOfColumns");
376 getProperty(m_settings.boardSize.height,
"NumberOfRows");
380 getProperty(m_settings.squareSize,
"PatternSquareSize");
382 getProperty(m_settings.nrFrames,
"NumberOfImages");
386 m_settings.aspectRatio = focal_lengths.at(0) / focal_lengths.at(1);
390 m_settings.aspectRatio = 0;
393 m_settings.writePoints =
true;
395 m_settings.writeExtrinsics =
false;
397 m_settings.flipVertical =
false;
399 m_settings.outputFileName = m_outputFileName;
401 getProperty(m_settings.useK3ToK6,
"UseAdditionalParams");
403 m_settings.validate();
404 if (not m_settings.goodInput)
406 throw std::runtime_error(
"Invalid input detected. Application stopping.");
409 ARMARX_INFO <<
"Board info: " << m_settings.boardSize.width <<
"x"
410 << m_settings.boardSize.height <<
" squares with square size of "
411 << m_settings.squareSize;
417 delete cameraImages[0];
418 delete cameraImages[1];
419 delete[] cameraImages;
437 if (IceUtil::Time::now() - timeOfLastCapture < waitingTimeBetweenCaptures)
442 ARMARX_INFO <<
"Capturing image for calibration #" << imagePoints.size();
444 timeOfLastCapture = IceUtil::Time::now();
448 if (m_mode == CAPTURING && imagePoints.size() >= (
size_t)m_settings.nrFrames)
451 if (runCalibrationAndSave(m_settings, imageSize, cameraMatrix, distCoeffs, imagePoints))
458 imageSize = view.size();
459 if (m_settings.flipVertical)
461 cv::flip(view, view, 0);
465 std::vector<cv::Point2f> pointBuf;
469 int chessBoardFlags =
470 cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE | cv::CALIB_CB_FAST_CHECK;
472 switch (m_settings.calibrationPattern)
476 found = cv::findChessboardCorners(
477 view, m_settings.boardSize, pointBuf, chessBoardFlags);
480 found = cv::findCirclesGrid(view, m_settings.boardSize, pointBuf);
483 found = cv::findCirclesGrid(
484 view, m_settings.boardSize, pointBuf, cv::CALIB_CB_ASYMMETRIC_GRID);
499 cv::cvtColor(view, viewGray, cv::COLOR_BGR2GRAY);
505 cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 30, 0.1));
508 if (m_mode == CAPTURING)
510 imagePoints.push_back(pointBuf);
514 cv::drawChessboardCorners(view, m_settings.boardSize, cv::Mat(pointBuf), found);
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
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.
static std::string GetDefaultName()
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.