1 前言

  本章将是Qt 媒体应用的最后一章节了,讲完本章,Qt媒体库的学习将到此为止!本章将学习Qt如何使用摄像头进行拍照和摄像等相关操作,下面先介绍以下有关Qt 摄像头的概述。

2 效果图

qt android opencv 摄像头 qt获取摄像头_开发语言

3 摄像头控制概述

  Qt多媒体模块为摄像头控制提供了几个类,可以用于获取摄像头设备信息,通过摄像头进行拍照和录像。

1、摄像头设备信息类QCameraInfo。

QCameraInfo 用于获取系统的摄像头设备信息,有两个静态函数获取摄像头设备:

  • QList<QCameraInfo> availableCameras(), 返回 QCameraInfo 类的列表,表示系统可用的
    摄像头设备列表;
  • QCameraInfo defaultCamera(), 返回系统缺省的摄像头设备信息。

QCameraInfo 有几个函数表示摄像头信息:

  • QString description(), 返回摄像头设备描述;
  • QString deviceName(), 返回摄像头设备名称;
  • QCamera::Position position(), 返回摄像头的位置,如手机设备上一般有两个摄像头,前
    置摄像头位置类型为QCamera::FrontFace, 后置摄像头位置类型为QCamera::BackFace,
    未指定位置的是QCamera::UnspecifiedPosition

2、 摄像头控制类QCamera

  QCamera 是用于控制摄像头的类,创建 QCamera 对象时需传递一个 QCameraInfo 对象作为参数,QCamera 主要的功能函数包括以下几个。

  • setViewfinder() ,为摄像头指定一个QVideoWidgetQGraphicsVideoItem对象作为取景器,
    用于摄像头图像预览。
  • QCameraExposure *exposure(), 返回用于曝光控制的对象。
  • QCameraFocus *focus(), 返回用于聚焦控制的对象。
  • setCaptureMode(QCamera::CaptureModes mode), 用于设置摄像头处于不同的工作模式,
    QCamera::CaptureModes 枚举类型的取值有:
    QCamera::CaptureViewfinder,取景器模式;
    QCamera::CaptureStillImage,抓取静态图片模式; .
    QCamera::CaptureVideo,视频录制模式。
  • bool isCaptureModeSupported(CaptureModes mode) ,判断摄像头是否支持某种抓取模式。

3、 静态图片抓取类QCameralmageCapture

  QCameralmageCapture 用于控制摄像头进行静态图片的抓取。

4、视频和音频录制类QMediaRecorder。

  QMediaRecorder 通过摄像头和音频输入设备进行录像。

注意: 使用Qt多媒体模块的摄像头相关类无法在Windows平台上进行视频录制,只能进行静态图片抓取但是在Linux平台上可以实现静态图片抓取和视频录制。

  Qt多媒体模块的功能实现是依赖于平台的。在Windows平台上,Qt 多媒体模块依赖于两个
插件,一个是使用Microsoft DirectShow API的插件,DirectShow API在Windows 98引入,在Windows XP以后就逐渐过时了;另一个是Windows Media Foundation ( WMF)架构的插件,WMF插件在Windows Vista引入,用于替代DirectShow API。

  Qt中的WMF插件目前无法提供摄像头支持,对摄像头的有限支持是由DirectShow插件提供
的,目前只能显示取景器和抓取静态图片,其他大部分功能不支持。所以,目前在Windows平台上,Qt的摄像头控制不支持视频录制功能,也不支持底层的视频功能,如使用QVideoProbe监测视频帧。

  对于这些限制的原文介绍可以参考Qt官方文档上 Qt Multimedia on Windows 的解释,如图:

qt android opencv 摄像头 qt获取摄像头_qt_02

  所以,本节的实例程序,在window系统中进行拍照,不进行录像处理,下图所示是程序运行时界面。.

qt android opencv 摄像头 qt获取摄像头_Qt_03

  工具栏上几个按钮的功能一目了然,摄像头参数 里显示摄像头的参数,以及录制视频的一些设置。窗口工作区左侧的 摄像头预览框 里显示摄像头的实时图像预览,右侧 抓取图片
