计算机视觉组件

简介

ACOMP CV (Computer Vision) 是 ARCS SDK 的计算机视觉组件,提供基于图像流的 OCR 文字识别、图像拼接和切割线检测功能。该组件支持多种图像格式输入,提供灵活的扫描模式配置和事件回调机制。

主要特性

  • OCR 文字识别:支持基于图像流的文字识别,返回识别结果

  • 图像拼接:支持多帧图像拼接,返回拼接帧数据

  • 切割线检测:支持文档切割线检测

  • 多种扫描模式:支持正常扫描和快扫模式

  • 左右模式配置:支持左右手扫描方向设置

  • 事件回调机制:支持 OCR 结果、拼接帧、状态变更、帧处理完成等多种事件通知

  • 图像流管理:提供完整的图像流通道管理和缓冲区操作接口

  • 图像保存回传:支持 AP 核将处理后的图像(原始图、拼接图、切割线图)回传给 CP 核

  • 跨核通信:基于 IPC 机制实现图像流的跨核传输

图像保存数据结构

当 AP 核处理完图像后,可以通过 CV_CB_EVENT_IMG_SAVE 事件将图像数据回传给 CP 核:

typedef struct {
    uint32_t img_addr;    /* 图像数据在共享内存中的地址 */
    uint16_t width;       /* 图像宽度(像素) */
    uint16_t height;      /* 图像高度(像素) */
    uint8_t  img_type;    /* 图像类型 */
} cv_img_save_info_t;

图像类型定义

类型

宏定义

说明

0

CV_IMG_TYPE_RAW

原始图像

1

CV_IMG_TYPE_STITCH

拼接图像

2

CV_IMG_TYPE_CUTLINE

切割线图像

配置选项

基本配置

配置项

说明

默认值

CONFIG_ACOMP

启用 ACOMP 组件

n

CONFIG_ACOMP_CV

启用 ACOMP 组件的 CV 组件

n

资源配置

配置项

说明

默认值

CONFIG_ACOMP_CV_RES_CUTLINE_EMMC_ADDR

切割线模型 eMMC 地址

0x9b00000

CONFIG_ACOMP_CV_RES_CUTLINE_SIZE

切割线模型大小(字节)

462320

CONFIG_ACOMP_CV_RES_OCR_EMMC_ADDR

OCR 模型 eMMC 地址

0xa000000

CONFIG_ACOMP_CV_RES_OCR_SIZE

OCR 模型大小(字节)

2289296

快速开始

1. 启用组件

在项目的 prj.conf 文件中添加:

# 启用 ACOMP 组件
CONFIG_ACOMP=y

# 启用 ACOMP 组件的 CV 组件
CONFIG_ACOMP_CV=y

# 配置 CV 组件所需资源在 eMMC 的位置和大小
CONFIG_ACOMP_CV_RES_CUTLINE_EMMC_ADDR=0x9b00000
CONFIG_ACOMP_CV_RES_CUTLINE_SIZE=462320
CONFIG_ACOMP_CV_RES_OCR_EMMC_ADDR=0xa000000
CONFIG_ACOMP_CV_RES_OCR_SIZE=2289296

2. 初始化 CV 组件

在应用程序中初始化 CV 组件:

#include "acomp.h"
#include "cv/acomp_cv.h"

int main(int argc, char **argv)
{
    // 初始化 ACOMP 框架
    acomp_init();
    
    // 初始化 CV 组件
    int ret = acomp_cv_init();
    if (ret != ACOMP_ERR_OK) {
        printf("CV init failed: %d\n", ret);
        return -1;
    }
    
    // 你的应用代码
    return 0;
}

3. 注册事件回调

使用事件回调函数接收 CV 处理结果:

#include "cv/acomp_cv.h"

