项目实战:Qt+Opencv相机标定工具v1.3.0(支持打开摄像头、视频文件和网络地址,支持标定过程查看、删除和动态评价误差率,支持追加标定等等)

若该文为原创文章,转载请注明出处

本文章博客地址:https://hpzwl.blog.csdn.net/article/details/141334834

长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...

Qt开发专栏:项目实战(点击传送门)

需求

1.打开摄像头,可设置帧率、分辨率(可设置);

2.可打开usb、rtsp和本地文件(直接输入地址自动判断);

3.opencv摄像头操作子线程处理;

4.支持设置棋盘格的行列角点数;

5.支持标定过程可控制;

6.采集标定图、可对标定图进行查看、删除;

7.可对已有的标定图查看评价像素误差率;

8.标定完成后,可以追加标定,继续开始基于原来的标定采集图继续标定;

9.支持定制配置文件的导出和导出(测试运行包不对外开放该功能);

相关博客

OpenCV开发笔记(〇):使用mingw530_32编译openCV3.4.1源码,搭建Qt5.9.3的openCV开发环境

OpenCV开发笔记(三):OpenCV图像的概念和基本操作

OpenCV开发笔记(四):OpenCV图片和视频数据的读取与存储

OpenCV开发笔记(五):OpenCV读取与操作摄像头

OpenCV开发笔记(六):OpenCV基础数据结构、颜色转换函数和颜色空间

OpenCV开发笔记(七十六):相机标定(一):识别棋盘并绘制角点

OpenCV开发笔记(七十七):相机标定(二):通过棋盘标定计算相机内参矩阵矫正畸变摄像头图像

Demo:calibrateTool_v1.3.0 windows运行包

广角摄像头标定过程

鱼眼摄像头标定过程

动态标定过程:查看、删除和评价

CSDN粉丝0积分下载:https://download.csdn.net/download/qq21497936/89652658

QQ群:博客首页扫码进入QQ技术群,点击"文件 "搜索"calibrateTool",群内与博文同步更新)

模块化部署

关键源码

CalibrateManager.h

cpp 复制代码
#ifndef CALIBRATEMANAGER_H
#define CALIBRATEMANAGER_H

// opencv
#include "opencv/highgui.h"
#include "opencv/cxcore.h"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/xphoto.hpp"
#include "opencv2/dnn/dnn.hpp"
// opencv_contrib
#include <opencv2/xphoto.hpp>
#include <opencv2/ximgproc.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/xfeatures2d/nonfree.hpp>

#include "cvui.h"
#include "calibrateCommon.h"
#include <QImage>
#include <QTimer>


class CalibrateManager: public QObject
{
    Q_OBJECT
public:
    explicit CalibrateManager(QObject *parent = 0);
    ~CalibrateManager();

public slots:
    void testOpencvEnv();                       // 测试环境

public:
    double  getBrightness()     const;          // 亮度
    double  getContrast()       const;          // 对比度
    double  getSaturation()     const;          // 饱和度
    double  getHue()            const;          // 色调
    double  getGain()           const;          // 增益
    double  getExposure()       const;          // 曝光度
    bool    getShowProperty()   const;          // 显示属性
    int     getCalibrateRegionX() const;        // 区域x
    int     getCalibrateRegionY() const;        // 区域y
    int     getCalibrateRegionWidth() const;    // 区域width
    int     getCalibrateRegionHeight() const;   // 区域height
    int     getChessboardColCornerCount() const;// 棋盘行角点数量
    int     getChessboardRowCornerCount() const;// 棋盘列角点数量
    QString getSerialize() const;               // 获取序列化参数


public:
    void setBrightness  (double value);         // 亮度
    void setContrast    (double value);         // 对比度
    void setSaturation  (double value);         // 饱和度
    void setHue         (double value);         // 色调
    void setGain        (double value);         // 增益
    void setExposure    (double value);         // 曝光度
    void setShowProperty(bool value);           // 显示属性
    void setCalibrateRegionX(int x);            // 区域x
    void setCalibrateRegionY(int y);            // 区域y
    void setCalibrateRegionWidth(int width);    // 区域width
    void setCalibrateRegionHeight(int height);  // 区域height
    void setChessboardColCornerCount(int count);// 棋盘行角点数量
    void setChessboardRowCornerCount(int count);// 棋盘列角点数量
    bool setSerialize(QString str);             // 获取序列化参数

signals:
    void signal_opened(bool result);            // 打开摄像头信号
    void signal_closed();                       // 关闭摄像头信号
    void signal_captureOneFrame(cv::Mat mat);   // 接收图像后抛出信号
    void signal_captureOneFrame(QImage image);  // 接收图像后抛出信号
    void signal_captureOneResultFrame(cv::Mat mat);   // 接收图像后抛出信号
    void signal_captureOneResultFrame(QImage image);  // 接收图像后抛出信号
    void signal_startedCalibrate(bool result);  // 开始标定结果
    void signal_regionChanged(int x, int y, int width, int height);
    void signal_fpsChanged(int fps);            // 帧率
    void signal_stopedCalibrate();              // 结束标定结果(这是强制中断,不是标定完成)
    void signal_finishedCalibrate();            // 标定完成
    void signal_cameraInfo(CameraInfo cameraInfo);  // 更新截图相机信息


public slots:
    void slot_startCapture(int usb, int width = 0, int height = 0, int fps = 0);
                                                // 打开摄像头, 0...
    void slot_startCapture(QString url, int width = 0, int height = 0, int fps = 0);
                                                // 打开摄像头, 网络摄像头地址
    void slot_stopCapture();                    // 当正在采集中时(>>时),关闭摄像头会导致程序崩溃,所以采集与停止放一个线程中(消息循环)
    void slot_startCalibrate();                 // 开始标定
    void slot_addCalibrate();                   // 继续标定
    void slot_snapshot();                       // 快照
    void slot_deleteSnapshot(int index);        // 删除快照
    void slot_stopCalibrate();                  // 停止标定
    void slot_finishCalibrate();                // 完成标定

public slots:
    void slot_start();                       // 开启线程
    void slot_stop();                        // 关闭线程

protected slots:
    void slot_captrueFrame();           // 消息循环获取图像

protected:
    void initControl();
    void updateCalibrateResult();       // 更新标定结果
    void calculateCalibrateErrors();    // 计算误差

protected:
    bool findChessboard(int rowCornerCount, int colCornerCount, cv::Mat &mat, std::vector<cv::Point2f> &vectorPoint2fCorners);

public:
    static QImage mat2Image(cv::Mat mat);      // cv::Mat 转 QImage

private:
    bool _running;                      // 线程是否运行

private:
    cv::VideoCapture *_pVideoCapture;   // 摄像头实例

