1

我有一个基于 QML 的应用程序,我需要从相机捕获图像以便进行 QR 码识别/解码(使用 qzxing)。

按照CameraCapture QML 类的示例,我可以捕获图像,但是它们将始终保存到本地文件中。我不想保存到文件,因为我不想强调底层存储(闪存、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. 在控制台中,我收到几个警告/以下输出:
** (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

谁能指导我找到一个可行的解决方案来实现以下目标:

  • 向用户显示相机的预览(视频)
  • 从相机拍摄一张照片,这样我就可以触发二维码解码
  • 必须与现有的 QML 接口实现一起使用(但可以包含 C++ 代码,没问题)

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

4

1 回答 1

2

您可以访问QCamerausing 项目的mediaObject属性,Camera然后使用设置模式的QCameraImageCapture位置QCameraImageCapture::CaptureToBuffer并使用imageCaptured信号获取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();
        }
    }
}
于 2021-06-28T07:13:48.967 回答