原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/108489004
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…(点击传送门)
开发专栏:项目实战(点击传送门)
OpenCV开发专栏(点击传送门)
需求
嵌入式windows设备上的相机程序。
打开摄像头,兼容多种摄像头,摄像头分辨率切换(摄像头管理)。
对图像进行翻转、旋转、亮度调整(图像基本算法管理)
对调整后的图像进行拍照、延时拍照。
对调整后的图像进行录像(编码录制)。
对照片和录像进行回看(图片浏览器、视频播放器)
长时间运行稳定。
Demo
体验下载地址
CSDN:https://download.csdn.net/download/qq21497936/12827160
QQ群:1047134658(点击“文件”搜索“camera”,群内与博文同步更新)
原理
使用ffmpeg处理摄像头、使用OpenCV处理录像和播放;
相关博客
《项目实战:Qt+ffmpeg摄像头检测工具》
《项目实战:Qt+OpenCV视频播放器(支持播放器操作,如暂停、恢复、停止、时间、进度条拽托等)》
《OpenCV开发笔记(四):OpenCV图片和视频数据的读取与存储》
《FFmpeg开发笔记(一):ffmpeg介绍、windows开发环境搭建(mingw和msvc)》
v1.5.0功能
打开摄像头,兼容多种摄像头,摄像头分辨率切换(摄像头管理)。
对图像进行翻转、旋转、亮度调整(图像基本算法管理)
对调整后的图像进行拍照、延时拍照。
对调整后的图像进行录像(编码录制)。
对照片和录像进行回看(图片浏览器、视频播放器)
Demo核心代码
FfmpegCameraManager.h:摄像头管理类
#ifndef FFMPEGCAMERAMANAGER_H
#define FFMPEGCAMERAMANAGER_H
/************************************************************\
* 控件名称: FfmpegCameraManager, ffmpeg管理类(用于摄像头操作)
* 控件描述:
* 1.打开摄像头
* 2.支持动态切换分辨率
* 作者:红模仿 联系方式:QQ21497936
* 博客地址:https://blog.csdn.net/qq21497936
* 日期 版本 描述
* 2018年09年14日 v1.0.0 ffmpeg模块封装空类
* 2020年09年05日 v1.1.0 ffmpeg打开摄像头,支持的动态分辨率切换
* 2020年09年08日 v1.2.0 兼容各种摄像头,解决内存溢出bug,对最高帧率做了支持范围内的限制
* 限制帧率一般为25fps(除非最大小于25fps或者最小大于25fps)
\************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavformat/version.h"
#include "libavutil/time.h"
#include "libavutil/mathematics.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "errno.h"
#include "error.h"
}
#define LOG qDebug()<<__FILE__<<__LINE__< class FfmpegCameraManager : public QObject { Q_OBJECT public: public: explicit FfmpegCameraManager(QObject *parent = nullptr); signals: void signal_captureOneFrame(QImage image); public: static QString getAvcodecConfiguration(); public: bool init(); bool openUsbCamera(); QString getUsbCameraName(); QList int getCurrentFps(); int getCurrentSizeFpsIndex(); QList public slots: void slot_start(); void slot_stop(); void slot_setSizeFps(int index); protected slots: void slot_captureOneFrame(); signals: public slots: private: static bool _init; AVFormatContext *_pAVFormatContext; // 全局上下文 AVInputFormat *_pAVInputFormat; AVDictionary* _pAVDictionary; // 打开编码器的配置 AVCodecContext *_pAVCodecContextForAudio; // 音频解码器上下文 AVCodecContext *_pAVCodecContextForVideo; // 视频解码器上下文(不带音频) AVCodec * _pAVCodecForAudio; // 音频解码器 AVCodec * _pAVCodecForVideo; // 视频解码器(不带音频) int _streamIndexForAudio; // 音频流序号 int _streamIndexForVideo; // 视频流序号 SwrContext *_pSwrContextForAudio; // 音频转换上下文 bool _running; bool _first; bool _opened; uint8_t *_pOutBuffer; AVFrame * _pFrame; AVFrame * _pFrameRGB; AVPacket *_pAVPacket; SwsContext *_pSwsContext; int _videoIndex; QString _cameraDescription; QList QList QList int _currentSizeFpsIndex; }; #endif // FfmpegCameraManager_H FfmpegCameraManager.cpp:摄像头管理类 ... bool FfmpegCameraManager::openUsbCamera() { if(!_init) { LOG << "未初始化"; return true; } _pAVInputFormat = av_find_input_format("dshow"); if(!_pAVInputFormat) { LOG << "Failed to av_find_input_format"; return false; } if(_cameraDescription == "") { LOG << "无摄像头"; return false; } QString cameraDescription = QString("video=%1").arg(_cameraDescription); if(_listSizeFpsInfo.size() == 0) { LOG << "未获取到分辨率和帧率"; return false; } // 设置分辨率 av_dict_set(&_pAVDictionary, "video_size", QString("%1x%2").arg(_listSize.at(_currentSizeFpsIndex).width()) .arg(_listSize.at(_currentSizeFpsIndex).height()).toUtf8().data(), 0); // 设置帧率 int frame = _listFps.at(_currentSizeFpsIndex); av_dict_set(&_pAVDictionary, "framerate", QString("%1").arg(frame).toUtf8().data(), 0); LOG << "打开摄像头:" << _cameraDescription << "分辨率:" << _listSize.at(_currentSizeFpsIndex).width() << "x" << _listSize.at(_currentSizeFpsIndex).height() << "帧率:" << _listFps.at(_currentSizeFpsIndex); if(avformat_open_input(&_pAVFormatContext, cameraDescription.toUtf8().data(), _pAVInputFormat, &_pAVDictionary) != 0) { LOG << "打开摄像头失败"; return false; } LOG << "打开摄像头成功"; _first = true; _opened = true; QTimer::singleShot(0, this, SLOT(slot_captureOneFrame())); return true; } ... OpenCVManager.h:录像与播放视频类 #ifndef OPENCVMANAGER_H #define OPENCVMANAGER_H /************************************************************\ * 控件名称: OpenCVManager,OpenCV管理类 * 控件描述: * 1.OpenCV操作支持 * 2.支持录像(.avi格式) * 作者:红模仿 联系方式:QQ21497936 * 博客地址:https://blog.csdn.net/qq21497936 * 日期 版本 描述 * 2019年11月09日 v1.0.0 opencv拍照和录像Demo * 2020年09月07日 v1.1.0 增加了单纯录像的接口 \************************************************************/ #include #include #include #include // opencv #include "opencv/highgui.h" #include "opencv/cxcore.h" #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/opencv.hpp" class OpenCVManager : public QObject { Q_OBJECT public: explicit OpenCVManager(QObject *parent = nullptr); ~OpenCVManager(); public: QString getWindowTitle() const; double getBrightness(); double getContrast() const; double getSaturation() const; double getHue() const; double getGain() const; bool getShowProperty() const; double getExposure() const; int getRotate() const; bool getMirror() const; public: void setBrightness(double value); void setContrast(double value); void setSaturation(double value); void setHue(double value); void setGain(double value); void setShowProperty(bool value); void setExposure(double value); void setRotate(int rotate); void setMirror(bool mirror); signals: void signal_captureOneFrame(cv::Mat mat); // 接收图像后抛出信号 public: bool startCapture(int usb, int width = 1280, int height = 720); // 打开摄像头, 0... bool startCapture(QString url, int width = 1280, int height = 720); // 打开摄像头, 网络摄像头地址 bool stopCapture(); // 关闭摄像头 void startRecord(QString filePath); // 开始录像(使用的是opencv打开的摄像头) void stopRecord(); // 停止录像(使用的是opencv打开的摄像头) public slots: void slot_inputRecordImage(QImage image); void slot_stopRecordFormOut(); public: // 单独的一块业务,使用的是开始录像后,从类外面输入QImage进行录像 void startRecordFromOut(QString filePath, int fps); void inputRecordImage(QImage image); void stopRecordFormOut(); public slots: bool start(); // 开启线程 bool stop(); // 关闭线程 protected slots: void slot_captrueFrame(); // 消息循环获取图像 void slot_stopCapture(); // 当正在采集中时(>>时),关闭摄像头会导致程序崩溃,所以采集与停止放一个线程中(消息循环) protected slots: void slot_startRecord(QString filePath); // 录像(使用的是opencv打开的摄像头) void slot_stopRecord(); // 停止录屏(使用的是opencv打开的摄像头) public: static QImage cvMat2QImage(const cv::Mat &mat); static cv::Mat image2Mat(QImage image); // Qimage 转 cv::Mat static QImage mat2Image(cv::Mat mat); // cv::Mat 转 QImage private: cv::VideoCapture *_pVideoCapture; // 摄像头实例 cv::VideoWriter *_pVideoWrite; // 录像实例 QString _recordFilePath; // 录制文件路径 bool _running; // 线程是否运行 bool _showProperty; // 是否显示属性参数 double _brightness; // 亮度 double _contrast; // 对比度 double _saturation; // 饱和度 double _hue; // 色调 double _gain; // 增益 double _exposure; // 曝光度 int _width; // 宽度 int _height; // 高度 bool _recording; // 标志是否正在录像 bool _startRecording; int _rotate; // 旋转度数 bool _mirror; // 是否翻转 int _fps; // 帧率 int _index; // 帧序号 private: cv::VideoWriter *_pVideoWriteForOut; // 录像实例(从外部输入图像,非从opencv打开摄像头) QString _recordFilePathForOut; // 录像文件路径(从外部输入图像,非从opencv打开摄像头) private: QString _windowTitle; }; #endif // OPENCVMANAGER_H OpenCVManager.h:录像与播放视频类 ... void OpenCVManager::inputRecordImage(QImage image) { if(!_startRecording) { return; } cv::Mat mat = image2Mat(image); if(!_recording) { QString ext = _recordFilePath.mid(_recordFilePathForOut.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 << "avi" << cvFourcc; }else if(ext == "mp4") { // mp4目前录制不成功(可以生成文件,但是打开失败) cvFourcc = CV_FOURCC('M','P','4','2'); qDebug() << __FILE__ << __LINE__<< ext << "MP42" << cvFourcc; } qDebug() << __FILE__ << __LINE__ << mat.type() << mat.channels(); _pVideoWriteForOut->open(_recordFilePath.toStdString(), cvFourcc, _fps, cv::Size(mat.cols, mat.rows)); std::vector cv::split(mat, listMat); std::vector // 由于opencv对avi中mat的限制大小只能为0xFFFF,修改源码突破限制为0xFFFFFFFF后 // 在录像时,发现录入的mat是正确的,录制出来通道颜色变换了,需要手动对颜色通道进行修正 // 注意:仅限avi使用mjpg编码格式 // 1 2 0 偏绿 // 0 1 2 偏蓝 // 0 2 1 偏绿 // 1 2 3 严重不对 // 2 0 1 偏蓝 // 2 1 0 偏蓝 listMat2.push_back(listMat.at(0)); listMat2.push_back(listMat.at(1)); listMat2.push_back(listMat.at(2)); cv::merge(listMat2, mat); _pVideoWriteForOut->write(mat); _recording = true; }else{ std::vector cv::split(mat, listMat); std::vector listMat2.push_back(listMat.at(0)); listMat2.push_back(listMat.at(1)); listMat2.push_back(listMat.at(2)); cv::merge(listMat2, mat); _pVideoWriteForOut->write(mat); } } ... 若该文为原创文章,未经允许不得转载 原博主博客地址:https://blog.csdn.net/qq21497936 原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/108489004