Qt实用技巧:使用OpenCV库操作摄像头拍照、调节参数和视频录制

-----------------------------------------------------------------

说明:

错误的理解记录

1. Qt 是一个图形界面程序,色彩(亮度、色度、)属于算法。因此,Qt 目前没有直接调节色彩的方法?会是这样的吗?

也许等有空时,去看看 Qt 有关多媒体的类,确认一下是否有这方面的方法?

2. 要想调节视频的色彩,需要通过第三方库,如 opencv ?

Qt 可以直接调整亮度等:

QVideoWidget Class

Public Functions
QVideoWidget(QWidget *parent = nullptr)
virtual
~QVideoWidget()
Qt::AspectRatioMode
aspectRatioMode() const
int brightness() const
int contrast() const

int hue() const
bool isFullScreen() const
int saturation() const

可能?局限?

Qt 在做播放器时,可以做到色彩调节?

但涉及到摄像头方面,如“标定”等等的操作时,Qt 目前没有这类功能,还得要 OpenCV ?

在本文后部分,QCamera Class 官方手册:Qt 5.14.2 Reference Documentation

 

在人世间,没有包治百病的药,也没有无所不能的工具。

扬长避短,利用好每一种工具的长处,避免使用其短处,以此来搭建自己的程序架构。

=========================================

Qt实用技巧:使用OpenCV库操作摄像头拍照、调节参数和视频录制

  https://cloud.tencent.com/developer/article/1536760

需求

使用OpenCV做功能,播放摄像头(usb和网络),对摄像头设备进行参数调整(亮度、对比度、饱和度、色调、增益、曝光度)调节,拍照和录像。

原理

使用OpenCV打开摄像头(可打开USB和网路哦摄像头),渲染图像显示,可使用OpenCV属性调整摄像头的各项参数,使用拍照可以将当前图片拍照,使用录像可以从当前时间点开始录像直至停止录像

注意

目前测试,即使PC上有编码器,但是OpenCV存储mat为对应的录像视频文件失败,出现:

  • 录制完视频大小为200多B(基本为0),mp4格式时(查看入坑一)
  • 录制完视频大小为6KB,avi格式时
  • 录制avi传入图像mat,源码内部出现错误宕机

运行效果:

核心代码

打开摄像头代码

bool OpenCVManager::startCapture(int usb, int width, int height)
{
    if(!_pVideoCapture->open(usb))
    {
        qDebug() << __FILE__ << __LINE__ << "Failed to start capture :" << usb;
        return false;
    }
    _width = width;
    _height = height;
    _pVideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, _width);
    _pVideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, _height);
    _width = _pVideoCapture->get(CV_CAP_PROP_FRAME_WIDTH);
    _height = _pVideoCapture->get(CV_CAP_PROP_FRAME_HEIGHT);
    _pVideoCapture->set(CV_CAP_PROP_FPS, 25);
    _brightness  = _pVideoCapture->get(cv::CAP_PROP_BRIGHTNESS);
    _contrast    = _pVideoCapture->get(cv::CAP_PROP_CONTRAST);
    _saturation  = _pVideoCapture->get(cv::CAP_PROP_SATURATION);
    _hue         = _pVideoCapture->get(cv::CAP_PROP_HUE);
    _gain        = _pVideoCapture->get(cv::CAP_PROP_GAIN);
    _exposure    = _pVideoCapture->get(cv::CAP_PROP_EXPOSURE);
    QTimer::singleShot(0, this, SLOT(slot_captrueFrame()));
    return true;
}

调整属性代码

bool OpenCVManager::getShowProperty() const
{
    return _showProperty;
}

void OpenCVManager::setShowProperty(bool value)
{
    _showProperty = value;
}

double OpenCVManager::getBrightness()
{
    return _brightness;
}

void OpenCVManager::setBrightness(double value)
{
    _brightness = value;
    if(_pVideoCapture)
    {
        _pVideoCapture->set(cv::CAP_PROP_BRIGHTNESS, _brightness);
    }
}

double OpenCVManager::getContrast() const
{
    return _contrast;
}

void OpenCVManager::setContrast(double value)
{
    _contrast = value;
    if(_pVideoCapture)
    {
        _pVideoCapture->set(cv::CAP_PROP_CONTRAST, _contrast);
    }
}

double OpenCVManager::getSaturation() const
{
    return _saturation;
}

void OpenCVManager::setSaturation(double value)
{
    _saturation = value;
    if(_pVideoCapture)
    {
        _pVideoCapture->set(cv::CAP_PROP_SATURATION, _saturation);
    }
}

double OpenCVManager::getHue() const
{
    return _hue;
}

void OpenCVManager::setHue(double value)
{
    _hue = value;
    if(_pVideoCapture)
    {
        _pVideoCapture->set(cv::CAP_PROP_HUE, _hue);
    }
}

double OpenCVManager::getGain() const
{
    return _gain;
}

