はじめに
前回(QML から OpenCV で取得したカメラ画を表示してみた - 凹みTips)は、QQmlImageProvider を用いたアプローチで C++ 側で作成したピクセルデータを QML 側で扱う方法を紹介しました。今回は、qmlRegisterType によって登録できる QML 要素から扱う方法を紹介します。やる内容は前回と同じく、OpenCV で取得したカメラ画を表示します
出来るようになること
import QtQuick 2.0 import OpenCV 1.0 Rectangle { width: 1280 height: 720 Camera { anchors.fill: parent } }
これでカメラ画が出るようになります。
概要
QQuickPaintedItem を継承したクラスを作成し、qmlRegisterType で登録します。これは QML の要素の基となる QQuickItem を継承したクラスで、描画を virtual なメンバ関数の paint に渡ってくる QPainter を通じて行うことが出来ます。
- QtQuick 5.0: QQuickItem Class | Documentation | Qt Project
- QtQuick 5.0: QQuickPaintedItem Class | Documentation | Qt Project
- QtGui 5.0: QPainter Class | Documentation | Qt Project
virtual void paint(QPainter *painter) = 0;
さっそく作ってみましょう。
コード
最小限のコードは以下になります。
camera.h
#include <QQuickPaintedItem> class CameraItem : public QQuickPaintedItem { Q_OBJECT public: CameraItem(QQuickItem *parent = 0); void paint(QPainter *painter); private: cv::VideoCapture camera_; };
camera.cpp
CameraItem::CameraItem(QQuickItem *parent) : QQuickPaintedItem(parent), camera_(0) { } void CameraItem::paint(QPainter *painter) { // get camera input cv::Mat input_img; camera_ >> input_img; // BGR -> ARGB cv::cvtColor(input_img, input_img, CV_BGR2BGRA); std::vector<cv::Mat> bgra; cv::split(input_img, bgra); std::swap(bgra[0], bgra[3]); std::swap(bgra[1], bgra[2]); // cv::Mat から QImage へ変換、リサイズしてから描画 QImage output_img(input_img.data, input_img.cols, input_img.rows, QImage::Format_ARGB32); output_img = output_img.scaled(width(), height()); painter->drawImage(width()/2 - output_img.size().width()/2, height()/2 - output_img.size().height()/2, output_img); }
main.cpp
#include <QtGui/QGuiApplication> #include "qtquick2applicationviewer.h" #include "camera.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QtQuick2ApplicationViewer viewer; // 作成したクラスを登録 qmlRegisterType<CameraItem>("OpenCV", 1, 0, "Camera"); viewer.setMainQmlFile(QStringLiteral("qml/LegoAnalyzer/main.qml")); viewer.showExpanded(); return app.exec(); }
これで冒頭の QML でカメラ画が描画されます。ただ、ウィンドウを拡大縮小して paint を再度呼んでもらわないとカメラ画が更新されません(静止画になってしまいます)。再描画を行うには update を呼べば良いのですが、C++ 側でスレッド切ってやるのは面倒なので前回と同じく QML 側から呼んでみます。
import QtQuick 2.0 import OpenCV 1.0 Rectangle { width: 1280 height: 720 Camera { id: camera property int frameRate: 30 anchors.fill: parent } Timer { interval: 1000 / camera.frameRate running: true repeat: true onTriggered: { camera.update(); } } }
これで動画として表示されます。
おわりに
前回の方法では、別スレッドで動作する利点や URI をパラメタとしてもらえる利点はあるものの、カメラ自体の設定を変更したいとすると URI をパースしたり、QQmlImageProvider を継承したクラスを操作するクラスを別に作ってそのインターフェースを QML 側に公開する、という方法を取らなければならず面倒です。今回の方法では直接公開するクラスにモリモリ肉付け出来るので楽です。用途に応じて使い分けるのが良いと思います。
参考
- QtQuick 5.0: QQuickItem Class | Documentation | Qt Project
- QQuickItem での Custom Painter の利用方法
- http://qt.digia.com/Global/Images/Qt/Files/QtEssentialsQtQuick/qml-cpp-integration.pdf
- Digia 社の C++ - QML インテグの資料です