[solved] To call function in C++ from QML
-
Add an invokable method to MyItem class where you release the camera. Then simply make sure you call this method before showing your second.qml.
-
Add some debug message in your destructor, in my cases
every classes destroy by the Loader will call destructorI guess your c++ codes maybe has some problem(s)
I do not see any codes to release the camera in your destructor
or you just omit it?The resource allocated by openCV1 has to
destroy by the user explicitly because there are no RAII nor
garbage collector in c.In c++, we don't have to call destructor explicitly
in most of the time except of some special cases(like using
malloc to allocate memory but not new).The purpose of destructor
is make sure the resource will be deleted when the object leave the
scope(die), you have to implement your own resource cleaning codes
in destructor.If you find yourself need to call destructor explicitly,
maybe you are doing something wrong(smell of bug).The other sign of bug in c++ is not using smart pointer(RAII) to handle
your resources when you can, openCV2 widely use the technique of
RAII, the codes wrote by openCV2 are far more easier to maintain than
openCV1.If you use the api provided by openCV2, it would release the
camera when the object die(if you allocate the object on stack or wrap
it by smart pointer). In c++, we don't have to handle the memory all
by ourselves, stick with RAII, it will save you from a lot of troubles.The advices from the openCV developer
bq. The main downside of the C++ interface is that many embedded development systems at the moment support only C. Therefore, unless you are targeting embedded platforms, there’s no point to using the old methods (unless you’re a masochist programmer and you’re asking for trouble).
"opencv tutorial":http://docs.opencv.org/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.html#matthebasicimagecontainer
-
Exactly, in modern c++, with the helps of stl and smart pointer(RAII),
we usually don't have to design our destructor , because the resources
will be freed when they leave the scope(die).If your destructor really haven't been called after the Loader change
their source, you could consider the solution of sierdzio suggested.@
onClicked:{
if(pageloader.status == Loader.Ready){
pageloader.item.releaseCamera()
}
pageloader.source="second.qml"
}
@c++ is very powerful, flexible, the codes of c++ could be very high level or
very low level(as low as c).But we need disciplines(rules) to write robust,
good c++ codes(I am learning too).Simply apply the experiences from c, java or c# to c++
would bring us troubles, because I was a fan of c so I know the agony.My painful memories tell me"c with classes" is an anti-paradigm which we should
avoid in c++ most of the cases.To tell you the truth, I treat it as the sign of troubles
in c++ and pretty disgust it(in some rare cases, we still need it).You may have different opinions than mind or think I am too arrogant
to say something like this, if so, I am sorry and just ignore my ignorance bubble. -
sierdzio and stereo thank you for your great ideas. I thought by making this as global
@
CvCapture * camera = cvCreateCameraCapture(0);
IplImage * image=cvQueryFrame(camera);
@
camera is going to be initialized only once and the newly created object will get the output of camera. Ofcourse now I am getting the video in second screen also. The problem what I am facing is, the second screen video is overlaping the first screen. how to destroy the first screen. -
Set "visible" to false.
-
Yes you are right. Now only the second screen is visible. Here comes the new problem. when I go back and forth between main and second qml screen continuosly , there is a degradation in performance. video capturing is becoming slow and it is showing 2-5 frames per second.
-
May you show us your codes?Maybe your resources handle have some problem(forget to delete something etc)?
May I ask you why do you set the openCV camera as global parameter?
If you need the same camera parameter could be used
between different entity of the same class, you culd declare it as
static data member, this way could reduce the chance of name conflict.My implementation with openCV2 and Qt5
@
#ifndef OPENCVCAMERA_HPP
#define OPENCVCAMERA_HPP#include <QQuickPaintedItem>
#include <QString>//#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>class QPainter;
class QTimer;class openCVCamera : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(int imageHeight READ imageHeight WRITE setImageHeight NOTIFY imageHeightChanged)
Q_PROPERTY(int imageWidth READ imageWidth WRITE setImageWidth NOTIFY imageWidthChanged)
Q_PROPERTY(bool play READ play WRITE setPlay NOTIFY playChanged)
public:
explicit openCVCamera(QQuickItem *parent = 0);
openCVCamera(openCVCamera const&)=delete;
openCVCamera& operator=(openCVCamera const&)=delete;int imageHeight() const; int imageWidth() const; Q_INVOKABLE void open(int width, int height); void paint(QPainter *painter); bool play() const; Q_INVOKABLE void release(); void setImageHeight(int value); void setImageWidth(int value); void setPlay(bool value);
signals:
void imageHeightChanged();
void imageWidthChanged();void playChanged();
public slots:
void open(size_t index);private slots:
void captureFrame();private:
cv::VideoCapture camera_;
cv::Mat cameraFrame_;cv::Mat displayedFrame_; int imageHeight_; int imageWidth_; QString log_; bool play_; cv::Mat result_; QTimer *timer_;
};
#endif // OPENCVCAMERA_HPP
@
@
#include <QDebug>#include <QPainter>
#include <QTimer>#include <opencv2/core/core.hpp>
#include "openCVToQt.hpp"
#include "openCVCamera.hpp"
openCVCamera::openCVCamera(QQuickItem *parent) :
QQuickPaintedItem(parent), imageHeight_(320), imageWidth_(480), play_(false), timer_(new QTimer(this))
{
setRenderTarget(QQuickPaintedItem::FramebufferObject);
setPerformanceHint(QQuickPaintedItem::FastFBOResizing);
connect(timer_, &QTimer::timeout, this, &openCVCamera::captureFrame);open(imageWidth_, imageHeight_);
}
int openCVCamera::imageHeight() const
{
return imageHeight_;
}int openCVCamera::imageWidth() const
{
return imageWidth_;
}void openCVCamera::open(int width, int height)
{
if(!camera_.isOpened()){
camera_.open(0);
camera_.set(CV_CAP_PROP_FRAME_WIDTH, width);
camera_.set(CV_CAP_PROP_FRAME_HEIGHT, height);setPlay(true); }
}
void openCVCamera::open(size_t index)
{
camera_.open(index);
if(!camera_.isOpened()){
log_ = "could not access the camera or video";
qDebug() << log_;
}
}void openCVCamera::paint(QPainter *painter)
{
if(cameraFrame_.empty()){
log_ = "could not grab the camera frame";
qDebug() << log_;
return;
}if(camaraFrame_.cols != imageWidth_ || cameraFrame_.rows != imageHeight_){ cv::resize(cameraFrame_, result_, cv::Size(imageWidth_, imageHeight_)); }else{ result_ = cameraFrame_; } QImage const img = mat_to_qimage_ref(result_); painter->drawImage(0, 0, img);
}
bool openCVCamera::play() const
{
return play_;
}void openCVCamera::release()
{
camera_.release();
}void openCVCamera::setImageHeight(int value)
{
if(value != imageHeight()){
imageHeight_ = value;
qDebug()<<"height changed : "<<value;
emit imageHeightChanged();
}
}void openCVCamera::setImageWidth(int value)
{
if(value != imageWidth()){
imageWidth_ = value;
qDebug()<<"width changed : "<<value;
emit imageWidthChanged();
}
}void openCVCamera::setPlay(bool value)
{
if(value != play_){
play_ = value;if(play_){ timer_->start(33); }else{ timer_->stop(); } emit playChanged(); }
}
/**********************************************************
****************** implementation ************************
**********************************************************/void openCVCamera::captureFrame()
{
if(play_){
camera_ >> cameraFrame_;
if(cameraFrame_.empty()){
log_ = "could not grab the camera frame";
qDebug() << log_;
return;
}
update();
}
}@
qml
@
import QtQuick 2.0
import CVCamera 1.0Rectangle {
id: root
width: 480
height: 320Component{ id: compCamera CVCam{ id: cam1 imageHeight: root.height imageWidth: root.width } } Loader{ id: loaderCam anchors.fill: parent sourceComponent: compCamera } MouseArea{ anchors.fill: parent onClicked: { loaderCam.sourceComponent = null loaderCam.sourceComponent = compCamera } }
}
@
I haven't test all of the api(I prefer the Qt5 api when it is possible)
you could give it a try if you like -
sure. I haven't handled resources properly, I tried my best. Here is the source code
common.h
@
#ifndef COMMON_H
#define COMMON_Hextern int next_screen;
#endif // COMMON_H
@MyCameraWindow.h
@
#ifndef MYCAMERAWINDOW_H_
#define MYCAMERAWINDOW_H_#include <QWidget>
#include <QVBoxLayout>
#include "QOpenCVWidget.h"
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include<QDebug>class MyCameraWindow : public QWidget
{
Q_OBJECT
private:
QOpenCVWidget *cvwidget;
CvCapture *camera;
QVBoxLayout *layout;public:
MyCameraWindow(CvCapture *cam, QWidget *parent=0);
int timerid;protected:
void timerEvent(QTimerEvent*);
};#endif /MYCAMERAWINDOW_H_/
@myitem.h
@#ifndef MYITEM_H
#define MYITEM_H#include "MyCameraWindow.h"
#include <QtDeclarative/QDeclarativeItem>
#include <QGraphicsProxyWidget>
#include<QDebug>
#include <QObject>
#include<QOpenCVWidget.h>
#include"common.h"extern CvCapture * camera;
class MyItem : public QDeclarativeItem
{
Q_OBJECT
Q_PROPERTY(int func_next_screen READ func_next_screen WRITE set_func_next_screen)
public:explicit MyItem(QDeclarativeItem *parent = 0); MyCameraWindow *mycamera; int func_next_screen(){ return m_func_next_screen; } void set_func_next_screen(int &m_func){ next_screen=m_func; }
private:
QGraphicsProxyWidget* mProxy;
int m_func_next_screen;};
#endif // MYITEM_H
@QOpenCVWidget.h
@#ifndef QOPENCVWIDGET_H
#define QOPENCVWIDGET_H#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <QPixmap>
#include <QLabel>
#include <QWidget>
#include <QVBoxLayout>
#include <QImage>
#include<QDesktopWidget>
#include<QDialog>
#include<QApplication>
#include<QDebug>class QOpenCVWidget : public QWidget {
private:
QLabel *imagelabel;
QVBoxLayout *layout;QImage image; cv::Mat source_image; // Variable pointing to the same input frame cv::Mat dest_image;
public:
QOpenCVWidget(QWidget *parent = 0);
void putImage(IplImage *);
QDialog *aboutDialog;
QLabel *label;
};#endif
@
I used common.h for controlling but it didn't work. I used timer for changing the frame.
-
common.cpp
@
int next_screen=0;@
main.cpp
@
#include <QApplication>
#include"myitem.h"
#include<QDeclarativeView>#include<qdeclarative.h>
#include<QDeclarativeView>
#include<QDeclarativeItem>
#include<QDeclarativeContext>CvCapture * camera = cvCreateCameraCapture(0);
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qmlRegisterType<MyItem>("mywindow",1,0,"MyItem");QDeclarativeView view; view.setSource(QUrl("main.qml")); view.showMaximized(); return a.exec();
}
@
MyCameraWIndow.cpp
@#include "MyCameraWindow.h"
#include"myitem.h"
#include"common.h"MyCameraWindow::MyCameraWindow(CvCapture *cam, QWidget *parent) : QWidget(parent) {
camera = cam; layout = new QVBoxLayout; cvwidget = new QOpenCVWidget(this); layout->addWidget(cvwidget); layout->setContentsMargins(0,0,0,0); setLayout(layout); timerid=startTimer(10); // 0.1-second timer
}
void MyCameraWindow::timerEvent(QTimerEvent*) {
IplImage *image=cvQueryFrame(camera);
cvwidget->putImage(image);
if(next_screen==1)
{
killTimer(timerid);
delete cvwidget;
next_screen=0;
}
}@
myitem.cpp
@
#include "myitem.h"#include <QtDeclarative/qdeclarative.h>
#include <QGraphicsProxyWidget>
MyItem::MyItem(QDeclarativeItem *parent): QDeclarativeItem(parent)
{
mycamera = new MyCameraWindow(camera);
mProxy = new QGraphicsProxyWidget(this);
mProxy->setWidget(mycamera);
}@
QOpenCVWidget.cpp
@#include "QOpenCVWidget.h"
#include"myitem.h"// Constructor
QOpenCVWidget::QOpenCVWidget(QWidget *parent) : QWidget(parent) {aboutDialog = new QDialog; label = new QLabel(aboutDialog); layout = new QVBoxLayout; imagelabel = new QLabel; layout->addWidget(imagelabel); layout->setContentsMargins(0,0,0,0); setLayout(layout);
}
void QOpenCVWidget::putImage(IplImage *cvimage) {
source_image = cvimage; dest_image=cvimage; cv::resize(source_image, source_image, cv::Size(768,576) , 0, 0); cv::resize(dest_image, dest_image, cv::Size(1920,1080) , 0, 0); cv::cvtColor(source_image,source_image,CV_BGR2RGB); cv::cvtColor(dest_image,dest_image,CV_BGR2RGB); QImage qimg = QImage((const unsigned char*) source_image.data, source_image.cols, source_image.rows, QImage::Format_RGB888); QImage qimg1 = QImage((const unsigned char*) dest_image.data, dest_image.cols, dest_image.rows, QImage::Format_RGB888); // convert to QImage if(QApplication::desktop()->screenCount()==2) { aboutDialog->setGeometry(QApplication::desktop()->screenGeometry(1)); label->setPixmap(QPixmap::fromImage(qimg1)); aboutDialog->show(); } imagelabel->setPixmap(QPixmap::fromImage(qimg));
}
@
Important to know I am using dual screen that is why I am displaying it in 1080. I tried destructor but it crashed that is why I used common variable to stop the timer and delete the object(didn't work)
Here is the qml part
main.qml
@
import QtQuick 1.1
import mywindow 1.0Rectangle {
id:item_id width:1300 height:700 Rectangle{ width: 80 height: 60 color: "red" x:900 y:30 MouseArea{ anchors.fill: parent onClicked:{ myitem_id.func_next_screen=1; pageloader.source="second.qml" } } } Rectangle{ width:800 height: 500 x:100 y:100 MyItem{ id:myitem_id } } Loader { id: pageloader }
}
@second.qml
@
import QtQuick 1.1
import mywindow 1.0Rectangle {
id:item_id
width:1300
height:700Rectangle{ width: 80 height: 60 color: "red" x:30 y:30 MouseArea{ anchors.fill: parent onClicked:{ myitem_id.func_next_screen=1; pageloader.source="main.qml" } } } Rectangle{ width:800 height: 500 x:100 y:100 MyItem{ id:myitem_id } }
}
@
-
I don't have Qt4 and don't know how to use QGraphicsProxyWidget, but
there are five problems in your codes I find by now.1 : you can't delete cvwidget and you don't need to do that, because
cvwidget has a parent, the QObject(parent of cvwidget in this case) will
delete it when the class destroy. Delete it manually may cause undefined
behavior.2 : your codes wouldn't work in some cases
@
QImage qimg = QImage((const unsigned char*) source_image.data, source_image.cols, source_image.rows, QImage::Format_RGB888);
@
cv::Mat of openCV may or may not do some byte patching on
every row(for performance sake), you have to tell the QImage how
many bytes per row it isexample
@
QImage((T*)(mat.data), mat.cols, mat.rows, mat.step, format);
@by the way, wrap the codes of transfer between cv::Mat and QImage, this
should save you some headaches(maybe you already done)3 : please use the api of openCV2 to deal with the camera, it is very easy
to use, check the function I wrote above(open and captureFrame).The
camera of openCV2 will released when the object is destroyed, you don't
need to released it explicitly.Besides, with the camera of openCV2, you
don't have to convert between IplImage and cv::Mat, this should make
your program faster4 : you should not modify the IplImage you get from cvQueryFrame,
the IplImage you take may always point to the same buffer, if you
alter it, the camera may have some undefined behaviors.
You should copy the cv::Mat before you alter it5 : When you transfer between cv::Mat
and IplImage, you better don't do anything may reallocate memory, because
it may have some weird behaviors(my own experiences), but I also don't know
the reason.Another suggestion, please don't use global parameter if possible.
-
you can find it on github
"github":https://github.com/stereomatchingkiss/qimage_and_matThe function "copy_qimage_to_mat_policy" has bug
I should not copy the data by memcpy, a more proper solution
is take the reference of qimage and clone it.example
@
cv::Mat copy_qimage_to_mat_policy::start(QImage &img, int format)
{
return qimage_to_mat_ref(img).clone();
}
@There are another weird bug, when the images are bmp(or png),
the channel is one and you take the reference of the cv::Mat
by the function "mat_to_qimage_ref", the result of the QImage would
become very weird, don't know the reason yet.I use a workaround
(change one channel image to three channel image) to make the codes work,
not a good solution, hope someday I could find the source of the bug. -
I know this an old post but I was hoping someone could tell me how to physically display the camera on the QML side?