void OpenCVManager::setGain(double value)
{
    _gain = value;
    if(_pVideoCapture)
    {
        _pVideoCapture->set(cv::CAP_PROP_GAIN, _gain);
    }
}

double OpenCVManager::getExposure() const
{
    return _exposure;
}

void OpenCVManager::setExposure(double value)
{
    _exposure = value;
    if(_pVideoCapture)
    {
        _pVideoCapture->set(cv::CAP_PROP_EXPOSURE, _exposure);
    }
}

拍照代码

void PhotoAndRecordWidget::on_pushButton_photo_clicked()
{
    QString timeStr = QDateTime::currentDateTime().toString("yyyy-MM-hh hh_mm_ss");
    QString dirName = "photos";
    if(!QFile::exists(dirName))
    {
        QDir dir;
        if(!dir.mkdir(dirName))
        {
            ui->label_state->setText("创建文件夹 " + dirName + "失败!!!");
        }
    }
    QString filePath = QString("%1/%2.png").arg(dirName).arg(timeStr);
    if(_image.save(filePath))
    {
        ui->label_state->setText("保存照片至: " + filePath);
    }else{
        ui->label_state->setText("保存照片失败!!!");
    }
}

录像代码

开启录像

void OpenCVManager::startRecord(QString pathFile)
{
    // 多线程处理
    QMetaObject::invokeMethod(this, "slot_startRecord",
                              Qt::DirectConnection, Q_ARG(QString, pathFile));
}
void OpenCVManager::slot_startRecord(QString filePath)
{
    if(_pVideoWrite)
    {
        qDebug() << __FILE__ << __LINE__ << "It's recording!!!";
        return;
    }
    _pVideoWrite = new cv::VideoWriter;
    QString ext = filePath.mid(filePath.lastIndexOf(".") + 1);
    int cvFourcc = 0;
    if(ext == "mpg")
    {
        cvFourcc = CV_FOURCC('D','I','V','X');
        qDebug() << __FILE__ << __LINE__<< ext << "DIVX" << cvFourcc;
    }else if(ext == "avi")
    {
        cvFourcc = CV_FOURCC('M','J','P','G');
        qDebug() << __FILE__ << __LINE__<< ext << "MJPG" << cvFourcc;
    }else if(ext == "mp4")
    {
        // mp4目前录制不成功(可以生成文件,但是打开失败)
//        cvFourcc = CV_FOURCC('M','P','4','2');
        cvFourcc = CV_FOURCC('M','J','P','G');
//        cvFourcc = CV_FOURCC('I', 'Y', 'U', 'V');
        qDebug() << __FILE__ << __LINE__<< ext << "MP42" << cvFourcc;
    }
    _pVideoWrite->open(filePath.toStdString(), cvFourcc, 25, cv::Size(_width, _height));

    _recordFilePath = filePath;
    _recording = true;
}

录像过程

