[SOLVED] QML Camera - take a Square photo
-
Hello all!
How can I take a photo using Camera in QML but with custom dimensions, like a square photo for example?
Thanks a lot.
-
@guidupas I'm not sure if you can take a photo with custom dimension but you sure can crop the photo using
QImage
on C++ side. -
@p3c0 What about the Camera ImageCapture resolution, like the code below? Is not for this purpose?
Camera { id: camera captureMode: Camera.CaptureStillImage imageCapture { resolution: Qt.size(parent.width, parent.width) onImageCaptured: { previewImage.source = preview } } }
-
@guidupas Not sure. Does it take picture of the same dimensions that you specified ?
-
@p3c0 No. So I cant resize the resolution directly from the camera. But now I will crop the image and I am facing a now problem: how can I pass the cropped image from C++ to QML?
Thanks for the help.
-
@guidupas AFAIK you can't directly. Use QQuickImageProvider and do cropping inside
requestPixmap
function and return the updated image or create you own item by subclassingQQuickPaintedItem
then overridepaint
function and draw that updated image. -
@p3c0 Hi. Could you help me with a little piece of code. I am facing some difficulties to implement that.
Thanks a lot.
-
@guidupas The documentation explains the examples.
QQuickPaintedItem- register the class
- add functions to pass the image from QML
- call update to repaint with new image
- No need to make a plugin for
QQuickImageProvider
- Make sure the image provider class gets the updated image
- Need to reload the the
Image
url for updates to reflect
I think subclassing
QQuickPaintedItem
would be a better option. -
Hi @p3c0 . I am trying to implement it with QQuickImageProvider but I am not understanding why it is not working. My code is below. If you could help me.
caminhoimagens.h
#ifndef MANIPULAIMAGEM_H #define MANIPULAIMAGEM_H #include <QObject> #include <QImage> #include <QQuickImageProvider> class manipulaImagem : public QObject, public QQuickImageProvider { Q_OBJECT Q_PROPERTY(QString imagem READ imagem WRITE setImagem NOTIFY imagemChanged) public: manipulaImagem(); QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize); QString imagem() const; void setImagem(const QString &imagem_caminho); private: QString p_imagem; signals: void imagemChanged(); }; #endif // MANIPULAIMAGEM_H
manipulaimagem.cpp
#include "manipulaimagem.h" #include <QDebug> manipulaImagem::manipulaImagem() : QQuickImageProvider(QQuickImageProvider::Image) { } QImage manipulaImagem::requestImage(const QString &id, QSize *size, const QSize &requestedSize) { qDebug() << p_imagem; QImage imagemr(p_imagem); return imagemr; } QString manipulaImagem::imagem() const { return p_imagem; } void manipulaImagem::setImagem(const QString imagem_caminho) { if (imagem_caminho != p_imagem) { p_imagem = imagem_caminho; emit imagemChanged(); } }
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"); manipulaImagem *forneceImagem = new manipulaImagem(); QQmlApplicationEngine engine; engine.addImageProvider("provedorImagem", forneceImagem); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
QML
Camera { id: camera captureMode: Camera.CaptureStillImage imageCapture { onImageCaptured: { manipulaImagem.imagem = preview.toString(); console.log(preview.toString()); previewImage.source = "image://provedorImagem/fotoPerfil" } } } VideoOutput { id: viewfinder visible: true focus: visible anchors.top: parent.top width: parent.width height: parent.width source: camera fillMode: VideoOutput.PreserveAspectCrop } Rectangle { id: previewRectangle visible: false anchors.fill: parent Image { id: previewImage fillMode: Image.PreserveAspectFit anchors.top: parent.top width: parent.width height: parent.width } }
The response for this code is:
qml: image://camera/preview_1
""
QFSFileEngine::open: No file name specified
qrc:/CriarPerfil.qml:983:9: QML Image: Failed to get image from provider: image://provedorimagem/fotoPerfil -
@guidupas What does
preview.toString()
return ? Is it the actual image data or file name ? -
@p3c0 Hello!
When I use console.log it returns the same result for both options:
console.log(preview.toString()); console.log(preview);
The result is:
qml: image://camera/preview_1But there is something wrong when I pass values to the Q_PROPERTY(QString imagem READ imagem WRITE setImagem NOTIFY imagemChanged). No matter what I pass it returns me an empty string.
manipulaImagem.imagem = "AER"; manipulaImagem.imagem = preview; manipulaImagem.imagem = preview.toString();
All the options above returns the result:
""But I could not find what is missing.
-
@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