【QT常用技术讲解】multimedia实现指定分辨率打开摄像头

前言

摄像头是很常见的外设,本文分享的是通用QT自带的多媒体模块来操作摄像头,本应用支持选择摄像头,选择摄像头的分辨率,打开/关闭摄像头。

效果图

功能讲解

摄像头下拉框

内容包括3大要素:摄像头序号、摄像头名称、摄像头的VIDPID。相关的代码如下:

复制代码
void MainWindow::getcamerainfo() {
    qDebug() << __FUNCTION__;
    ui->comboBox->clear();
    cameralist.clear();

    // 断开之前的连接,避免重复连接
    disconnect(ui->comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
                  this, &MainWindow::on_camera_selection_changed);

    cameras = QCameraInfo::availableCameras();
    if (cameras.isEmpty()) {
        qWarning() << "No cameras found.";
        QMessageBox::information(this, "提醒", "未检测到摄像头设备");
        return;
    }

    int num = 0;
    for (const QCameraInfo &cameraInfo : cameras) {
        qDebug() << "Camera deviceName:" << cameraInfo.deviceName();
        QString vid = getvidpid("vid_", cameraInfo.deviceName());
        QString pid = getvidpid("pid_", cameraInfo.deviceName());
        qDebug() << "Camera name:" << cameraInfo.description() << " vid:" << vid << " pid:" << pid;

        QString camarastr = QString::number(num) + ". " + cameraInfo.description() + "(" + vid + ":" + pid + ")";
        cameralist.append(camarastr);
        ui->comboBox->addItem(camarastr);
        num++;
    }

    // 如果有摄像头,默认选择第一个并获取其分辨率
    if (ui->comboBox->count() > 0) {
        // 手动调用分辨率获取函数,而不是通过信号触发
        QTimer::singleShot(100, [this]() {
            int index = ui->comboBox->currentIndex();
            if (index >= 0 && index < cameras.size()) {
                QCameraInfo cameraInfo = cameras.at(index);
                updateResolutionComboBox(cameraInfo);
            }
        });
    }
}

在初始化函数中,调用以上函数

分辨率下拉框

复制代码
QList<QSize> MainWindow::getSupportedResolutions(const QCameraInfo &cameraInfo) {
    QList<QSize> resolutions;

    // 创建临时摄像头对象获取分辨率
    QCamera tempCamera(cameraInfo);

    // 使用QEventLoop等待摄像头加载完成
    QEventLoop loop;
    QTimer timer;
    timer.setSingleShot(true);
    QObject::connect(&tempCamera, &QCamera::stateChanged, [&](QCamera::State state) {
        if (state == QCamera::LoadedState) {
            loop.quit();
        }
    });
    QObject::connect(&timer, &QTimer::timeout, [&]() {
        if (loop.isRunning()) {
            qWarning() << "Camera loading timed out";
            loop.quit();
        }
    });
    tempCamera.load();
    timer.start(2000); // 2秒超时
    loop.exec();

    if (tempCamera.state() == QCamera::LoadedState) {
        QList<QCameraViewfinderSettings> supportedSettings = tempCamera.supportedViewfinderSettings();
        if (supportedSettings.isEmpty()) {
            qWarning() << "No supported viewfinder settings for camera:" << cameraInfo.description();

            // 备用方法:尝试通过图像捕获获取分辨率
            QCameraImageCapture imageCapture(&tempCamera);
            QList<QSize> imageResolutions = imageCapture.supportedResolutions();

            if (!imageResolutions.isEmpty()) {
                resolutions = imageResolutions;
            } else {
                // 提供一些常见分辨率作为备选
                resolutions << QSize(640, 480)
                            << QSize(800, 600)
                            << QSize(1024, 768)
                            << QSize(1280, 720)
                            << QSize(1920, 1080);
            }
        } else {
            // 提取分辨率并手动去重(不使用QSet)
            for (const QCameraViewfinderSettings &setting : supportedSettings) {
                if (setting.resolution().isValid() &&
                    setting.resolution().width() > 0 &&
                    setting.resolution().height() > 0) {
                    // 检查是否已存在相同分辨率
                    bool alreadyExists = false;
                    for (const QSize &existing : resolutions) {
                        if (existing == setting.resolution()) {
                            alreadyExists = true;
                            break;
                        }
                    }

                    if (!alreadyExists) {
                        resolutions.append(setting.resolution());
                    }
                }
            }
        }

        // 按面积排序(从小到大)
        std::sort(resolutions.begin(), resolutions.end(), [](const QSize &a, const QSize &b) {
            return a.width() * a.height() < b.width() * b.height();
        });
    } else {
        qWarning() << "Failed to load camera for resolution detection:" << cameraInfo.description();
        // 提供一些默认分辨率
        resolutions << QSize(640, 480)
                    << QSize(800, 600)
                    << QSize(1024, 768)
                    << QSize(1280, 720)
                    << QSize(1920, 1080);
    }
    tempCamera.unload();
    return resolutions;
}

