Obtain rtsp video stream or video source through c++ opencv, and display real-time video on qml.
1. Dynamically load pictures through QQuickPaintedItem in QML
In QML, you can use QQuickPaintedItem
to create custom drawables. By inheriting QQuickPaintedItem
the class, we can dynamically load pictures and draw them in QML. In this blog, we will learn how to use QQuickPaintedItem
Load and draw images.
1. opecv gets video stream picture
.pro
Introduce opencv in the file
#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
Create a thread class myThread
for getting pictures
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. Create a custom drawing item –ShowImage.h
First, we need to create a custom drawable item class. In this class, we will override QQuickPaintedItem
the paint
methods to implement the drawing logic.
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. Implement custom drawing items
Next, we need to implement the logic for custom drawing items.
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. Register the custom drawing class in main.cpp
main.cpp
Register custom classes in qml for use
#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. Use custom drawing class in 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:[email protected]/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. Run the program
Now, you can run the application and click the corresponding button to dynamically load and display the image. When you click the button, the method ShowImage
will be used updateImage
to load the picture with the specified path and draw it on the interface.
Take playing a video as an example:
qml real-time display
QQuickPaintedItem
This is the basic step of dynamically loading pictures in QML . You can further customize the functionality and appearance of custom draw items as needed.
Hope this blog is helpful to you! If you have any questions, please feel free to ask.