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

CSDN 2024-08-25 11:35:02 阅读 82

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

本文章博客地址: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

<code>#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



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。