[SOLVED] QML Camera - take a Square photo
-
@guidupas That is the url with image id. You can now get the imageProvider from QML engine using that url. Cast
QQmlImageProviderBase
toQQuickImageProvider
. Then you can use requestImage to get that actual camera image from image id. Once you get this image on C++ side perform operations and return it. -
@p3c0 Hello!
I am trying to resolve this problem by 2 ways:
First:
QImage manipulaImagem::requestImage(const QString &id, QSize *size, const QSize &requestedSize) { QUrl caminhoImagem(id); QString imageId = caminhoImagem.path().remove(0, 1); QImage imagem1(id); if(imagem1.isNull()) { qDebug() << "Erro"; } else { qDebug() << "OK"; } return imagem1; }
I call this method from QML using a image provider: previewImage.source = "image://ProvedorImagens/" + preview;
In this function using QImage imagem1(id) or QImage imagem1(imageId), both return me a NULL image.
It returns me message: QML Image: Failed to get image from provider: image://provedorimagens/image://camera/preview_1The other way is another function:
QImage manipulaImagem::recortarFotoPerfil(const QString &imagem) { QUrl caminhoImagem(imagem); QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine(); QQmlImageProviderBase *imageProviderBase = engine->imageProvider(caminhoImagem.host()); QQuickImageProvider *imageProvider = static_cast<QQuickImageProvider*>(imageProviderBase); QSize imageSize; QString imageId = caminhoImagem.path().remove(0, 1); QImage imagem1 = imageProvider->requestImage(imageId, &imageSize, imageSize); imagem1 = imageProvider->requestImage(imageId, &imageSize, imageSize); if(imagem1.isNull()) { qDebug() << "Erro"; } else { qDebug() << "OK"; } return imagem1; }
This function gets the image but when I return it I receive a message:
Error: Cannot assign QImage to QUrlI call this function directly from QML:
previewImage.source = manipulaImagem.recortarFotoPerfil(preview);I think you told me to use something like the second way but I cannot return the image to QML.
-
@guidupas Returning a
QImage
wont work whereQUrl
is expected. Make this Image available for the image provider that your registered and then return this image fromrequestImage
orrequestPixmap
. -
@p3c0 Hello!
The problem is exactly that, all my attempts to make the image available for the image provider fail. I have tried using a Q_PROPERTY and a QImage member inside the class, but my provider always returns a null image when I try to access it to return.
-
@guidupas But you already have the
QImage
(as per this error Error: Cannot assign QImage to QUrl in class manipulaImagem) as well asQQuickImageProvider
instance. Is it not possible to send the image using this instance ? -
@p3c0 I thing it can be possible but I am not figuring out how. Take a look at this code and I think you will understand what I am talking about.
.h
#ifndef MANIPULAIMAGEM_H #define MANIPULAIMAGEM_H #include <QObject> #include <QImage> #include <QQuickImageProvider> #include <QQmlEngine> #include <QQmlContext> class manipulaImagem : public QObject, public QQuickImageProvider { Q_OBJECT public slots: QString recortarFotoPerfil(const QString &imagem, QRect rectRecorte); public: manipulaImagem(QObject *parent = 0); QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize); private: void alocaImagem(const QString &imagem, QRect rectRecorte); QImage imagemEditada; }; #endif // MANIPULAIMAGEM_H
.cpp
#include "manipulaimagem.h" #include <QDebug> manipulaImagem::manipulaImagem(QObject *parent) : QQuickImageProvider(QQmlImageProviderBase::Image) { } QImage manipulaImagem::requestImage(const QString &id, QSize *size, const QSize &requestedSize) { if(imagemEditada.isNull()) { qDebug() << "Request image: (image is null)"; } else { qDebug() << "Request image: image is OK"; } return imagemEditada; } void manipulaImagem::alocaImagem(const QString &imagem, QRect rectRecorte) { QUrl caminhoImagem(imagem); QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine(); QQmlImageProviderBase *imageProviderBase = engine->imageProvider(caminhoImagem.host()); QQuickImageProvider *imageProvider = static_cast<QQuickImageProvider*>(imageProviderBase); QSize imageSize; QString imageId = caminhoImagem.path().remove(0, 1); imagemEditada = imageProvider->requestImage(imageId, &imageSize, imageSize); if(imagemEditada.isNull()) { qDebug() << "Loading image failed"; } else { qDebug() << "Loading image OK"; } } QString manipulaImagem::recortarFotoPerfil(const QString &imagem, QRect rectRecorte) { this->alocaImagem(imagem, rectRecorte); QString a = "image://ProvedorImagens/imagemEditada"; if(imagemEditada.isNull()) { qDebug() << "Imagem is null"; } else { qDebug() << "Imagem is loaded"; } return a; }
.qml
ManipulaImagem { id: manipulaImagem } Camera { id: camera captureMode: Camera.CaptureStillImage imageCapture { onImageCaptured: { previewImage.source = manipulaImagem.recortarFotoPerfil(preview, viewfinder.mapRectToSource(Qt.rect(viewfinder.x, viewfinder.y, viewfinder.width, viewfinder.height))); } } } Rectangle { id: previewRectangle visible: false anchors.fill: parent Image { id: previewImage fillMode: Image.PreserveAspectFit anchors.top: parent.top width: parent.width height: parent.width } }
Now take a look at the output:
Loading image OK
Imagem is loaded
Request image: (image is null)
QML Image: Failed to get image from provider: image://provedorimagens/imagemEditadaHow you can see when I call the functions the image is not null, but when I try to return the QImage using the provider it cant return the image. I dont know why but for the image provider the image is null.
-
@guidupas Strange.. According to the order of the execution of the code,
imagemEditada
should not be null when the image is requested byImage
element. May be I'm too missing something. -
@p3c0 I found the error. I am targeting the wrong pointer. Actually I need to connect the provider pointer im main.cpp and I am creating a new pointer to the provider.
-
@p3c0 Could you take a look at this topic? I need some help to do exactly that connection between the provider pointer with a signal that send to it the QImage.
https://forum.qt.io/topic/60383/connecting-qquickimageprovider -
-
@p3c0 I found the right pointer. It is in the main.cpp
Now, how can I connect a slot inside this pointer to a signal in processaImagem.cpp?
main.cpp#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QtQml> #include "manipulaxml.h" #include "caminhoimagens.h" #include "manipulaimagem.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); qmlRegisterType<manipulaXml>("ManipXML", 1, 0, "ManipulaXML"); qmlRegisterType<caminhoImagens>("PathImagens", 1, 0, "CaminhoImagens"); qmlRegisterType<manipulaImagem>("ManipImagem", 1, 0, "ManipulaImagem"); QQmlApplicationEngine engine; manipulaImagem *imageProvider = new manipulaImagem; engine.addImageProvider("ProvedorImagens", imageProvider); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
-
@guidupas Don't register the
manipulaImagem
class. Instead set it as a context property too as you already have that instance.
manipulaImagem *imageProvider = new manipulaImagem
In this way you access the same instance from QML for both Image and as context property. -
Answering my own question
Problem solved. Here is the solution step by step:1 - Create a
class
that inherits fromQQuickImageProvider
andQObject
and inside it create aImage
member(QImage)
that is the image to be provided.class provedorImagem : public QObject, public QQuickImageProvider
Implement the
virtual requestImage
method. This is the method that will return the image to QmlQImage requestImage(const QString &id, QSize *size, const QSize &requestedSize)
Create a method to load the provider’s image to return
void provedorImagem::carregaImagem(QImage imagemRecebida) { imagem = imagemRecebida; }
Now set it as the engine image provider in the
main.cpp
fileprovedorImagem *provedorImg = new provedorImagem; engine.rootContext()->setContextProperty("ProvedorImagem", provedorImg);
2 - Create another
class
that inherits fromQObject
.class processaImagem : public QObject
Inside this class you must implement a method that will get the image from
camera
provider, perform the image modifications and return the modified image.
PS: Thep_caminhoImagem
is aproperty
that I created inside theprocessaImagem class
that receives thecamera preview path
.QImage processaImagem::carregaImagem() { QUrl caminhoImagem(p_caminhoImagem); QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine(); QQmlImageProviderBase *imageProviderBase = engine->imageProvider(caminhoImagem.host()); QQuickImageProvider *imageProvider = static_cast<QQuickImageProvider*>(imageProviderBase); QSize imageSize; QString imageId = caminhoImagem.path().remove(0, 1); QImage imagem = imageProvider->requestImage(imageId, &imageSize, imageSize); if(imagem.isNull()) { imagem = QImage(); } else { //Perform the modifications } return imagem; }
3 - Now is the main part. The image
requestImage
provider method must receive the modified image from theprocessaImagem class
to provide it to QML. To do it the providerclass pointer
must be accessible to the QML file, so, in themain.cpp
file just make the pointer available to QML as aproperty
engine.rootContext()->setContextProperty("ProvedorImagem", provedorImg);
and register the
processaImagem class
as a QML typeqmlRegisterType<processaImagem>("ProcessaImagemQml", 1, 0, "ProcessaImagem");
Now we link it inside the QML file
ProvedorImagem.carregaImagem(processaImagem.carregaImagem());
4 - It is done. Now just request the image from the provider:
imagemPreview.source = "image://provedor/imagemEditada_" + camera.numeroImagem.toString();
Here is the entire code:
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QtQml> #include "processaimagem.h" #include "provedorimagem.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); qmlRegisterType<processaImagem>("ProcessaImagemQml", 1, 0, "ProcessaImagem"); QQmlApplicationEngine engine; provedorImagem *provedorImg = new provedorImagem; engine.rootContext()->setContextProperty("ProvedorImagem", provedorImg); engine.addImageProvider("provedor", provedorImg); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
main.qml
import QtQuick 2.4 import QtQuick.Window 2.2 import QtQuick.Controls 1.3 import QtMultimedia 5.4 import ProcessaImagemQml 1.0 Window { visible: true width: 360 height: 640 maximumHeight: 640 minimumHeight: 640 maximumWidth: 360 minimumWidth: 360 title: "Camera Preview Test" Rectangle { id: principal anchors.fill: parent ProcessaImagem { id: processaImagem caminhoImagem: camera.caminhoPreview caminhoSalvar: camera.caminhoSalvar rectRecorte: camera.rectRecorte tamanhoImagem: camera.tamanhoImagem anguloOrientacaoCamera: camera.orientation posicaoCamera: camera.position onCaminhoImagemChanged: { rectRecorte = cameraView.mapRectToSource(Qt.rect(cameraView.x, cameraView.y, cameraView.width, cameraView.height)); tamanhoImagem = Qt.size(cameraView.sourceRect.width, cameraView.sourceRect.height); ProvedorImagem.carregaImagem(processaImagem.carregaImagem()); } onCaminhoSalvarChanged: { removeImagemSalva(); } } Rectangle { id: cameraRectangle width: parent.width height: parent.width anchors.top: parent.top color: "lightGrey" visible: true Camera { id: camera property string caminhoPreview: "" property string caminhoSalvar: "" property int numeroImagem: 0 captureMode: Camera.CaptureStillImage imageCapture { onImageCaptured: { camera.caminhoPreview = preview; camera.stop(); imagemPreview.source = "image://provedor/imagemEditada_" + camera.numeroImagem.toString(); camera.numeroImagem = camera.numeroImagem + 1; imagemPreviewRectangle.visible = true; cameraRectangle.visible = false; } onImageSaved: { camera.caminhoSalvar = path; } } } VideoOutput { id: cameraView visible: true focus: visible anchors.fill: parent source: camera orientation: camera.orientation fillMode: VideoOutput.PreserveAspectCrop } } Rectangle { id: imagemPreviewRectangle width: parent.width height: parent.width anchors.top: parent.top color: "lightGrey" visible: false Image { id: imagemPreview fillMode: Image.PreserveAspectFit anchors.fill: parent } } Rectangle { id: controleRectangle width: parent.width height: parent.height - cameraRectangle.height color: "grey" anchors.top: cameraRectangle.bottom Button { id: tirarFotoButton text: "Tirar foto" anchors.left: parent.left anchors.top: parent.top onClicked: { camera.imageCapture.capture(); } } Button { id: novaFotoButton text: "Tirar nova foto" anchors.right: parent.right anchors.top: parent.top onClicked: { camera.start(); imagemPreviewRectangle.visible = false; cameraRectangle.visible = true; } } } } }
processaimagem.h
#ifndef PROCESSAIMAGEM_H #define PROCESSAIMAGEM_H #include <QObject> #include <QImage> #include <QQmlEngine> #include <QQmlContext> #include <QQuickImageProvider> #include <QFile> #include "provedorimagem.h" class processaImagem : public QObject { Q_OBJECT Q_PROPERTY(QString caminhoImagem READ caminhoImagem WRITE setCaminhoImagem NOTIFY caminhoImagemChanged) Q_PROPERTY(QString caminhoSalvar READ caminhoSalvar WRITE setCaminhoSalvar NOTIFY caminhoSalvarChanged) Q_PROPERTY(QRect rectRecorte READ rectRecorte WRITE setRectRecorte NOTIFY rectRecorteChanged) Q_PROPERTY(QSize tamanhoImagem READ tamanhoImagem WRITE setTamanhoImagem NOTIFY tamanhoImagemChanged) Q_PROPERTY(int anguloOrientacaoCamera READ anguloOrientacaoCamera WRITE setAnguloOrientacaoCamera NOTIFY anguloOrientacaoCameraChanged) Q_PROPERTY(int posicaoCamera READ posicaoCamera WRITE setPosicaoCamera NOTIFY posicaoCameraChanged) public slots: QImage carregaImagem(); void removeImagemSalva(); public: processaImagem(QObject *parent = 0); QString caminhoImagem() const; void setCaminhoImagem(const QString valor); QString caminhoSalvar() const; void setCaminhoSalvar(const QString valor); QRect rectRecorte() const; void setRectRecorte(const QRect valor); QSize tamanhoImagem() const; void setTamanhoImagem(const QSize valor); int anguloOrientacaoCamera() const; void setAnguloOrientacaoCamera(const int valor); int posicaoCamera() const; void setPosicaoCamera(const int valor); private: QString p_caminhoImagem = ""; QString p_caminhoSalvar = ""; QRect p_rectRecorte = QRect(0, 0, 0, 0); QSize p_tamanhoImagem = QSize(0, 0); int p_anguloOrientacaoCamera = 0; int p_posicaoCamera = 0; signals: void caminhoImagemChanged(); void caminhoSalvarChanged(); void rectRecorteChanged(); void tamanhoImagemChanged(); void anguloOrientacaoCameraChanged(); void posicaoCameraChanged(); }; #endif // PROCESSAIMAGEM_H
processaimagem.cpp
#include "processaimagem.h" #include <QDebug> processaImagem::processaImagem(QObject *parent) { } QImage processaImagem::carregaImagem() { QUrl caminhoImagem(p_caminhoImagem); QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine(); QQmlImageProviderBase *imageProviderBase = engine->imageProvider(caminhoImagem.host()); QQuickImageProvider *imageProvider = static_cast<QQuickImageProvider*>(imageProviderBase); QSize imageSize; QString imageId = caminhoImagem.path().remove(0, 1); QImage imagem = imageProvider->requestImage(imageId, &imageSize, imageSize); if(imagem.isNull()) { qDebug() << "Erro ao carregar a imagem"; imagem = QImage(); } else { if((p_anguloOrientacaoCamera == 90) || (p_anguloOrientacaoCamera == 270)) { int larguraImagem = p_tamanhoImagem.width(); int alturaImagem = p_tamanhoImagem.height(); p_tamanhoImagem.setWidth(alturaImagem); p_tamanhoImagem.setHeight(larguraImagem); int recorteX = p_rectRecorte.x(); int recorteY = p_rectRecorte.y(); int recorteLargura = p_rectRecorte.width(); int recorteAltura = p_rectRecorte.height(); p_rectRecorte.setRect(recorteY, recorteX, recorteAltura, recorteLargura); if(imagem.size().width() > imagem.size().height()) { QTransform rotacao; rotacao.rotate(360 - p_anguloOrientacaoCamera); imagem = imagem.transformed(rotacao); qDebug() << "Rodou"; } } if(imagem.width() != p_tamanhoImagem.width()) { imagem = imagem.scaled(p_tamanhoImagem); } imagem = imagem.copy(p_rectRecorte); } return imagem; } void processaImagem::removeImagemSalva() { QFile::remove(p_caminhoSalvar); } QString processaImagem::caminhoImagem() const { return p_caminhoImagem; } void processaImagem::setCaminhoImagem(const QString valor) { if (valor != p_caminhoImagem) { p_caminhoImagem = valor; emit caminhoImagemChanged(); } } QString processaImagem::caminhoSalvar() const { return p_caminhoSalvar; } void processaImagem::setCaminhoSalvar(const QString valor) { if (valor != p_caminhoSalvar) { p_caminhoSalvar = valor; emit caminhoSalvarChanged(); } } QRect processaImagem::rectRecorte() const { return p_rectRecorte; } void processaImagem::setRectRecorte(const QRect valor) { bool alterou = false; if (valor.x() != p_rectRecorte.x()) { p_rectRecorte.setX(valor.x()); alterou = true; } if (valor.y() != p_rectRecorte.y()) { p_rectRecorte.setY(valor.y()); alterou = true; } if (valor.width() != p_rectRecorte.width()) { p_rectRecorte.setWidth(valor.width()); alterou = true; } if (valor.height() != p_rectRecorte.height()) { p_rectRecorte.setHeight(valor.height()); alterou = true; } if(alterou) { emit rectRecorteChanged(); } } QSize processaImagem::tamanhoImagem() const { return p_tamanhoImagem; } void processaImagem::setTamanhoImagem(const QSize valor) { bool alterou = false; if (valor.width() != p_tamanhoImagem.width()) { p_tamanhoImagem.setWidth(valor.width()); alterou = true; } if (valor.height() != p_tamanhoImagem.height()) { p_tamanhoImagem.setHeight(valor.height()); alterou = true; } if(alterou) { emit tamanhoImagemChanged(); } } int processaImagem::anguloOrientacaoCamera() const { return p_anguloOrientacaoCamera; } void processaImagem::setAnguloOrientacaoCamera(const int valor) { if (valor != p_anguloOrientacaoCamera) { p_anguloOrientacaoCamera = valor; emit anguloOrientacaoCameraChanged(); } } int processaImagem::posicaoCamera() const { return p_posicaoCamera; } void processaImagem::setPosicaoCamera(const int valor) { if (valor != p_posicaoCamera) { p_posicaoCamera = valor; emit posicaoCameraChanged(); } }
provedorimagem.h
#ifndef PROVEDORIMAGEM_H #define PROVEDORIMAGEM_H #include <QObject> #include <QImage> #include <QQuickImageProvider> class provedorImagem : public QObject, public QQuickImageProvider { Q_OBJECT public: provedorImagem(); QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize); public slots: void carregaImagem(QImage imagemRecebida); private: QImage imagem; }; #endif // PROVEDORIMAGEM_H
provedorimagem.cpp
#include "provedorimagem.h" #include <QDebug> provedorImagem::provedorImagem() : QQuickImageProvider(QQuickImageProvider::Image) { } QImage provedorImagem::requestImage(const QString &id, QSize *size, const QSize &requestedSize) { if(imagem.isNull()) { qDebug() << "Erro ao prover a imagem"; } return imagem; } void provedorImagem::carregaImagem(QImage imagemRecebida) { imagem = imagemRecebida; }
-
@guidupas That is what I said in earlier post :). Please make the post as solved if done.
-
@p3c0 Yes, but was not easy to figure out how to send the image to the provider.
By the way, thank you for all the help.