前言
突然想起opencv,一直想做人脸识别,可是理论基础太水,只能慢慢来,去年学习了一会,然后公司让我去搞app和网络,就一直搁着,现在学习qml,突然想能不能在qml里面使用opencv,所以就有了这篇文章。
QQuickItem和QObject
在QML中,可视化的基础组件是Item,不可视化的就是QtObject,它们对应C++中的QQuickItem和QObject类,扩展QML组件一个继续基于QML中的Item扩张,还有就是继承QQuickItem,我们想把opencv加到QML中,那么只有继承QQuickItem了。
怎么使用,那还要看QML新的渲染机制,Qt5的QML渲染基于OpenGL,其场景的渲染在单独的线程进行,我们需要需要QQuickItem返回能够描述场景的对象,就是QSGNode。实现QQuick的updatePaintNode函数就OK了,我们在updatePaintNode,描述怎么渲染。
类的关系
OpenCVcapture继承QOjbect,其是图像捕获的基类,OpenCVcamera是OpenCVcapture子类,完成从摄像头捕获数据。OpenCVaction类封装了opencv图像算法操作。OpenCVshowFrame继承QQuickItem,实现可视化。
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml/qqml.h>
#include "opencvcamera.h"
#include "opencvshowframe.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qmlRegisterType<OpenCVcamera>("OpenCV", 1, 0, "OpenCVcamera");
qmlRegisterType<OpenCVshowFrame>("OpenCV", 1, 0, "OpenCVshowFrame");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import OpenCV 1.0
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
visible: true
menuBar: MenuBar {
Menu {
title: qsTr("&File")
MenuItem {
text: qsTr("&Open")
onTriggered: {
//messageDialog.show(qsTr("Open OpenCV triggered"));
opencvCamera.m_run = true
}
}
MenuItem {
text: qsTr("&Close")
onTriggered: {
//messageDialog.show(qsTr("Open OpenCV triggered"));
opencvCamera.m_run = false
}
}
MenuItem {
text: qsTr("E&xit")
onTriggered: Qt.quit();
}
}
}
OpenCVcamera {
id:opencvCamera
m_cameraId: 1
m_run: false
//width: 320
//height: 240
}
OpenCVshowFrame {
anchors.centerIn: parent
id:opencvShowFrame
m_capture: opencvCamera
m_frameRate: 33
m_run: true
width: 480
height: 320
}
MessageDialog {
id: messageDialog
title: qsTr("May I have your attention, please?")
function show(caption) {
messageDialog.text = caption;
messageDialog.open();
}
}
}
效果
我封装了轮廓扫描的算法操作。
updatePaintNode
QSGNode* OpenCVshowFrame::updatePaintNode(QSGNode *old, UpdatePaintNodeData *)
{
QSGSimpleTextureNode *texture = static_cast<QSGSimpleTextureNode*>(old);
if (texture == NULL) {
texture = new QSGSimpleTextureNode();
}
QImage img;
IplImage *iplImage = NULL;
IplImage *out = NULL;
if (m_capture) {
iplImage = static_cast<OpenCVcapture*>(m_capture)->getFrame();
}
if (iplImage != NULL) {
out = doActions(iplImage);
uchar *imgData = (uchar *)out->imageData;
//qDebug() << out->depth << out->nChannels;
img = QImage(imgData, out->width, out->height, QImage::Format_RGB888);
} else {
img = QImage(boundingRect().size().toSize(), QImage::Format_RGB888);
}
QSGTexture *t = window()->createTextureFromImage(img.scaled(boundingRect().size().toSize()));
if (t) {
QSGTexture *tt = texture->texture();
if (tt) {
tt->deleteLater();
}
texture->setRect(boundingRect());
texture->setTexture(t);
}
if (out) {
cvReleaseImage(&out);
}
return texture;
}
使用了QSGSimpleTextureNode,然后我们将opencv的图像,作为一个QSGTexture,然后返回给渲染的线程进行场景的渲染。
这里还要说下,Qt的QImage,不支持单通道的灰度图,我们需要转换成RGB才能正确的显示。