计算机视觉组件
简介
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 |
|
原始图像 |
1 |
|
拼接图像 |
2 |
|
切割线图像 |
配置选项
基本配置
配置项 |
说明 |
默认值 |
|---|---|---|
|
启用 ACOMP 组件 |
n |
|
启用 ACOMP 组件的 CV 组件 |
n |
资源配置
配置项 |
说明 |
默认值 |
|---|---|---|
|
切割线模型 eMMC 地址 |
0x9b00000 |
|
切割线模型大小(字节) |
462320 |
|
OCR 模型 eMMC 地址 |
0xa000000 |
|
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");
}
注意事项
初始化顺序:必须先调用
acomp_init()初始化 ACOMP 框架,再调用acomp_cv_init()生命周期管理:启动前必须调用
acomp_cv_prepare(),停止后应调用acomp_cv_cleanup()释放资源prepare 参数:
acomp_cv_prepare()需要传入acomp_ipc_prepare_t结构指针,包含算法所需资源信息状态检查:所有 API 都会进行状态检查,确保在正确的状态下调用相应的函数
事件回调:回调函数在组件内部线程中执行,应避免长时间阻塞操作
内存管理:组件使用动态内存分配(PSRAM),确保系统有足够的堆内存
资源配置:确保配置的模型地址和大小与实际资源匹配
图像流管理:使用图像流接口时,必须成对调用 alloc/submit
跨核通信:图像流基于 IPC 机制,注意跨核数据传输的延迟和同步问题
双核协作:本组件运行在 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 算法库:计算机视觉算法引擎摄像头驱动:图像采集硬件驱动