QML 应用程序:如何在内存中捕获相机图像

QML app: How to capture camera image in-memory

我有一个基于 QML 的应用程序,我需要从相机捕获图像才能生成二维码 recognition/decoding(使用 qzxing)。

按照 CameraCapture QML class 的示例,我可以捕获图像,但是它们将始终保存到本地文件中。我不想保存到文件,因为我不想强调底层存储(闪存、sd 卡)来进行 QR 码识别。 因此,我想在内存中抓取相机图像。谷歌搜索结果证明仅使用 QML 似乎是不可能的。

所以我一直在寻找基于 C++ 的解决方案,但我似乎无法想出一个可行的解决方案。

目前我有代码试图在 C++ 中捕获图像,同时在 QML 中为用户提供可见的相机预览:

QML

   ImageReader {
      id: reader
   }

   Rectangle {
      width: 400
      height: 400
      x: 30; y: 90

      Camera {
         id: camera
      }

      VideoOutput {
         source: camera
         focus: visible
         anchors.fill: parent
      }
   }

   Timer {
      id: scanTimer
      interval: 3000
      running: true
      repeat: false

      onTriggered: {
         console.log("capture image")
         reader.startCapture()
      }
   }

C++

ImageReader::ImageReader(QObject *parent) : QObject(parent)
{
   doCapture = true;
   camera = new QCamera();
   capture = new QCameraImageCapture(camera);
   capture->setCaptureDestination(QCameraImageCapture::CaptureToBuffer);

   QObject::connect(capture, &QCameraImageCapture::imageCaptured, [=] (int id, QImage img) {
      qDebug() << "Captured image";
      camera->stop();
   });

   QObject::connect(capture, &QCameraImageCapture::readyForCaptureChanged, [=] (bool state) {
      qDebug() << "Ready for capture changed: " << state;

      if (!doCapture)
         return;

      if(state == true) {
         qDebug() << "is ready for capture";
         camera->searchAndLock();
         capture->capture();
         camera->unlock();

         doCapture = false;
      }
   });
}

Q_INVOKABLE void ImageReader::startCapture()
{
   camera->start();
}

这种方法有几个问题:

  1. imageCaptured 从未被触发,lambda 也从未被调用,因此没有图像被捕获
  2. readyForCaptureChange 调用,然而,预览(QML代码)全白并且永远不会回到相机的实时视频
  3. 在控制台中我得到几个 warnings/the 以下输出:
** (myapp:24700): WARNING **: 19:26:09.403: capsfilter2: transform_caps returned caps which are not a real subset of the filter caps

** (myapp:24700): WARNING **: 19:26:09.406: capsfilter2: transform_caps returned caps which are not a real subset of the filter caps

** (myapp:24700): WARNING **: 19:26:09.412: imagebin-capsfilter: transform_caps returned caps which are not a real subset of the filter caps

** (myapp:24700): WARNING **: 19:26:09.426: viewfinderbin-capsfilter: transform_caps returned caps which are not a real subset of the filter caps
[D] onTriggered:65 - capture image
[W] unknown:0 - Starting camera without viewfinder available

** (myapp:24700): WARNING **: 19:26:12.463: capsfilter8: transform_caps returned caps which are not a real subset of the filter caps

** (myapp:24700): WARNING **: 19:26:12.469: capsfilter8: transform_caps returned caps which are not a real subset of the filter caps

** (myapp:24700): WARNING **: 19:26:12.483: imagebin-capsfilter: transform_caps returned caps which are not a real subset of the filter caps

** (myapp:24700): WARNING **: 19:26:12.495: viewfinderbin-capsfilter: transform_caps returned caps which are not a real subset of the filter caps
[D] ImageReader::ImageReader(QObject*)::<lambda:26 - Ready for capture changed:  true
[D] ImageReader::ImageReader(QObject*)::<lambda:32 - is ready for capture
[D] ImageReader::ImageReader(QObject*)::<lambda:26 - Ready for capture changed:  false
[D] ImageReader::ImageReader(QObject*)::<lambda:26 - Ready for capture changed:  true

任何人都可以指导我找到一个可行的解决方案来实现以下目标:

我 运行 在 XPeria 10 ii phone 上安装了 SailfishOS 4.1.0.24。因此,QT版本应该是5.2.

您可以访问 QCamera using the mediaObject property of the Camera item and then use the QCameraImageCapture where the QCameraImageCapture::CaptureToBuffer mode is set and use the imageCaptured signal to get the QImage

#include <QCamera>
#include <QCameraImageCapture>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

class CameraHelper: public QObject{
    Q_OBJECT
    Q_PROPERTY(QObject* qcamera READ qcamera WRITE setQcamera NOTIFY qcameraChanged)
public:
    using QObject::QObject;
    QObject *qcamera() const{
        return q_qcamera;
    }
    void setQcamera(QObject *qcamera)
    {
        if (q_qcamera == qcamera)
            return;
        if(m_capture){
            m_capture->deleteLater();
        }
        q_qcamera = qcamera;
        if(q_qcamera){
            if(QCamera *camera = qvariant_cast<QCamera *>(q_qcamera->property("mediaObject"))){
                m_capture = new QCameraImageCapture(camera, this);
                m_capture->setCaptureDestination(QCameraImageCapture::CaptureToBuffer);
                connect(m_capture, &QCameraImageCapture::imageCaptured, this, &CameraHelper::handleImageCaptured);
            }
            else
                m_capture = nullptr;
        }
        emit qcameraChanged();
    }
public Q_SLOTS:
    void capture(){
        if(m_capture)
            m_capture->capture();
    }
Q_SIGNALS:
    void qcameraChanged();
    void imageChanged(const QImage & image);
private:
    void handleImageCaptured(int , const QImage &preview){
        Q_EMIT imageChanged(preview);
    }
    QPointer<QObject> q_qcamera;
    QCameraImageCapture *m_capture = nullptr;
};

int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);
    CameraHelper camera_helper;
    QObject::connect(&camera_helper, &CameraHelper::imageChanged, [](const QImage & qimage){
        qDebug() << qimage;
    });
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("cameraHelper", &camera_helper);
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);
    return app.exec();
}

#include "main.moc"
import QtQuick 2.12
import QtQuick.Window 2.12
import QtMultimedia 5.15

Window {
    width: 640
    height: 480
    visible: true
    Camera {
        id: camera
        Component.onCompleted: cameraHelper.qcamera = camera
    }
    VideoOutput {
        source: camera
        focus : visible // to receive focus and capture key events when visible
        anchors.fill: parent

        MouseArea {
            anchors.fill: parent;
            onClicked: cameraHelper.capture();
        }
    }
}