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();
}
这种方法有几个问题:
imageCaptured
从未被触发,lambda 也从未被调用,因此没有图像被捕获
readyForCaptureChange
被调用,然而,预览(QML代码)全白并且永远不会回到相机的实时视频
- 在控制台中我得到几个 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
任何人都可以指导我找到一个可行的解决方案来实现以下目标:
- 向用户显示相机的预览(视频)
- 从相机拍摄一张照片,这样我就可以触发二维码解码
- 必须使用现有的 QML 接口实现(但可以包含 C++ 代码,没问题)
我 运行 在 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();
}
}
}
我有一个基于 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();
}
这种方法有几个问题:
imageCaptured
从未被触发,lambda 也从未被调用,因此没有图像被捕获readyForCaptureChange
被调用,然而,预览(QML代码)全白并且永远不会回到相机的实时视频- 在控制台中我得到几个 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
任何人都可以指导我找到一个可行的解决方案来实现以下目标:
- 向用户显示相机的预览(视频)
- 从相机拍摄一张照片,这样我就可以触发二维码解码
- 必须使用现有的 QML 接口实现(但可以包含 C++ 代码,没问题)
我 运行 在 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();
}
}
}