    bool _showProperty;                 // 是否显示属性参数
    double _brightness;                 // 亮度
    double _contrast;                   // 对比度
    double _saturation;                 // 饱和度
    double _hue;                        // 色调
    double _gain;                       // 增益
    double _exposure;                   // 曝光度

    int _width;                         // 宽度
    int _height;                        // 高度
    int _fps;                           // 帧率
    bool _opened;                       // 摄像头是否打开

    bool _calibratingBefore;            // 标定前一个变化状态
    bool _calibrating;                  // 正在标定
    bool _calibratFinished;             // 校准完了(当前最近一个已经校准)

    int _calibrateRegionX;              // 标定region区域像素起始x坐标
    int _calibrateRegionY;              // 标定region区域像素起始y坐标
    int _calibrateRegionWidth;          // 标定region区域像素宽度
    int _calibrateRegionHeight;         // 标定region区域像素高度

    cv::Mat _mat;                       // 缓存一帧
    cv::Mat _resultMat;                 // 结果
    int _chessboardColCornerCount;      // 一列多少个角点
    int _chessboardRowCornerCount;      // 一行多少个角点
    std::vector<std::vector<cv::Point3f>> _vectorObjectPoint;    // 缓存点
    std::vector<std::vector<cv::Point2f>> _vectorImagePoint;
    bool _snapshot;                     // 拍照

private:                                // 计算内参和畸变系数
    cv::Mat _cameraMatrix;              // 相机矩阵(接收输出)
    cv::Mat _distCoeffs;                // 畸变系数(接收输出)
    std::vector<cv::Mat> _rotate;       // 旋转量(接收输出)
    std::vector<cv::Mat> _translate;    // 偏移量(接收输出)

private:
    CameraInfo _cameraInfo;
};

#endif // CALIBRATEMANAGER_H

CalibrateManager.cpp

