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

Qt6.6中如何将指定窗口捕获为QPixmap用于OpenCV处理?

Qt6.6 指定窗口实时捕获并转换为OpenCV格式解决方案

问题背景

已实现Qt6.6全屏实时捕获并转OpenCV格式,但使用QWindowCapture捕获指定窗口时,无法直接获取QPixmap,卡在帧转换步骤。当前环境为Ubuntu,后续需适配Windows平台。

核心解决方案

QWindowCapture采用异步信号机制返回捕获帧,需通过**frameCaptured(QVideoFrame)信号**接收帧数据,再将QVideoFrame转换为QPixmap,最终转成OpenCV的cv::Mat。

修改后的完整代码

main.cpp

#include <QApplication>
#include <QScreen>
#include <QPixmap>
#include <QImage>
#include <opencv2/opencv.hpp>
#include <QAbstractListModel>
#include <QCapturableWindow>
#include <QWindowCapture>
#include <QVideoFrame>
#include <QTimer>
#include <qDebug>

QT_USE_NAMESPACE

class WindowListModel : public QAbstractListModel
{
    Q_OBJECT

public:
    explicit WindowListModel(QObject *parent = nullptr) : QAbstractListModel(parent) {}

    int rowCount(const QModelIndex &parent = QModelIndex()) const override {
        Q_UNUSED(parent);
        return windowList.size();
    }

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
        if (!index.isValid() || index.row() >= windowList.size())
            return QVariant();

        if (role == Qt::DisplayRole) {
            const QCapturableWindow &win = windowList[index.row()];
            return win.title() + " (" + QString::number(win.winId()) + ")";
        }
        return QVariant();
    }

    QCapturableWindow window(const QModelIndex &index) const {
        if (index.isValid() && index.row() < windowList.size())
            return windowList[index.row()];
        return QCapturableWindow();
    }

public Q_SLOTS:
    void populate() {
        beginResetModel();
        windowList = QWindowCapture::capturableWindows();
        endResetModel();
    }

private:
    QList<QCapturableWindow> windowList;
};

// QVideoFrame转QPixmap工具函数
QPixmap videoFrameToPixmap(const QVideoFrame &frame) {
    QVideoFrame cloneFrame = frame;
    if (!cloneFrame.map(QAbstractVideoBuffer::ReadOnly))
        return QPixmap();

    QImage image(cloneFrame.width(), cloneFrame.height(), QImage::Format_RGBA8888);
    memcpy(image.bits(), cloneFrame.bits(), cloneFrame.mappedBytes());
    cloneFrame.unmap();

    return QPixmap::fromImage(image);
}

// QPixmap转cv::Mat工具函数
cv::Mat pixmapToMat(const QPixmap &pixmap) {
    QImage image = pixmap.toImage();
    cv::Mat mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());
    // 转换为OpenCV默认的BGR格式(可选,根据后续处理需求调整)
    cv::cvtColor(mat, mat, cv::COLOR_RGBA2BGR);
    return mat;
}

class WindowCaptureHandler : public QObject
{
    Q_OBJECT
public:
    WindowCaptureHandler(QObject *parent = nullptr) : QObject(parent) {}

    void startCapture(const QCapturableWindow &targetWindow) {
        windowCapture.setWindow(targetWindow);
        connect(&windowCapture, &QWindowCapture::frameCaptured, this, &WindowCaptureHandler::onFrameCaptured);
        windowCapture.start();

        // 定时刷新OpenCV窗口,避免阻塞Qt事件循环
        QTimer *timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, [this](){
            if (!currentMat.empty()) {
                cv::imshow("Target Window Live View", currentMat);
                // ESC按键退出捕获
                if (cv::waitKey(1) == 27) {
                    windowCapture.stop();
                    cv::destroyAllWindows();
                    qApp->quit();
                }
            }
        });
        timer->start(30); // 30ms刷新一次,对应约30fps
    }

private slots:
    void onFrameCaptured(const QVideoFrame &frame) {
        QPixmap pixmap = videoFrameToPixmap(frame);
        if (!pixmap.isNull()) {
            currentMat = pixmapToMat(pixmap);
        }
    }

private:
    QWindowCapture windowCapture;
    cv::Mat currentMat;
};

void captureAndShowWindow(const QString& windowName) {
    WindowListModel windowListModel;
    windowListModel.populate();

    QCapturableWindow targetWindow;
    bool windowFound = false;

    for (int i = 0; i < windowListModel.rowCount(); i++) {
        QModelIndex index = windowListModel.index(i);
        QString currentWindowDesc = windowListModel.data(index, Qt::DisplayRole).toString();
        if (currentWindowDesc.contains(windowName)) {
            targetWindow = windowListModel.window(index);
            windowFound = true;
            break;
        }
    }

    if (!windowFound) {
        qDebug() << "Window not found!";
        return;
    }

    WindowCaptureHandler handler;
    handler.startCapture(targetWindow);
}

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    // 打印可用窗口列表
    WindowListModel model;
    model.populate();
    for (int i = 0; i < model.rowCount(); i++) {
        QModelIndex index = model.index(i);
        QString windowDesc = model.data(index, Qt::DisplayRole).toString();
        std::cout << i + 1 << ". " << windowDesc.toStdString() << std::endl;
    }

    // 启动指定窗口捕获(示例:捕获标题含"Settings"的窗口)
    captureAndShowWindow("Settings");

    return app.exec();
}

#include "main.moc"

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

project(ScreenshotApp)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

include_directories(include)

list(APPEND CMAKE_PREFIX_PATH "/home/user/Qt/6.6.0/gcc_64/lib/cmake")

# 依赖Qt组件:Widgets用于基础环境,Multimedia用于窗口捕获
find_package(Qt6 COMPONENTS Widgets Multimedia REQUIRED)
find_package(OpenCV 4.7.0 REQUIRED)

add_executable(main main.cpp)
target_link_libraries(main Qt6::Widgets Qt6::Multimedia ${OpenCV_LIBS})

关键说明

  1. 异步帧捕获:QWindowCapture无同步获取帧的方法,必须通过frameCaptured信号接收捕获结果
  2. 格式转换流程:严格遵循QVideoFrame→QImage→QPixmap→cv::Mat的转换顺序,注意图像通道格式适配
  3. 事件循环兼容:用QtQTimer定时刷新OpenCV窗口,避免死循环阻塞Qt事件循环导致程序无响应
  4. 跨平台适配:
    • Ubuntu:Wayland桌面需添加QT_QPA_PLATFORM=xcb环境变量启动程序,确保窗口捕获权限
    • Windows:需在系统设置中开启应用的屏幕捕获权限,否则无法捕获第三方窗口

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 17:55:21