Obtenha fluxo de vídeo rtsp ou fonte de vídeo por meio de c++ opencv e exiba vídeo em tempo real em qml.
1. Carregue imagens dinamicamente através de QQuickPaintedItem em QML
Em QML, você pode usar QQuickPaintedItem
para criar drawables personalizados. Ao herdar QQuickPaintedItem
a classe, podemos carregar imagens dinamicamente e desenhá-las em QML. Neste blog, aprenderemos como carregar QQuickPaintedItem
e desenhar imagens.
1. opecv obtém a imagem do stream de vídeo
.pro
Introduzir opencv no arquivo
#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
Crie uma classe de thread myThread
para obter fotos
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. Crie um item de desenho personalizado –ShowImage.h
Primeiro, precisamos criar uma classe de item drawable personalizada. Nesta aula, substituiremos QQuickPaintedItem
os paint
métodos para implementar a lógica do desenho.
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. Implemente itens de desenho personalizados
Em seguida, precisamos implementar a lógica para itens de desenho personalizados.
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. Registre a classe de desenho personalizada em main.cpp
main.cpp
Registre classes personalizadas em qml para uso
#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 classe de desenho personalizada em 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. Execute o programa
Agora, você pode executar o aplicativo e clicar no botão correspondente para carregar e exibir a imagem dinamicamente. Ao clicar no botão, o método ShowImage
será utilizado updateImage
para carregar a figura com o caminho especificado e desenhá-la na interface.
Veja a reprodução de um vídeo como exemplo:
qml exibição em tempo real
QQuickPaintedItem
Este é o passo básico para carregar imagens dinamicamente em QML . Você pode personalizar ainda mais a funcionalidade e a aparência dos itens de desenho personalizados, conforme necessário.
Espero que este blog seja útil para você! Se você tiver alguma dúvida, sinta-se à vontade para perguntar.