框里显示抓取的静态图片。

4 实例设计与初始化

  实例中的主窗口是基于 QWidget 的类 Camera ,界面由 UI设计器 设计。
在窗口左侧 摄像头预览 框里放置一个 QWidget 组件,并提升为 QCameraViewfinder ,这是从 QVideoWidget 继承的类,专门用于摄像头预览显示。在窗口右侧 抓取图片 框里放置一个QLabel组件,用于显示抓取的图片。

  主窗口类 Camera 的定义如下:

class Camera : public QWidget
{
    Q_OBJECT

public:
    Camera(QWidget *parent = nullptr);
    ~Camera();

private slots:
    //QCamera的槽函数
    void onCameraStateChanged(QCamera::State state);
    void onCameraCaptureModeChanged(QCamera::CaptureModes mode);

    //QCameraImageCapture的槽函数
    void onImageReadyForCapture(bool ready);
    void onImageCaptured(int id, const QImage &preview);
    void onImageSaved(int id, const QString &fileName);

    // QMediaRecorder 的槽函数
    void onVideoStateChanged(QMediaRecorder::State state);
    void onVideoDurationChanged(qint64 duration);

    //ui 设计器自动关联槽函数
    void on_actStartCamera_clicked();
    void on_actStopCamera_clicked();
    void on_actCapture_clicked();
    void on_actVideoRecord_clicked();
    void on_actVideoStop_clicked();

private:

    void  iniCamera();//初始化
    void  iniImageCapture(); //初始化静态抓图
    void  iniVideoRecorder(); //初始化视频录制
private:
    Ui::Camera *ui;

    QCamera * curCamera=Q_NULLPTR; //拍照
    QCameraImageCapture * imageCapture = Q_NULLPTR; //抓图
    QMediaRecorder *mediaRecorder = Q_NULLPTR; //录像
};

  这里定义了QCameraQCameralmageCaptureQmediaRecorder 3个类型的私有变量。定义了3个私有函数,分别用于3个对象的创建和初始化。

  定义了多个槽函数,分别与3个对象的相应信号关联。

  下面是 Camera 的构造函数的代码:

Camera::Camera(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Camera)
{
    ui->setupUi(this);
    QList<QCameraInfo> cameras = QCameraInfo::availableCameras() ;
    if (cameras.size()>0){

        ui->actStartCamera->setEnabled(true);
        ui->actStopCamera->setDisabled(true);
        iniCamera(); //初始化摄像头
        iniImageCapture() ;//初始化静态抓图
        iniVideoRecorder() ;//初始化视频录制
        curCamera->start() ;
    }
    else
    {
        ui->actStartCamera->setDisabled(true);
        ui->actStopCamera->setDisabled(true);
        ui->actCapture->setDisabled(true);
        ui->actVideoRecord->setDisabled(true);
        ui->actVideoStop->setDisabled(true);
    }
}

  构造函数首先使用QCameraInfo的静态函数 availableCameras()获取摄像头列表cameras,如果存在摄像头设备,就依次调用3个初始化函数进行初始化,然后打开摄像头。否则失能所有的按钮触发。

5 QCamera对象创建与控制

  iniCamera() 函数进行QCamera 对象的创建与初始化,在 Camera 的构造函数里调用,下面是 iniCamera() 函数的代码:

