忽然想起opencv,一直想作人臉識別,但是理論基礎太水,只能慢慢來,去年學習了一會,而後公司讓我去搞app和網絡,就一直擱着,如今學習qml,忽然想能不能在qml裏面使用opencv,因此就有了這篇文章。算法
在QML中,可視化的基礎組件是Item,不可視化的就是QtObject,它們對應C++中的QQuickItem和QObject類,擴展QML組件一個繼續基於QML中的Item擴張,還有就是繼承QQuickItem,咱們想把opencv加到QML中,那麼只有繼承QQuickItem了。
怎麼使用,那還要看QML新的渲染機制,Qt5的QML渲染基於OpenGL,其場景的渲染在單獨的線程進行,咱們須要須要QQuickItem返回可以描述場景的對象,就是QSGNode。實現QQuick的updatePaintNode函數就OK了,咱們在updatePaintNode,描述怎麼渲染。markdown
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(); } } }
效果
我封裝了輪廓掃描的算法操做。app
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才能正確的顯示。學習