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

news2024/9/23 19:24:10

若该文为原创文章,转载请注明出处
本文章博客地址: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

#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

...
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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2053848.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

运维小技能:通过调整JVM的默认内存配置来解决内存溢出(‌OutOfMemoryError)‌或栈溢出(‌StackOverflowError)‌等错误

文章目录 引言I 调整JVM的默认堆内存配置1.1 java命令启动jar包时配置JVM 的内存参数1.2 基于Tomcat服务器部署的java应用,配置JVM 的内存参数II 案例: Linux 操作系统设置tomcat的 JVM 的内存参数查找Tomcat位置: 快速定位服务状态和部署位置具体配置步骤扩展: 监测Nginx访…

配置stm32cubemx采集stm32H743IIT6,通过DMA实现多通道和多模块ADC的采集,亲测有效!

之前写到stm32cubemx通过阻塞实现单通道和多通道的ADC的采集。 本文分享通过DMA实现单模块多通道和多模块多通道的ADC采集。 stm32cubemx的版本6.10.0。 一、DMA采集多通道ADC数据 阻塞采集是每次采集adc数据&#xff0c;cpu死等&#xff0c;直到采集完或者在设定时间超时没…

不能使用乘除法、for、while、if、else、switch、case求1+2+3+...+n

求123...n_牛客题霸_牛客网 (nowcoder.com) 描述 求123...n&#xff0c;要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句&#xff08;A?B:C&#xff09;。 数据范围&#xff1a; 0<n≤2000<n≤200 进阶&#xff1a; 空间复杂度 O(1)O(…

PCRNet: Point Cloud Registration Network using PointNet Encoding 论文解读

目录 一、导言 二、先导知识 1、Frobenius范数 三、相关工作 1、点云配准工作 2、PointNet 3、基于深度学习的点云配准 四、PCRNet 1、PCRNet 2、Iterative PCRNet 3、损失函数 五、实验 一、导言 本论文收录于CVPR2019&#xff0c;本论文提出了一种依赖PointNet网…

快排补充(挖坑法,lomuto前后指针,非递归法)

挖坑法 挖坑法动态示意图 挖坑法方法分析 创建左右指针。⾸先从右向左找出⽐基准⼩的数据&#xff0c;找到后⽴即放⼊左边坑中&#xff0c;当前位置变为新 的"坑"&#xff0c;然后从左向右找出⽐基准⼤的数据&#xff0c;找到后⽴即放⼊右边坑中&#xff0c;当前位置…

STM32——CAN通讯基础知识

CAN 协议简介 CAN 是控制器局域网络 (Controller Area Network) 的简称&#xff0c;它是由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的&#xff0c;并最终成为国际标准(ISO11519以及ISO11898),是国际上应用最广泛的现场总线之一。差异点如下&#xff1a; 高速CAN可以达…

关于shortlink项目重构增加了del_time字段自己出现的问题

最近刚做完shortlink项目的重构问题,其中增加del_time作为t_link表的唯一索引要注意几点问题. 1.数据库表中的del_time字段类型为什么,一定要和DO中的类型统一. 2.del_time作为唯一索引不要写死了,不然就无法创建短连接了,这里我是将del_time修改成date类型,然后通过new date…

Python数分实战

学习视频&#xff1a;【课程3.0】Python基础与分析实战_哔哩哔哩_bilibili 由于学习过python进行数据分析&#xff0c;所以就简单记录一下&#xff0c;最主要学习的还是视频最后的两个项目&#xff0c;进行实战 之前想不明白明明有很智能的软件做数据分析&#xff0c;为什么还要…

XXX【5】观察者模式

文件分割案例&#xff1a; 用户输入文件路径filePath和分割数量number&#xff0c;初始化出一个FileSplitter实例对象&#xff0c;然后调用split方法文件分割。 假如&#xff1a;我要加一个进度条的设计 抽象不能依赖于实现细节&#xff1a;在第6行的m_progressBar是一个进度通…