void Camera::iniCamera()
{
    // 创建QCamera对象
    QCameraInfo curCameraInfo=QCameraInfo::defaultCamera(); //获取缺省 摄像头
    ui->comboCamera->addItem(curCameraInfo.description());
    ui->comboCamera->setCurrentIndex (0) ;

    curCamera=new QCamera (curCameraInfo, this); //创建摄像头对象

    curCamera->setViewfinder(ui->viewFinder); //设置 预览框
    curCamera->setCaptureMode(QCamera::CaptureViewfinder) ;
    //判断摄像头是否支持抓图、录制视频
    ui->checkStillImage->setChecked(curCamera->isCaptureModeSupported (QCamera::CaptureStillImage)) ; //抓图
    ui ->checkVideo->setChecked (curCamera->isCaptureModeSupported (QCamera::CaptureVideo)) ;//视频录制

    connect (curCamera, SIGNAL (stateChanged (QCamera: :State)),this, SLOT (onCameraStateChanged(QCamera::State)));
    //Windows平台上不支持captureModeChanged()信号
    connect (curCamera, SIGNAL(captureModeChanged(QCamera::CaptureModes)), this, SLOT (onCameraCaptureModeChanged(QCamera::CaptureModes))) ;
}

  这里用 QCameralnfo::defaultCamera() 获取缺省的摄像头设备,然后用于创建 QCamera 对象实例curCamera

  QCamera::setViewfinder() 函数用于设置取景框预览组件。

  QCamera::setCaptureMode() 函数设置摄像头的抓取模式。

  然后查询 curCamera 是否支持抓图、支持录像,并更新界面上的复选框的状态。

  为curCamera的两个信号分别设置了关联的槽函数。stateChanged()在摄像头状态变化时发射,**captureModeChanged()则在摄像头抓取模式变化时发射,不过在Windows 平台上不会发射captureModeChanged()**信号。这两个槽函数的代码如下:

void Camera::onCameraStateChanged(QCamera::State state)
{
    switch (state){
    case QCamera:: UnloadedState:
        ui->LabCameraState->setText ("摄像头state: UnloadedState") ;
        break;
    case QCamera:: LoadedState:
        ui->LabCameraState->setText ("摄像头state: LoadedState") ;
        break;
    case QCamera::ActiveState:
        ui->LabCameraState->setText ("摄像头state: ActiveState") ;
    }
    ui ->actStartCamera->setEnabled (state!=QCamera::ActiveState);
    ui ->actStopCamera->setEnabled (state==QCamera::ActiveState);
}
void Camera::onCameraCaptureModeChanged(QCamera::CaptureModes mode)
{
    if (mode==QCamera::CaptureStillImage)
        ui->LabCameraMode->setText ("抓取模式: StillImage") ;
    else if (mode==QCamera::CaptureVideo)
        ui->LabCameraMode->setText ("抓取模式: Video") ;
    else
        ui->LabCameraMode->setText ("抓取模式: Viewfinder") ;
}

  窗口工具栏上的 开启摄像头关闭摄像头 两个按钮控制摄像头的开启和关闭,其关联按钮点击的槽函数代码如下:

void Camera::on_actStartCamera_clicked()
{
    //开启摄像头
    curCamera->start();
}

void Camera::on_actStopCamera_clicked()
{
    //关闭摄像头
    curCamera->stop();
}

  开启和关闭摄像头时,QCamera对象会发射stateChanged()信号。开启摄像头之后,摄像头状态为QCamera::ActiveState,关闭之后状态会变成QCamera::LoadedState

6 QCameralmageCapture抓取静态图片

  QCameralmageCapture 类对象 imageCapture 用于通过摄像头抓取静态图片,iniImageCapture()函数用于创建 imageCapture 并进行初始化设置,Camera 构造函数调用此函数。iniImageCapture() 函数的代码如下:

void Camera::iniImageCapture()
{
    //创建QCameraImageCapture对象
    imageCapture = new QCameraImageCapture(curCamera, this) ;
    imageCapture->setBufferFormat(QVideoFrame::Format_Jpeg); //缓冲区格式
    imageCapture->setCaptureDestination (QCameraImageCapture::CaptureToFile); //保存目标
    connect (imageCapture, SIGNAL (readyForCaptureChanged (bool)),this, SLOT (onImageReadyForCapture (bool) ) ) ;
    connect (imageCapture, SIGNAL (imageCaptured(int, const QImage &)) ,  this, SLOT (onImageCaptured(int, const QImage &))) ;
    connect (imageCapture, SIGNAL (imageSaved(int, const QString &)) ,   this, SLOT (onImageSaved(int, const QString &))) ;
}

  创建 QCameralmageCapture 对象 imageCapture 时传递QCamera对象curCamera作为输入参数,建立与摄像头设备的关联。
  setBufferFormat(QVideoFrame::Format_Jpeg) 设置缓冲区里图片为JPG格式
  setCaptureDestination(QCameraImageCapture::CaptureToFile) 设置抓图存储目标为文件,抓取的图片文件会自动保存到用户目录的图片文件夹里。

  为 imageCapture 的3个信号关联了自定义槽函数,这3个槽函数的代码如下:

