Qt Demo之 deepseek 帮我写的关于双目标定的小界面
Qt Demo之 deepseek 帮我写的关于双目标定的小界面
-
界面形式:
-
包含以下功能:
具体实现:
- 搜索描述:
你好,我想用C++, qt写个关于双目相机标定的界面,主要包括以下内容:
1.分别打开两张图像,然后用户可以手动在图像上通过鼠标点击进行选点,选点后自动出现图像坐
标,并且选点动作支持撤销当前点,撤销全部点
2. 包含标定按钮 ,图像界面支持缩放、拖拽及平移
3. 状态栏实时显示鼠标移动时对应的图像坐标
4. 4.给出完整可用代码,以.h .cpp给出,中文字符以QString::fromLocal8Bit形式给出
- 头文件 (stereocalibration.h):
#ifndef STEREOCALIBRATION_H
#define STEREOCALIBRATION_H#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QMouseEvent>
#include <QPointF>
#include <QList>
#include <QStatusBar>
#include <QLabel>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QMessageBox>
#include <QScrollBar>
#include <QWheelEvent>
#include <opencv2/opencv.hpp>class ZoomGraphicsView : public QGraphicsView
{Q_OBJECT
public:explicit ZoomGraphicsView(QWidget *parent = nullptr);void setImage(const QPixmap &pixmap);void clearPoints();void addPoint(const QPointF &point);QList<QPointF> getPoints() const;bool hasImage() const;signals:void mouseMoved(QPointF pos);void mouseClicked(QPointF pos);protected:void wheelEvent(QWheelEvent *event) override;void mouseMoveEvent(QMouseEvent *event) override;void mousePressEvent(QMouseEvent *event) override;void drawForeground(QPainter *painter, const QRectF &rect) override;private:QGraphicsScene *scene;QGraphicsPixmapItem *pixmapItem;QList<QPointF> points;double scaleFactor;
};class StereoCalibration : public QMainWindow
{Q_OBJECTpublic:StereoCalibration(QWidget *parent = nullptr);~StereoCalibration();private slots:void openLeftImage();void openRightImage();void calibrate();void undoPoint();void resetPoints();void onLeftViewMouseMoved(QPointF pos);void onRightViewMouseMoved(QPointF pos);void onLeftViewMouseClicked(QPointF pos);void onRightViewMouseClicked(QPointF pos);private:void setupUI();void updateStatusBar();cv::Mat QImageToCvMat(const QImage &image);ZoomGraphicsView *leftView;ZoomGraphicsView *rightView;QLabel *statusLabel;QPushButton *openLeftBtn;QPushButton *openRightBtn;QPushButton *calibrateBtn;QPushButton *undoBtn;QPushButton *resetBtn;cv::Mat leftImage;cv::Mat rightImage;QList<QPointF> leftPoints;QList<QPointF> rightPoints;bool isCalibrated;cv::Mat cameraMatrix1, distCoeffs1;cv::Mat cameraMatrix2, distCoeffs2;cv::Mat R, T, E, F;
};#endif // STEREOCALIBRATION_H
- 源文件 (stereocalibration.cpp):
#include "stereocalibration.h"
#include <cmath>// ZoomGraphicsView 实现
ZoomGraphicsView::ZoomGraphicsView(QWidget *parent): QGraphicsView(parent), scene(new QGraphicsScene(this)), pixmapItem(nullptr), scaleFactor(1.0)
{setScene(scene);setDragMode(QGraphicsView::ScrollHandDrag);setTransformationAnchor(QGraphicsView::AnchorUnderMouse);setResizeAnchor(QGraphicsView::AnchorUnderMouse);setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);setRenderHint(QPainter::Antialiasing);
}void ZoomGraphicsView::setImage(const QPixmap &pixmap)
{scene->clear();points.clear();pixmapItem = scene->addPixmap(pixmap);scene->setSceneRect(pixmap.rect());scaleFactor = 1.0;resetTransform();
}void ZoomGraphicsView::clearPoints()
{points.clear();scene->update();
}void ZoomGraphicsView::addPoint(const QPointF &point)
{points.append(point);scene->update();
}QList<QPointF> ZoomGraphicsView::getPoints() const
{return points;
}bool ZoomGraphicsView::hasImage() const
{return pixmapItem != nullptr;
}void ZoomGraphicsView::wheelEvent(QWheelEvent *event)
{if (event->modifiers() & Qt::ControlModifier) {double factor = (event->angleDelta().y() > 0) ? 1.1 : 0.9;scaleFactor *= factor;scale(factor, factor);event->accept();} else {QGraphicsView::wheelEvent(event);}
}void ZoomGraphicsView::mouseMoveEvent(QMouseEvent *event)
{QPointF scenePos = mapToScene(event->pos());emit mouseMoved(scenePos);QGraphicsView::mouseMoveEvent(event);
}void ZoomGraphicsView::mousePressEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton && pixmapItem) {QPointF scenePos = mapToScene(event->pos());// 检查点击位置是否在图像范围内if (scenePos.x() >= 0 && scenePos.y() >= 0 && scenePos.x() < pixmapItem->pixmap().width() && scenePos.y() < pixmapItem->pixmap().height()) {emit mouseClicked(scenePos);}}QGraphicsView::mousePressEvent(event);
}void ZoomGraphicsView::drawForeground(QPainter *painter, const QRectF &rect)
{Q_UNUSED(rect);if (!hasImage()) return;// 绘制所有点painter->setPen(QPen(Qt::red, 3));for (const QPointF &point : points) {painter->drawEllipse(point, 5, 5);}// 绘制坐标文本painter->setPen(QPen(Qt::green, 2));QFont font = painter->font();font.setPointSize(10);painter->setFont(font);for (const QPointF &point : points) {QString text = QString("(%1, %2)").arg(point.x(), 0, 'f', 1).arg(point.y(), 0, 'f', 1);painter->drawText(point + QPointF(10, -10), text);}
}// StereoCalibration 实现
StereoCalibration::StereoCalibration(QWidget *parent): QMainWindow(parent), isCalibrated(false)
{setupUI();resize(1200, 800);setWindowTitle(QString::fromLocal8Bit("双目相机标定"));
}StereoCalibration::~StereoCalibration()
{
}cv::Mat StereoCalibration::QImageToCvMat(const QImage &image)
{cv::Mat mat;switch (image.format()) {case QImage::Format_RGB32:case QImage::Format_ARGB32:case QImage::Format_ARGB32_Premultiplied:mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());cv::cvtColor(mat, mat, cv::COLOR_BGRA2BGR);break;case QImage::Format_RGB888:mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR);break;case QImage::Format_Grayscale8:mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());break;default:break;}return mat;
}void StereoCalibration::setupUI()
{QWidget *centralWidget = new QWidget(this);setCentralWidget(centralWidget);// 创建工具栏QHBoxLayout *toolLayout = new QHBoxLayout();openLeftBtn = new QPushButton(QString::fromLocal8Bit("打开左图像"), this);openRightBtn = new QPushButton(QString::fromLocal8Bit("打开右图像"), this);calibrateBtn = new QPushButton(QString::fromLocal8Bit("标定"), this);undoBtn = new QPushButton(QString::fromLocal8Bit("撤销点"), this);resetBtn = new QPushButton(QString::fromLocal8Bit("重置所有点"), this);toolLayout->addWidget(openLeftBtn);toolLayout->addWidget(openRightBtn);toolLayout->addWidget(calibrateBtn);toolLayout->addWidget(undoBtn);toolLayout->addWidget(resetBtn);toolLayout->addStretch();// 创建图像显示区域QHBoxLayout *imageLayout = new QHBoxLayout();leftView = new ZoomGraphicsView(this);rightView = new ZoomGraphicsView(this);imageLayout->addWidget(leftView);imageLayout->addWidget(rightView);// 创建主布局QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);mainLayout->addLayout(toolLayout);mainLayout->addLayout(imageLayout);// 创建状态栏statusLabel = new QLabel(QString::fromLocal8Bit("就绪"));statusBar()->addWidget(statusLabel);// 连接信号槽connect(openLeftBtn, &QPushButton::clicked, this, &StereoCalibration::openLeftImage);connect(openRightBtn, &QPushButton::clicked, this, &StereoCalibration::openRightImage);connect(calibrateBtn, &QPushButton::clicked, this, &StereoCalibration::calibrate);connect(undoBtn, &QPushButton::clicked, this, &StereoCalibration::undoPoint);connect(resetBtn, &QPushButton::clicked, this, &StereoCalibration::resetPoints);connect(leftView, &ZoomGraphicsView::mouseMoved, this, &StereoCalibration::onLeftViewMouseMoved);connect(rightView, &ZoomGraphicsView::mouseMoved, this, &StereoCalibration::onRightViewMouseMoved);connect(leftView, &ZoomGraphicsView::mouseClicked, this, &StereoCalibration::onLeftViewMouseClicked);connect(rightView, &ZoomGraphicsView::mouseClicked, this, &StereoCalibration::onRightViewMouseClicked);
}void StereoCalibration::openLeftImage()
{QString fileName = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("打开左图像"), "", QString::fromLocal8Bit("图像文件 (*.png *.jpg *.bmp *.tif)"));if (!fileName.isEmpty()) {QImage qimage(fileName);if (!qimage.isNull()) {QPixmap pixmap = QPixmap::fromImage(qimage);leftView->setImage(pixmap);leftImage = QImageToCvMat(qimage);leftPoints.clear();updateStatusBar();statusLabel->setText(QString::fromLocal8Bit("左图像加载成功"));} else {QMessageBox::warning(this, QString::fromLocal8Bit("错误"), QString::fromLocal8Bit("无法加载左图像"));}}
}void StereoCalibration::openRightImage()
{QString fileName = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("打开右图像"), "", QString::fromLocal8Bit("图像文件 (*.png *.jpg *.bmp *.tif)"));if (!fileName.isEmpty()) {QImage qimage(fileName);if (!qimage.isNull()) {QPixmap pixmap = QPixmap::fromImage(qimage);rightView->setImage(pixmap);rightImage = QImageToCvMat(qimage);rightPoints.clear();updateStatusBar();statusLabel->setText(QString::fromLocal8Bit("右图像加载成功"));} else {QMessageBox::warning(this, QString::fromLocal8Bit("错误"), QString::fromLocal8Bit("无法加载右图像"));}}
}void StereoCalibration::calibrate()
{if (!leftView->hasImage() || !rightView->hasImage()) {QMessageBox::warning(this, QString::fromLocal8Bit("错误"), QString::fromLocal8Bit("请先加载左右图像"));return;}leftPoints = leftView->getPoints();rightPoints = rightView->getPoints();if (leftPoints.size() != rightPoints.size() || leftPoints.size() < 8) {QMessageBox::warning(this, QString::fromLocal8Bit("错误"), QString::fromLocal8Bit("需要至少8对对应点才能进行标定"));return;}// 准备标定数据std::vector<std::vector<cv::Point2f>> imagePoints;std::vector<cv::Point2f> leftPointsVec, rightPointsVec;for (const QPointF &point : leftPoints) {leftPointsVec.emplace_back(point.x(), point.y());}for (const QPointF &point : rightPoints) {rightPointsVec.emplace_back(point.x(), point.y());}imagePoints.push_back(leftPointsVec);imagePoints.push_back(rightPointsVec);// 生成对象点 (假设是平面标定板)std::vector<std::vector<cv::Point3f>> objectPoints;std::vector<cv::Point3f> obj;for (int i = 0; i < leftPoints.size(); i++) {obj.emplace_back(i % 4, i / 4, 0.0f); // 简单的棋盘格假设}objectPoints.push_back(obj);// 执行标定cv::Size imageSize(leftImage.cols, leftImage.rows);double rms = cv::stereoCalibrate(objectPoints, std::vector<std::vector<cv::Point2f>>{leftPointsVec}, std::vector<std::vector<cv::Point2f>>{rightPointsVec},cameraMatrix1, distCoeffs1,cameraMatrix2, distCoeffs2,imageSize, R, T, E, F,cv::CALIB_FIX_INTRINSIC,cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 30, 1e-6));isCalibrated = true;QMessageBox::information(this, QString::fromLocal8Bit("标定完成"), QString::fromLocal8Bit("双目标定完成\n重投影误差: %1").arg(rms));
}void StereoCalibration::undoPoint()
{if (!leftPoints.isEmpty() && !rightPoints.isEmpty()) {leftPoints.removeLast();rightPoints.removeLast();leftView->clearPoints();rightView->clearPoints();for (const QPointF &point : leftPoints) {leftView->addPoint(point);}for (const QPointF &point : rightPoints) {rightView->addPoint(point);}updateStatusBar();}
}void StereoCalibration::resetPoints()
{leftPoints.clear();rightPoints.clear();leftView->clearPoints();rightView->clearPoints();updateStatusBar();
}void StereoCalibration::onLeftViewMouseMoved(QPointF pos)
{statusLabel->setText(QString::fromLocal8Bit("左图像坐标: (%1, %2)").arg(pos.x(), 0, 'f', 1).arg(pos.y(), 0, 'f', 1));
}void StereoCalibration::onRightViewMouseMoved(QPointF pos)
{statusLabel->setText(QString::fromLocal8Bit("右图像坐标: (%1, %2)").arg(pos.x(), 0, 'f', 1).arg(pos.y(), 0, 'f', 1));
}void StereoCalibration::onLeftViewMouseClicked(QPointF pos)
{leftPoints.append(pos);leftView->addPoint(pos);updateStatusBar();
}void StereoCalibration::onRightViewMouseClicked(QPointF pos)
{rightPoints.append(pos);rightView->addPoint(pos);updateStatusBar();
}void StereoCalibration::updateStatusBar()
{QString status = QString::fromLocal8Bit("左图像点数: %1, 右图像点数: %2").arg(leftPoints.size()).arg(rightPoints.size());if (leftPoints.size() != rightPoints.size()) {status += QString::fromLocal8Bit(" (点数不匹配)");}statusLabel->setText(status);
}
- 主程序文件 main.cpp
#include "stereocalibration.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);StereoCalibration w;w.show();return a.exec();
}
- 项目文件 (StereoCalibration.pro),若通过CMake编译有用:
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++17# OpenCV配置 - 请根据您的安装路径修改
unix:!macx {INCLUDEPATH += /usr/local/include/opencv4LIBS += -L/usr/local/lib -lopencv_core -lopencv_imgproc -lopencv_calib3d -lopencv_imgcodecs
}win32 {INCLUDEPATH += C:/opencv/build/includeLIBS += -LC:/opencv/build/x64/vc15/lib \-lopencv_core451 \-lopencv_imgproc451 \-lopencv_calib3d451 \-lopencv_imgcodecs451
}macx {INCLUDEPATH += /usr/local/include/opencv4LIBS += -L/usr/local/lib -lopencv_core -lopencv_imgproc -lopencv_calib3d -lopencv_imgcodecs
}SOURCES += \main.cpp \stereocalibration.cppHEADERS += \stereocalibration.h# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target