void MainWindow::updateResolutionComboBox(const QCameraInfo &cameraInfo) {
    qDebug() << "Updating resolution combo box for camera:" << cameraInfo.description();

    // 在状态栏显示加载信息
    statusBar()->showMessage("正在获取摄像头支持的分辨率...");

    // 使用单次定时器异步获取分辨率,避免界面卡顿
    QTimer::singleShot(100, [this, cameraInfo]() {
        QList<QSize> resolutions = getSupportedResolutions(cameraInfo);

        ui->comboBox_resolution->clear();

        if (resolutions.isEmpty()) {
            ui->comboBox_resolution->addItem("默认分辨率");
            statusBar()->showMessage("无法获取分辨率列表,将使用默认分辨率", 3000);
        } else {
            for (const QSize &resolution : resolutions) {
                QString resText = QString("%1 x %2").arg(resolution.width()).arg(resolution.height());
                ui->comboBox_resolution->addItem(resText, resolution);
            }
            statusBar()->showMessage(QString("找到 %1 个支持的分辨率").arg(resolutions.size()), 3000);
        }
    });
}

void MainWindow::on_camera_selection_changed(int index) {
    if (index < 0 || index >= cameras.size()) return;

    ui->comboBox_resolution->clear();

    // 获取选中的摄像头
    QCameraInfo cameraInfo = cameras.at(index);
    updateResolutionComboBox(cameraInfo);
}

在初始化函数中引用

复制代码
// 连接摄像头选择变化信号
    connect(ui->comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
            this, &MainWindow::on_camera_selection_changed);

打开摄像头

复制代码
void MainWindow::on_btn_open_clicked() {
    if (camera != nullptr) return;
    if (cameralist.size() < 1) {
        QMessageBox::information(this, "提醒", "摄像头不存在");
        return;
    }

    int cameraNo = ui->comboBox->currentIndex();
    QCameraInfo cameraInfo = cameras.at(cameraNo);

    qDebug() << "Opening camera" << cameraNo << cameraInfo.description();

    // 创建摄像头对象
    camera = new QCamera(cameraInfo, this);
    if (!camera->isAvailable()) {
        qWarning() << "Camera is not available.";
        QMessageBox::warning(this, "错误", "摄像头不可用");
        delete camera;
        camera = nullptr;
        return;
    }

    // 设置分辨率(如果用户选择了特定分辨率)
    if (ui->comboBox_resolution->currentIndex() > 0) {
        QSize selectedResolution = ui->comboBox_resolution->currentData().toSize();
        if (selectedResolution.isValid()) {
            QCameraViewfinderSettings settings;
            settings.setResolution(selectedResolution);
            camera->setViewfinderSettings(settings);
            qDebug() << "Set resolution to:" << selectedResolution;
        }
    }

    // 创建视图查找器
    viewfinder = new QCameraViewfinder();
    //让摄像头画面自适应填充整个widget_video区域
    viewfinder->setAspectRatioMode(Qt::IgnoreAspectRatio);  // 忽略原始宽高比
    viewfinder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    camera->setViewfinder(viewfinder);

    // 清除之前的布局
    if (ui->widget_video->layout()) {
        delete ui->widget_video->layout();
    }

    // 创建新布局并添加视图查找器
    layout = new QVBoxLayout;
    layout->addWidget(viewfinder);
    ui->widget_video->setLayout(layout);

    // 开始预览
    camera->start();
    status = true;
}

以上通过视图查找器预览摄像头的内容,增加了一下两行代码,让摄像头画面自适应填充整个显示区域:

复制代码
viewfinder->setAspectRatioMode(Qt::IgnoreAspectRatio);  // 忽略原始宽高比
    viewfinder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

去掉以上代码,则按照原来的分辨率大小进行显示。

关闭摄像头

复制代码
void MainWindow::on_btn_close_clicked() {
    if (camera == nullptr) return;
    camera->stop();

    // 清除布局
    if (ui->widget_video->layout()) {
        QLayoutItem *item;
        while ((item = ui->widget_video->layout()->takeAt(0)) != nullptr) {
            delete item->widget();
            delete item;
        }
        delete ui->widget_video->layout();
    }

    delete camera;
    camera = nullptr;
    status = false;

}

篇尾

QT自带的媒体模块,适合处理一些常用的功能,后续也分享使用opencv方式实现相同的功能。

相关推荐
番石榴AI4 小时前
基于机器学习优化的主图选择方法(酒店,景点,餐厅等APP上的主图展示推荐)
图像处理·人工智能·python·机器学习
十五年专注C++开发8 小时前
Qt-Nice-Frameless-Window: 一个跨平台无边框窗口(Frameless Window)解决方案
开发语言·c++·qt
多喝开水少熬夜9 小时前
损失函数系列:focal-Dice-vgg
图像处理·python·算法·大模型·llm
江公望9 小时前
装了新的QtCreator17,没有用Qt5.12自带的QtCreator4,导致QtCreator17无法找到Qt5.12帮助文档
qt·qml
ctgu9011 小时前
PyQt5(八):ui设置为可以手动随意拉伸功能
开发语言·qt·ui
进击的大海贼13 小时前
QT/C++ 消息定时管理器
开发语言·c++·qt
Lj2_jOker15 小时前
QT 给Qimage数据赋值,显示异常,像素对齐的坑
开发语言·前端·qt
PixelMind16 小时前
【LUT技术专题】SVDLUT: 基于SVD优化的3DLUT
图像处理·深度学习·lut
rengang6618 小时前
13-卷积神经网络(CNN):探讨CNN在图像处理中的应用和优势
图像处理·人工智能·深度学习·神经网络·cnn
孤独的追光者18 小时前
使用Qt Designer开发上位机
开发语言·python·qt