void Camera::onImageReadyForCapture(bool ready)
{
    //可以抓图 了
    ui->actCapture->setEnabled(ready) ;
}

void Camera::onImageCaptured(int id, const QImage &preview)
{
    //抓取图片后显示
    Q_UNUSED(id);

    QImage scaledImage = preview.scaled (ui->LabCapturedImage->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
    ui->LabCapturedImage->setPixmap(QPixmap::fromImage(scaledImage));

}

void Camera::onImageSaved(int id, const QString &fileName)
{
    //文件保存后显示保存的文件名
    Q_UNUSED(id) ;
    qInfo()<<"onImageSaved ="<<fileName;
    ui->editOutputFile->setText(fileName);
}

  单击窗口工具栏上的 抓图 按钮可实现抓取静态图片功能,其代码如下:

void Camera::on_actCapture_clicked()
{
    //抓图按钮
    if (curCamera->captureMode () !=QCamera::CaptureStillImage){
        curCamera->setCaptureMode (QCamera::CaptureStillImage) ;
    }

    imageCapture->capture() ;
}

  这里首先将curCamera的抓取模式设置为 QCamera::CaptureStillmage, 然后使用QCameralmageCapture::capture( ) 函数抓图。

7 QMediaRecorder视频录制

  QMediaRecorder 类对象 mediaRecorder 用于通过摄像头进行视频录制, iniVideoRecorder()函数创建mediaRecorder并进行初始化设置,Camera 构造函数调用此函数。iniVideoRecorder()函数代码如下:

void Camera::iniVideoRecorder()
{
    //创建QMediaRecorder对象
    mediaRecorder = new QMediaRecorder(curCamera, this) ;
    ui->chkMute->setChecked (mediaRecorder->isMuted() ) ;
    connect (mediaRecorder,SIGNAL (stateChanged(QMediaRecorder::State)) , this, SLOT (onVideoStateChanged(QMediaRecorder::State))) ;
    connect (mediaRecorder, SIGNAL (durationChanged (qint64)) , this, SLOT(onVideoDurationChanged(qint64)));
}

  mediaRecorder 创建时传递QCamera对象 curCamera 作为输入参数。
mediaRecorder 的两个信号设置了关联槽函数,两个槽函数的代码如下:

void Camera::on_actVideoRecord_clicked()
{
    //开始录像
    if ( !mediaRecorder->isAvailable() )
    {
        QMessageBox::critical (this, "错误",
                               "不支持视频录制! \n mediaRecorder->isAvailable()==false") ;
        return;
    }
    if (curCamera->captureMode () !=QCamera::CaptureVideo)
        curCamera->setCaptureMode (QCamera::CaptureVideo);

    QString selectedFile=ui->editOutputFile->text().trimmed();

    if (selectedFile. isEmpty())
    {
        QMessageBox::critical (this, "错误", "请先设置录音输出文件") ;
        return;
    }
    if (QFile::exists(selectedFile) )
        if (!QFile::remove(selectedFile) )
        {
            QMessageBox::critical (this, "错误", "所设置录音输出文件被占用,无法删除") ;
            return;
        }

    mediaRecorder->setOutputLocation (QUrl::fromLocalFile(selectedFile)) ;
    mediaRecorder->record() ;
}

void Camera::on_actVideoStop_clicked()
{
    //停止录像
  mediaRecorder->stop();
}

  开始录像之前,还需要将摄像头的抓取状态设置为QCamera::CaptureVideo,然后检查设置的视频输出文件,当文件没问题后用setOutputLocation()函数设置视频录制输出文件。
调用QMediaRecorder的record()函数开始录像,stop()函数停止录像。