Qt实用技巧:使用OpenCV库操作摄像头拍照、调节参数和视频录制
- 2019 年 11 月 11 日
- 笔记
投稿作者: 红模仿_红胖子 研究方向:OpenCV/OpenGL/QT/软硬件结合 博客地址:https://blog.csdn.net/qq21497936 文字编辑:gloomyfish
需求
使用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),除非设置的分辨率正好是摄像头本身支持。
所以设置分辨率是需要摄像头硬件支持。

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