You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt6中如何直接从QCamera获取帧?相机就绪报错求助

问题解决与逐帧接收实现方案

一、修复「Camera is not ready」错误

你的代码出现该错误的核心原因是相机启动为异步操作,调用camera()->start()后相机不会立刻进入就绪状态,此时调用capture()必然报错。此外代码还存在对象重复创建、信号重复连接的问题,以下是修正后的关键逻辑:

1. 优化相机启动流程,等待就绪状态

先清理旧资源,再创建新设备,并监听相机状态确保就绪后再允许捕获:

// 类成员变量需提前声明:QCamera* m_camera = nullptr; QImageCapture* m_imageCapture = nullptr;

QObject::connect(ui->start, &QPushButton::clicked, [this]() {
    // 清理旧资源,避免内存泄漏
    if (m_camera) {
        if (m_camera->isActive()) m_camera->stop();
        delete m_camera;
        m_camera = nullptr;
    }
    if (m_imageCapture) {
        delete m_imageCapture;
        m_imageCapture = nullptr;
    }

    auto item = ui->list->currentItem();
    auto cam = item->data(Qt::UserRole).value<QCameraDevice>();
    m_camera = new QCamera(cam);
    m_imageCapture = new QImageCapture;

    capturesession.setCamera(m_camera);
    capturesession.setImageCapture(m_imageCapture);

    // 监听相机状态,就绪后给出提示
    connect(m_camera, &QCamera::cameraStatusChanged, this, [this](QCamera::Status status) {
        if (status == QCamera::ActiveStatus) {
            ui->text->appendPlainText("相机已就绪,可执行捕获");
        }
    });

    // 直接连接图像捕获信号,避免重复连接
    connect(m_imageCapture, &QImageCapture::imageCaptured, this, [this](int id, const QImage &preview) {
        ui->text->appendPlainText(QString("Image Captured:%1").arg(id));
        ui->label->setPixmap(QPixmap::fromImage(preview));
    });

    connect(m_imageCapture, &QImageCapture::errorOccurred, this, [this](int id, QImageCapture::Error error, const QString &errorString) {
        ui->text->appendPlainText(QString("[%1]error:%2").arg(id).arg(errorString));
    });

    m_camera->start();
});

2. 修改捕获按钮逻辑,增加就绪判断

QObject::connect(ui->capture, &QPushButton::clicked, [this]() {
    if (!m_camera || m_camera->status() != QCamera::ActiveStatus) {
        ui->text->appendPlainText("相机未就绪,请等待");
        return;
    }

    // 打印相机状态
    ui->text->appendPlainText(
        QString("camera[%1]:isActive=%2, isAvailable=%3, isFlashReady=%4.\n")
            .arg(m_camera->cameraDevice().id())
            .arg(m_camera->isActive())
            .arg(m_camera->isAvailable())
            .arg(m_camera->isFlashReady())
    );

    // 执行捕获
    m_imageCapture->capture();
});

二、直接从相机接收逐帧并显示(无需QVideoWidget)

可以通过QVideoSink实时获取相机每一帧,直接处理后显示在QLabel或QGraphicsView上,步骤如下:

1. 添加成员变量

在类中声明:QVideoSink* m_videoSink = nullptr;

2. 修改相机启动逻辑,设置QVideoSink

在start按钮的槽函数中,创建QVideoSink并绑定到捕获会话,监听帧变化信号:

// 在start按钮槽函数创建m_camera和m_imageCapture后添加:
m_videoSink = new QVideoSink(this);
capturesession.setVideoSink(m_videoSink);

// 监听每帧数据变化
connect(m_videoSink, &QVideoSink::videoFrameChanged, this, [this](const QVideoFrame &frame) {
    // 将QVideoFrame转换为QImage
    QVideoFrame cloneFrame = frame;
    if (!cloneFrame.map(QVideoFrame::ReadOnly)) return;
    
    QImage image(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(), 
                 QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat()));
    image = image.copy(); // 解除映射前复制图像,避免内存问题
    cloneFrame.unmap();

    // 显示到QLabel(保持比例适配控件大小)
    ui->label->setPixmap(QPixmap::fromImage(image).scaled(ui->label->size(), Qt::KeepAspectRatio));
});

3. 完善停止逻辑

QObject::connect(ui->stop, &QPushButton::clicked, [this]() {
    if (m_camera && m_camera->isActive()) {
        m_camera->stop();
    }
    // 如需完全释放资源,可在此处delete m_camera、m_imageCapture、m_videoSink
});

这种方式可以实时获取相机原始帧,无需依赖QVideoWidget即可完成帧处理与显示。

内容的提问来源于stack exchange,提问作者Ershat Dilmurat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 20:00:03