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

2 篇文章 1 订阅

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.

  • 16
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您好!要在 Qt使用 OpenCV 线程读取摄像头视频,可以按照以下步骤操作: 1.首先,您需要在项目中包含 OpenCV 。您可以通过在 .pro 文件中添加以下行来实现: ``` INCLUDEPATH += /usr/local/include/opencv4 LIBS += -L/usr/local/lib -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_videoio ``` 注意:根据您的系统和 OpenCV 安装位置,您需要相应地更改路径。 2.创建一个新的类,例如 CameraThread,来处理摄像头视频的读取和处理。此类应该继承自 QThread。 3.在 CameraThread 类中,您可以使用 OpenCV 的 VideoCapture 类来读取摄像头视频。您可以按照以下示例代码编写 run() 函数: ``` void CameraThread::run() { cv::VideoCapture cap(0); // 0 表示摄像头设备编号,如果有多个摄像头,可以尝试其他编号 if (!cap.isOpened()) { qDebug() << "Error opening camera"; return; } cv::Mat frame; while (!isInterruptionRequested()) { cap.read(frame); // 读取一帧视频 if (frame.empty()) { qDebug() << "No frame captured"; break; } // 在这里处理视频帧,例如进行图像处理或显示 ... // 将 OpenCV Mat 转换为 Qt QImage 并发送到主线程 QImage img(frame.data, frame.cols, frame.rows, QImage::Format_RGB888); emit sendImage(img); } cap.release(); // 释放摄像头 } ``` 注意:在处理视频帧时,您可以使用 OpenCV 的图像处理函数来进行图像处理,例如 cv::cvtColor()、cv::GaussianBlur() 等等。 4.在 CameraThread 类中添加一个信号 sendImage(),用于将 Qt QImage 发送到主线程。例如: ``` signals: void sendImage(QImage); ``` 5.在主窗口中,您可以创建一个 QLabel 或 QGraphicsScene 来显示视频帧。然后,连接 CameraThread 的 sendImage() 信号到主窗口中的槽函数,例如: ``` connect(cameraThread, &CameraThread::sendImage, this, &MainWindow::updateImage); ``` 其中,updateImage() 函数用于更新 QLabel 或 QGraphicsScene 中的图像。 6.在主窗口中创建 CameraThread 对象,并启动线程。例如: ``` CameraThread *cameraThread = new CameraThread(); cameraThread->start(); ``` 注意:当您关闭应用程序时,应该停止线程并释放摄像头资源。例如,在主窗口的析构函数中添加以下代码: ``` cameraThread->requestInterruption(); cameraThread->wait(); delete cameraThread; ``` 希望这些步骤对您有所帮助!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值