OpenCV 5 support (#1732)

* OpenCV 5 support

* RTABMapConfig.cmake, guard from including stereoRectifyFisheye.h

* unified opencv components at the same place

* fixing android build

* Confirmed stereo calibration with fisheye works. Fix camera start/top progress dialog not drawn. Opencv >=4.7 using new opencv's ArucoDetector class.

* pinning opencv for downstream apps

* Avoid changing object/image points between fisheye calibration

* Fixed stereo calib diverging when recalibrating same data

* removed not needed opencv c api

* fixing depthai build on opencv5

* bumped version

* Adding ci opencv5 with homebrew

* fixed ci script

* Fixed OptimizerCeres build with opencv5
This commit is contained in:
matlabbe
2026-07-29 22:48:39 -07:00
committed by GitHub
parent 89998284bc
commit d9f3337f97
79 changed files with 741 additions and 366 deletions
@@ -36,7 +36,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtCore/QSet>
#include <QtGui/QImage>
#include <opencv2/core/core.hpp>
#if CV_MAJOR_VERSION < 5
#include <opencv2/features2d/features2d.hpp>
#else
#include <opencv2/features.hpp>
#endif
#include <set>
#include <vector>
#include <pcl/point_cloud.h>
+5
View File
@@ -34,7 +34,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtCore/QRectF>
#include <QtCore/QMultiMap>
#include <QtCore/QSettings>
#include <opencv2/core/version.hpp>
#if CV_MAJOR_VERSION < 5
#include <opencv2/features2d/features2d.hpp>
#else
#include <opencv2/features.hpp>
#endif
#include <map>
#include "rtabmap/utilite/UCv2Qt.h"
#include <rtabmap/core/CameraModel.h>
@@ -34,7 +34,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QGraphicsTextItem>
#include <QtGui/QPen>
#include <QtGui/QBrush>
#include <opencv2/core/version.hpp>
#if CV_MAJOR_VERSION < 5
#include <opencv2/features2d/features2d.hpp>
#else
#include <opencv2/features.hpp>
#endif
namespace rtabmap {
+179 -70
View File
@@ -28,15 +28,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/gui/CalibrationDialog.h"
#include "ui_calibrationDialog.h"
#include <algorithm>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/imgproc_c.h>
#if CV_MAJOR_VERSION >= 5
#include <opencv2/calib.hpp>
#include <opencv2/geometry.hpp>
#else
#include <opencv2/calib3d/calib3d.hpp>
#if CV_MAJOR_VERSION >= 3
#include <opencv2/calib3d/calib3d_c.h>
#endif
#include <opencv2/highgui/highgui.hpp>
#if CV_MAJOR_VERSION > 2 or (CV_MAJOR_VERSION == 2 and (CV_MINOR_VERSION >4 or (CV_MINOR_VERSION == 4 and CV_SUBMINOR_VERSION >=10)))
#if (CV_MAJOR_VERSION > 2 and CV_MAJOR_VERSION < 5) or (CV_MAJOR_VERSION == 2 and (CV_MINOR_VERSION >4 or (CV_MINOR_VERSION == 4 and CV_SUBMINOR_VERSION >=10)))
#include <rtabmap/core/stereo/stereoRectifyFisheye.h>
#endif
@@ -276,18 +278,23 @@ void CalibrationDialog::generateBoard()
if(ui_->comboBox_board_type->currentIndex() >= 1 )
{
try {
const int marginInPixels = squareSizeInPixels/4;
cv::Size size(
squareSizeInPixels*ui_->spinBox_boardWidth->value() + 2*marginInPixels,
squareSizeInPixels*ui_->spinBox_boardHeight->value() + 2*marginInPixels);
UINFO("Creating board image of %dx%d pixels (%dx%d squares)",
size.width, size.height,
ui_->spinBox_boardWidth->value(), ui_->spinBox_boardHeight->value());
#if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION >= 7)
charucoBoard_->generateImage(
cv::Size(squareSizeInPixels*ui_->spinBox_boardWidth->value(),
squareSizeInPixels*ui_->spinBox_boardHeight->value()),
size,
image,
squareSizeInPixels/4, 1);
marginInPixels, 1);
#else
charucoBoard_->draw(
cv::Size(squareSizeInPixels*ui_->spinBox_boardWidth->value(),
squareSizeInPixels*ui_->spinBox_boardHeight->value()),
size,
image,
squareSizeInPixels/4, 1);
marginInPixels, 1);
#endif
int arucoDict = ui_->comboBox_marker_dictionary->currentIndex();
@@ -297,7 +304,7 @@ void CalibrationDialog::generateBoard()
}
catch(const cv::Exception & e)
{
UERROR("%f", e.what());
UERROR("%s", e.what());
QMessageBox::critical(this, tr("Generating Board"),
tr("Cannot generate the board. Make sure the dictionary "
"selected is big enough for the board size. Error:\"%1\"").arg(e.what()));
@@ -737,7 +744,7 @@ void CalibrationDialog::processImages(const cv::Mat & imageLeft, const cv::Mat &
cv::Size boardSize(ui_->spinBox_boardWidth->value(), ui_->spinBox_boardHeight->value());
if(!viewGray.empty())
{
int flags = CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_NORMALIZE_IMAGE;
int flags = cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE;
if(!viewGray.empty())
{
@@ -748,7 +755,7 @@ void CalibrationDialog::processImages(const cv::Mat & imageLeft, const cv::Mat &
if( scale == 1 )
timg = viewGray;
else
cv::resize(viewGray, timg, cv::Size(), scale, scale, CV_INTER_CUBIC);
cv::resize(viewGray, timg, cv::Size(), scale, scale, cv::INTER_CUBIC);
#ifdef HAVE_CHARUCO
if(ui_->comboBox_board_type->currentIndex() >= 1 )
@@ -833,7 +840,7 @@ void CalibrationDialog::processImages(const cv::Mat & imageLeft, const cv::Mat &
float ratio = ui_->comboBox_board_type->currentIndex() >= 1 ?6.0f:2.0f;
float radius = minSquareDistance==-1.0f?5.0f:(minSquareDistance/ratio);
cv::cornerSubPix( viewGray, pointBuf[id], cv::Size(radius, radius), cv::Size(-1,-1),
cv::TermCriteria( CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1 ));
cv::TermCriteria( cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 30, 0.1 ));
// Filter points that drifted to far (caused by reflection or bad subpixel gradient)
float threshold = ui_->doubleSpinBox_subpixel_error->value();
@@ -1388,6 +1395,13 @@ void CalibrationDialog::calibrate()
UINFO("Calibrating camera %d (samples=%d)", id, (int)imagePoints_[id].size());
logStream << "Calibrating camera " << id << " (samples=" << imagePoints_[id].size() << ")" << ENDL;
// Work on local copies: the fisheye auto-prune below removes ill-conditioned views,
// and we must NOT mutate the persistent buffers, otherwise clicking Calibrate again
// would run on a smaller (already-pruned) sample set and give different results.
std::vector<std::vector<cv::Point3f> > objectPoints = objectPoints_[id];
std::vector<std::vector<cv::Point2f> > imagePoints = imagePoints_[id];
std::vector<int> imageIds = imageIds_[id];
//calibrate
std::vector<cv::Mat> rvecs, tvecs;
std::vector<float> reprojErrs;
@@ -1401,26 +1415,78 @@ void CalibrationDialog::calibrate()
if(fishEye)
{
try
// cv::fisheye::calibrate() with CALIB_CHECK_COND throws as soon as a single
// view is ill-conditioned (e.g. too few / poorly spread ChArUco corners),
// aborting the whole calibration. Auto-prune the offending view (its index is
// reported in the exception message) and retry until it succeeds, keeping the
// CHECK_COND safety without discarding every good view.
const int minFisheyeViews = COUNT_MIN/2;
bool calibrated = false;
while(!calibrated)
{
rms = cv::fisheye::calibrate(
objectPoints_[id],
imagePoints_[id],
imageSize_[id],
K,
D,
rvecs,
tvecs,
cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC |
cv::fisheye::CALIB_CHECK_COND |
cv::fisheye::CALIB_FIX_SKEW);
}
catch(const cv::Exception & e)
{
UERROR("Error: %s (try restarting the calibration)", e.what());
QMessageBox::warning(this, tr("Calibration failed!"), tr("Error: %1 (try restarting the calibration)").arg(e.what()));
processingData_ = false;
return;
try
{
rms = cv::fisheye::calibrate(
objectPoints,
imagePoints,
imageSize_[id],
K,
D,
rvecs,
tvecs,
cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC |
cv::fisheye::CALIB_CHECK_COND |
cv::fisheye::CALIB_FIX_SKEW);
calibrated = true;
}
catch(const cv::Exception & e)
{
// Parse the ill-conditioned view index, e.g.
// "CALIB_CHECK_COND - Ill-conditioned matrix for input array 43"
int badIndex = -1;
const QString token = "input array ";
QString msg = e.what();
int tokenPos = msg.indexOf(token);
if(tokenPos >= 0)
{
bool ok = false;
int v = msg.mid(tokenPos + token.length()).section(' ', 0, 0).toInt(&ok);
if(ok)
{
badIndex = v;
}
}
if(badIndex >= 0 && badIndex < (int)objectPoints.size() &&
(int)objectPoints.size() > minFisheyeViews)
{
int removedImageId = badIndex < (int)imageIds.size() ? imageIds[badIndex] : -1;
UWARN("Fisheye calibration: view %d (image %d) is ill-conditioned, "
"removing it and retrying (%d views left).",
badIndex, removedImageId, (int)objectPoints.size()-1);
logStream << "Fisheye calibration: removed ill-conditioned view " << badIndex
<< " (image " << removedImageId << "), "
<< (int)objectPoints.size()-1 << " views left" << ENDL;
// Prune the local copies only (never the persistent buffers). Keep them
// aligned: the per-view reprojection loop below indexes them together
// with rvecs/tvecs.
objectPoints.erase(objectPoints.begin()+badIndex);
imagePoints.erase(imagePoints.begin()+badIndex);
if(badIndex < (int)imageIds.size())
{
imageIds.erase(imageIds.begin()+badIndex);
}
// loop and retry with the pruned set
}
else
{
UERROR("Error: %s (try restarting the calibration)", e.what());
QMessageBox::warning(this, tr("Calibration failed!"), tr("Error: %1 (try restarting the calibration)").arg(e.what()));
processingData_ = false;
return;
}
}
}
}
else
@@ -1429,8 +1495,8 @@ void CalibrationDialog::calibrate()
cv::Mat stdDevsMatInt, stdDevsMatExt;
cv::Mat perViewErrorsMat;
rms = cv::calibrateCamera(
objectPoints_[id],
imagePoints_[id],
objectPoints,
imagePoints,
imageSize_[id],
K,
D,
@@ -1440,14 +1506,14 @@ void CalibrationDialog::calibrate()
stdDevsMatExt,
perViewErrorsMat,
ui_->comboBox_calib_model->currentIndex()==2?cv::CALIB_RATIONAL_MODEL:0);
if((int)imageIds_[id].size() == perViewErrorsMat.rows)
if((int)imageIds.size() == perViewErrorsMat.rows)
{
UINFO("Per view errors:");
logStream << "Per view errors:" << ENDL;
for(int i=0; i<perViewErrorsMat.rows; ++i)
{
UINFO("Image %d: %f", imageIds_[id][i], perViewErrorsMat.at<double>(i,0));
logStream << "Image " << imageIds_[id][i] << ": " << perViewErrorsMat.at<double>(i,0) << ENDL;
UINFO("Image %d: %f", imageIds[i], perViewErrorsMat.at<double>(i,0));
logStream << "Image " << imageIds[i] << ": " << perViewErrorsMat.at<double>(i,0) << ENDL;
}
}
}
@@ -1459,23 +1525,23 @@ void CalibrationDialog::calibrate()
std::vector<cv::Point2f> imagePoints2;
int i, totalPoints = 0;
double totalErr = 0, err;
reprojErrs.resize(objectPoints_[id].size());
reprojErrs.resize(objectPoints.size());
for( i = 0; i < (int)objectPoints_[id].size(); ++i )
for( i = 0; i < (int)objectPoints.size(); ++i )
{
#if CV_MAJOR_VERSION > 2 or (CV_MAJOR_VERSION == 2 and (CV_MINOR_VERSION >4 or (CV_MINOR_VERSION == 4 and CV_SUBMINOR_VERSION >=10)))
if(fishEye)
{
cv::fisheye::projectPoints( cv::Mat(objectPoints_[id][i]), imagePoints2, rvecs[i], tvecs[i], K, D);
cv::fisheye::projectPoints( cv::Mat(objectPoints[i]), imagePoints2, rvecs[i], tvecs[i], K, D);
}
else
#endif
{
cv::projectPoints( cv::Mat(objectPoints_[id][i]), rvecs[i], tvecs[i], K, D, imagePoints2);
cv::projectPoints( cv::Mat(objectPoints[i]), rvecs[i], tvecs[i], K, D, imagePoints2);
}
err = cv::norm(cv::Mat(imagePoints_[id][i]), cv::Mat(imagePoints2), CV_L2);
err = cv::norm(cv::Mat(imagePoints[i]), cv::Mat(imagePoints2), cv::NORM_L2);
int n = (int)objectPoints_[id][i].size();
int n = (int)objectPoints[i].size();
reprojErrs[i] = (float) std::sqrt(err*err/n);
totalErr += err*err;
totalPoints += n;
@@ -1576,9 +1642,14 @@ void CalibrationDialog::calibrate()
cv::Mat P = stereoModel_.right().P().clone();
P.at<double>(0,3) = -P.at<double>(0,0)*ui_->doubleSpinBox_stereoBaseline->value();
double scale = ui_->doubleSpinBox_stereoBaseline->value() / stereoModel_.baseline();
UWARN("Scale %f (setting square size from %f to %f)", scale, ui_->doubleSpinBox_squareSize->value(), ui_->doubleSpinBox_squareSize->value()*scale);
logStream << "Baseline rescaled from " << stereoModel_.baseline() << " to " << ui_->doubleSpinBox_stereoBaseline->value() << " scale=" << scale << ENDL;
ui_->doubleSpinBox_squareSize->setValue(ui_->doubleSpinBox_squareSize->value()*scale);
UWARN("Scale %f applied to stereo baseline (computed %f m -> expected %f m). "
"If the mismatch is caused by the measured square size, it would be %f m instead of %f m.",
scale, stereoModel_.baseline(), ui_->doubleSpinBox_stereoBaseline->value(),
ui_->doubleSpinBox_squareSize->value()*scale, ui_->doubleSpinBox_squareSize->value());
logStream << "Baseline rescaled from " << stereoModel_.baseline() << " to " << ui_->doubleSpinBox_stereoBaseline->value()
<< " scale=" << scale << " (implied square size " << ui_->doubleSpinBox_squareSize->value()*scale
<< " m instead of " << ui_->doubleSpinBox_squareSize->value() << " m)" << ENDL;
UASSERT(!stereoModel_.T().empty());
stereoModel_ = StereoCameraModel(
stereoModel_.name(),
stereoModel_.left().imageSize(),stereoModel_.left().K_raw(), stereoModel_.left().D_raw(), stereoModel_.left().R(), stereoModel_.left().P(),
@@ -1708,26 +1779,58 @@ StereoCameraModel CalibrationDialog::stereoCalibration(const CameraModel & left,
cv::Vec4d D_right(right.D_raw().at<double>(0,0), right.D_raw().at<double>(0,1), right.D_raw().at<double>(0,4), right.D_raw().at<double>(0,5));
UASSERT(stereoImagePoints_[0].size() == stereoImagePoints_[1].size());
UASSERT(stereoObjectPoints_.size() == stereoImagePoints_[0].size());
// cv::fisheye::stereoCalibrate() reads the number of points from the first view and
// lays out its Jacobian assuming EVERY view has that same count (fisheye.cpp
// "reshape(1, n_points*2)"). ChArUco detects a variable number of corners per view,
// so we make the counts uniform by evenly subsampling every view down to the common
// minimum count (kept spatially spread, not just the first N).
size_t minPoints = stereoImagePoints_[0][0].size();
for(unsigned int i =0; i<stereoImagePoints_[0].size(); ++i)
{
minPoints = std::min(minPoints, stereoImagePoints_[0][i].size());
}
std::vector<std::vector<cv::Point3d> > objectPoints(stereoObjectPoints_.size());
std::vector<std::vector<cv::Point2d> > leftPoints(stereoImagePoints_[0].size());
std::vector<std::vector<cv::Point2d> > rightPoints(stereoImagePoints_[1].size());
bool subsampled = false;
for(unsigned int i =0; i<stereoImagePoints_[0].size(); ++i)
{
UASSERT(stereoImagePoints_[0][i].size() == stereoImagePoints_[1][i].size());
leftPoints[i].resize(stereoImagePoints_[0][i].size());
rightPoints[i].resize(stereoImagePoints_[1][i].size());
for(unsigned int j =0; j<stereoImagePoints_[0][i].size(); ++j)
UASSERT(stereoObjectPoints_[i].size() == stereoImagePoints_[0][i].size());
const size_t n = stereoImagePoints_[0][i].size();
if(n != minPoints)
{
leftPoints[i][j].x = stereoImagePoints_[0][i][j].x;
leftPoints[i][j].y = stereoImagePoints_[0][i][j].y;
rightPoints[i][j].x = stereoImagePoints_[1][i][j].x;
rightPoints[i][j].y = stereoImagePoints_[1][i][j].y;
subsampled = true;
}
objectPoints[i].resize(minPoints);
leftPoints[i].resize(minPoints);
rightPoints[i].resize(minPoints);
for(size_t k =0; k<minPoints; ++k)
{
// evenly spread the kept indices over [0, n-1]
size_t j = minPoints>1 ? (size_t)((k*(n-1))/(minPoints-1)) : 0;
objectPoints[i][k].x = stereoObjectPoints_[i][j].x;
objectPoints[i][k].y = stereoObjectPoints_[i][j].y;
objectPoints[i][k].z = stereoObjectPoints_[i][j].z;
leftPoints[i][k].x = stereoImagePoints_[0][i][j].x;
leftPoints[i][k].y = stereoImagePoints_[0][i][j].y;
rightPoints[i][k].x = stereoImagePoints_[1][i][j].x;
rightPoints[i][k].y = stereoImagePoints_[1][i][j].y;
}
}
if(subsampled)
{
UWARN("Fisheye stereo calibration requires the same number of points in every "
"view; sub-sampled all %d views to the common minimum of %d points.",
(int)stereoImagePoints_[0].size(), (int)minPoints);
if(logStream) (*logStream) << "Fisheye stereo: sub-sampled all views to " << (int)minPoints << " points" << ENDL;
}
try
{
rms = cv::fisheye::stereoCalibrate(
stereoObjectPoints_,
objectPoints,
leftPoints,
rightPoints,
left.K_raw(), D_left, right.K_raw(), D_right,
@@ -1739,30 +1842,41 @@ StereoCameraModel CalibrationDialog::stereoCalibration(const CameraModel & left,
catch(const cv::Exception & e)
{
UERROR("Error: %s (try restarting the calibration)", e.what());
QMessageBox::warning(const_cast<CalibrationDialog*>(this), tr("Calibration failed!"), tr("Error: %1 (try restarting the calibration)").arg(e.what()));
return output;
}
std::cout << "R = " << R << std::endl;
std::cout << "T = " << Tvec << std::endl;
// cv::fisheye::stereoCalibrate() returns the translation as a Vec3d (Tvec) and does
// not fill the cv::Mat T; populate it here so the returned model always carries a
// valid 3x1 extrinsic translation (used e.g. by the baseline rescaling below).
T = cv::Mat(3, 1, CV_64FC1);
T.at<double>(0,0) = Tvec[0];
T.at<double>(1,0) = Tvec[1];
T.at<double>(2,0) = Tvec[2];
if(imageSize_[0] == imageSize_[1] && !ignoreStereoRectification)
{
UINFO("Compute stereo rectification");
cv::Mat R1, R2, P1, P2, Q;
#if CV_MAJOR_VERSION < 5
stereoRectifyFisheye(
left.K_raw(), D_left,
right.K_raw(), D_right,
imageSize, R, Tvec, R1, R2, P1, P2, Q,
cv::CALIB_ZERO_DISPARITY, 0, imageSize);
// Very hard to get good results with this one:
/*double balance = 0.0, fov_scale = 1.0;
#else
// Very hard to get good results with this one, however we cannot use the previous one anymore in opencv5
double balance = 0.0, fov_scale = 1.0;
cv::fisheye::stereoRectify(
left.K_raw(), D_left,
right.K_raw(), D_right,
imageSize, R, Tvec, R1, R2, P1, P2, Q,
cv::CALIB_ZERO_DISPARITY, imageSize, balance, fov_scale);*/
cv::CALIB_ZERO_DISPARITY, imageSize, balance, fov_scale);
#endif
std::cout << "R1 = " << R1 << std::endl;
std::cout << "R2 = " << R2 << std::endl;
@@ -1791,11 +1905,6 @@ StereoCameraModel CalibrationDialog::stereoCalibration(const CameraModel & left,
std::cout << "P1n = " << P1 << std::endl;
std::cout << "P2n = " << P2 << std::endl;
cv::Mat T(3,1,CV_64FC1);
T.at <double>(0,0) = Tvec[0];
T.at <double>(1,0) = Tvec[1];
T.at <double>(2,0) = Tvec[2];
output = StereoCameraModel(
cameraName_.toStdString(),
imageSize_[0], left.K_raw(), left.D_raw(), R1, P1,
@@ -1915,8 +2024,8 @@ StereoCameraModel CalibrationDialog::stereoCalibration(const CameraModel & left,
{
int npt = (int)stereoImagePoints_[0][i].size();
cv::Mat imgpt0 = cv::Mat(stereoImagePoints_[0][i]);
cv::Mat imgpt1 = cv::Mat(stereoImagePoints_[1][i]);
std::vector<cv::Point2f> imgpt0 = stereoImagePoints_[0][i];
std::vector<cv::Point2f> imgpt1 = stereoImagePoints_[1][i];
cv::undistortPoints(imgpt0, imgpt0, left.K_raw(), left.D_raw(), R1, P1);
cv::undistortPoints(imgpt1, imgpt1, right.K_raw(), right.D_raw(), R2, P2);
computeCorrespondEpilines(imgpt0, 1, F, lines[0]);
@@ -1925,10 +2034,10 @@ StereoCameraModel CalibrationDialog::stereoCalibration(const CameraModel & left,
double sampleErr = 0.0;
for(int j = 0; j < npt; j++ )
{
double errij = fabs(stereoImagePoints_[0][i][j].x*lines[1][j][0] +
stereoImagePoints_[0][i][j].y*lines[1][j][1] + lines[1][j][2]) +
fabs(stereoImagePoints_[1][i][j].x*lines[0][j][0] +
stereoImagePoints_[1][i][j].y*lines[0][j][1] + lines[0][j][2]);
double errij = fabs(imgpt0[j].x*lines[1][j][0] +
imgpt0[j].y*lines[1][j][1] + lines[1][j][2]) +
fabs(imgpt1[j].x*lines[0][j][0] +
imgpt1[j].y*lines[0][j][1] + lines[0][j][2]);
sampleErr += errij;
}
UINFO("Stereo image %d: %f", stereoImageIds_[i], sampleErr/npt);
+3 -2
View File
@@ -85,7 +85,7 @@ CameraViewer::CameraViewer(QWidget * parent, const ParametersMap & parameters) :
showScanCheckbox_->setChecked(true);
markerCheckbox_ = new QCheckBox("Detect markers", this);
#if defined(HAVE_OPENCV_ARUCO) || defined(RTABMAP_APRILTAG)
#if ((CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION==4 && CV_MINOR_VERSION >=7)) && defined(HAVE_OPENCV_OBJDETECT)) || defined(HAVE_OPENCV_ARUCO) || defined(RTABMAP_APRILTAG)
markerCheckbox_->setEnabled(true);
markerDetector_ = new MarkerDetector(parameters);
#else
@@ -175,7 +175,8 @@ void CameraViewer::showImage(const rtabmap::SensorData & data)
if(!models.empty() && models[0].isValidForProjection())
{
cv::Mat imageWithDetections;
detections = markerDetector_->detect(left, models, depthOrRight, _landmarksSize, &imageWithDetections);
cv::Mat depth = (depthOrRight.type()==CV_16UC1 || depthOrRight.type()==CV_32FC1) ? depthOrRight : cv::Mat();
detections = markerDetector_->detect(left, models, depth, _landmarksSize, &imageWithDetections);
imageView_->setImage(uCvMat2QImage(imageWithDetections));
for(std::map<int, MarkerInfo>::iterator iter=detections.begin(); iter!=detections.end(); ++iter)
{
+2 -4
View File
@@ -46,8 +46,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UDirectory.h>
#include <rtabmap/utilite/UConversion.h>
#include <opencv2/core/core_c.h>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <rtabmap/utilite/UTimer.h>
#include <rtabmap/utilite/UFile.h>
@@ -5980,7 +5978,7 @@ void DatabaseViewer::updateStereo(const SensorData * data)
cv::Mat leftMono;
if(data->imageRaw().channels() == 3)
{
cv::cvtColor(data->imageRaw(), leftMono, CV_BGR2GRAY);
cv::cvtColor(data->imageRaw(), leftMono, cv::COLOR_BGR2GRAY);
}
else
{
@@ -5989,7 +5987,7 @@ void DatabaseViewer::updateStereo(const SensorData * data)
cv::Mat rightMono;
if(data->rightRaw().channels() == 3)
{
cv::cvtColor(data->rightRaw(), rightMono, CV_BGR2GRAY);
cv::cvtColor(data->rightRaw(), rightMono, cv::COLOR_BGR2GRAY);
}
else
{
+59
View File
@@ -0,0 +1,59 @@
/*
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RTABMAP_GUILIB_SRC_GUIUTIL_H_
#define RTABMAP_GUILIB_SRC_GUIUTIL_H_
#include <QWidget>
#include <QApplication>
#include <QEventLoop>
#include <QElapsedTimer>
#include <QtGui/QWindow>
namespace rtabmap {
// Show a widget and block until it is actually painted on screen. show() only
// schedules window mapping + painting
inline void showAndWaitExposed(QWidget * widget)
{
widget->show();
if(widget->windowHandle())
{
QElapsedTimer timer;
timer.start();
while(!widget->windowHandle()->isExposed() && timer.elapsed() < 2000)
{
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents, 50);
}
}
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
widget->repaint(); // synchronous, unlike update()
}
} // namespace rtabmap
#endif /* RTABMAP_GUILIB_SRC_GUIUTIL_H_ */
+7 -10
View File
@@ -28,6 +28,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/gui/MainWindow.h"
#include "ui_mainWindow.h"
#include "GuiUtil.h"
#include "rtabmap/core/CameraRGB.h"
#include "rtabmap/core/CameraStereo.h"
@@ -93,6 +94,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QInputDialog>
#include <QToolButton>
#if CV_MAJOR_VERSION >= 5
#include <opencv2/geometry.hpp>
#endif
//RGB-D stuff
#include "rtabmap/core/CameraRGBD.h"
#include "rtabmap/core/Odometry.h"
@@ -125,10 +130,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rtabmap/core/global_map/GridMap.h>
#endif
#ifdef HAVE_OPENCV_ARUCO
#include <opencv2/aruco.hpp>
#endif
#define LOG_FILE_NAME "LogRtabmap.txt"
#define SHARE_SHOW_LOG_FILE "share/rtabmap/showlogs.m"
#define SHARE_GET_PRECISION_RECALL_FILE "share/rtabmap/getPrecisionRecall.m"
@@ -5959,9 +5960,7 @@ void MainWindow::startDetection()
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
showAndWaitExposed(&progress);
if(_preferencesDialog->getLidarSourceDriver() != PreferencesDialog::kSrcUndef)
{
@@ -6275,9 +6274,7 @@ void MainWindow::stopDetection()
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
showAndWaitExposed(&progress);
}
// kill the processes
+22 -30
View File
@@ -61,6 +61,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QMainWindow>
#include <QProgressDialog>
#include <QApplication>
#include <QEventLoop>
#include <QElapsedTimer>
#include <QtGui/QWindow>
#include <QLabel>
#include <functional>
#include <QScrollBar>
@@ -70,6 +73,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtGui/QCloseEvent>
#include "ui_preferencesDialog.h"
#include "GuiUtil.h"
#include "rtabmap/core/Version.h"
#include "rtabmap/core/Parameters.h"
@@ -506,10 +510,10 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->checkBox_showOdomFrustums->setChecked(false);
#endif
#if !defined(HAVE_OPENCV_ARUCO) && !defined(RTABMAP_APRILTAG)
#if !((CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION==4 && CV_MINOR_VERSION >=7)) && defined(HAVE_OPENCV_OBJDETECT)) && !defined(HAVE_OPENCV_ARUCO) && !defined(RTABMAP_APRILTAG)
_ui->label_markerDetection->setText(_ui->label_markerDetection->text()+" This option works only if OpenCV has been built with \"aruco\" module and/or RTAB-Map has been built with AprilTag library support.");
#endif
#ifndef HAVE_OPENCV_ARUCO
#if !(((CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION==4 && CV_MINOR_VERSION >=7)) && defined(HAVE_OPENCV_OBJDETECT)) || defined(HAVE_OPENCV_ARUCO))
_ui->MarkerStrategy->setItemData(0, 0, Qt::UserRole - 1);
#endif
#ifndef RTABMAP_APRILTAG
@@ -2639,19 +2643,19 @@ void PreferencesDialog::restoreConfigOwnership(const QString & filePath)
gid_t gid = (gid_t)atoi(sudoGid);
// Restore the config file and its containing directory so the user
// can still write preferences without sudo afterwards.
if(!filePath.isEmpty() && QFile::exists(filePath))
auto restoreOwnership = [uid, gid](const QString & path)
{
if(chown(filePath.toStdString().c_str(), uid, gid) != 0)
if(!path.isEmpty() && QFile::exists(path))
{
UWARN("Could not restore ownership of \"%s\" to uid=%d (%s).",
filePath.toStdString().c_str(), (int)uid, strerror(errno));
if(chown(path.toStdString().c_str(), uid, gid) != 0)
{
UWARN("Could not restore ownership of \"%s\" to uid=%d (%s).",
path.toStdString().c_str(), (int)uid, strerror(errno));
}
}
}
QString dir = QFileInfo(filePath).absolutePath();
if(!dir.isEmpty())
{
chown(dir.toStdString().c_str(), uid, gid);
}
};
restoreOwnership(filePath);
restoreOwnership(QFileInfo(filePath).absolutePath());
}
}
#else
@@ -7846,9 +7850,7 @@ void PreferencesDialog::testOdometry()
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
showAndWaitExposed(&progress);
Camera * camera = this->createCamera();
progress.hide();
@@ -7989,9 +7991,7 @@ void PreferencesDialog::testOdometry()
// at function scope end (not in join()), so 'progress' stays visible across it. On Windows
// the first 2-3 RealSense closes per launch stall ~20s in the Motion Module stop().
progress.setLabelText(tr("Closing camera..."));
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
showAndWaitExposed(&progress);
cameraThread.join(true);
odomThread.join(true);
@@ -8033,9 +8033,7 @@ void PreferencesDialog::testCamera()
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
showAndWaitExposed(&progress);
// createCamera() init()s the device on the GUI thread (required by ZED) and takes a few seconds.
Camera * camera = this->createCamera();
@@ -8092,9 +8090,7 @@ void PreferencesDialog::testCamera()
// stays visible across it. On Windows the first 2-3 RealSense closes per launch stall
// ~20s in the Motion Module stop() (librealsense warm-up); this keeps the user informed.
progress.setLabelText(tr("Closing camera..."));
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
showAndWaitExposed(&progress);
cameraThread.join(true); // cameraThread's destructor (scope end) closes the device
// deleteLater() (not delete): defer destruction to the event loop so Qt finishes
// tearing down the OpenGL widget's context and window-proc subclass and drains
@@ -8582,9 +8578,7 @@ void PreferencesDialog::testLidar()
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
showAndWaitExposed(&progress);
Lidar * lidar = this->createLidar();
progress.hide();
@@ -8613,9 +8607,7 @@ void PreferencesDialog::testLidar()
// destructor at scope end (not in join()), so 'progress' - declared in the outer
// scope - stays visible across it.
progress.setLabelText(tr("Closing sensor..."));
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
showAndWaitExposed(&progress);
lidarThread.join(true); // lidarThread's destructor (scope end) closes the device
// deleteLater() (not delete): see testCamera() - avoids a dangling OpenGL platform
// window that crashes in QWindowsWindow::alertWindow when Preferences later closes.
+4 -4
View File
@@ -345,10 +345,10 @@
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="toolTip">
<string/>
<string>Number of inner squares on the board</string>
</property>
<property name="text">
<string>Square Size</string>
<string>Board Size</string>
</property>
</widget>
</item>
@@ -385,10 +385,10 @@
<item row="3" column="0">
<widget class="QLabel" name="label_12">
<property name="toolTip">
<string>Number of inner squares on the board</string>
<string/>
</property>
<property name="text">
<string>Board Size</string>
<string>Square Size</string>
</property>
</widget>
</item>