6#include <yarp/os/LogStream.h>
10YARP_LOG_COMPONENT(STEREOCALIBRATIONTHREAD,
"yarp.tools.stereoCalibThread")
15 std::string formatCalibrationMatrix(
const cv::Mat& matrix)
17 std::ostringstream stream;
22 void logCameraCalibration(
const char* cameraName,
25 yCInfo(STEREOCALIBRATIONTHREAD) <<
"[CAMERA_CALIBRATION_" << cameraName <<
"]";
26 yCInfo(STEREOCALIBRATIONTHREAD) <<
"w" << camera.
imageSize.width <<
"h" << camera.
imageSize.height;
27 yCInfo(STEREOCALIBRATIONTHREAD) <<
"fx" << camera.
K.at<
double>(0, 0)
28 <<
"fy" << camera.
K.at<
double>(1, 1)
29 <<
"cx" << camera.
K.at<
double>(0, 2)
30 <<
"cy" << camera.
K.at<
double>(1, 2);
33 yCInfo(STEREOCALIBRATIONTHREAD) <<
"k1" << camera.
D.at<
double>(0, 0)
34 <<
"k2" << camera.
D.at<
double>(1, 0)
35 <<
"k3" << camera.
D.at<
double>(2, 0)
36 <<
"k4" << camera.
D.at<
double>(3, 0);
40 yCInfo(STEREOCALIBRATIONTHREAD) <<
"k1" << camera.
D.at<
double>(0, 0)
41 <<
"k2" << camera.
D.at<
double>(1, 0)
42 <<
"p1" << camera.
D.at<
double>(2, 0)
43 <<
"p2" << camera.
D.at<
double>(3, 0);
45 yCInfo(STEREOCALIBRATIONTHREAD) <<
"K =" << formatCalibrationMatrix(camera.
K);
46 yCInfo(STEREOCALIBRATIONTHREAD) <<
"monocular RMS =" << camera.
rms;
51 yCInfo(STEREOCALIBRATIONTHREAD) <<
"========== Calibration result (not written to disk) ==========";
55 logCameraCalibration(
"LEFT", result.
leftCamera);
64 cv::Mat homogeneousTransform = cv::Mat::eye(4, 4, CV_64F);
65 result.
stereo.
R.copyTo(homogeneousTransform(cv::Rect(0, 0, 3, 3)));
66 result.
stereo.
T.reshape(1, 3).copyTo(homogeneousTransform(cv::Rect(3, 0, 1, 3)));
68 yCInfo(STEREOCALIBRATIONTHREAD) <<
"[STEREO_DISPARITY]";
69 yCInfo(STEREOCALIBRATIONTHREAD) <<
"Stereo RMS =" << result.
stereo.
rms
70 <<
"baseline norm =" << cv::norm(result.
stereo.
T);
71 yCInfo(STEREOCALIBRATIONTHREAD) <<
"R =" << formatCalibrationMatrix(result.
stereo.
R);
72 yCInfo(STEREOCALIBRATIONTHREAD) <<
"T =" << formatCalibrationMatrix(result.
stereo.
T.t());
74 yCInfo(STEREOCALIBRATIONTHREAD) <<
"HN =" << formatCalibrationMatrix(homogeneousTransform);
76 yCInfo(STEREOCALIBRATIONTHREAD) <<
"======================================================================";
81 if(modelString ==
"pinhole")
85 else if(modelString ==
"fisheye")
91 yCError(STEREOCALIBRATIONTHREAD) <<
"Invalid camera model string:" << modelString
92 <<
". Setting to Invalid.";
99 if(modeString ==
"MonocularLeft")
103 else if(modeString ==
"MonocularRight")
107 else if(modeString ==
"MonocularBoth")
111 else if(modeString ==
"StereoFull")
117 yCError(STEREOCALIBRATIONTHREAD) <<
"Invalid calibration mode string:" << modeString
118 <<
". Using default StereoFull.";
127 this->maxQueueSize = maxQueueSize;
135 std::lock_guard<std::mutex> lock(_mutex);
144 frame.
image.copy(leftFrame);
145 frame.
stamp = timestamp;
147 leftQueue.push_back(std::move(frame));
156 frame.
image.copy(rightFrame);
157 frame.
stamp = timestamp;
159 rightQueue.push_back(std::move(frame));
164void StereoPairSynchronizer::trimLeftQueue()
166 while(leftQueue.size() > maxQueueSize)
168 leftQueue.pop_front();
169 std::lock_guard<std::mutex> lock(_mutex);
174void StereoPairSynchronizer::trimRightQueue()
176 while(rightQueue.size() > maxQueueSize)
178 rightQueue.pop_front();
179 std::lock_guard<std::mutex> lock(_mutex);
186 while(!leftQueue.empty() && !rightQueue.empty())
188 const double leftStamp = leftQueue.front().stamp.getTime();
189 const double rightStamp = rightQueue.front().stamp.getTime();
191 const double absoluteDelta = std::abs(leftStamp - rightStamp);
192 if(absoluteDelta <= toleranceSeconds)
194 pair.
left = std::move(leftQueue.front().image);
195 pair.
right = std::move(rightQueue.front().image);
196 pair.
leftStamp = leftQueue.front().stamp;
201 leftQueue.pop_front();
202 rightQueue.pop_front();
204 std::lock_guard<std::mutex> lock(_mutex);
212 if(leftStamp < rightStamp)
215 leftQueue.pop_front();
216 std::lock_guard<std::mutex> lock(_mutex);
221 rightQueue.pop_front();
222 std::lock_guard<std::mutex> lock(_mutex);
232 moduleName=rf.check(
"name", Value(
"stereoCalib"),
"module name (string)").asString().c_str();
233 robotName=rf.check(
"robotName",Value(
"icub"),
"module name (string)").asString().c_str();
235 this->inputLeftPortName =
"/"+moduleName;
236 this->inputLeftPortName +=rf.check(
"imgLeft",Value(
"/cam/left:i"),
"Input image port (string)").asString().c_str();
238 this->inputRightPortName =
"/"+moduleName;
239 this->inputRightPortName += rf.check(
"imgRight", Value(
"/cam/right:i"),
"Input image port (string)").asString().c_str();
241 this->outNameRight =
"/"+moduleName;
242 this->outNameRight += rf.check(
"outRight",Value(
"/cam/right:o"),
"Output image port (string)").asString().c_str();
244 this->outNameLeft =
"/"+moduleName;
245 this->outNameLeft +=rf.check(
"outLeft",Value(
"/cam/left:o"),
"Output image port (string)").asString().c_str();
247 Bottle stereoCalibOpts=rf.findGroup(
"STEREO_CALIBRATION_CONFIGURATION");
248 this->boardWidth = stereoCalibOpts.check(
"boardWidth", Value(8)).asInt32();
249 this->boardHeight = stereoCalibOpts.check(
"boardHeight", Value(6)).asInt32();
250 this->numOfPairs = stereoCalibOpts.check(
"numberOfPairs", Value(30)).asInt32();
251 if(this->numOfPairs < 30)
253 yCWarning(STEREOCALIBRATIONTHREAD) <<
"numberOfPairs must be at least 30; using 30";
254 this->numOfPairs = 30;
256 this->squareSize = (float)stereoCalibOpts.check(
"boardSize", Value(0.09241)).asFloat64();
257 this->boardType = stereoCalibOpts.check(
"boardType", Value(
"CHESSBOARD")).asString();
258 const double syncToleranceMs = stereoCalibOpts.check(
"syncToleranceMs", Value(20.0)).asFloat64();
259 _syncToleranceSeconds = syncToleranceMs / 1000.0;
260 const int configuredQueueSize = stereoCalibOpts.check(
"syncQueueSize", Value(5)).asInt32();
261 if(configuredQueueSize <= 0)
263 yCWarning(STEREOCALIBRATIONTHREAD) <<
"Invalid syncQueueSize; using 5";
268 _syncQueueSize =
static_cast<std::size_t
>(configuredQueueSize);
271 if(_syncToleranceSeconds <= 0.0)
273 yCWarning(STEREOCALIBRATIONTHREAD) <<
"Invalid syncToleranceMs; using 20 ms";
274 _syncToleranceSeconds = 0.020;
277 synchronizer.
configure(_syncToleranceSeconds, _syncQueueSize);
279 this->minCaptureIntervalSeconds = stereoCalibOpts.check(
"minCaptureIntervalSeconds", Value(2.0)).asFloat64();
280 this->minimumBoardSpanRatio = stereoCalibOpts.check(
"minimumBoardSpanRatio", Value(0.15)).asFloat64();
281 this->commandPort=commPort;
282 this->imageDir=imageDir;
283 this->collectionResetRequested.store(
false);
284 this->calibrationState.store(CalibrationState::Idle);
287 this->camCalibFile=rf.getHomeContextPath().c_str();
288 this->standalone = rf.check(
"standalone", Value(
false)).asBool();
289 string fileName=
"outputCalib.ini";
291 this->camCalibFile=this->camCalibFile+
"/"+fileName.c_str();
293 _observationsFile = stereoCalibOpts.check(
294 "observationsFile", Value(
"calibrationObservations.yml")).asString();
295 if(!_observationsFile.empty() && _observationsFile.front() !=
'/')
297 _observationsFile = this->imageDir +
"/" + _observationsFile;
301 _chessboardConfiguration.
cornersX = this->boardWidth;
302 _chessboardConfiguration.
cornersY = this->boardHeight;
307 _saveImages = stereoCalibOpts.check(
"saveImages", Value(1)).asInt32() != 0;
308 _drawDiagnosticCorners = stereoCalibOpts.check(
"drawDiagnosticCorners", Value(1)).asInt32() != 0;
309 _cameraModel = parseCameraModel(stereoCalibOpts.check(
"cameraModel", Value(
"pinhole")).asString());
310 _calibrationMode = parseCalibrationMode(stereoCalibOpts.check(
"calibrationMode", Value(
"StereoFull")).asString());
312 if(!_chessboardConfiguration.
isValid())
314 yCError(STEREOCALIBRATIONTHREAD) <<
"Invalid chessboard configuration";
320 if (!imagePortInLeft.open(inputLeftPortName.c_str())) {
321 cout <<
": unable to open port " << inputLeftPortName << endl;
325 if (!imagePortInRight.open(inputRightPortName.c_str())) {
326 cout <<
": unable to open port " << inputRightPortName << endl;
330 if (!outPortLeft.open(outNameLeft.c_str())) {
331 cout <<
": unable to open port " << outNameLeft << endl;
335 if (!outPortRight.open(outNameRight.c_str())) {
336 cout <<
": unable to open port " << outNameRight << endl;
341 if(standalone)
return true;
347 optHead.put(
"device",
"remote_controlboard");
348 optHead.put(
"remote",(
"/"+robotName+
"/head").c_str());
349 optHead.put(
"local",
"/"+moduleName+
"/client/head");
350 if (!polyHead.open(optHead) ||
351 !polyHead.view(posHead) ||
354 yCError(STEREOCALIBRATIONTHREAD) <<
"Unable to acquire the head encoder interface";
359 optTorso.put(
"device",
"remote_controlboard");
360 optTorso.put(
"remote",(
"/"+robotName+
"/torso").c_str());
361 optTorso.put(
"local",
"/"+moduleName+
"/client/torso");
364 if (!polyTorso.open(optTorso) ||
365 !polyTorso.view(posTorso) ||
368 yCWarning(STEREOCALIBRATIONTHREAD,
"Unable to connect to torso! Continuing without...");
372 yarp::sig::Vector head_angles(6,0.0);
373 posHead->getEncoders(head_angles.data());
375 yarp::sig::Vector torso_angles(3,0.0);
377 posTorso->getEncoders(torso_angles.data());
379 qL.resize(torso_angles.length()+head_angles.length()-1);
380 for(
size_t i=0; i<torso_angles.length(); i++)
381 qL[i]=torso_angles[torso_angles.length()-i-1];
383 for(
size_t i=0; i<head_angles.length()-2; i++)
384 qL[i+torso_angles.length()]=head_angles[i];
385 qL[7]=head_angles[4]+(0.5-(
LEFT))*head_angles[5];
388 qR.resize(torso_angles.length()+head_angles.length()-1);
389 for(
size_t i=0; i<torso_angles.length(); i++)
390 qR[i]=torso_angles[torso_angles.length()-i-1];
392 for(
size_t i=0; i<head_angles.length()-2; i++)
393 qR[i+torso_angles.length()]=head_angles[i];
394 qR[7]=head_angles[4]+(0.5-(
RIGHT))*head_angles[5];
400 yCInfo(STEREOCALIBRATIONTHREAD,
"Running synchronized fisheye calibration pipeline... \n");
404void stereoCalibThread::processSynchronizedPair(
SynchronizedPair& pair, Size boardSize)
408 const double previousProcessedCandidateTime = lastProcessedCandidateTime;
409 if(previousProcessedCandidateTime >= 0.0 && (pairTime - previousProcessedCandidateTime) < minCaptureIntervalSeconds)
411 yCDebug(STEREOCALIBRATIONTHREAD) <<
"Skipping candidate pair due to minimum capture interval";
414 if(previousProcessedCandidateTime >= 0.0)
416 yCDebug(STEREOCALIBRATIONTHREAD) <<
"Timestamp delta between processed pairs:" << (pairTime - previousProcessedCandidateTime) <<
"seconds";
418 lastProcessedCandidateTime = pairTime;
423 const Size leftSize(pair.
left.width(), pair.
left.height());
424 const Size rightSize(pair.
right.width(), pair.
right.height());
426 if(leftSize != _expectedImageSize || rightSize != _expectedImageSize)
428 if(_expectedImageSize.empty())
430 _expectedImageSize = leftSize;
434 if(leftSize != _expectedImageSize || rightSize != _expectedImageSize)
436 yCError(STEREOCALIBRATIONTHREAD) <<
"Left and right images have different sizes:" <<
437 "Left:" << leftSize.width <<
"x" << leftSize.height <<
438 "Right:" << rightSize.width <<
"x" << rightSize.height;
440 std::lock_guard<std::mutex> lock(mtx);
441 _calibrationError =
"Input images do not match the expected calibration resolution.";
444 calibrationState.store(CalibrationState::Error);
449 LeftRgb=yarp::cv::toCvMat(pair.
left);
450 RightRgb=yarp::cv::toCvMat(pair.
right);
453 Mat leftGray, rightGray;
454 cvtColor(LeftRgb,leftGray,CV_RGB2GRAY);
455 cvtColor(RightRgb,rightGray,CV_RGB2GRAY);
457 std::vector<Point2f> leftCorners;
458 std::vector<Point2f> rightCorners;
461 if(boardType ==
"CIRCLES_GRID") {
462 foundL = findCirclesGrid(LeftRgb, boardSize, leftCorners, CALIB_CB_SYMMETRIC_GRID | CALIB_CB_CLUSTERING);
463 foundR = findCirclesGrid(RightRgb, boardSize, rightCorners, CALIB_CB_SYMMETRIC_GRID | CALIB_CB_CLUSTERING);
464 yCWarning(STEREOCALIBRATIONTHREAD) <<
"Board type:" << boardType <<
"not yet implemented.";
465 }
else if(boardType ==
"ASYMMETRIC_CIRCLES_GRID") {
466 foundL = findCirclesGrid(LeftRgb, boardSize, leftCorners, CALIB_CB_ASYMMETRIC_GRID | CALIB_CB_CLUSTERING);
467 foundR = findCirclesGrid(RightRgb, boardSize, rightCorners, CALIB_CB_ASYMMETRIC_GRID | CALIB_CB_CLUSTERING);
468 yCWarning(STEREOCALIBRATIONTHREAD) <<
"Board type:" << boardType <<
"not yet implemented.";
469 }
else if(boardType ==
"CHESSBOARD_SECTOR_BASED") {
470 foundL = findChessboardCornersSB(leftGray, boardSize, leftCorners);
471 foundR = findChessboardCornersSB(rightGray, boardSize, rightCorners);
472 yCWarning(STEREOCALIBRATIONTHREAD) <<
"Board type:" << boardType <<
"not yet implemented.";
474 foundL = findChessboardCorners(leftGray, boardSize, leftCorners, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE | CALIB_CB_FILTER_QUADS);
475 foundR = findChessboardCorners(rightGray, boardSize, rightCorners, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE | CALIB_CB_FILTER_QUADS);
480 yCDebug(STEREOCALIBRATIONTHREAD) <<
"Found calibration board corners in both left and right images";
482 const Rect leftBoardBounds = boundingRect(leftCorners);
483 const Rect rightBoardBounds = boundingRect(rightCorners);
484 const bool leftBoardTooSmall =
485 leftBoardBounds.width < leftSize.width * minimumBoardSpanRatio ||
486 leftBoardBounds.height < leftSize.height * minimumBoardSpanRatio;
487 const bool rightBoardTooSmall =
488 rightBoardBounds.width < rightSize.width * minimumBoardSpanRatio ||
489 rightBoardBounds.height < rightSize.height * minimumBoardSpanRatio;
491 if(leftBoardTooSmall || rightBoardTooSmall)
493 yCWarning(STEREOCALIBRATIONTHREAD) <<
"Skipping stereo pair: chessboard is too small."
494 <<
"Left board:" << leftBoardBounds.width <<
"x" << leftBoardBounds.height
495 <<
"of" << leftSize.width <<
"x" << leftSize.height <<
";"
496 <<
"right board:" << rightBoardBounds.width <<
"x" << rightBoardBounds.height
497 <<
"of" << rightSize.width <<
"x" << rightSize.height <<
"."
498 <<
"Each board must span at least" << (minimumBoardSpanRatio * 100.0)
499 <<
"% of both image dimensions.";
501 ++_rejectedDetections;
505 TermCriteria criteria = TermCriteria(TermCriteria::EPS+TermCriteria::COUNT, 30, 0.01);
506 cornerSubPix(leftGray, leftCorners, Size(5,5), Size(-1,-1), criteria);
507 cornerSubPix(rightGray, rightCorners, Size(5,5), Size(-1,-1), criteria);
527 yCError(STEREOCALIBRATIONTHREAD) <<
"Generated invalid stereo observation";
528 ++_rejectedDetections;
532 std::size_t observationIndex = 0;
533 observationIndex = _observations.size();
539 std::string imageError;
546 yCError(STEREOCALIBRATIONTHREAD) <<
"Could not save accepted calibration image pair:" << imageError;
548 std::lock_guard<std::mutex> lock(mtx);
549 if (calibrationState.load() != CalibrationState::Collecting)
554 _calibrationError = imageError;
556 calibrationState.store(CalibrationState::Error);
564 std::lock_guard<std::mutex> lock(mtx);
565 if (calibrationState.load() != CalibrationState::Collecting)
569 _observations.push_back(std::move(observation));
574 if(_drawDiagnosticCorners)
576 drawChessboardCorners(LeftRgb, boardSize, leftCorners, foundL);
577 drawChessboardCorners(RightRgb, boardSize, rightCorners, foundR);
580 ImageOf<PixelRgb>& outimL = outPortLeft.prepare();
585 ImageOf<PixelRgb>& outimR = outPortRight.prepare();
588 outPortRight.write();
593 ++_rejectedDetections;
603 std::lock_guard<std::mutex> lock(mtx);
605 switch(calibrationState.load())
607 case CalibrationState::Idle:
608 result.
state =
"Idle";
610 case CalibrationState::Collecting:
611 result.
state =
"Collecting";
613 case CalibrationState::Calibrating:
614 result.
state =
"Calibrating";
616 case CalibrationState::Completed:
617 result.
state =
"Completed";
619 case CalibrationState::Error:
620 result.
state =
"Error";
630 if(syncStats.pairedFrames > 0)
632 result.
meanTimestampDeltaMs = 1000.0 * syncStats.accumulatedTimeStampDelta /
static_cast<double>(syncStats.pairedFrames);
638 if(calibrationState.load() == CalibrationState::Completed && _calibrationResults.
isValid())
641 switch(_calibrationResults.
mode)
675bool stereoCalibThread::shouldQueueFrameForCollection(
const Stamp& timestamp)
const
677 return lastProcessedCandidateTime < 0.0 ||
678 (timestamp.getTime() - lastProcessedCandidateTime) >= minCaptureIntervalSeconds;
681void stereoCalibThread::stereoCalibRun()
684 boardSize.width=this->boardWidth;
685 boardSize.height=this->boardHeight;
687 while (!isStopping())
692 if(calibrationState.load() == CalibrationState::Error)
698 if(collectionResetRequested.exchange(
false))
700 synchronizer.
reset();
701 lastProcessedCandidateTime = -1.0;
702 _expectedImageSize = {};
703 _observations.clear();
704 _rejectedDetections = 0;
706 std::lock_guard<std::mutex> lock(mtx);
708 _calibrationError.clear();
712 bool areFramesReceived =
false;
713 ImageOf<PixelRgb> *tmpL = imagePortInLeft.read(
false);
717 areFramesReceived =
true;
720 imagePortInLeft.getEnvelope(TSLeft);
723 ImageOf<PixelRgb>& outimL = outPortLeft.prepare();
725 outPortLeft.setEnvelope(TSLeft);
729 if(calibrationState.load() == CalibrationState::Collecting &&
730 shouldQueueFrameForCollection(TSLeft))
732 synchronizer.
pushLeft(*tmpL, TSLeft);
736 ImageOf<PixelRgb> *tmpR = imagePortInRight.read(
false);
739 areFramesReceived =
true;
742 imagePortInRight.getEnvelope(TSRight);
745 ImageOf<PixelRgb>& outimR=outPortRight.prepare();
747 outPortRight.setEnvelope(TSRight);
748 outPortRight.write();
752 if(calibrationState.load() == CalibrationState::Collecting &&
753 shouldQueueFrameForCollection(TSRight))
759 std::vector<stereo_calib::StereoObservation> observationSnapshot;
761 if(calibrationState.load() == CalibrationState::Collecting)
764 while(calibrationState.load() == CalibrationState::Collecting &&
768 processSynchronizedPair(pair, boardSize);
770 if(calibrationState.load() != CalibrationState::Collecting)
774 if(_observations.size() >=
static_cast<std::size_t
>(numOfPairs))
776 yCInfo(STEREOCALIBRATIONTHREAD,
"Collected %zu valid stereo observations. Stopping collection.", _observations.size());
777 observationSnapshot = _observations;
778 calibrationState.store(CalibrationState::Calibrating);
779 yCInfo(STEREOCALIBRATIONTHREAD) <<
"Observation collection complete";
785 if(calibrationState.load() == CalibrationState::Calibrating)
787 yCInfo(STEREOCALIBRATIONTHREAD) <<
"Starting the calibration process";
788 if(observationSnapshot.empty())
790 observationSnapshot = _observations;
794 std::string calibrationError;
795 bool success =
false;
801 success = _pinholeCalibrationEngine.
calibrate(
814 success = _fisheyeCalibrationEngine.
calibrate(
823 calibrationError =
"Unsupported camera model.";
833 yCError(STEREOCALIBRATIONTHREAD) <<
"Calibration failed:" << calibrationError;
835 std::lock_guard<std::mutex> lock(mtx);
837 _calibrationError = calibrationError.empty()
838 ?
"Calibration failed without an error message."
841 calibrationState.store(CalibrationState::Error);
849 if(syncStats.pairedFrames > 0)
851 calibrationResult.
quality.
meanTimestampDeltaMs = 1000.0 * syncStats.accumulatedTimeStampDelta /
static_cast<double>(syncStats.pairedFrames);
856 std::string persistenceError;
857 if(!_calibrationWriter.
write(camCalibFile, calibrationResult, persistenceError))
859 yCError(STEREOCALIBRATIONTHREAD) <<
"Could not save calibration results:" << persistenceError;
861 std::lock_guard<std::mutex> lock(mtx);
862 _calibrationResults = std::move(calibrationResult);
863 _calibrationError = persistenceError;
865 calibrationState.store(CalibrationState::Error);
868 if(!_calibrationWriter.
writeObservations(_observationsFile, observationSnapshot, persistenceError))
870 yCError(STEREOCALIBRATIONTHREAD) <<
"Could not save calibration observations:" << persistenceError;
872 std::lock_guard<std::mutex> lock(mtx);
873 _calibrationResults = std::move(calibrationResult);
874 _calibrationError = persistenceError;
876 calibrationState.store(CalibrationState::Error);
880 logCalibrationResult(calibrationResult);
882 std::lock_guard<std::mutex> lock(mtx);
883 _calibrationResults = std::move(calibrationResult);
884 _calibrationError.clear();
887 calibrationState.store(CalibrationState::Completed);
888 yCInfo(STEREOCALIBRATIONTHREAD) <<
"Entire calibration process completed";
891 if(!areFramesReceived)
901 imagePortInRight.close();
902 imagePortInLeft.close();
904 outPortRight.close();
905 commandPort->close();
907 if (polyHead.isValid())
910 if (polyTorso.isValid())
916 calibrationState.store(CalibrationState::Idle);
917 collectionResetRequested.store(
false);
918 imagePortInRight.interrupt();
919 imagePortInLeft.interrupt();
920 outPortLeft.interrupt();
921 outPortRight.interrupt();
922 commandPort->interrupt();
927 std::lock_guard<std::mutex> lock(mtx);
929 const CalibrationState currentState = calibrationState.load();
931 if(currentState == CalibrationState::Collecting || currentState == CalibrationState::Calibrating)
933 yCWarning(STEREOCALIBRATIONTHREAD) <<
"Cannot start a new calibration while calibration is already running";
938 _calibrationError.clear();
939 calibrationState.store(CalibrationState::Collecting);
940 collectionResetRequested.store(
true);
942 yCInfo(STEREOCALIBRATIONTHREAD) <<
"Calibration collection started";
946 std::lock_guard<std::mutex> lock(mtx);
948 if(calibrationState.load() != CalibrationState::Collecting)
950 yCWarning(STEREOCALIBRATIONTHREAD) <<
"Cannot stop calibration collection when it is not running";
953 calibrationState.store(CalibrationState::Idle);
954 collectionResetRequested.store(
true);
956 yCInfo(STEREOCALIBRATIONTHREAD) <<
"Calibration collection stopped";
constexpr double tolerance
SynchronizerStatistics getStatistics() const
void configure(double tolerance, std::size_t maxQueueSize)
void pushRight(const ImageOf< PixelRgb > &rightFrame, const Stamp ×tamp)
bool tryPopPair(SynchronizedPair &pair)
void pushLeft(const ImageOf< PixelRgb > &leftFrame, const Stamp ×tamp)
stereoCalibThread(ResourceFinder &rf, Port *commPort, const char *imageDir)
StereoCalibStatus getStatus() const
bool writeImagePair(const std::string &outputDirectory, std::size_t observationIndex, const cv::Mat &leftImg, const cv::Mat &rightImg, std::string &leftFile, std::string &rightFile, std::string &errorMessage) const
bool write(const std::string &outputFile, const CalibrationResult &result, std::string &errorMessage) const
bool writeObservations(const std::string &outputFile, const std::vector< StereoObservation > &observations, std::string &errorMessage) const
bool calibrate(const std::vector< StereoObservation > &observations, const FisheyeCalibrationOptions &options, CalibrationResult &result, std::string &errorMessage) const
bool calibrate(const std::vector< StereoObservation > &observations, const PinholeCalibrationOptions &options, CalibrationResult &result, std::string &errorMessage) const
constexpr double CTRL_DEG2RAD
PI/180.
ImageOf< PixelRgb > image
bool calibrationAvailable
std::string lastCalibrationError
std::size_t droppedLeftFrames
std::string calibrationMode
double meanTimestampDeltaMs
double maxTimestampDeltaMs
std::size_t droppedRightFrames
ImageOf< PixelRgb > right
double accumulatedTimeStampDelta
std::size_t droppedLeftFrames
std::size_t droppedRightFrames
std::size_t synchronizedPairs
std::size_t acceptedObservations
double meanTimestampDeltaMs
double maxTimestampDeltaMs
std::size_t rejectedDetections
CameraCalibrationResult leftCamera
StereoCalibrationResult stereo
CameraCalibrationResult rightCamera
CalibrationQualityMetrics quality
std::vector< cv::Point3f > createObjectPoints() const
CalibrationMode calibrationMode
CommonCalibrationOptions common
double cameraFocalLengthGuess
CommonCalibrationOptions common
double timestampDeltaSeconds
int64_t leftSequenceNumber
std::string leftImageFilename
int64_t rightSequenceNumber
std::vector< cv::Point2f > leftImagePoints
std::vector< cv::Point3f > objectPoints
std::string rightImageFilename
double rightTimestampSeconds
double leftTimestampSeconds
std::vector< cv::Point2f > rightImagePoints