C

Qt Quick Ultralite 相机示例

演示了如何将平台特定的图像数据与 QML UI 集成。

概述

此示例演示了如何处理平台特定的图像数据。它具有一个简单的用户界面来控制相机并使用 Qul::SharedImage 访问其帧。示例使用支持板上摄像头硬件,以及为其他板的模拟实现。

目标平台

项目结构

示例由一个简单的 UI(camera.qml)、摄像头界面定义(camerainterface.h)、NXP平台上摄像头界面的实现以及摄像头界面的模拟实现(在 platform 目录中)组成。此外,还有一个背景图像资产(bg_qt.png)。

CMake 项目文件

除 Qt Quick Ultralite 应用程序的 CMake 设置外,还包括包含 NXP 和模拟摄像头界面实现的 platform 子目录。

add_subdirectory(platform)

在 QmlProject 文件中,它还注册了摄像头界面,以便 Qt Quick Ultralite 能够找到它。

        InterfaceFiles {
                files: [
                        "camerainterface.h"
                ]
        }

platform 目录中的 CMakeLists.txt 文件在选择实现之前会检查平台。

message(STATUS "Board manufacturer: ${BOARD_MANUFACTURER_NAME}")

string(REGEX MATCHALL "([^\-]+|[^\-]+$)" PLATFORM_COMPONENTS ${QUL_PLATFORM})
list(GET PLATFORM_COMPONENTS 0 BOARD_NAME)
string(TOLOWER ${BOARD_NAME} BOARD_NAME)

if(NOT ${BOARD_NAME} STREQUAL "mimxrt1170" AND ${BOARD_MANUFACTURER_NAME} STREQUAL "nxp")
    add_subdirectory(nxp)
else()
    add_subdirectory(dummy)
endif()

该示例依赖于一些由 MCUXpresso SDK 提供的代码。而不是将该代码添加到 Qt Quick Ultralite 平台库中,它被添加到示例中。此代码主要涉及 CSI 引脚复用配置和摄像头 I2C 功能。此外,摄像头支持代码和驱动程序是使用 MCUXpresso SDK 编译的。

应用程序 UI

用户界面是在 camera.qml 中实现的。

应用程序通过显示背景图像和“启动相机”按钮开始。点击该按钮后,将启动摄像头图像流并启用“拍摄”和“停止”按钮。点击“拍摄”按钮停止摄像头流到可见的帧,并启用“最小化”和“关闭”按钮。点击“最小化”按钮将当前可见的帧最小化到右上角,点击“关闭”按钮将启用先前状态。点击“停止”按钮停止摄像头流并切换回开始状态。

用户界面代码中有趣的部分是 cameraView Image 类型。

    Text {
        id: message
        color: "red"
        font.pixelSize: 14
    }

    Image {
        id: cameraView
        anchors.right: parent.right
        anchors.top: parent.top
        source: CameraInterface.image
        width: 0
        height: 0

        Behavior on width { NumberAnimation { duration: 500; easing.type: Easing.OutCubic } }
        Behavior on height { NumberAnimation { duration: 500; easing.type: Easing.OutCubic } }

        Component.onCompleted: {
            if (!CameraInterface.initCamera()) {
                console.log("Camera initialization failed");
                message.text =
                    "Camera initialization failed.\nConnect a camera and/or check the serial console output.";
            }
        }

        SequentialAnimation on opacity {
            id: shootAnimation
            PropertyAnimation { property: "opacity"; from: 1.0; to: 0.0; duration: 200}
            PropertyAnimation { property: "opacity"; from: 0.0; to: 1.0; duration: 200}
        }
    }

它将相机界面的 image 属性绑定到其 source 属性。它还通过事件处理器调用 initCamera() 来初始化相机。

相机接口

相机接口是一个简单的 Qul::Singleton 接口,包含 Qul::EventQueue。事件队列用于将相机帧异步地从平台传递到 Qt Quick Ultralite 应用程序。

首先,定义了一个简单的结构体,用于持有相机帧数据的指针

struct FrameEvent
{
    uint8_t *newFrame;
};

此结构体用作 Qul::EventQueue 的事件数据类型。

以下是相机接口的定义

struct CameraInterface : public Qul::Singleton<CameraInterface>, public Qul::EventQueue<FrameEvent>
{
    Qul::Property<Qul::SharedImage> image;

    bool initCamera();
    void startCamera();
    void stopCamera();

private:
    void onEvent(const FrameEvent &frame) QUL_DECL_OVERRIDE;

    friend struct Qul::Singleton<CameraInterface>;
    CameraInterface() {}
    CameraInterface(const CameraInterface &);
    CameraInterface &operator=(const CameraInterface &);
};

首先,相机接口引入了类型为 imageQul::SharedImage 属性。此属性包含来自平台相机流的帧数据。在此之后,声明了用于控制相机的函数。有 initCamera() 函数以初始化平台相机,以及 startCamera()stopCamera() 以开始和停止相机流。覆盖了 Qul::EventQueueonEvent() 函数以处理图像更新。最后,阻止了外部构造和复制。

相机接口实现

相机接口的实现位于 platform 目录中。该 dummy 子目录包含一个演示实现,该实现实现了接口的存根函数。这可以作为将示例移植到另一个平台的起点。

#include "camerainterface.h"

/* Initialize your platform's camera hardware in this function. */
bool CameraInterface::initCamera()
{
    return false;
}

/* Start camera stream. */
void CameraInterface::startCamera() {}

/* Stop camera stream. */
void CameraInterface::stopCamera() {}

/* When the camera frame is ready, the image property can be updated in this event handler. */
void CameraInterface::onEvent(const FrameEvent &frameEvent)

nxp 子目录包含了针对 NXP 板的实现。该实现使用 NXP 的相机接收器 API。在 initCamera() 中初始化相机接收器,其中设置了一个回调函数以接收新帧,并将 this 指针作为用户数据传递,以便通过 Qul::EventQueue 发布事件。

    err = CAMERA_RECEIVER_Init(&cameraReceiver, &cameraConfig, newCameraFrame, this);

还提交了空的相机帧缓冲区到相机接收器。

    for (uint32_t i = 0; i < CAMERA_BUFFER_COUNT; i++) {
        err = CAMERA_RECEIVER_SubmitEmptyBuffer(&cameraReceiver, (uint32_t) (s_cameraBuffers[i]));
        RETURN_FALSE_IF(err != kStatus_Success, "Failed to submit camera buffer!");
    }

相机接收器通过 newCameraFrame 回调通知新帧。回调函数随后发布一个包含指向新相机帧的指针的 FrameEvent

    me->postEventFromInterrupt(FrameEvent{(uint8_t *) cameraFrameAddr});

onEvent() 中处理 FrameEvent,在此处构建了具有自定义清理函数的 Qul::Image,并将其封装在 Qul::SharedImage 中,然后将它设置为 image 属性的值。

    image.setValue(Qul::SharedImage(newFrame));

当 Qt Quick Ultralite 引擎销毁 Qul::SharedImage 引用时,将调用 cleanup 函数。函数的 memory 参数指向使用 image 属性传递给 Qt Quick Ultralite 引擎的缓冲区。空缓冲区再次重用,以便每次有新相机帧可用时都可以访问新相机帧。

    status_t err = CAMERA_RECEIVER_SubmitEmptyBuffer(&cameraReceiver, (uint32_t) (memory));

文件

图片

另请参阅Qul::SharedImageQul::ImageQul::EventQueueQul::Singleton

在特定 Qt 许可证下提供。
了解更多信息。