void OpenCVManager::slot_captrueFrame()
{
    if(!_running)
    {
        return;
    }
    if(_pVideoCapture->isOpened())
    {
        cv::Mat mat;
        *_pVideoCapture >> mat;
        if(_showProperty)
        {
            cv::putText(mat, QString("brightness: %1").arg(_brightness).toStdString(),
                        cvPoint(0, 30), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
            cv::putText(mat, QString("  contrast: %1").arg(_contrast  ).toStdString(),
                        cvPoint(0, 60), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
            cv::putText(mat, QString("saturation: %1").arg(_saturation).toStdString(),
                        cvPoint(0, 90), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
            cv::putText(mat, QString("       hue: %1").arg(_hue       ).toStdString(),
                        cvPoint(0, 120), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
            cv::putText(mat, QString("      gain: %1").arg(_gain      ).toStdString(),
                        cvPoint(0, 150), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
            cv::putText(mat, QString("  exposure: %1").arg(_exposure  ).toStdString(),
                        cvPoint(0, 180), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
            cv::putText(mat, QString("press ESC out").toStdString(),
                        cvPoint(0, 210), cv::FONT_HERSHEY_COMPLEX, 1.0, cv::Scalar(255));
        }
        if(_recording)
        {
            _pVideoWrite->write(mat);
        }
        emit signal_captureOneFrame(mat);
        QTimer::singleShot(10, this, SLOT(slot_captrueFrame()));
    }
}

关闭录像

void OpenCVManager::stopRecord()
{
    // 多线程处理
    QMetaObject::invokeMethod(this, "slot_stopRecord", Qt::DirectConnection);
}

void OpenCVManager::slot_stopRecord()
{
    if(_pVideoWrite)
    {
        _recording = false;
        _pVideoWrite->release();
        delete _pVideoWrite;
        _pVideoWrite = 0;
    }
}

入坑记录

入坑一:录制视频保存为空

解决方法:

编解码器得问题,cv::VideoWrite查阅相关资料发现其只支持固定的几个格式,其中就包括avi。

入坑二:录制视频奔溃

原因:

因为初始设置摄像头的宽高(400 x 400),根据测试推断摄像头会默认给最接近初始化设置的分辨率,但是却不是直接是设置的(400 x 400)而是返回了最接近的分辨率(320 x 240),除非设置的分辨率正好是摄像头本身支持。

所以设置分辨率是需要摄像头硬件支持。

解决方法:

进一步验证同时解决该问题

======


官方手册:Qt 5.14.2 Reference Documentation

QCamera Class

The QCamera class provides interface for system camera devices. More...

Header:
#include <QCamera>
qmake:
QT += multimedia
Inherits:
QMediaObject

List of all members, including inherited members

Public Types

FrameRateRange
enum CaptureMode { CaptureViewfinder, CaptureStillImage, CaptureVideo }
flags CaptureModes
enum Error { NoError, CameraError, InvalidRequestError, ServiceMissingError, NotSupportedFeatureError }
enum LockChangeReason { UserRequest, LockAcquired, LockFailed, LockLost, LockTemporaryLost }
 

enum LockStatus { Unlocked, Searching, Locked }
enum LockType { NoLock, LockExposure, LockWhiteBalance, LockFocus }
flags LockTypes
enum Position { UnspecifiedPosition, BackFace, FrontFace }
enum State { UnloadedState, LoadedState, ActiveState }
enum Status { ActiveStatus, StartingStatus, StoppingStatus, StandbyStatus, LoadedStatus, …, UnavailableStatus }

Properties

captureMode : QCamera::CaptureModes
lockStatus : const QCamera::LockStatus
state : const QCamera::State
status : const QCamera::Status
 

Public Functions

QCamera(QCamera::Position position, QObject *parent = nullptr)

QCamera(const QCameraInfo &cameraInfo, QObject *parent = nullptr)

QCamera(const QByteArray &deviceName, QObject *parent = nullptr)

QCamera(QObject *parent = nullptr)
virtual ~QCamera()

 
QCamera::CaptureModes captureMode() const
QCamera::Error error() const
QString errorString() const
QCameraExposure *
exposure() const
QCameraFocus *
focus() const
QCameraImageProcessing *
imageProcessing() const
 

bool isCaptureModeSupported(QCamera::CaptureModes mode) const
QCamera::LockStatus lockStatus() const
QCamera::LockStatus lockStatus(QCamera::LockType lockType) const
QCamera::LockTypes requestedLocks() const
void setViewfinder(QVideoWidget *viewfinder)
void setViewfinder(QGraphicsVideoItem *viewfinder)
 

void setViewfinder(QAbstractVideoSurface *surface)
void setViewfinderSettings(const QCameraViewfinderSettings &settings)
QCamera::State state() const
QCamera::Status status() const
QCamera::LockTypes supportedLocks() const

 
QList<QCamera::FrameRateRange> supportedViewfinderFrameRateRanges(const QCameraViewfinderSettings &settings = QCameraViewfinderSettings()) const
 

QList<QVideoFrame::PixelFormat> supportedViewfinderPixelFormats(const QCameraViewfinderSettings &settings = QCameraViewfinderSettings()) const
 

QList<QSize> supportedViewfinderResolutions(const QCameraViewfinderSettings &settings = QCameraViewfinderSettings()) const
QList<QCameraViewfinderSettings> supportedViewfinderSettings(const QCameraViewfinderSettings &settings = QCameraViewfinderSettings()) const

QCameraViewfinderSettings viewfinderSettings() const

Reimplemented Public Functions

virtual QMultimedia::AvailabilityStatus
availability() const override

Public Slots

void load()
void searchAndLock(QCamera::LockTypes locks)
void searchAndLock()
void setCaptureMode(QCamera::CaptureModes mode)
void start()
void stop()
void unload()
void unlock(QCamera::LockTypes locks)
void unlock()

Signals

void captureModeChanged(QCamera::CaptureModes mode)
void error(QCamera::Error value)
void lockFailed()
void lockStatusChanged(QCamera::LockType lock, QCamera::LockStatus status,  

QCamera::LockChangeReason reason) void lockStatusChanged(QCamera::LockStatus status, QCamera::LockChangeReason reason)
 

void locked()
void stateChanged(QCamera::State state)
void statusChanged(QCamera::Status status)

Detailed Description

QCamera can be used with QCameraViewfinder for viewfinder display, QMediaRecorder for video recording and QCameraImageCapture for image taking.
You can use QCameraInfo to list available cameras and choose which one to use.

  const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
  for (const QCameraInfo &cameraInfo : cameras) {
      if (cameraInfo.deviceName() == "mycamera")
          camera = new QCamera(cameraInfo);
  }

See the camera overview for more information.

GitHub 加速计划 / opencv31 / opencv
77.34 K
55.71 K
下载
OpenCV: 开源计算机视觉库
最近提交(Master分支:25 天前 )
e1fec156 features2d: fixed out of bounds access in SIFT 3 天前
63087396 - 3 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