通过c++ opencv获取rtsp视频流,或者视频源,在qml上进行实时视频显示。
一、在QML中通过QQuickPaintedItem动态加载图片
在QML中,可以使用QQuickPaintedItem
来创建自定义的可绘制项。通过继承QQuickPaintedItem
类,我们可以在QML中动态加载图片并进行绘制。在本篇博客中,我们将学习如何使用QQuickPaintedItem
加载并绘制图片。
1. opecv 获取视频流图片
.pro
文件中引入opencv
#windows
INCLUDEPATH +=D:\soft\opencv3.4.0\OpenCV-MinGW-Build-OpenCV-3.4.5\include
D:\soft\opencv3.4.0\OpenCV-MinGW-Build-OpenCV-3.4.5\include\opencv
D:\soft\opencv3.4.0\OpenCV-MinGW-Build-OpenCV-3.4.5\include\opencv2
LIBS +=D:\soft\opencv3.4.0\OpenCV-MinGW-Build-OpenCV-3.4.5\x86\mingw\bin\libopencv_*.dll
创建一个线程类myThread
用于获取图片
myThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include "QThread"
#include <QTimer>
#include <QImage>
#include <QMutex>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include<QtNetwork/QNetworkRequest>
#include<QtNetwork/QNetworkReply>
#include<QtNetwork/QNetworkAccessManager>
class myThread : public QThread
{
Q_OBJECT
public:
explicit myThread(QObject *parent = nullptr);
myThread(QString url);
protected:
void run() override;
private:
cv::VideoCapture video; // 视频抓取
cv::Mat src_frame; // mat类用于存储矩阵类型数据,这里用来存储图像
QString rtsp; // rtsp地址
bool isStop=false; // 线程是否运行
QImage image;
QNetworkAccessManager * manager;
signals:
void sendArray(QImage qimg); // 发送图片的信号
void sendImg(QImage qimg); // 发送空白图片
public slots:
void getFrame(); // 获取图像
void closeFrame(); // 关闭画面
void closeThread(); // 关闭线程
};
#endif // MYTHREAD_H
myThread.cpp
#include "mythread.h"
#include "QDebug"
myThread::myThread(QObject *parent) : QThread(parent)
{
manager=new QNetworkAccessManager(this);
}
myThread::myThread(QString url)
{
rtsp=url;
}
void myThread::run()
{
qDebug() << "线程开始执行:"<< QThread::currentThread()<<rtsp;
// rtsp读取网络视频
video=cv::VideoCapture(rtsp.toStdString());
if(video.isOpened())
{
while (!isStop) {
getFrame();
msleep(25);
}
if(isStop){
closeFrame();
isStop=false;
}
}
}
// 关闭线程
void myThread::closeThread(){
isStop=true;
qDebug() << __FUNCTION__;
}
// 关闭画面
void myThread::closeFrame(){
qDebug() << __FUNCTION__;
emit sendImg(QImage(":/Image/Button/013.png"));
}
// 图像处理
void myThread::getFrame()
{
video>>src_frame; //从视频取帧
// mat to qimage
if(src_frame.rows>0 && src_frame.cols>0){
if(src_frame.channels() == 3) {
// RGB image
cvtColor(src_frame,src_frame,CV_BGR2RGB);
image = QImage((const uchar*)(src_frame.data), //(const unsigned char*)
src_frame.cols,src_frame.rows,
src_frame.cols*src_frame.channels(), //new add
QImage::Format_RGB888);
}
else {
// gray image
image = QImage((const uchar*)(src_frame.data),
src_frame.cols,src_frame.rows,
src_frame.cols*src_frame.channels(),
QImage::Format_Indexed8);
}
// 发送信号
emit sendArray(image);
}else{
qDebug() << __FUNCTION__<<"null";
}
}
2. 创建自定义绘制项–ShowImage.h
首先,我们需要创建一个自定义的绘制项类。在这个类中,我们将重写QQuickPaintedItem
的paint
方法来实现绘制逻辑。
ShowImage.h
#ifndef SHOWIMAGE_H
#define SHOWIMAGE_H
#include <QObject>
#include <QQuickPaintedItem>
#include <QImage>
#include <QPainter>
#include <mythread.h>
// qml 实时加载图片第一种方式--继承QQuickPaintedItem,自定义绘制
class ShowImage : public QQuickPaintedItem
{
Q_OBJECT
public:
explicit ShowImage(QQuickItem *parent = nullptr);
public slots:
void updateImage(const QImage &);
void start_camera(QString rtsp);
void close_camera();
signals:
void close_thread();
protected:
void paint(QPainter *painter);
private:
QImage m_imageThumb;
QImage image_null=QImage(":/image/test.png"); // 默认图片
myThread *m_thread=nullptr;
};
#endif // SHOWIMAGE_H
3. 实现自定义绘制项
接下来,我们需要实现自定义绘制项的逻辑。
ShowImage.cpp
#include "showimage.h"
ShowImage::ShowImage(QQuickItem *parent)
{
//默认图片
m_imageThumb = image_null;
}
// 更新图片
void ShowImage::updateImage(const QImage &image)
{
m_imageThumb = image;
update();
}
// 开启视频捕获
void ShowImage::start_camera(QString rtsp)
{
if(m_thread!=nullptr){
close_camera();
}
m_thread=new myThread(rtsp);
connect(m_thread,&myThread::sendArray,this,&ShowImage::updateImage);
connect(m_thread,&myThread::sendImg,this,&ShowImage::updateImage);
connect(this,&ShowImage::close_thread,m_thread,&myThread::closeThread);
m_thread->start();
}
// 关闭视频捕获
void ShowImage::close_camera()
{
qDebug() <<__FUNCTION__;
emit close_thread();
if(m_thread!=nullptr){
m_thread->quit();
m_thread->wait();
delete m_thread;
m_thread=nullptr;
}
}
// 图片绘制
void ShowImage::paint(QPainter *painter)
{
if(!m_imageThumb.isNull()){
painter->drawImage(this->boundingRect(), m_imageThumb);
}else{
painter->drawImage(this->boundingRect(), QImage(image_null));
}
}
4. 在main.cpp中注册自定义绘制类
在main.cpp
中注册自定义类给qml使用
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <showimage.h>
#include "myimageprovider.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// 获取上下文对象
QQmlContext *context=engine.rootContext();
// 注册自定义类
qmlRegisterType<ShowImage>("ShowImage",1,0,"ShowImage");
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
5. qml中使用自定义绘制类
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
import ShowImage 1.0 // 导入自定义模块
Window {
visible: true
width: screenW
height: 480
title: qsTr("Hello World")
// 第一种方式--使用自定义模块
ShowImage{
anchors.fill: parent
id : showimg
transformOrigin: Item.Center
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
Button {
id:ok
text: "相机"
anchors.left: parent.left
anchors.leftMargin: 20
anchors.bottom: parent.bottom
anchors.bottomMargin: 50
width: parent.width*0.2
height: 50
signal start_time_qml() // 定义信号
Connections { // 连接信号与槽
target: ok
onStart_time_qml: {
showimg.start_camera("rtsp://admin:abcd1234@192.168.1.104/smart265/ch1/main/av_stream")
}
}
onClicked: {
ok.start_time_qml()
}
}
Button {
id:video
text: "视频源"
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 50
width: parent.width*0.2
height: 50
x:parent.width*0.32
signal start_video_qml()
Connections {
target: video
onStart_video_qml: {
showimg.start_camera("H:\\360MoveData\\Users\\Administrator\\Desktop\\11.mp4")
}
}
onClicked: {
video.start_video_qml()
}
}
Button {
id:clear
text: "关闭"
anchors.right: parent.right
anchors.rightMargin: 20
anchors.bottom: parent.bottom
anchors.bottomMargin: 50
width: parent.width*0.2
height: 50
x:parent.width*0.62
signal clear_time_qml()
Connections {
target: clear
onClear_time_qml: {
showimg.close_camera()
}
}
onClicked: {
clear.clear_time_qml()
}
}
}
6. 运行程序
现在,你可以运行应用程序并点击对应按钮来动态加载并显示图片。当你点击按钮时,ShowImage
将使用updateImage
方法加载指定路径的图片,并将其绘制到界面上。
以播放视频为例:
qml 实时显示
这就是在QML中通过QQuickPaintedItem
动态加载图片的基本步骤。你可以根据需要进一步定制自定义绘制项的功能和外观。
希望本篇博客对你有所帮助!如果你有任何问题,请随时提问。
源代码