void cv_event_handler(uint32_t event, void *event_data,
                      uint32_t event_data_len, void *priv)
{
    if (event & CV_CB_EVENT_OCR_RESULT) {
        // OCR 识别结果
        printf("OCR result: %.*s\n", event_data_len, (char *)event_data);
    } else if (event & CV_CB_EVENT_STITCH_FRAME) {
        // 拼接帧数据
        printf("Stitch frame received, len: %d\n", event_data_len);
    } else if (event & CV_CB_EVENT_STATUS) {
        // 状态变更
        uint32_t status = *(uint32_t *)event_data;
        printf("CV status: %u\n", status);
    } else if (event & CV_CB_EVENT_FRAME_DONE) {
        // AP 处理完帧
        uint32_t fb_addr = *(uint32_t *)event_data;
        printf("Frame done, fb_addr: 0x%x\n", fb_addr);
    } else if (event & CV_CB_EVENT_IMG_SAVE) {
        // AP 回传处理后的图像
        cv_img_save_info_t *info = (cv_img_save_info_t *)event_data;
        printf("Image saved: type=%d, %dx%d, addr=0x%x\n",
               info->img_type, info->width, info->height, info->img_addr);
    }
}

// 注册回调
int ret = acomp_cv_add_callback(
    CV_CB_EVENT_OCR_RESULT |
    CV_CB_EVENT_STITCH_FRAME |
    CV_CB_EVENT_STATUS |
    CV_CB_EVENT_FRAME_DONE |
    CV_CB_EVENT_IMG_SAVE,
    cv_event_handler,
    NULL
);

4. 启动 CV 服务

完整的启动流程:

#include "cv/acomp_cv.h"

void start_cv_service(acomp_ipc_prepare_t *prepare)
{
    int ret;
    
    // 1. 就绪组件(传入 prepare 数据,初始化算法资源)
    ret = acomp_cv_prepare(prepare);
    if (ret != ACOMP_ERR_OK) {
        printf("CV prepare failed: %d\n", ret);
        return;
    }
    
    // 2. 启动 CV
    ret = acomp_cv_start();
    if (ret != ACOMP_ERR_OK) {
        printf("CV start failed: %d\n", ret);
        return;
    }
    
    printf("CV service started\n");
}

void stop_cv_service(void)
{
    int ret;
    
    // 1. 停止 CV
    ret = acomp_cv_stop();
    if (ret != ACOMP_ERR_OK) {
        printf("CV stop failed: %d\n", ret);
        return;
    }
    
    // 2. 清理资源
    ret = acomp_cv_cleanup();
    if (ret != ACOMP_ERR_OK) {
        printf("CV cleanup failed: %d\n", ret);
        return;
    }
    
    printf("CV service stopped\n");
}

5. 配置扫描参数

设置扫描模式和相关参数:

#include "cv/acomp_cv.h"

void configure_cv(void)
{
    // 设置扫描模式
    acomp_cv_set_scan_mode(0);

    // 设置左右模式
    acomp_cv_set_lr_mode(0);

    // 设置屏幕高度
    acomp_cv_set_screen_height(240);

    // 设置启动类型(0=正常启动, 1=快扫启动)
    acomp_cv_set_boot_type(0);
}

6. 创建图像输入流并发送图像数据

使能 M2R (Master to Remote) 流通道,用于将图像数据发送给 AP 核:

#include "cv/acomp_cv.h"

#define TX_STREAM_CH_INDEX    0
#define TX_STREAM_CH_NAME     "stream.cv_image"

void setup_tx_stream(void)
{
    // 1. 创建 M2R 流通道描述符
    acomp_stream_chn_create_desc_t desc = {
        .cname = TX_STREAM_CH_NAME,
        .direction = ACOMP_STREAM_DIRECTION_M2R,  // Master to Remote
        .index = TX_STREAM_CH_INDEX,
        .buffer_size = 320 * 240 * 2,   // 图像缓冲区大小
        .num_descs = 4,                  // 缓冲区描述符数量
        .kick_policy = 1,               // 自动触发
    };
    
    // 2. 使能流通道
    int ret = acomp_cv_stream_ch_enable(TX_STREAM_CH_INDEX, &desc);
    if (ret != ACOMP_ERR_OK) {
        printf("Failed to enable tx stream: %d\n", ret);
    }
}

