diff --git a/CMakeLists.txt b/CMakeLists.txt index 873e3c4..a9bed90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,5 @@ cmake_minimum_required(VERSION 2.6) find_package(Rock) rock_init(qcam_calib 0.1) +rock_opencv_autodetect(OPENCV_PACKAGE) rock_standard_layout() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a57786e..bd81e50 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,7 +23,7 @@ rock_library(QCamCalib SOURCES ${SRCS} ${QtApp_RCC_SRCS} HEADERS ${HDRS} MOC ${MOC_HDRS} - DEPS_PKGCONFIG QtCore QtGui opencv + DEPS_PKGCONFIG QtCore QtGui ${OPENCV_PACKAGE} UI ${UI_FILES} ) #QT4_ADD_RESOURCES(QtApp_RCC_SRCS ${QtApp_RCCS}) diff --git a/src/Items.cpp b/src/Items.cpp index 7d26949..d3b8d83 100644 --- a/src/Items.cpp +++ b/src/Items.cpp @@ -1,59 +1,67 @@ -#include #include +#include #include #include #include "Items.hpp" -#include #include +#include using namespace qcam_calib; -QVector convertToQt(const std::vector&points1) +QVector convertToQt(const std::vector& points1) { QVector points2; std::vector::const_iterator iter = points1.begin(); - for(;iter != points1.end();++iter) - points2.push_back(QPointF(iter->x,iter->y)); + for (; iter != points1.end(); ++iter) + points2.push_back(QPointF(iter->x, iter->y)); return points2; } -std::vector convertFromQt(const QVector&points1) +std::vector convertFromQt(const QVector& points1) { std::vector points2; QVector::const_iterator iter = points1.begin(); - for(;iter != points1.end();++iter) - points2.push_back(cv::Point2f(iter->x(),iter->y())); + for (; iter != points1.end(); ++iter) + points2.push_back(cv::Point2f(iter->x(), iter->y())); return points2; } -CameraParameterItem::CameraParameterItem(const QString &string): - QCamCalibItem(string) +CameraParameterItem::CameraParameterItem(const QString& string) + : QCamCalibItem(string) { setEditable(false); setColumnCount(2); - setParameter("fx",0); - setParameter("fy",0); - setParameter("cx",0); - setParameter("cy",0); - setParameter("k1",0); - setParameter("k2",0); - setParameter("p1",0); - setParameter("p2",0); - setParameter("projection error",0); - setParameter("pixel error",0); + setParameter("fx", 0); + setParameter("fy", 0); + setParameter("cx", 0); + setParameter("cy", 0); + setParameter("k1", 0); + setParameter("k2", 0); + setParameter("p1", 0); + setParameter("p2", 0); + setParameter("projection error", 0); + setParameter("pixel error", 0); + setParameter("horizontal fov", 0); + setParameter("vertical fov", 0); + // By using the ValidROI from the image with blackbars, one can calculate the + // percentage of the image that is cropped to maintain the aspect ratio after + // the undistort. + setParameter("Horizontal FOV percentage after undistort", 0); }; - -void CameraParameterItem::setParameter(const QString &name,double val) +void CameraParameterItem::setParameter(const QString& name, double val) { QModelIndexList list; - if(model() && index().isValid()) - list = model()->match(index().child(0,0),Qt::DisplayRole,QVariant(name),1,Qt::MatchExactly); - if(list.empty()) - { + if (model() && index().isValid()) + list = model()->match(index().child(0, 0), + Qt::DisplayRole, + QVariant(name), + 1, + Qt::MatchExactly); + if (list.empty()) { QList items; items.append(new QCamCalibItem(name)); items.back()->setEditable(false); @@ -61,23 +69,25 @@ void CameraParameterItem::setParameter(const QString &name,double val) items.back()->setEditable(false); appendRow(items); } - else - { - QStandardItem *item = child(list.front().row(),1); - if(item) + else { + QStandardItem* item = child(list.front().row(), 1); + if (item) item->setText(QString::number(val)); } } -double CameraParameterItem::getParameter(const QString &name)const +double CameraParameterItem::getParameter(const QString& name) const { QModelIndexList list; - if(model() && index().isValid()) - list = model()->match(index().child(0,0),Qt::DisplayRole,QVariant(name),1,Qt::MatchExactly); - if(!list.empty()) - { - QStandardItem *item = child(list.front().row(),1); - if(item) + if (model() && index().isValid()) + list = model()->match(index().child(0, 0), + Qt::DisplayRole, + QVariant(name), + 1, + Qt::MatchExactly); + if (!list.empty()) { + QStandardItem* item = child(list.front().row(), 1); + if (item) return item->data(Qt::EditRole).toDouble(); } else @@ -85,32 +95,33 @@ double CameraParameterItem::getParameter(const QString &name)const return 0; } -void CameraParameterItem::save(const QString &path)const +void CameraParameterItem::save(const QString& path) const { - cv::Mat k = cv::Mat::zeros(3,3,CV_64FC1); - cv::Mat dist(4,1,CV_64FC1); - k.at(0,0) = getParameter("fx"); - k.at(1,1) = getParameter("fy"); - k.at(0,2) = getParameter("cx"); - k.at(1,2) = getParameter("cy"); - k.at(2,2) = 1.0; + cv::Mat k = cv::Mat::zeros(3, 3, CV_64FC1); + cv::Mat dist(4, 1, CV_64FC1); + k.at(0, 0) = getParameter("fx"); + k.at(1, 1) = getParameter("fy"); + k.at(0, 2) = getParameter("cx"); + k.at(1, 2) = getParameter("cy"); + k.at(2, 2) = 1.0; dist.at(0) = getParameter("k1"); dist.at(1) = getParameter("k2"); dist.at(2) = getParameter("p1"); dist.at(3) = getParameter("p2"); - + cv::FileStorage fs(path.toStdString(), cv::FileStorage::WRITE); - time_t rawtime; time(&rawtime); + time_t rawtime; + time(&rawtime); fs << "calibrationDate" << asctime(localtime(&rawtime)); fs << "cameraMatrix" << k << "distCoeffs" << dist; fs.release(); } -CameraItem::CameraItem(int id, const QString &string): - QCamCalibItem(string), - camera_id(id), - camera_parameter(NULL), - images(NULL) +CameraItem::CameraItem(int id, const QString& string) + : QCamCalibItem(string) + , camera_id(id) + , camera_parameter(NULL) + , images(NULL) { setEditable(false); camera_parameter = new CameraParameterItem("Parameter"); @@ -119,6 +130,7 @@ CameraItem::CameraItem(int id, const QString &string): images = new QStandardItem("images"); images->setEditable(false); appendRow(images); + images->setColumnCount(2); }; int CameraItem::getId() @@ -131,87 +143,216 @@ bool CameraItem::isCalibrated() return (camera_parameter->getParameter("fx") > 0); } -void CameraItem::saveParameter(const QString &path)const +void CameraItem::saveParameter(const QString& path) const { camera_parameter->save(path); } +cv::Mat CameraItem::getCameraMatrix() +{ + return m_k; +} + +cv::Mat CameraItem::getDistCoeffs() +{ + return m_dist; +} + +cv::Mat CameraItem::getFullCameraMatrix() +{ + return m_full_camera_matrix; +} + +cv::Rect CameraItem::getValidROI() +{ + return m_valid_ROI; +} + +cv::Rect CameraItem::getPreservedROI() +{ + return m_preserved_ROI; +} + +std::vector CameraItem::getRotationVector() +{ + return m_rvecs; +} + +std::vector CameraItem::getTranslationVector() +{ + return m_tvecs; +} + int CameraItem::countChessboards() { int count = 0; - for(int row=0;row < images->rowCount(); ++row) - { - ImageItem *item = dynamic_cast(images->child(row,0)); - if(item && !item->getChessboardCorners().empty()) + for (int row = 0; row < images->rowCount(); ++row) { + ImageItem* item = dynamic_cast(images->child(row, 0)); + if (item && !item->getChessboardCorners().empty()) ++count; } return count; } -void CameraItem::calibrate(int cols,int rows,float dx,float dy) +void CameraItem::calibrate(int cols, int rows, float dx, float dy, int iterations) { - std::vector > object_points; - std::vector > image_points; + std::vector> object_points; + std::vector> image_points; cv::Size image_size; - //generate chessboard points + // generate chessboard points std::vector points3f; - for(int row=0;row < rows; ++row) - { - for(int col=0;col < cols; ++col) - points3f.push_back(cv::Point3f(dx*col,dy*row,0)); + for (int row = 0; row < rows; ++row) { + for (int col = 0; col < cols; ++col) + points3f.push_back(cv::Point3f(dx * col, dy * row, 0)); } - //collect image points - for(int row=0;row < images->rowCount(); ++row) - { - ImageItem *item = dynamic_cast(images->child(row,0)); - if(item && item->getChessboardCorners().size() ==(int) points3f.size()) - { - image_size = cv::Size(item->getImage().width(),item->getImage().height()); + // collect image points + for (int row = 0; row < images->rowCount(); ++row) { + ImageItem* item = dynamic_cast(images->child(row, 0)); + if (item && item->getChessboardCorners().size() == (int)points3f.size()) { + image_size = cv::Size(item->getImage().width(), item->getImage().height()); image_points.push_back(convertFromQt(item->getChessboardCorners())); object_points.push_back(points3f); } } - if(object_points.size() < 1) + if (object_points.size() < 1) throw std::runtime_error("not enough detected chessboards"); - cv::Mat k(3,3,CV_64FC1); - cv::Mat dist(4,1,CV_64FC1); + cv::Mat k(3, 3, CV_64FC1); + cv::Mat dist(4, 1, CV_64FC1); std::vector rvecs; std::vector tvecs; - double error = cv::calibrateCamera(object_points,image_points, - image_size,k,dist,rvecs,tvecs,0); - //cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 50, DBL_EPSILON)); - - //store parameters - camera_parameter->setParameter("fx",k.at(0,0)); - camera_parameter->setParameter("fy",k.at(1,1)); - camera_parameter->setParameter("cx",k.at(0,2)); - camera_parameter->setParameter("cy",k.at(1,2)); - camera_parameter->setParameter("k1",dist.at(0)); - camera_parameter->setParameter("k2",dist.at(1)); - camera_parameter->setParameter("p1",dist.at(2)); - camera_parameter->setParameter("p2",dist.at(3)); - camera_parameter->setParameter("projection error",error); - camera_parameter->setParameter("pixel error",sqrt(error/points3f.size())); + cv::Mat stdDeviationsIntrisics; + cv::Mat stdDeviationsExtrisics; + std::vector perViewErrors; + + double error = cv::calibrateCamera(object_points, + image_points, + image_size, + k, + dist, + rvecs, + tvecs, + stdDeviationsIntrisics, + stdDeviationsExtrisics, + perViewErrors, + 0, + cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, + iterations, + DBL_EPSILON)); + + double fx = k.at(0, 0); + double fy = k.at(1, 1); + + // store parameters + camera_parameter->setParameter("fx", fx); + camera_parameter->setParameter("fy", fy); + camera_parameter->setParameter("cx", k.at(0, 2)); + camera_parameter->setParameter("cy", k.at(1, 2)); + camera_parameter->setParameter("k1", dist.at(0)); + camera_parameter->setParameter("k2", dist.at(1)); + camera_parameter->setParameter("p1", dist.at(2)); + camera_parameter->setParameter("p2", dist.at(3)); + camera_parameter->setParameter("projection error", error); + camera_parameter->setParameter("pixel error", sqrt(error / points3f.size())); + + size_t errorSize = perViewErrors.size(); + size_t errorCounter = 0; + + for (int row = 0; row < images->rowCount(); ++row) { + ImageItem* item = dynamic_cast(images->child(row, 0)); + auto chessboard = item->getChessboardCorners(); + if (item->parent()) { + QStandardItem* item_cell = images->child(row, 1); + if (item_cell) { + if (!chessboard.empty() && errorCounter <= errorSize) { + item_cell->setText( + QString::number(perViewErrors[errorCounter], 'f', 2)); + errorCounter++; + } + else + item_cell->setText("unavailable"); + } + } + } + + double fov_x = calculateFOV(fx, image_size.width); + double fov_y = calculateFOV(fy, image_size.height); + + camera_parameter->setParameter("horizontal fov", fov_x); + camera_parameter->setParameter("vertical fov", fov_y); + + cv::Rect valid_ROI; + cv::Mat full_camera_matrix = + getOptimalNewCameraMatrix(k, dist, image_size, 1, image_size, &valid_ROI); + float desired_aspect_ratio = + static_cast(image_size.width) / static_cast(image_size.height); + cv::Rect preserved_aspect_ratio_ROI = + adjustToDesiredAspectRatio(valid_ROI, desired_aspect_ratio); + float percentage_fov = static_cast(preserved_aspect_ratio_ROI.width) / + static_cast(valid_ROI.width); + camera_parameter->setParameter("Horizontal FOV percentage after undistort", + percentage_fov); + + m_k = k.clone(); + m_dist = dist.clone(); + m_full_camera_matrix = full_camera_matrix.clone(); + m_valid_ROI = valid_ROI; + m_preserved_ROI = preserved_aspect_ratio_ROI; +} + +double CameraItem::calculateFOV(double focal_length, double image_size) +{ + return 2 * atan(image_size / (2 * focal_length)) * (180.0 / CV_PI); } -ImageItem* CameraItem::getImageItem(const QString &name) +cv::Rect CameraItem::adjustToDesiredAspectRatio(const cv::Rect& original_rect, + const float target_aspect_ratio) { - QModelIndexList list = model()->match(index(),Qt::DisplayRole,QVariant(name),1,Qt::MatchExactly); - ImageItem *item = NULL; - if(!list.empty()) + float current_aspect_ratio = + static_cast(original_rect.width) / original_rect.height; + + cv::Rect adjusted_rect; + + if (current_aspect_ratio > target_aspect_ratio) { + // Original is wider than target aspect ratio → adjust width to match height + adjusted_rect.width = + static_cast(std::round(original_rect.height * target_aspect_ratio)); + adjusted_rect.height = original_rect.height; + adjusted_rect.x = + original_rect.x + (original_rect.width - adjusted_rect.width) / 2; + adjusted_rect.y = original_rect.y; + } + else { + // Original is taller than target aspect ratio → adjust height to match width + adjusted_rect.height = + static_cast(std::round(original_rect.width / target_aspect_ratio)); + adjusted_rect.width = original_rect.width; + adjusted_rect.x = original_rect.x; + adjusted_rect.y = + original_rect.y + (original_rect.height - adjusted_rect.height) / 2; + } + + return adjusted_rect; +} + +ImageItem* CameraItem::getImageItem(const QString& name) +{ + QModelIndexList list = + model()->match(index(), Qt::DisplayRole, QVariant(name), 1, Qt::MatchExactly); + ImageItem* item = NULL; + if (!list.empty()) item = dynamic_cast(model()->itemFromIndex(list.front())); - if(!item) + if (!item) throw std::runtime_error("Cannot find image item"); return item; } -ImageItem *CameraItem::addImage(const QString &name,const QImage &image) +ImageItem* CameraItem::addImage(const QString& name, const QImage& image) { - ImageItem *item = new ImageItem(name,image); + ImageItem* item = new ImageItem(name, image); QList items; items.append(item); items.back()->setEditable(false); @@ -221,65 +362,67 @@ ImageItem *CameraItem::addImage(const QString &name,const QImage &image) return item; } -ImageItem::ImageItem(const QString &name, const QImage &image): - QCamCalibItem(name), - raw_image(image.copy()), - image(raw_image) +ImageItem::ImageItem(const QString& name, const QImage& image) + : QCamCalibItem(name) + , raw_image(image.copy()) + , image(raw_image) { } ImageItem::~ImageItem() { } -const QVector &ImageItem::getChessboardCorners()const +const QVector& ImageItem::getChessboardCorners() const { return chessboard; } -QVector ImageItem::findChessboard(const QImage &image,int cols ,int rows) +QVector ImageItem::findChessboard(const QImage& image, int cols, int rows) { std::vector points; QImage img = image.convertToFormat(QImage::Format_RGB888); cv::Mat mat(img.height(), img.width(), CV_8UC3, img.bits(), img.bytesPerLine()); cv::Mat gray; - //opencv is not thread save here - // static QMutex mutex; - // mutex.lock(); - cv::cvtColor(mat,gray,cv::COLOR_RGB2GRAY); - cv::findChessboardCorners(gray,cv::Size(cols,rows),points,cv::CALIB_CB_ADAPTIVE_THRESH + cv::CALIB_CB_NORMALIZE_IMAGE + cv::CALIB_CB_FAST_CHECK); - // mutex.unlock(); + // opencv is not thread save here + // static QMutex mutex; + // mutex.lock(); + cv::cvtColor(mat, gray, cv::COLOR_RGB2GRAY); + cv::findChessboardCorners(gray, + cv::Size(cols, rows), + points, + cv::CALIB_CB_ADAPTIVE_THRESH + cv::CALIB_CB_NORMALIZE_IMAGE + + cv::CALIB_CB_FAST_CHECK); + // mutex.unlock(); return convertToQt(points); } -bool ImageItem::findChessboard(int cols ,int rows) +bool ImageItem::findChessboard(int cols, int rows) { - setChessboard(ImageItem::findChessboard(raw_image,cols,rows),cols,rows); - if(chessboard.empty()) + setChessboard(ImageItem::findChessboard(raw_image, cols, rows), cols, rows); + if (chessboard.empty()) return false; return true; } -void ImageItem::setChessboard(const QVector &chessboard,int cols,int rows) +void ImageItem::setChessboard(const QVector& chessboard, int cols, int rows) { this->chessboard = chessboard; - if(chessboard.empty()) + if (chessboard.empty()) image = raw_image; - else - { + else { QImage img = raw_image.convertToFormat(QImage::Format_RGB888); cv::Mat mat(img.height(), img.width(), CV_8UC3, img.bits(), img.bytesPerLine()); std::vector points = convertFromQt(chessboard); - cv::drawChessboardCorners(mat,cv::Size(cols,rows),points,true); - image = QImage(mat.data, mat.cols, mat.rows,mat.step,QImage::Format_RGB888).copy(); + cv::drawChessboardCorners(mat, cv::Size(cols, rows), points, true); + image = + QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGB888).copy(); } - if(parent()) - { - QStandardItem *item = parent()->child(row(),1); - if(item) - { - if(!chessboard.empty()) + if (parent()) { + QStandardItem* item = parent()->child(row(), 1); + if (item) { + if (!chessboard.empty()) item->setText("ok"); else item->setText("no chessboard"); @@ -287,15 +430,74 @@ void ImageItem::setChessboard(const QVector &chessboard,int cols,int ro } } - -QImage &ImageItem::getImage() +QImage& ImageItem::getImage() { return image; } -QImage &ImageItem::getRawImage() +QImage& ImageItem::getRawImage() { return raw_image; } +QImage& ImageItem::getUndistortedImage(cv::Mat k, cv::Mat dist) +{ + QImage img = raw_image.convertToFormat(QImage::Format_RGB888); + cv::Mat mat(img.height(), img.width(), CV_8UC3, img.bits(), img.bytesPerLine()); + cv::Mat out; + cv::undistort(mat, out, k, dist); + undistorted_image = + QImage(out.data, out.cols, out.rows, out.step, QImage::Format_RGB888).copy(); + return undistorted_image; +} + +QImage& ImageItem::getUndistortedImageWithBlackBars(cv::Mat k, + cv::Mat dist, + cv::Mat full_camera_matrix, + cv::Rect valid_ROI, + cv::Rect preserved_aspect_ratio_ROI) +{ + QImage img = raw_image.convertToFormat(QImage::Format_RGB888); + cv::Mat mat(img.height(), img.width(), CV_8UC3, img.bits(), img.bytesPerLine()); + cv::Mat out; + cv::undistort(mat, out, k, dist, full_camera_matrix); + cv::Scalar green(0, 255, 0); + cv::rectangle(out, cv::Point(valid_ROI.x, valid_ROI.y), valid_ROI.br(), green, 2); + cv::Scalar blue(0, 0, 255); + cv::rectangle(out, + cv::Point(preserved_aspect_ratio_ROI.x, preserved_aspect_ratio_ROI.y), + preserved_aspect_ratio_ROI.br(), + blue, + 2); + undistorted_image_with_black_bars = + QImage(out.data, out.cols, out.rows, out.step, QImage::Format_RGB888).copy(); + return undistorted_image_with_black_bars; +} + +QImage& ImageItem::getReprojectedPointsImage(cv::Mat k, + cv::Mat dist, + cv::Size pattern_size) +{ + QImage img = raw_image.convertToFormat(QImage::Format_RGB888); + cv::Mat mat(img.height(), img.width(), CV_8UC3, img.bits(), img.bytesPerLine()); + cv::Mat gray; + cv::cvtColor(mat, gray, cv::COLOR_RGB2GRAY); + std::vector objectPoints; + bool found = cv::findChessboardCorners(gray, + pattern_size, + objectPoints, + cv::CALIB_CB_ADAPTIVE_THRESH + cv::CALIB_CB_NORMALIZE_IMAGE + + cv::CALIB_CB_FAST_CHECK); + + if (!found) { + return raw_image; + } + std::vector undistortedPoints; + cv::undistortPoints(objectPoints, undistortedPoints, k, dist, cv::noArray(), k); + cv::Mat out(mat.clone()); + cv::drawChessboardCorners(out, pattern_size, undistortedPoints, true); + reprojected_image = + QImage(out.data, out.cols, out.rows, out.step, QImage::Format_RGB888).copy(); + return reprojected_image; +} \ No newline at end of file diff --git a/src/Items.hpp b/src/Items.hpp index 4606461..be7b17e 100644 --- a/src/Items.hpp +++ b/src/Items.hpp @@ -1,65 +1,92 @@ #ifndef QCAMCALIB_ITEM_MODELS_HPP #define QCAMCALIB_ITEM_MODELS_HPP -#include #include +#include #include -namespace qcam_calib -{ - class QCamCalibItem: public QStandardItem - { - public: - QCamCalibItem():QStandardItem(){}; - QCamCalibItem(const QString &string):QStandardItem(string){}; - QCamCalibItem(float val):QStandardItem(val){}; +#include + +namespace qcam_calib { + class QCamCalibItem : public QStandardItem { + public: + QCamCalibItem() + : QStandardItem() {}; + QCamCalibItem(const QString& string) + : QStandardItem(string) {}; + QCamCalibItem(float val) + : QStandardItem(val) {}; }; - class CameraParameterItem: public QCamCalibItem - { - public: - CameraParameterItem(const QString &string); - void setParameter(const QString &name,double val=0); - void save(const QString &path)const; - double getParameter(const QString &name)const; + class CameraParameterItem : public QCamCalibItem { + public: + CameraParameterItem(const QString& string); + void setParameter(const QString& name, double val = 0); + void save(const QString& path) const; + double getParameter(const QString& name) const; }; - class ImageItem : public QCamCalibItem - { - public: - static QVector findChessboard(const QImage &image,int cols ,int rows); + class ImageItem : public QCamCalibItem { + public: + static QVector findChessboard(const QImage& image, int cols, int rows); - ImageItem(const QString &name, const QImage &image); - virtual ~ImageItem(); - QImage &getImage(); - QImage &getRawImage(); - const QVector &getChessboardCorners()const; + ImageItem(const QString& name, const QImage& image); + virtual ~ImageItem(); + QImage& getImage(); + QImage& getRawImage(); + QImage& getUndistortedImage(cv::Mat k, cv::Mat dist); + QImage& getUndistortedImageWithBlackBars(cv::Mat k, + cv::Mat dist, + cv::Mat full_camera_matrix, + cv::Rect valid_ROI, + cv::Rect preserved_aspect_ratio_ROI); + QImage& getReprojectedPointsImage(cv::Mat k, cv::Mat dist, cv::Size pattern_size); - bool findChessboard(int cols ,int rows); - void setChessboard(const QVector &chessboard,int cols,int rows); + const QVector& getChessboardCorners() const; + bool findChessboard(int cols, int rows); + void setChessboard(const QVector& chessboard, int cols, int rows); - private: - QImage raw_image; - QImage image; // image with chessboard overlay - QVector chessboard; + private: + QImage raw_image; + QImage image; // image with chessboard overlay + QImage undistorted_image; // undistorted image + QImage undistorted_image_with_black_bars; // undistorted image with black bars + QImage reprojected_image; // reprojected image + QVector chessboard; }; - class CameraItem: public QCamCalibItem - { - public: - CameraItem(int id, const QString &string); - int getId(); - ImageItem* addImage(const QString &name,const QImage &image); - ImageItem* getImageItem(const QString &name); - void calibrate(int cols,int rows,float dx,float dy); - void saveParameter(const QString &path)const; - bool isCalibrated(); - int countChessboards(); + class CameraItem : public QCamCalibItem { + public: + CameraItem(int id, const QString& string); + int getId(); + cv::Mat getCameraMatrix(); + cv::Mat getDistCoeffs(); + cv::Mat getFullCameraMatrix(); + cv::Rect getValidROI(); + cv::Rect getPreservedROI(); + cv::Rect adjustToDesiredAspectRatio(const cv::Rect& original_rect, + const float target_aspect_ratio); + double calculateFOV(double focal_length, double image_size); + std::vector getRotationVector(); + std::vector getTranslationVector(); + ImageItem* addImage(const QString& name, const QImage& image); + ImageItem* getImageItem(const QString& name); + void calibrate(int cols, int rows, float dx, float dy, int iterations); + void saveParameter(const QString& path) const; + bool isCalibrated(); + int countChessboards(); - private: - int camera_id; - CameraParameterItem* camera_parameter; - QStandardItem *images; + private: + int camera_id; + CameraParameterItem* camera_parameter; + QStandardItem* images; + cv::Mat m_dist; + cv::Mat m_k; + cv::Mat m_full_camera_matrix; + cv::Rect m_valid_ROI; + cv::Rect m_preserved_ROI; + std::vector m_rvecs; + std::vector m_tvecs; }; } diff --git a/src/QCamCalib.cpp b/src/QCamCalib.cpp index f20c32c..6576470 100644 --- a/src/QCamCalib.cpp +++ b/src/QCamCalib.cpp @@ -1,11 +1,11 @@ #include "QCamCalib.hpp" -#include "Items.hpp" #include "ImageView.hpp" +#include "Items.hpp" #include "ui_main_gui.h" #include -#include #include +#include #include #include @@ -15,19 +15,19 @@ using namespace qcam_calib; const char* CAMERA_BASE_NAME = "camera_"; -QCamCalib::QCamCalib(QWidget *parent) : - QWidget(parent), - current_load_path("."), - tree_model(NULL), - camera_item_menu(NULL), - tree_view_menu(NULL), - image_item_menu(NULL), - progress_dialog_images(NULL), - progress_dialog_chessboard(NULL), - progress_dialog_calibrate(NULL), - future_watcher_images(NULL), - future_watcher_chessboard(NULL), - future_watcher_calibrate(NULL) +QCamCalib::QCamCalib(QWidget* parent) + : QWidget(parent) + , current_load_path(".") + , tree_model(NULL) + , camera_item_menu(NULL) + , tree_view_menu(NULL) + , image_item_menu(NULL) + , progress_dialog_images(NULL) + , progress_dialog_chessboard(NULL) + , progress_dialog_calibrate(NULL) + , future_watcher_images(NULL) + , future_watcher_chessboard(NULL) + , future_watcher_calibrate(NULL) { Ui::MainGui gui; gui.setupUi(this); @@ -37,86 +37,126 @@ QCamCalib::QCamCalib(QWidget *parent) : tree_model->setHorizontalHeaderLabels((QStringList() << "Cameras" << "Value")); tree_model->setColumnCount(2); gui.treeView->setModel(tree_model); - connect(gui.treeView,SIGNAL(customContextMenuRequested(const QPoint&)),SLOT(contextMenuTreeView(const QPoint&))); - connect(gui.treeView,SIGNAL(clicked(const QModelIndex&)),SLOT(clickedTreeView(const QModelIndex&))); - connect(gui.treeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(clickedTreeView(const QModelIndex&))); + connect(gui.treeView, + SIGNAL(customContextMenuRequested(const QPoint&)), + SLOT(contextMenuTreeView(const QPoint&))); + connect(gui.treeView, + SIGNAL(clicked(const QModelIndex&)), + SLOT(clickedTreeView(const QModelIndex&))); + connect(gui.treeView->selectionModel(), + SIGNAL(currentChanged(QModelIndex, QModelIndex)), + this, + SLOT(clickedTreeView(const QModelIndex&))); // camera item menu camera_item_menu = new QMenu(this); - QAction* act = new QAction("load images",this); - connect(act,SIGNAL(triggered()),this,SLOT(loadImages())); + QAction* act = new QAction("load images", this); + connect(act, SIGNAL(triggered()), this, SLOT(loadImages())); camera_item_menu->addAction(act); - act = new QAction("calibrate",this); - connect(act,SIGNAL(triggered()),this,SLOT(calibrateCamera())); + act = new QAction("calibrate", this); + connect(act, SIGNAL(triggered()), this, SLOT(calibrateCamera())); camera_item_menu->addAction(act); - act = new QAction("save parameter",this); - connect(act,SIGNAL(triggered()),this,SLOT(saveCameraParameter())); + act = new QAction("save parameter", this); + connect(act, SIGNAL(triggered()), this, SLOT(saveCameraParameter())); camera_item_menu->addAction(act); - QAction *act_remove = new QAction("remove",this); - connect(act_remove,SIGNAL(triggered()),this,SLOT(removeCurrentItem())); + QAction* act_remove = new QAction("remove", this); + connect(act_remove, SIGNAL(triggered()), this, SLOT(removeCurrentItem())); camera_item_menu->addAction(act_remove); // tree view menu tree_view_menu = new QMenu(this); - act = new QAction("add Camera",this); - connect(act,SIGNAL(triggered()),this,SLOT(addCamera())); + act = new QAction("add Camera", this); + connect(act, SIGNAL(triggered()), this, SLOT(addCamera())); tree_view_menu->addAction(act); // image item menu image_item_menu = new QMenu(this); image_item_menu->addAction(act_remove); - act = new QAction("find chessboard",this); - connect(act,SIGNAL(triggered()),this,SLOT(findChessBoard())); + act = new QAction("find chessboard", this); + connect(act, SIGNAL(triggered()), this, SLOT(findChessBoard())); + image_item_menu->addAction(act); + act = new QAction("undistort image", this); + connect(act, SIGNAL(triggered()), this, SLOT(undistortImage())); + image_item_menu->addAction(act); + act = new QAction("undistort image with black bars", this); + connect(act, SIGNAL(triggered()), this, SLOT(undistortImageWithBlackBars())); + image_item_menu->addAction(act); + act = new QAction("project points", this); + connect(act, SIGNAL(triggered()), this, SLOT(reprojectPoints())); image_item_menu->addAction(act); - //graphics view + // graphics view image_view = gui.imageView; // add initial camera addCamera(); // progress dialog - progress_dialog_images = new QProgressDialog("loading images","cancel",0,0,this); + progress_dialog_images = new QProgressDialog("loading images", "cancel", 0, 0, this); future_watcher_images = new QFutureWatcher(this); - connect(future_watcher_images, SIGNAL(progressValueChanged(int)), progress_dialog_images, SLOT(setValue(int))); - connect(future_watcher_images, SIGNAL(progressRangeChanged(int, int)), progress_dialog_images, SLOT(setRange(int, int))); - connect(future_watcher_images, SIGNAL(finished()), progress_dialog_images, SLOT(accept())); - - progress_dialog_chessboard = new QProgressDialog("searching for chessboards","cancel",0,0,this); - future_watcher_chessboard= new QFutureWatcher >(this); - connect(future_watcher_chessboard, SIGNAL(progressValueChanged(int)), progress_dialog_chessboard, SLOT(setValue(int))); - connect(future_watcher_chessboard, SIGNAL(finished()), progress_dialog_chessboard, SLOT(accept())); - // connect(future_watcher_chessboard, SIGNAL(progressRangeChanged(int, int)), progress_dialog_chessboard, SLOT(setRange(int, int))); - - progress_dialog_calibrate = new QProgressDialog("calibrate camera","cancel",0,0,this); + connect(future_watcher_images, + SIGNAL(progressValueChanged(int)), + progress_dialog_images, + SLOT(setValue(int))); + connect(future_watcher_images, + SIGNAL(progressRangeChanged(int, int)), + progress_dialog_images, + SLOT(setRange(int, int))); + connect(future_watcher_images, + SIGNAL(finished()), + progress_dialog_images, + SLOT(accept())); + + progress_dialog_chessboard = + new QProgressDialog("searching for chessboards", "cancel", 0, 0, this); + future_watcher_chessboard = new QFutureWatcher>(this); + connect(future_watcher_chessboard, + SIGNAL(progressValueChanged(int)), + progress_dialog_chessboard, + SLOT(setValue(int))); + connect(future_watcher_chessboard, + SIGNAL(finished()), + progress_dialog_chessboard, + SLOT(accept())); + // connect(future_watcher_chessboard, SIGNAL(progressRangeChanged(int, int)), + // progress_dialog_chessboard, SLOT(setRange(int, int))); + + progress_dialog_calibrate = + new QProgressDialog("calibrate camera", "cancel", 0, 0, this); progress_dialog_calibrate->setCancelButton(NULL); future_watcher_calibrate = new QFutureWatcher(this); - connect(future_watcher_calibrate, SIGNAL(progressValueChanged(int)), progress_dialog_calibrate, SLOT(setValue(int))); - connect(future_watcher_calibrate, SIGNAL(finished()), progress_dialog_calibrate, SLOT(accept())); + connect(future_watcher_calibrate, + SIGNAL(progressValueChanged(int)), + progress_dialog_calibrate, + SLOT(setValue(int))); + connect(future_watcher_calibrate, + SIGNAL(finished()), + progress_dialog_calibrate, + SLOT(accept())); } QCamCalib::~QCamCalib() { } -void QCamCalib::contextMenuTreeView(const QPoint &point) +void QCamCalib::contextMenuTreeView(const QPoint& point) { - QTreeView *tree_view = findChild("treeView"); - if(!tree_view) + QTreeView* tree_view = findChild("treeView"); + if (!tree_view) throw std::runtime_error("Cannot find treeView object"); QModelIndex index = tree_view->indexAt(point); - if(index.isValid()) - { - QCamCalibItem *item = dynamic_cast(tree_model->itemFromIndex(index)); - if(!item) + if (index.isValid()) { + QCamCalibItem* item = + dynamic_cast(tree_model->itemFromIndex(index)); + if (!item) return; - else if(dynamic_cast(item)) + else if (dynamic_cast(item)) camera_item_menu->exec(tree_view->viewport()->mapToGlobal(point)); - else if(dynamic_cast(item)) + else if (dynamic_cast(item)) image_item_menu->exec(tree_view->viewport()->mapToGlobal(point)); } else { @@ -126,16 +166,19 @@ void QCamCalib::contextMenuTreeView(const QPoint &point) void QCamCalib::addCamera(int camera_id) { - if(camera_id < 0) + if (camera_id < 0) camera_id = 0; - for(int i=0;i<=tree_model->rowCount();++i) - { + for (int i = 0; i <= tree_model->rowCount(); ++i) { std::stringstream strstr; - strstr << CAMERA_BASE_NAME << (camera_id+i); - QModelIndexList list = tree_model->match(tree_model->index(0,0),Qt::DisplayRole,QVariant(strstr.str().c_str()),1,Qt::MatchExactly); - if(list.empty()) - { - tree_model->appendRow(new qcam_calib::CameraItem(camera_id+i,QString(strstr.str().c_str()))); + strstr << CAMERA_BASE_NAME << (camera_id + i); + QModelIndexList list = tree_model->match(tree_model->index(0, 0), + Qt::DisplayRole, + QVariant(strstr.str().c_str()), + 1, + Qt::MatchExactly); + if (list.empty()) { + tree_model->appendRow( + new qcam_calib::CameraItem(camera_id + i, QString(strstr.str().c_str()))); break; } } @@ -143,215 +186,315 @@ void QCamCalib::addCamera(int camera_id) void QCamCalib::saveCameraParameter(int camera_id) { - CameraItem *item = getCameraItem(camera_id); - if(!item->isCalibrated()) - { + CameraItem* item = getCameraItem(camera_id); + if (!item->isCalibrated()) { calibrateCamera(camera_id); - if(!item->isCalibrated()) + if (!item->isCalibrated()) return; } - QString path = QFileDialog::getSaveFileName(this, "Save Parameter",current_load_path, "config (*.yml *.xml)"); - if(path.size() != 0) + QString path = QFileDialog::getSaveFileName(this, + "Save Parameter", + current_load_path, + "config (*.yml *.xml)"); + if (path.size() != 0) item->saveParameter(path); } void QCamCalib::removeCamera(int camera_id) { - CameraItem *item = getCameraItem(camera_id); + CameraItem* item = getCameraItem(camera_id); tree_model->removeRow(item->row()); } -QImage loadImage(const QString &path) +QImage loadImage(const QString& path) { return QImage(path); } void QCamCalib::loadImages(int camera_id) { - QSpinBox *cols = findChild("spinBoxCols"); - QSpinBox *rows = findChild("spinBoxRows"); - if(!cols || !rows) + QSpinBox* cols = findChild("spinBoxCols"); + QSpinBox* rows = findChild("spinBoxRows"); + if (!cols || !rows) throw std::runtime_error("cannot find chessboard config"); - //select images - QStringList paths = QFileDialog::getOpenFileNames(this, "Open images",current_load_path, "Images (*.png *.jpg)"); + // select images + QStringList paths = QFileDialog::getOpenFileNames(this, + "Open images", + current_load_path, + "Images (*.png *.jpg)"); - //load images in parallel - QFuture images = QtConcurrent::mapped(paths,loadImage); + // load images in parallel + QFuture images = QtConcurrent::mapped(paths, loadImage); future_watcher_images->setFuture(images); - if(QDialog::Accepted != progress_dialog_images->exec() && future_watcher_images->isCanceled()) + if (QDialog::Accepted != progress_dialog_images->exec() && + future_watcher_images->isCanceled()) return; future_watcher_images->waitForFinished(); - //progress_dialog_images->close(); + // progress_dialog_images->close(); - //find chess boards in parallel - QFuture > chessboards; + // find chess boards in parallel + QFuture> chessboards; chessboards = QtConcurrent::mapped(images, - boost::bind(static_cast(*)(const QImage&,int,int)>(ImageItem::findChessboard),_1,cols->value(),rows->value())); + boost::bind(static_cast (*)(const QImage&, int, int)>( + ImageItem::findChessboard), + _1, + cols->value(), + rows->value())); future_watcher_chessboard->setFuture(chessboards); - progress_dialog_chessboard->setRange(progress_dialog_images->minimum(),progress_dialog_images->maximum()); - if(QDialog::Accepted != progress_dialog_chessboard->exec() && future_watcher_chessboard->isCanceled()) + progress_dialog_chessboard->setRange(progress_dialog_images->minimum(), + progress_dialog_images->maximum()); + if (QDialog::Accepted != progress_dialog_chessboard->exec() && + future_watcher_chessboard->isCanceled()) return; future_watcher_chessboard->waitForFinished(); progress_dialog_chessboard->close(); - //add items + // add items QStringList::const_iterator iter_path = paths.begin(); QFuture::const_iterator iter_image = images.begin(); - QFuture >::const_iterator iter_chessboard = chessboards.begin(); - ImageItem *last_image_item = NULL; - for(;iter_path!=paths.end() && iter_image!=images.end() && iter_chessboard!=chessboards.end();++iter_path,++iter_image,++iter_chessboard) - { + QFuture>::const_iterator iter_chessboard = chessboards.begin(); + ImageItem* last_image_item = NULL; + for (; iter_path != paths.end() && iter_image != images.end() && + iter_chessboard != chessboards.end(); + ++iter_path, ++iter_image, ++iter_chessboard) { QFileInfo info(*iter_path); current_load_path = info.absolutePath(); - CameraItem *item = getCameraItem(camera_id); - last_image_item = item->addImage(info.fileName(),*iter_image); - last_image_item->setChessboard(*iter_chessboard,cols->value(),rows->value()); + CameraItem* item = getCameraItem(camera_id); + last_image_item = item->addImage(info.fileName(), *iter_image); + last_image_item->setChessboard(*iter_chessboard, cols->value(), rows->value()); } - if(last_image_item) + if (last_image_item) displayImage(last_image_item->getImage()); } void QCamCalib::calibrateCamera(int camera_id) { - QSpinBox *cols = findChild("spinBoxCols"); - QSpinBox *rows = findChild("spinBoxRows"); - QDoubleSpinBox *dx = findChild("spinBoxDx"); - QDoubleSpinBox *dy = findChild("spinBoxDy"); - if(!cols || !rows || !dx || !dy) + QSpinBox* cols = findChild("spinBoxCols"); + QSpinBox* rows = findChild("spinBoxRows"); + QDoubleSpinBox* dx = findChild("spinBoxDx"); + QDoubleSpinBox* dy = findChild("spinBoxDy"); + if (!cols || !rows || !dx || !dy) throw std::runtime_error("cannot find chessboard config"); - - //select images - CameraItem *item = getCameraItem(camera_id); - if(item->countChessboards() < 5) - { + + QSpinBox* iterations = findChild("spinBoxIterations"); + if (!iterations) + throw std::runtime_error("cannot find calibration iteration config"); + + // select images + CameraItem* item = getCameraItem(camera_id); + if (item->countChessboards() < 5) { QErrorMessage box; - box.showMessage("Not enough chessboards for calibration. Add more images." ); + box.showMessage("Not enough chessboards for calibration. Add more images."); box.exec(); return; } - future_watcher_calibrate->setFuture(QtConcurrent::run(item,&CameraItem::calibrate,cols->value(),rows->value(),dx->value(),dy->value())); - progress_dialog_calibrate->setRange(0,0); - if(QDialog::Accepted != progress_dialog_calibrate->exec() && future_watcher_calibrate->isCanceled()) + future_watcher_calibrate->setFuture(QtConcurrent::run(item, + &CameraItem::calibrate, + cols->value(), + rows->value(), + dx->value(), + dy->value(), + iterations->value())); + progress_dialog_calibrate->setRange(0, 0); + if (QDialog::Accepted != progress_dialog_calibrate->exec() && + future_watcher_calibrate->isCanceled()) return; } -CameraItem *QCamCalib::getCameraItem(int camera_id) +CameraItem* QCamCalib::getCameraItemFromImageItem(int camera_id) { - CameraItem *item = NULL; - if(camera_id < 0) - { - QTreeView *tree_view = findChild("treeView"); - if(!tree_view) + CameraItem* item = NULL; + if (camera_id < 0) { + QTreeView* tree_view = findChild("treeView"); + if (!tree_view) throw std::runtime_error("Cannot find treeView object"); QModelIndex index = tree_view->currentIndex(); - if(index.isValid()) + QModelIndex parent_index = index.parent().parent(); + + if (index.isValid()) { + item = dynamic_cast(tree_model->itemFromIndex(parent_index)); + } + } + else { + for (int i = 0; i < tree_model->rowCount(); ++i) { + item = dynamic_cast(tree_model->item(i, 0)); + if (item && item->getId() == camera_id) + break; + else + item = NULL; + } + } + if (item == NULL) + throw std::runtime_error("Internal error: cannot find camera"); + return item; +} + +CameraItem* QCamCalib::getCameraItem(int camera_id) +{ + CameraItem* item = NULL; + if (camera_id < 0) { + QTreeView* tree_view = findChild("treeView"); + if (!tree_view) + throw std::runtime_error("Cannot find treeView object"); + QModelIndex index = tree_view->currentIndex(); + if (index.isValid()) item = dynamic_cast(tree_model->itemFromIndex(index)); } - else - { - for(int i=0;irowCount();++i) - { - item = dynamic_cast(tree_model->item(i,0)); - if(item && item->getId() == camera_id) + else { + for (int i = 0; i < tree_model->rowCount(); ++i) { + item = dynamic_cast(tree_model->item(i, 0)); + if (item && item->getId() == camera_id) break; else item = NULL; } } - if(item == NULL) + if (item == NULL) throw std::runtime_error("Internal error: cannot find camera"); return item; } -ImageItem *QCamCalib::getImageItem(int camera_id,const QString &name) +ImageItem* QCamCalib::getImageItem(int camera_id, const QString& name) { - ImageItem *item = NULL; - if(camera_id < 0) - { - QTreeView *tree_view = findChild("treeView"); - if(!tree_view) + ImageItem* item = NULL; + if (camera_id < 0) { + QTreeView* tree_view = findChild("treeView"); + if (!tree_view) throw std::runtime_error("Cannot find treeView object"); QModelIndex index = tree_view->currentIndex(); - if(index.isValid()) + if (index.isValid()) item = dynamic_cast(tree_model->itemFromIndex(index)); } - else - { - for(int i=0;irowCount()&&!item;++i) - { - CameraItem *camera = dynamic_cast(tree_model->item(i,0)); - if(camera) + else { + for (int i = 0; i < tree_model->rowCount() && !item; ++i) { + CameraItem* camera = dynamic_cast(tree_model->item(i, 0)); + if (camera) item = camera->getImageItem(name); } } - if(!item) + if (!item) throw std::runtime_error("Internal error: cannot find image item"); return item; } -void QCamCalib::addImage(const QString &name,const QImage &image,int camera_id) +void QCamCalib::addImage(const QString& name, const QImage& image, int camera_id) { - CameraItem *item = getCameraItem(camera_id); - item->addImage(name,image); + CameraItem* item = getCameraItem(camera_id); + item->addImage(name, image); } -void QCamCalib::findChessBoard(int camera_id,const QString &name) +void QCamCalib::findChessBoard(int camera_id, const QString& name) { - QSpinBox *cols = findChild("spinBoxCols"); - QSpinBox *rows = findChild("spinBoxRows"); - if(!cols || !rows) + QSpinBox* cols = findChild("spinBoxCols"); + QSpinBox* rows = findChild("spinBoxRows"); + if (!cols || !rows) throw std::runtime_error("cannot find chessboard config"); - ImageItem *item = getImageItem(camera_id,name); + ImageItem* item = getImageItem(camera_id, name); QList images; images.push_back(item->getRawImage()); - QFuture > chessboards; + QFuture> chessboards; chessboards = QtConcurrent::mapped(images, - boost::bind(static_cast(*)(const QImage&,int,int)>(ImageItem::findChessboard),_1,cols->value(),rows->value())); + boost::bind(static_cast (*)(const QImage&, int, int)>( + ImageItem::findChessboard), + _1, + cols->value(), + rows->value())); future_watcher_chessboard->setFuture(chessboards); - progress_dialog_chessboard->setRange(0,1); - if(QDialog::Accepted != progress_dialog_chessboard->exec() && future_watcher_chessboard->isCanceled()) + progress_dialog_chessboard->setRange(0, 1); + if (QDialog::Accepted != progress_dialog_chessboard->exec() && + future_watcher_chessboard->isCanceled()) return; - item->setChessboard(chessboards.results().front(),cols->value(),rows->value()); + item->setChessboard(chessboards.results().front(), cols->value(), rows->value()); displayImage(item->getImage()); } +void QCamCalib::undistortImage(int camera_id, const QString& name) +{ + CameraItem* item = getCameraItemFromImageItem(camera_id); + if (!item->isCalibrated()) { + throw std::runtime_error("Cannot undistort uncalibrated camera."); + } + + cv::Mat k = item->getCameraMatrix(); + cv::Mat dist = item->getDistCoeffs(); + ImageItem* image_item = getImageItem(camera_id, name); + displayImage(image_item->getUndistortedImage(k, dist)); +} + +void QCamCalib::undistortImageWithBlackBars(int camera_id, const QString& name) +{ + CameraItem* item = getCameraItemFromImageItem(camera_id); + if (!item->isCalibrated()) { + throw std::runtime_error("Cannot undistort uncalibrated camera."); + } + + cv::Mat k = item->getCameraMatrix(); + cv::Mat dist = item->getDistCoeffs(); + cv::Mat full_camera_matrix = item->getFullCameraMatrix(); + cv::Rect valid_ROI = item->getValidROI(); + cv::Rect preserved_ROI = item->getPreservedROI(); + ImageItem* image_item = getImageItem(camera_id, name); + displayImage(image_item->getUndistortedImageWithBlackBars(k, + dist, + full_camera_matrix, + valid_ROI, + preserved_ROI)); +} + +void QCamCalib::reprojectPoints(int camera_id, const QString& name) +{ + QSpinBox* cols = findChild("spinBoxCols"); + QSpinBox* rows = findChild("spinBoxRows"); + if (!cols || !rows) + throw std::runtime_error("cannot find pattern size"); + + cv::Size pattern_size(cols->value(), rows->value()); + CameraItem* item = getCameraItemFromImageItem(camera_id); + if (!item->isCalibrated()) { + throw std::runtime_error("Cannot undistort uncalibrated camera."); + } + + cv::Mat k = item->getCameraMatrix(); + cv::Mat dist = item->getDistCoeffs(); + ImageItem* image_item = getImageItem(camera_id, name); + displayImage(image_item->getReprojectedPointsImage(k, dist, pattern_size)); +} + void QCamCalib::removeCurrentItem() { - QTreeView *tree_view = findChild("treeView"); - if(!tree_view) + QTreeView* tree_view = findChild("treeView"); + if (!tree_view) throw std::runtime_error("Cannot find treeView object"); QModelIndex index = tree_view->currentIndex(); - if(index.isValid()) - { - if(index.parent().isValid()) + if (index.isValid()) { + if (index.parent().isValid()) tree_model->itemFromIndex(index.parent())->removeRow(index.row()); else tree_model->removeRow(index.row()); } } -void QCamCalib::displayImage(const QImage &image) +void QCamCalib::displayImage(const QImage& image) { image_view->displayImage(image); } void QCamCalib::clickedTreeView(const QModelIndex& index) { - if(!index.isValid()) + if (!index.isValid()) return; - const QStandardItemModel *model = dynamic_cast(index.model()); - if(!model) + const QStandardItemModel* model = + dynamic_cast(index.model()); + if (!model) return; - QStandardItem *item = model->itemFromIndex(index); - if(!item) + QStandardItem* item = model->itemFromIndex(index); + if (!item) return; - ImageItem *image = dynamic_cast(item); - if(image) + ImageItem* image = dynamic_cast(item); + if (image) image_view->displayImage(image->getImage()); } - - diff --git a/src/QCamCalib.hpp b/src/QCamCalib.hpp index 1f203aa..c2deab6 100644 --- a/src/QCamCalib.hpp +++ b/src/QCamCalib.hpp @@ -1,18 +1,16 @@ #ifndef QCAMCALIB_HPP #define QCAMCALIB_HPP -#include #include #include +#include -namespace qcam_calib -{ +namespace qcam_calib { class CameraItem; class ImageItem; class ImageView; } - /** * \brief Widget for calibrating pinhole cameras * @@ -22,18 +20,18 @@ namespace qcam_calib * * \author Alexander.Duda@dfki.de */ -class QCamCalib : public QWidget -{ +class QCamCalib : public QWidget { Q_OBJECT public: - QCamCalib(QWidget *parent = 0); + QCamCalib(QWidget* parent = 0); virtual ~QCamCalib(); public slots: /** * \brief Adds a new camera to the workspace * - * \param[in] camera_id The id of the camera. If no id is given the next free id is used starting from 0 + * \param[in] camera_id The id of the camera. If no id is given the next free id is + * used starting from 0 * \author Alexander.Duda@dfki.de */ void addCamera(int camera_id = -1); @@ -41,7 +39,8 @@ public slots: /** * \brief Removes a camera from the workspace * - * \note If no camera id is given it is assumed that a camera item is selected in the TreeView. + * \note If no camera id is given it is assumed that a camera item is selected in the + * TreeView. * * \param[in] camera_id The id of the camera. * \author Alexander.Duda@dfki.de @@ -49,9 +48,11 @@ public slots: void removeCamera(int camera_id = -1); /** - * \brief Opens a dialog to select images which are going to be loaded and added to the camera. + * \brief Opens a dialog to select images which are going to be loaded and added to + * the camera. * - * \note If no camera id is given it is assumed that a camera item is selected in the TreeView. + * \note If no camera id is given it is assumed that a camera item is selected in the + * TreeView. * * This call automatically performs chessboard detection * @@ -63,19 +64,21 @@ public slots: /** * \brief Adds an image to a camera * - * \note If no camera id is given it is assumed that a camera item is selected in the TreeView. + * \note If no camera id is given it is assumed that a camera item is selected in the + * TreeView. * * \param[in] name The name of the image * \param[in] image The image * \param[in] camera_id The id of the camera. * \author Alexander.Duda@dfki.de */ - void addImage(const QString &name,const QImage &image,int camera_id=-1); + void addImage(const QString& name, const QImage& image, int camera_id = -1); /** * \brief Opens a file dialog and saves the camera parameter as YAML or XML * - * \note If no camera id is given it is assumed that a camera item is selected in the TreeView. + * \note If no camera id is given it is assumed that a camera item is selected in the + * TreeView. * * \param[in] camera_id The id of the camera. * \author Alexander.Duda@dfki.de @@ -85,7 +88,8 @@ public slots: /** * \brief Calibrates a camera * - * \note If no camera id is given it is assumed that a camera item is selected in the TreeView. + * \note If no camera id is given it is assumed that a camera item is selected in the + * TreeView. * * \param[in] camera_id The id of the camera. * \author Alexander.Duda@dfki.de @@ -95,46 +99,88 @@ public slots: /** * \brief Finds chessboard corners in an image * - * \note if no camera id is given it is assumed that an image is selected in the TreeView + * \note if no camera id is given it is assumed that an image is selected in the + * TreeView * * \param[in] camera_id The id of the camera. * \param[in] name The name of the image * \author Alexander.Duda@dfki.de */ - void findChessBoard(int camera_id=-1,const QString &name=QString("")); + void findChessBoard(int camera_id = -1, const QString& name = QString("")); + + /** + * \brief Undistorts the image + * + * \note if no camera id is given it is asumed that an image is selected in the + * TreeView + * + * \param[in] camera_id The id of the camera + * \param[in] name The name of the image + * \author Higormeloo@gmail.com + */ + void undistortImage(int camera_id = -1, const QString& name = QString("")); + + /** + * \brief Undistorts the image but keeps all the pixels (black bars), + * also draws 2 boxes corresponding to the max aspect ratio without black pixels + * drawn in green and the original image aspect ratio without black pixels drawn in + * blue + * + * \note if no camera id is given it is asumed that an image is selected in the + * TreeView + * + * \param[in] camera_id The id of the camera + * \param[in] name The name of the image + * \author Higormeloo@gmail.com + */ + void undistortImageWithBlackBars(int camera_id = -1, + const QString& name = QString("")); + + /** + * \brief Reproject points in an image + * + * \note if no camera id is given it is assumed that an image is selected in the + * TreeView + * + * \param[in] camera_id The id of the camera. + * \param[in] name The name of the image + * \author Higormeloo@gmail.com + */ + void reprojectPoints(int camera_id = -1, const QString& name = QString("")); private slots: - void contextMenuTreeView(const QPoint &point); + void contextMenuTreeView(const QPoint& point); void clickedTreeView(const QModelIndex& index); - void displayImage(const QImage &image); + void displayImage(const QImage& image); void removeCurrentItem(); private: - qcam_calib::CameraItem *getCameraItem(int camera_id); - qcam_calib::ImageItem *getImageItem(int camera_id,const QString &name); + qcam_calib::CameraItem* getCameraItem(int camera_id); + qcam_calib::ImageItem* getImageItem(int camera_id, const QString& name); + qcam_calib::CameraItem* getCameraItemFromImageItem(int camera_id); private: // file paths QString current_load_path; // tree model - QStandardItemModel *tree_model; + QStandardItemModel* tree_model; // menues - QMenu *camera_item_menu; - QMenu *tree_view_menu; - QMenu *image_item_menu; + QMenu* camera_item_menu; + QMenu* tree_view_menu; + QMenu* image_item_menu; // image dispay - qcam_calib::ImageView *image_view; + qcam_calib::ImageView* image_view; // progress stuff - QProgressDialog *progress_dialog_images; - QProgressDialog *progress_dialog_chessboard; - QProgressDialog *progress_dialog_calibrate; - QFutureWatcher *future_watcher_images; - QFutureWatcher > *future_watcher_chessboard; - QFutureWatcher *future_watcher_calibrate; + QProgressDialog* progress_dialog_images; + QProgressDialog* progress_dialog_chessboard; + QProgressDialog* progress_dialog_calibrate; + QFutureWatcher* future_watcher_images; + QFutureWatcher>* future_watcher_chessboard; + QFutureWatcher* future_watcher_calibrate; }; #endif /* QCAMCALIB_HPP */ diff --git a/src/main_gui.ui b/src/main_gui.ui index cea679b..79956ae 100644 --- a/src/main_gui.ui +++ b/src/main_gui.ui @@ -58,7 +58,7 @@ 999.990000000000009 - 35.000000000000000 + 50.000000000000000 @@ -88,7 +88,7 @@ 999.990000000000009 - 35.000000000000000 + 50.000000000000000 @@ -153,8 +153,38 @@ - - + + + + + 9 + + + + 30 + + + 9999 + + + 100 + + + + + + + + 9 + + + + iterations : + + + + +