QML Camera - 拍一张方形照片

QML Camera - take a Square photo

如何使用 QML 中的相机拍摄自定义尺寸的照片,例如方形照片?我正在尝试使用 QQuickImageProvider 实现它,但我不明白为什么它不起作用。

QML代码

Camera {
    id: camera

    captureMode: Camera.CaptureStillImage

    imageCapture {
        onImageCaptured: {
            manipulaImagem.imagem = preview;

            previewImage.source = "image://ProvedorImagens/" + preview;

            //previewImage.source = manipulaImagem.recortarFotoPerfil(preview, viewfinder.mapRectToSource(Qt.rect(viewfinder.x, viewfinder.y, viewfinder.width, viewfinder.height)));
        }
    }
}
Image {
    id: previewImage

    fillMode: Image.PreserveAspectFit
    anchors.top: parent.top

    width: parent.width
    height: parent.width
}

我正在尝试通过两种方式解决这个问题:

第一个:

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;
}

我使用图像提供程序从 QML 调用此方法:previewImage.source = "image://ProvedorImagens/" + 预览; 在使用 QImage imagem1(id) 或 QImage imagem1(imageId) 的函数中,return 都是 NULL 图像。 它 return 是我的消息:QML 图像:无法从提供商处获取图像:图像://provedorimagens/image://camera/preview_1

另一种方式是另一个函数:

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;
}

此函数获取图像,但当我 return 它时,我收到一条消息: 错误:无法将 QImage 分配给 QUrl

我直接从 QML 调用这个函数: previewImage.source = manipulaImagem.recortarFotoPerfil(预览);

感谢您的帮助。

回答我自己的问题 问题解决了。这是一步一步的解决方案:

1 - 创建一个继承自 QQuickImageProviderQObjectclass,并在其中创建一个 Image 成员 (QImage),它是图像提供。

class provedorImagem : public QObject, public QQuickImageProvider

实施virtual requestImage方法。这是将 return 图像转换为 Qml

的方法
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize)

创建一个方法来将提供程序的图像加载到 return

void provedorImagem::carregaImagem(QImage imagemRecebida)
{
    imagem = imagemRecebida;
}

现在在main.cpp文件中将其设置为引擎图像提供者

provedorImagem *provedorImg = new provedorImagem;
engine.rootContext()->setContextProperty("ProvedorImagem", provedorImg);

2 - 创建另一个继承自 QObjectclass

class processaImagem : public QObject

在此 class 中,您必须实现一个方法,该方法将从 camera 提供商获取图像,执行图像修改和 return 修改后的图像。 PS:p_caminhoImagem 是我在接收 camera preview path.

processaImagem class 中创建的 property
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 - 现在是主要部分。图像 requestImage 提供者方法必须从 processaImagem class 接收修改后的图像以将其提供给 QML。为此,QML 文件必须可以访问提供者 class pointer,因此,在 main.cpp 文件中,只需将指针作为 property

提供给 QML
engine.rootContext()->setContextProperty("ProvedorImagem", provedorImg);

并将 processaImagem class 注册为 QML 类型

qmlRegisterType<processaImagem>("ProcessaImagemQml", 1, 0, "ProcessaImagem");

现在我们link它在QML文件里面

ProvedorImagem.carregaImagem(processaImagem.carregaImagem());

4 - 完成了。现在只需向提供商​​请求图像:

imagemPreview.source = "image://provedor/imagemEditada_" + camera.numeroImagem.toString();

完整代码如下:

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;
}