Qt Demo之 deepseek 帮我写的关于双目标定的小界面

发布于:2025-09-02 ⋅ 阅读:(12) ⋅ 点赞:(0)

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_OBJECT

public:
    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 += widgets

CONFIG += c++17

# OpenCV配置 - 请根据您的安装路径修改
unix:!macx {
    INCLUDEPATH += /usr/local/include/opencv4
    LIBS += -L/usr/local/lib -lopencv_core -lopencv_imgproc -lopencv_calib3d -lopencv_imgcodecs
}

win32 {
    INCLUDEPATH += C:/opencv/build/include
    LIBS += -LC:/opencv/build/x64/vc15/lib \
            -lopencv_core451 \
            -lopencv_imgproc451 \
            -lopencv_calib3d451 \
            -lopencv_imgcodecs451
}

macx {
    INCLUDEPATH += /usr/local/include/opencv4
    LIBS += -L/usr/local/lib -lopencv_core -lopencv_imgproc -lopencv_calib3d -lopencv_imgcodecs
}

SOURCES += \
    main.cpp \
    stereocalibration.cpp

HEADERS += \
    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

网站公告

今日签到

点亮在社区的每一天
去签到