void send_image_to_cv(uint8_t *image_data, uint32_t image_len)
{
    uint8_t *buffer;
    uint32_t buf_size;
    uint16_t desc_idx;
    
    // 1. 分配发送缓冲区
    buffer = acomp_cv_stream_tx_buffer_alloc(TX_STREAM_CH_INDEX,
                                              &buf_size, &desc_idx);
    if (buffer && buf_size > 0) {
        // 2. 填充图像数据到缓冲区
        memcpy(buffer, image_data, image_len);
        
        // 3. 提交缓冲区发送
        acomp_cv_stream_tx_buffer_submit(TX_STREAM_CH_INDEX,
                                          buffer, image_len, desc_idx);
    }
}

API 参考

生命周期管理

acomp_cv_init

int acomp_cv_init(void);

功能:初始化 CV 组件

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_NO_MEM:内存不足

  • ACOMP_ERR_INVALID_STATE:无效状态

  • ACOMP_ERR_NOT_FOUND:设备未找到

acomp_cv_prepare

int acomp_cv_prepare(acomp_ipc_prepare_t *prepare);

功能:就绪 CV 组件,将调用方提供的 prepare 数据发送给 AP 端初始化算法资源

说明:在调用 acomp_cv_start() 之前必须调用此函数

参数

  • prepare:调用方提供的 prepare 数据结构指针

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_NO_MEM:内存不足

  • ACOMP_ERR_INVALID_STATE:无效状态

acomp_cv_cleanup

int acomp_cv_cleanup(void);

功能:复位 CV 组件,释放内存块及算法资源

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_INVALID_STATE:无效状态

acomp_cv_start

int acomp_cv_start(void);

功能:启动 CV 组件

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_INVALID_STATE:无效状态

acomp_cv_stop

int acomp_cv_stop(void);

功能:停止 CV 组件

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_INVALID_STATE:无效状态

参数配置

acomp_cv_set_scan_mode

int acomp_cv_set_scan_mode(uint8_t scan_mode);

功能:设置扫描模式

参数

  • scan_mode:扫描模式值

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_INVALID_ARG:参数错误

acomp_cv_set_lr_mode

int acomp_cv_set_lr_mode(uint8_t lr_mode);

功能:设置左右模式

参数

  • lr_mode:左右模式值

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_INVALID_ARG:参数错误

acomp_cv_set_screen_height

int acomp_cv_set_screen_height(uint32_t screen_height);

功能:设置屏幕高度

参数

  • screen_height:屏幕高度(像素)

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_INVALID_ARG:参数错误

acomp_cv_set_boot_type

int acomp_cv_set_boot_type(uint8_t boot_type);

功能:设置启动类型

参数

  • boot_type:启动类型

    • 0:正常启动

    • 1:快扫启动

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_INVALID_ARG:参数错误

事件回调

acomp_cv_add_callback

int acomp_cv_add_callback(uint32_t events, cv_event_cb_t cb, void *priv);

功能:添加事件回调函数

参数

  • events:事件位掩码,可同时注册多个事件

    • CV_CB_EVENT_OCR_RESULT:OCR 识别结果返回

    • CV_CB_EVENT_STITCH_FRAME:拼接帧数据返回

    • CV_CB_EVENT_STATUS:状态变更通知

    • CV_CB_EVENT_STREAM_UPDATE:数据流更新

    • CV_CB_EVENT_FRAME_DONE:AP 处理完帧通知

    • CV_CB_EVENT_IMG_SAVE:AP 回传 CV 处理后的图像

  • cb:回调函数指针

  • priv:回调函数的私有数据指针

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_NO_MEM:内存不足

  • ACOMP_ERR_INVALID_ARG:参数错误

  • ACOMP_ERR_INVALID_STATE:无效状态

acomp_cv_remove_callback

int acomp_cv_remove_callback(cv_event_cb_t cb);

功能:移除回调函数