【深度解析】WRF-LES与PALM微尺度气象大涡模拟

查看原文>>>【深度解析】WRF-LES与PALM微尺度气象大涡模拟 针对微尺度气象的复杂性&#xff0c;大涡模拟&#xff08;LES&#xff09;提供了一种无可比拟的解决方案。微尺度气象学涉及对小范围内的大气过程进行精确模拟&#xff0c;这些过程往往与天气模式、地形影响和…

vimplus出现的错误,如何解决??

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

C++面向对象编程(上)

类与对象属于面向对象的程序设计思想(Object Oriented Programming)&#xff0c;简称OOP。 面向对象基础理论 面向对象是一种对现实世界理解和抽象的方法&#xff0c;是计算机编程技术发展到一定阶段后的产物&#xff0c;是一种软件开发的方法 面向对象四大特性 1.抽象 忽…

数据库(五):多表设计和多表查询

项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求及业务模块之间的关系&#xff0c;分析并设计表结构&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也存在各种联系&#xff0c;基本上分为三种&#xff1a;一对一、多对一、一对多…

如何在寂静中用电脑找回失踪的手机?远程控制了解一下

经过一番努力&#xff0c;我终于成功地将孩子哄睡了。夜深人静&#xff0c;好不容易有了一点自己的时间&#xff0c;就想刷手机放松放松&#xff0c;顺便看看有没有重要信息。但刚才专心哄孩子去了&#xff0c;一时就忘记哄孩子之前&#xff0c;顺手把手机放哪里去了。 但找过手…

进程相关知识

进程和程序的区别 程序 程序是静态的&#xff0c;是存储在硬盘、SSD等存储介质中的一个文件&#xff0c;通常由源代码&#xff08;如 .c 文件&#xff09;编译生成的二进制可执行文件&#xff08;如 a.out&#xff09;。程序包含了指令和数据&#xff0c;但在未被执行时&#…

【计算机操作系统】基本分页存储管理

文章目录 基本分页存储管理分页存储的概念重要的数据结构——页表页表项大小计算地址转换实现 基本地址变换机构具有快表的地址变换机构快表&#xff08;TLB&#xff09;的概念引入快表后的地址变换局部性原理 两级页表单级页表 vs 两级页表 基本分页存储管理 非连续分配&#…

使用Python编写AI程序,让机器变得更智能

人工智能&#xff08;AI&#xff09;是当今科技领域最热门的话题之一。随着Python编程语言的逐渐流行&#xff0c;它已经成为许多人工智能编程的首选语言。本文将介绍如何使用Python编写AI程序&#xff0c;让机器变得更智能。 首先&#xff0c;Python提供了大量的AI库和工具&a…

Easysearch 性能测试方法概要

&#xff08;公众号用的是QQ音乐&#xff0c;可以随时听&#xff09; INFINI Easysearch INFINI Easysearch 是一个分布式的近实时搜索与分析引擎&#xff0c;核心引擎基于开源的 Apache Lucene。Easysearch 衍生自基于开源协议Apache 2.0 的Elasticsearch 7.10 版本&#xff0…

4-1-2 直流电机(电机专项教程)

4-1-2 直流电机&#xff08;电机专项教程&#xff09; 4-1-2 直流电机主要参数尺寸参数额定电压额定电流空载转速 如何控制直流电机有刷直流电机转向控制H桥电路控制转向 如何控制电机转速PWM控制电机转速 4-1-2 直流电机 之前学习了有刷直流电机的基本结构个工作原理&#xff…

[数据集][图像分类]电力场景电力线固定处连接处腐蚀有鸟巢分类数据集1279张3类别

数据集类型&#xff1a;图像分类用&#xff0c;不可用于目标检测无标注文件 数据集格式&#xff1a;仅仅包含jpg图片&#xff0c;每个类别文件夹下面存放着对应图片 图片数量(jpg文件个数)&#xff1a;1279 分类类别数&#xff1a;3 类别名称:["corrosion","nes…