【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方式实现相同的功能。

相关推荐
无敌最俊朗@4 小时前
Qt 按钮点击事件全链路解析:从系统驱动到槽函数
开发语言·qt·计算机外设
格林威4 小时前
工业视觉检测里的 “柔性” 是什么?
图像处理·人工智能·深度学习·yolo·计算机视觉·视觉检测
aitav05 小时前
⚡ arm 32位嵌入式 Linux 系统移植 QT 程序
linux·arm开发·qt
菜鸡爱玩5 小时前
Qt3D--箭头示例
c++·qt
掘根5 小时前
【Qt】多线程
java·开发语言·qt
兰亭妙微6 小时前
兰亭妙微QT软件开发与UI设计协同:如何避免设计与实现脱节?
开发语言·qt·ui
格林威7 小时前
不同光谱的工业相机有哪些?能做什么?
图像处理·人工智能·深度学习·数码相机·计算机视觉·视觉检测
CiLerLinux7 小时前
第三十八章 ESP32S3 SPIFFS 实验
图像处理·人工智能·单片机·嵌入式硬件
ajassi20007 小时前
开源 C++ QT QML 开发(四)复杂控件--Listview
c++·qt·开源