参数

  • cb:待移除的回调函数

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_INVALID_ARG:参数错误

  • ACOMP_ERR_INVALID_STATE:无效状态

图像流管理

acomp_cv_stream_ch_enable

int acomp_cv_stream_ch_enable(int chn, acomp_stream_chn_create_desc_t *desc);

功能:使能图像流通道

参数

  • chn:通道索引

  • desc:通道描述符指针

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_INVALID_STATE:无效状态

  • ACOMP_ERR_INVALID_ARG:参数错误

  • ACOMP_ERR_CREATE_STREAM_FAILED:创建流失败

acomp_cv_stream_ch_disable

int acomp_cv_stream_ch_disable(int chn);

功能:禁用图像流通道

参数

  • chn:通道索引

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_INVALID_STATE:无效状态

  • ACOMP_ERR_INVALID_ARG:参数错误

acomp_cv_stream_tx_buffer_alloc

void* acomp_cv_stream_tx_buffer_alloc(int chn, uint32_t* len, uint16_t* desc_idx);

功能:分配 TX 流缓冲区用于向 AP 核发送图像数据

参数

  • chn:通道索引

  • len:可用缓冲区长度指针(输出)

  • desc_idx:描述符索引指针(输出)

返回值:缓冲区指针,失败返回 NULL

acomp_cv_stream_tx_buffer_submit

int acomp_cv_stream_tx_buffer_submit(int chn, void* buffer, uint32_t len, uint16_t desc_idx);

功能:提交 TX 流缓冲区发送图像数据到 AP 核

参数

  • chn:通道索引

  • buffer:缓冲区指针

  • len:数据长度

  • desc_idx:描述符索引

返回值

  • ACOMP_ERR_OK:成功

  • ACOMP_ERR_INVALID_STATE:无效状态

  • ACOMP_ERR_INVALID_ARG:参数错误

使用示例

完整示例

#include "acomp.h"
#include "cv/acomp_cv.h"
#include "lisa_log.h"

#define TAG "cv_demo"

// 事件回调函数
void cv_event_handler(uint32_t event, void *event_data,
                      uint32_t event_data_len, void *priv)
{
    if (event & CV_CB_EVENT_OCR_RESULT) {
        LISA_LOGI(TAG, "OCR result: %.*s", event_data_len, (char *)event_data);
    } else if (event & CV_CB_EVENT_STITCH_FRAME) {
        LISA_LOGI(TAG, "Stitch frame received, len: %d", event_data_len);
    } else if (event & CV_CB_EVENT_STATUS) {
        uint32_t status = *(uint32_t *)event_data;
        LISA_LOGI(TAG, "CV status changed: %u", status);
    } else if (event & CV_CB_EVENT_FRAME_DONE) {
        uint32_t fb_addr = *(uint32_t *)event_data;
        LISA_LOGI(TAG, "Frame done, fb_addr: 0x%x", fb_addr);
    } else if (event & CV_CB_EVENT_IMG_SAVE) {
        cv_img_save_info_t *info = (cv_img_save_info_t *)event_data;
        LISA_LOGI(TAG, "Image saved: type=%d, %dx%d",
                  info->img_type, info->width, info->height);
    }
}

