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),除非設置的分辨率正好是攝像頭本身支持。
所以設置分辨率是需要攝像頭硬件支持。

解決方法:
進一步驗證同時解決該問題
