共计 4103 个字符,预计需要花费 11 分钟才能阅读完成。
最近须要应用 opencv,就简略写了一个测试示例并记录下来。
其中 QCamera 等相干的类同样能够实现以后性能。
仓库
性能
- 反对有线或 IP 摄像头连贯
- 反对视频流播放
- 反对多分辨率设置
- 反对视频录制
- 反对拍照
- 视频抽帧、拍照、录制等由独单线程解决
测试可用的视频流
CCTV1 高清 http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8 | |
CCTV3 高清 http://ivi.bupt.edu.cn/hls/cctv3hd.m3u8 | |
CCTV5+ 高清 http://ivi.bupt.edu.cn/hls/cctv5phd.m3u8 | |
CCTV6 高清 http://ivi.bupt.edu.cn/hls/cctv6hd.m3u8 |
camara.h
#ifndef CAMARA_H | |
#define CAMARA_H | |
#include <QImage> | |
#include <QObject> | |
#include <QThread> | |
#include <QTimer> | |
#include <opencv2/core/core.hpp> | |
#include <opencv2/highgui/highgui.hpp> | |
#include <opencv2/imgproc/imgproc.hpp> | |
Q_DECLARE_METATYPE(QImage); | |
/* | |
*@brief Camara 在独自线程中实现视频抽帧、拍照及录像性能 | |
*@brief open、close 等是耗时操作,请保障在 Camara 线程工夫中实现调用(可思考异步槽函数、QMetaObject::invokeMethod)*/ | |
class Camara : public QObject | |
{ | |
Q_OBJECT | |
public: | |
Camara(); | |
~Camara(); | |
bool isOpened() const; | |
bool isTakevideoed() const; | |
QString getSavePath() const; | |
QSize getResolutions() const; | |
bool isTakeVideo() const; | |
signals: | |
void updateImage(QImage); | |
void statusChanged(bool isOpen); | |
public slots: | |
void openCamara(const QString &url); | |
void openCamara(int index); | |
void closeCamara(); | |
void takePicture(); | |
void takeVideo(); | |
void setSavePath(const QString &path); | |
void setResolutions(const QSize &size); | |
private slots: | |
void tbegin(); | |
void tend(); | |
void captureCamara(); | |
private: | |
QString m_savepath; | |
QAtomicInteger<bool> m_isTakepicture = false; | |
QAtomicInteger<bool> m_isTakevideo = false; | |
QAtomicInteger<bool> m_isflip = false; | |
QScopedPointer<cv::VideoCapture> m_capture; | |
QScopedPointer<cv::VideoWriter> m_writer; | |
QTimer *m_timer = nullptr; | |
QThread m_thread; | |
}; | |
#endif // CAMARA_H |
#include "camara.h" | |
#include <QDateTime> | |
Camara::Camara() | |
{moveToThread(&m_thread); | |
connect(&m_thread, &QThread::started, this, &Camara::tbegin); | |
connect(&m_thread, &QThread::finished, this, &Camara::tend); | |
m_thread.start(QThread::HighPriority); | |
} | |
Camara::~Camara() | |
{m_thread.quit(); | |
m_thread.wait();} | |
void Camara::tbegin() | |
{m_capture.reset(new cv::VideoCapture); | |
m_writer.reset(new cv::VideoWriter); | |
m_timer = new QTimer(this); | |
m_timer->setTimerType(Qt::PreciseTimer); | |
connect(m_timer, &QTimer::timeout, this, &Camara::captureCamara); | |
} | |
void Camara::tend() | |
{closeCamara(); | |
} | |
void Camara::openCamara(const QString &url) | |
{if (!m_capture->isOpened() && m_capture->open(url.toLatin1().data())) | |
{ | |
m_isflip = false; | |
m_timer->start(33); | |
emit statusChanged(true); | |
} | |
else | |
{emit statusChanged(false); | |
} | |
} | |
void Camara::openCamara(int index) | |
{if (!m_capture->isOpened() && m_capture->open(index)) | |
{ | |
m_isflip = true; | |
m_timer->start(33); | |
emit statusChanged(true); | |
} | |
else | |
{emit statusChanged(false); | |
} | |
} | |
void Camara::closeCamara() | |
{m_timer->stop(); | |
if (m_writer->isOpened()) | |
m_writer->release(); | |
if (m_capture->isOpened()) | |
m_capture->release();} | |
void Camara::captureCamara() | |
{ | |
cv::Mat originalframe; | |
cv::Mat flipframe; | |
*m_capture >> originalframe; | |
if (m_isflip) | |
cv::flip(originalframe, flipframe, 1); | |
else | |
flipframe = originalframe; | |
QImage img = QImage(flipframe.data, flipframe.cols, flipframe.rows, QImage::Format_RGB888).rgbSwapped(); | |
if (!img.isNull()) | |
{if (m_isTakepicture) | |
{ | |
m_isTakepicture = !m_isTakepicture; | |
QString name = m_savepath + QDateTime::currentDateTime().toString("yyyy-MM-hh hh_mm_ss") + ".jpeg"; | |
img.save(name, "jpeg"); | |
} | |
if (m_isTakevideo) | |
{*m_writer << flipframe;} | |
updateImage(img); | |
} | |
originalframe.release(); | |
flipframe.release();} | |
void Camara::takePicture() | |
{m_isTakepicture = true;} | |
void Camara::takeVideo() | |
{if (!m_isTakevideo) | |
{QString name = m_savepath + QDateTime::currentDateTime().toString("yyyy-MM-hh hh_mm_ss") + ".avi"; | |
if (m_writer->open(name.toLatin1().data(), cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), 30.0, cv::Size(m_capture->get(cv::CAP_PROP_FRAME_WIDTH), m_capture->get(cv::CAP_PROP_FRAME_HEIGHT)), true)) | |
{m_isTakevideo = true;} | |
} | |
else | |
{ | |
m_isTakevideo = false; | |
m_writer->release();} | |
} | |
void Camara::setSavePath(const QString &path) | |
{m_savepath = path + '/';} | |
void Camara::setResolutions(const QSize &size) | |
{if (m_capture->isOpened()) | |
{m_capture->set(cv::CAP_PROP_FRAME_WIDTH, size.width()); | |
m_capture->set(cv::CAP_PROP_FRAME_HEIGHT, size.height()); | |
} | |
} | |
QString Camara::getSavePath() const | |
{return m_savepath;} | |
bool Camara::isOpened() const | |
{return m_capture->isOpened(); | |
} | |
bool Camara::isTakevideoed() const | |
{return m_isTakevideo;} | |
QSize Camara::getResolutions() const | |
{ | |
QSize ret; | |
ret.setWidth(m_capture->get(cv::CAP_PROP_FRAME_WIDTH)); | |
ret.setHeight(m_capture->get(cv::CAP_PROP_FRAME_HEIGHT)); | |
return ret; | |
} | |
bool Camara::isTakeVideo() const | |
{return m_writer->isOpened(); | |
} |
正文完