cpp 复制代码
...
void CalibrateManager::slot_captrueFrame()
{
    if(!_running)
    {
        return;
    }
    if(_pVideoCapture->isOpened())
    {

        *_pVideoCapture >> _mat;
        if(_showProperty)
        {
            cv::putText(_mat, QString("brightness: %1")
                        .arg(_brightness).toStdString(),
                        cvPoint(0, 30), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
            cv::putText(_mat, QString("  contrast: %1").arg(_contrast  ).toStdString(),
                        cvPoint(0, 60), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
            cv::putText(_mat, QString("saturation: %1").arg(_saturation).toStdString(),
                        cvPoint(0, 90), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
            cv::putText(_mat, QString("       hue: %1").arg(_hue       ).toStdString(),
                        cvPoint(0, 120), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
            cv::putText(_mat, QString("      gain: %1").arg(_gain      ).toStdString(),
                        cvPoint(0, 150), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
            cv::putText(_mat, QString("  exposure: %1").arg(_exposure  ).toStdString(),
                        cvPoint(0, 180), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
            cv::putText(_mat, QString("press ESC out").toStdString(),
                        cvPoint(0, 210), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
        }
        // 第一次进入标定
        if(!_calibratingBefore && _calibrating)
        {
            _calibrateRegionX = 0;
            _calibrateRegionY = 0;
            _calibrateRegionWidth = _width;
            _calibrateRegionHeight = _height;
            _calibratingBefore = true;
            emit signal_regionChanged(_calibrateRegionX, _calibrateRegionY, _calibrateRegionWidth, _calibrateRegionHeight);
            QImage srcImage = mat2Image(_mat);
            emit signal_captureOneResultFrame(srcImage);
        }else if(_calibrating)
        {
            QImage srcImage = mat2Image(_mat);
            // 获取
            std::vector<cv::Point2f> imagePoints;
            if(findChessboard(_chessboardRowCornerCount,
                              _chessboardColCornerCount,
                              _mat,
                              imagePoints))
            {
                // 这是拍照截图
                if(_snapshot)
                {
                    // 三维世界坐标系
                    std::vector<cv::Point3f> objectPoints;
                    for(int i = 0; i < _chessboardRowCornerCount; i++)
                    {
                        for(int j = 0; j < _chessboardColCornerCount; j++)
                        {
                            objectPoints.push_back(cv::Point3f(j, i, 0));
                        }
                    }
                    // 图像识别出来的角点(一张图一组)
                    _vectorObjectPoint.push_back(objectPoints);
                    _vectorImagePoint.push_back(imagePoints);

                    _snapshot = false;


                    {
                        SnapShot snapShot;
                        snapShot.dateTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz");
                        snapShot.srcImage = srcImage;
                        snapShot.drawChessboardImage = mat2Image(_mat);
                        snapShot.imagePoints = imagePoints;
                        snapShot.objectPoints = objectPoints;
                        _cameraInfo.listSnapShot.append(snapShot);

                        // 更新标定结果
                        updateCalibrateResult();
                        // 计算误差率
                        calculateCalibrateErrors();
                        // 抛出更新
                        emit signal_cameraInfo(_cameraInfo);
                    }
                }
            }
//            if(_cameraInfo.listSnapShot.size() == 0)
//            {
//                QImage srcImage = mat2Image(_mat);
//                emit signal_captureOneResultFrame(srcImage);
//            }else{
//                cv::undistort(_mat, _resultMat, _cameraMatrix, _distCoeffs);
//                QImage image = mat2Image(_resultMat);
//                emit signal_captureOneResultFrame(image);
//            }
        }else if(_calibratFinished)
        {
//            if(_cameraInfo.listSnapShot.size() == 0)
//            {
//                QImage srcImage = mat2Image(_mat);
//                emit signal_captureOneResultFrame(srcImage);
//            }else{
//                cv::undistort(_mat, _resultMat, _cameraMatrix, _distCoeffs);
//                QImage image = mat2Image(_resultMat);
//                emit signal_captureOneResultFrame(image);
//            }
        }
        // 抛出原图
        QImage image = mat2Image(_mat);
        emit signal_captureOneFrame(image);

        // 抛出校正图
        if(_cameraMatrix.empty())
        {
            emit signal_captureOneResultFrame(image);
        }else{
            LOG;
            cv::undistort(_mat, _resultMat, _cameraMatrix, _distCoeffs);
            QImage dstImage = mat2Image(_resultMat);
            emit signal_captureOneResultFrame(dstImage);
        }
        QTimer::singleShot(5, this, SLOT(slot_captrueFrame()));
    }
}
...

入坑

算法的研究优化过程中,受到摄像头光学、标定板、标定板所占视口大小,图像处理过程原本的流程优化、标定过程中动态的处理等多方面因素,坑多暂时未记录。

本文章博客地址:https://hpzwl.blog.csdn.net/article/details/141334834

相关推荐
锦亦之22337 小时前
QT+OSG+OSG-earth如何在窗口显示一个地球
开发语言·qt
柳鲲鹏10 小时前
编译成功!QT/6.7.2/Creator编译Windows64 MySQL驱动(MinGW版)
开发语言·qt·mysql
三玖诶10 小时前
如何在 Qt 的 QListWidget 中逐行添加和显示数据
开发语言·qt
阳光开朗_大男孩儿16 小时前
DBUS属性原理
linux·服务器·前端·数据库·qt
Alphapeople17 小时前
Qt Modbus
开发语言·qt
竹林海中敲代码17 小时前
Qt Creator 集成开发环境 常见问题
qt·qt工具常见问题
点PY17 小时前
基于Sparse Optical Flow 的Homography estimation
人工智能·opencv·计算机视觉
越甲八千17 小时前
opencv滤波算法总结
opencv
越甲八千17 小时前
opencv对比度增强方法算法汇总
人工智能·opencv·算法
独木三绝17 小时前
OpenCV第八章——腐蚀与膨胀
人工智能·opencv·计算机视觉