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})
关键说明
- 异步帧捕获:
QWindowCapture无同步获取帧的方法,必须通过frameCaptured信号接收捕获结果 - 格式转换流程:严格遵循
QVideoFrame→QImage→QPixmap→cv::Mat的转换顺序,注意图像通道格式适配 - 事件循环兼容:用Qt
QTimer定时刷新OpenCV窗口,避免死循环阻塞Qt事件循环导致程序无响应 - 跨平台适配:
- Ubuntu:Wayland桌面需添加
QT_QPA_PLATFORM=xcb环境变量启动程序,确保窗口捕获权限 - Windows:需在系统设置中开启应用的屏幕捕获权限,否则无法捕获第三方窗口
- Ubuntu:Wayland桌面需添加
内容的提问来源于stack exchange,提问作者Ja_cpp
相关产品推荐
相关产品推荐