void cv_demo(void)
{
    int ret;
    
    // 1. 初始化 ACOMP 框架
    acomp_init();
    
    // 2. 初始化 CV 组件
    ret = acomp_cv_init();
    if (ret != ACOMP_ERR_OK) {
        LISA_LOGE(TAG, "CV init failed: %d", ret);
        return;
    }
    
    // 3. 注册事件回调
    ret = acomp_cv_add_callback(
        CV_CB_EVENT_OCR_RESULT |
        CV_CB_EVENT_STITCH_FRAME |
        CV_CB_EVENT_STATUS |
        CV_CB_EVENT_FRAME_DONE |
        CV_CB_EVENT_IMG_SAVE,
        cv_event_handler,
        NULL
    );
    if (ret != ACOMP_ERR_OK) {
        LISA_LOGE(TAG, "Add callback failed: %d", ret);
        return;
    }
    
    // 4. 构建 prepare 数据并就绪组件
    // (prepare 数据的构建取决于具体业务需求)
    // acomp_cv_prepare(prepare);
    
    // 5. 启动 CV
    ret = acomp_cv_start();
    if (ret != ACOMP_ERR_OK) {
        LISA_LOGE(TAG, "CV start failed: %d", ret);
        return;
    }
    LISA_LOGI(TAG, "CV started");
    
    // 6. 配置扫描参数
    acomp_cv_set_scan_mode(0);
    acomp_cv_set_lr_mode(0);
    acomp_cv_set_screen_height(240);
    acomp_cv_set_boot_type(0);
    
    // 7. 创建图像输入流
    acomp_stream_chn_create_desc_t desc = {
        .cname = "stream.cv_image",
        .direction = ACOMP_STREAM_DIRECTION_M2R,
        .index = 0,
        .buffer_size = 320 * 240 * 2,
        .num_descs = 4,
        .kick_policy = 1,
    };
    acomp_cv_stream_ch_enable(0, &desc);
    
    // 8. 发送图像数据(示例)
    // send_image_to_cv(...);
    
    // 9. 运行一段时间
    vTaskDelay(pdMS_TO_TICKS(10000));
    
    // 10. 停止 CV
    acomp_cv_stop();
    LISA_LOGI(TAG, "CV stopped");
    
    // 11. 清理资源
    acomp_cv_cleanup();
    LISA_LOGI(TAG, "CV cleaned up");
}

注意事项

  1. 初始化顺序:必须先调用 acomp_init() 初始化 ACOMP 框架,再调用 acomp_cv_init()

  2. 生命周期管理:启动前必须调用 acomp_cv_prepare(),停止后应调用 acomp_cv_cleanup() 释放资源

  3. prepare 参数acomp_cv_prepare() 需要传入 acomp_ipc_prepare_t 结构指针,包含算法所需资源信息

  4. 状态检查:所有 API 都会进行状态检查,确保在正确的状态下调用相应的函数

  5. 事件回调:回调函数在组件内部线程中执行,应避免长时间阻塞操作

  6. 内存管理:组件使用动态内存分配(PSRAM),确保系统有足够的堆内存

  7. 资源配置:确保配置的模型地址和大小与实际资源匹配

  8. 图像流管理:使用图像流接口时,必须成对调用 alloc/submit

  9. 跨核通信:图像流基于 IPC 机制,注意跨核数据传输的延迟和同步问题

  10. 双核协作:本组件运行在 CP 核,需要 AP 核提供算法支持,确保 AP 核固件正确烧录

常见问题

Q: CV 组件初始化失败怎么办?

A: 检查以下几点:

  • 确保 ACOMP 框架已正确初始化

  • 检查系统是否有足够的堆内存(PSRAM)

  • 确认 AP 核固件和算法模型已正确烧录

Q: OCR 识别没有返回结果?

A: 检查:

  • 确认已正确注册 CV_CB_EVENT_OCR_RESULT 事件回调

  • 检查图像数据格式和尺寸是否正确

  • 确认 OCR 模型资源已正确配置

  • 检查图像质量(光线充足、文字清晰)

Q: 图像拼接效果不好怎么办?

A: 建议:

  • 确保相邻帧之间有足够的重叠区域

  • 检查扫描速度是否过快

  • 调整扫描模式和左右模式设置

  • 确保图像质量良好

Q: 如何切换正常扫描和快扫模式?

A: 使用 acomp_cv_set_boot_type() 函数:

// 正常启动
acomp_cv_set_boot_type(0);

// 快扫启动
acomp_cv_set_boot_type(1);

依赖项

  • ACOMP 框架:算法组件框架

  • ACOMP Stream IPC:图像流跨核通信

  • lisa_log:日志系统

  • FreeRTOS:实时操作系统

  • CV 算法库:计算机视觉算法引擎

  • 摄像头驱动:图像采集硬件驱动