5 constexpr std::size_t minimumObservations = 30;
6 constexpr std::size_t minimumPointsPerObservation = 12;
8 bool isFinite(
const cv::Point2f& point)
10 return std::isfinite(point.x) && std::isfinite(point.y);
13 bool isFinite(
const cv::Point3f& point)
15 return std::isfinite(point.x) && std::isfinite(point.y) && std::isfinite(point.z);
22 const std::vector<StereoObservation>& observations,
25 std::string& errorMessage)
const
40 errorMessage =
"Invalid pinhole calibration options.";
44 if(!validateObservations(observations, options.
common.
imageSize, errorMessage))
52 if(!calibrateMonocular(observations,
62 if(!calibrateMonocular(
87 !calibrateStereo(observations, options, calculated.
leftCamera,
94 errorMessage =
"Unknown calibration mode.";
101 errorMessage =
"Calibration produced an invalid result.";
105 result = std::move(calculated);
108 catch(
const cv::Exception&
e)
110 errorMessage = std::string(
"OpenCV exception during calibration: ") +
e.what();
112 catch(
const std::exception&
e)
114 errorMessage = std::string(
"Standard exception during calibration: ") +
e.what();
118 errorMessage =
"Unknown exception during calibration.";
125 bool PinholeCalibrationEngine::validateObservations(
126 const std::vector<StereoObservation>& observations,
127 const cv::Size& expectedImageSize,
128 std::string& errorMessage)
const
130 if (observations.size() < minimumObservations)
132 errorMessage =
"At least " + std::to_string(minimumObservations) +
133 " stereo observations are required.";
137 std::size_t expectedPointCount = 0;
138 std::vector<cv::Point3f> referenceObjectPoints;
139 for (std::size_t index = 0; index < observations.size(); ++index)
141 const StereoObservation& observation = observations[index];
142 const std::string prefix =
"Observation #" + std::to_string(index + 1) +
": ";
144 if (observation.imageSize != expectedImageSize)
146 std::ostringstream message;
147 message << prefix <<
"image size is " << observation.imageSize.width <<
"x"
148 << observation.imageSize.height <<
", expected " << expectedImageSize.width
149 <<
"x" << expectedImageSize.height <<
".";
150 errorMessage = message.str();
154 if (!observation.isValid())
156 errorMessage = prefix +
"has inconsistent point lists or an invalid timestamp delta.";
160 if (observation.objectPoints.size() < minimumPointsPerObservation)
162 errorMessage = prefix +
"contains fewer than " +
163 std::to_string(minimumPointsPerObservation) +
" calibration points.";
167 if (!std::isfinite(observation.leftTimestampSeconds) ||
168 !std::isfinite(observation.rightTimestampSeconds) ||
169 !std::isfinite(observation.timestampDeltaSeconds))
171 errorMessage = prefix +
"contains a non-finite timestamp.";
175 for (std::size_t pointIndex = 0; pointIndex < observation.objectPoints.size(); ++pointIndex)
177 if (!isFinite(observation.objectPoints[pointIndex]) ||
178 !isFinite(observation.leftImagePoints[pointIndex]) ||
179 !isFinite(observation.rightImagePoints[pointIndex]))
181 errorMessage = prefix +
"point #" + std::to_string(pointIndex + 1) +
182 " contains a non-finite coordinate.";
189 expectedPointCount = observation.objectPoints.size();
190 referenceObjectPoints = observation.objectPoints;
192 else if (observation.objectPoints.size() != expectedPointCount)
194 errorMessage = prefix +
"has " + std::to_string(observation.objectPoints.size()) +
195 " object points, expected " + std::to_string(expectedPointCount) +
".";
200 for (std::size_t pointIndex = 0; pointIndex < expectedPointCount; ++pointIndex)
202 if (cv::norm(observation.objectPoints[pointIndex] - referenceObjectPoints[pointIndex]) > 1
e-6)
204 errorMessage = prefix +
"uses object points different from observation #1.";
214 bool PinholeCalibrationEngine::calibrateMonocular(
215 const std::vector<StereoObservation>& observations,
217 const PinholeCalibrationOptions& options,
218 CameraCalibrationResult& result,
219 std::string& errorMessage)
const
221 std::vector<std::vector<cv::Point3f>> objectPoints;
222 std::vector<std::vector<cv::Point2f>> leftImagePoints;
223 std::vector<std::vector<cv::Point2f>> rightImagePoints;
224 if (!extractCalibrationPoints(observations, objectPoints, leftImagePoints, rightImagePoints, errorMessage))
229 const std::vector<std::vector<cv::Point2f>>& imagePoints =
231 cv::Mat intrinsic = cv::Mat::eye(3, 3, CV_64F);
232 if(options.cameraFocalLengthGuess > 0.0 && (options.monocularFlags & cv::CALIB_USE_INTRINSIC_GUESS))
234 intrinsic.at<
double>(0, 0) = options.cameraFocalLengthGuess;
235 intrinsic.at<
double>(1, 1) = options.cameraFocalLengthGuess;
236 intrinsic.at<
double>(0, 2) = options.common.imageSize.width * 0.5;
237 intrinsic.at<
double>(1, 2) = options.common.imageSize.height * 0.5;
242 cv::Mat distortion = cv::Mat::zeros(5, 1, CV_64F);
243 std::vector<cv::Mat> rotationVectors;
244 std::vector<cv::Mat> translationVectors;
246 const double rms = cv::calibrateCamera(objectPoints, imagePoints, options.common.imageSize,
247 intrinsic, distortion, rotationVectors,
248 translationVectors, options.monocularFlags,
251 result = CameraCalibrationResult{};
253 result.imageSize = options.common.imageSize;
254 result.K = intrinsic.clone();
255 result.D = distortion.reshape(1, 5).rowRange(0, 4).clone();
256 result.rotationVectors = std::move(rotationVectors);
257 result.translationVectors = std::move(translationVectors);
260 if (result.rotationVectors.size() != observations.size() ||
261 result.translationVectors.size() != observations.size())
263 errorMessage =
"OpenCV returned an incomplete set of monocular poses.";
267 result.perViewRms.reserve(observations.size());
268 for (std::size_t index = 0; index < observations.size(); ++index)
270 std::vector<cv::Point2f> projectedPoints;
271 cv::projectPoints(objectPoints[index],
272 result.rotationVectors[index],
273 result.translationVectors[index],
276 if (projectedPoints.size() != imagePoints[index].size())
278 errorMessage =
"OpenCV returned an incomplete projected point set.";
281 const double viewRms = cv::norm(projectedPoints, imagePoints[index], cv::NORM_L2) /
282 std::sqrt(
static_cast<double>(projectedPoints.size()));
283 result.perViewRms.push_back(viewRms);
286 if (!result.isValid())
288 errorMessage =
"Monocular calibration produced invalid camera parameters.";
294 bool PinholeCalibrationEngine::calibrateStereo(
295 const std::vector<StereoObservation>& observations,
296 const PinholeCalibrationOptions& options,
297 const CameraCalibrationResult& leftCamera,
298 const CameraCalibrationResult& rightCamera,
299 StereoCalibrationResult& result,
300 std::string& errorMessage)
const
302 std::vector<std::vector<cv::Point3f>> objectPoints;
303 std::vector<std::vector<cv::Point2f>> leftImagePoints;
304 std::vector<std::vector<cv::Point2f>> rightImagePoints;
305 if (!extractCalibrationPoints(observations, objectPoints, leftImagePoints, rightImagePoints, errorMessage))
310 cv::Mat leftK = leftCamera.K.clone();
311 cv::Mat rightK = rightCamera.K.clone();
312 cv::Mat leftD = leftCamera.D.clone();
313 cv::Mat rightD = rightCamera.D.clone();
314 cv::Mat Rotation, Translation, Essential, Fundamental;
316 const double rms = cv::stereoCalibrate(
317 objectPoints, leftImagePoints, rightImagePoints,
318 leftK, leftD, rightK, rightD,
319 options.common.imageSize, Rotation, Translation, Essential, Fundamental,
320 options.stereoFlags, options.criteria);
322 result = StereoCalibrationResult{};
323 result.R = Rotation.clone();
324 result.T = Translation.reshape(1, 3).clone();
327 if (!result.isValid())
329 errorMessage =
"Stereo calibration produced an invalid result.";
335 bool PinholeCalibrationEngine::extractCalibrationPoints(
336 const std::vector<StereoObservation>& observations,
337 std::vector<std::vector<cv::Point3f>>& objectPoints,
338 std::vector<std::vector<cv::Point2f>>& leftImagePoints,
339 std::vector<std::vector<cv::Point2f>>& rightImagePoints,
340 std::string& errorMessage)
const
343 objectPoints.clear();
344 leftImagePoints.clear();
345 rightImagePoints.clear();
346 objectPoints.reserve(observations.size());
347 leftImagePoints.reserve(observations.size());
348 rightImagePoints.reserve(observations.size());
350 for (
const StereoObservation& observation : observations)
352 objectPoints.push_back(observation.objectPoints);
353 leftImagePoints.push_back(observation.leftImagePoints);
354 rightImagePoints.push_back(observation.rightImagePoints);
356 if (objectPoints.empty())
358 errorMessage =
"No calibration points are available.";
bool calibrate(const std::vector< StereoObservation > &observations, const PinholeCalibrationOptions &options, CalibrationResult &result, std::string &errorMessage) const
CameraCalibrationResult leftCamera
StereoCalibrationResult stereo
CameraCalibrationResult rightCamera
CalibrationMode calibrationMode
CommonCalibrationOptions common