# 人脸识别演示 ## 功能说明 本演示是一个完整的人脸识别应用,集成了摄像头图像采集、人脸检测、活体检测、特征提取、人脸识别和可视化显示等功能。用户可通过按键来体验人脸注册和人脸识别的功能。 ## 系统架构 ``` ┌─────────────┐ │ 摄像头模块 │ GC0328 采集图像 (320x240) └─────────────┘ │ ├──────────────────────────────────┐ │ │ │ (实时图像) │ (部分图像) ▼ ▼ ┌─────────────┐ ┌────────────────────────┐ │ 显示模块 │ │ 人脸识别算法模块 (AP) │ │ (ST7789 LCD) │◄────────────────│ 检测/对齐/活体/特征/对比 │ └─────────────┘ (人脸结果) └────────────────────────┘ ▲ ▲ │ │ │ │ (控制命令) │ (显示控制) │ │ │ ┌─────────────┐ │ │ 按键模块 │───────────────────────────┘ │ (ADC 检测) │ K1:人脸注册 └─────────────┘ ``` ## 主要功能 #### 1. 实时图像采集 - GC0328 摄像头通过 DVP 接口连续采集 320x240 分辨率图像 - 支持输出 YUYV422彩色模式、YUYV422灰度模式(其中Cb=0, Cr=0)、RGB565模式,最大支持640*480分辨率 - 输出的每帧图片实时发给显示屏显示,同时部分图片也通过 IPC 自动发送到 AP 核进行算法处理 #### 2. 人脸识别算法 人脸识别算法在 AP 核上运行,对每帧图像执行完整的人脸识别流程: - **人脸检测**:检测图像中的人脸位置和大小,输出人脸矩形框坐标和置信度得分 - **人脸对齐**:定位 68 个人脸关键点(眼睛、鼻子、嘴巴、轮廓等),计算头部姿态角(yaw/pitch/roll) - **活体检测**:判断人脸是否为真人,输出活体检测结果(0、1)和得分,防止照片、视频攻击 - **特征提取**:提取 384 维人脸特征向量,用于唯一标识一个人脸 - **特征比对**:将提取的特征与已注册人脸进行相似度比对,输出比对得分( 阈值由用户决定,超过阈值则认为是同一人) 目前暂时支持注册最多 10 个人脸特征到内存库,可配置活体检测阈值以适应不同应用场景。 #### 3. 交互式操作 - **K1 键**:抓取当前检测到的人脸特征并尝试注册,注册成功后会加载到算法对比库 - **K2 键**:在手动比对模式下触发一次人脸比对 - **K3 键**:在手动比对模式下切换彩色/灰度显示 #### 4. 可视化显示 - LCD 显示字符来提示用户怎么操作 - LCD 实时显示摄像头采集的画面 - LCD 实时显示人脸检测算法的处理结果,包括检测到的人脸个数、人脸框、活体状态,活体得分等信息; - 如果已注册有人脸,LCD也会显示检测到人脸的比对得分,超过对比阈值则人脸框显示为绿色(并且显示人脸ID),否则显示为红色 - LCD 显示K1按键的人脸注册个数 ## 硬件连接 - **开发板**: ARCS-EVB开发板 - **摄像头**: GC0328 摄像头模块,通过 DVP 接口连接 - **显示屏**: ST7789 LCD 显示屏,通过 SPI 接口连接 - **按键**: 通过 ADC 引脚检测,支持 3 个按键(有效按键取决于配置模式) - **K1**: 注册人脸特征 - **K2**: 手动触发人脸识别(仅 `CONFIG_ONLY_FACE_REGISTER=n` 时生效) - **K3**: 切换彩色/灰度显示(仅 `CONFIG_ONLY_FACE_REGISTER=n` 时生效) - **调试串口**: - CP 核 UART0 (PA3),波特率 921600 - AP 核 UART1 (PA21),波特率 921600 ## 使用说明 1. 上电后系统自动初始化,LCD 显示摄像头实时画面 2. 检测到人脸时,屏幕显示绿色矩形框 3. 按 `K1` 注册当前人脸特征(最多 10 个) 4. 按模式执行比对: - **自动比对模式**(`CONFIG_ONLY_FACE_REGISTER=y`,默认):仅 `K1` 有效,注册后每帧自动进行比对并实时显示结果 - **手动比对模式**(`CONFIG_ONLY_FACE_REGISTER=n`):`K1` 注册、`K2` 触发比对、`K3` 切换彩色/灰度 > 注意: 本demo没有使用持久化存储注册的人脸特征,因此每次上电后需重新注册人脸 ## 人脸注册与比对判定条件 ### K1 注册成功条件(需同时满足) - 当前帧检测到人脸(`results_cnt > 0`) - 人脸质量满足: - `face_score > 0.8` - `n_align_point > 0` - 姿态角范围:`-30 < yaw/pitch/roll < 30` - 活体结果有效:`live_result.status == 1` - 特征点有效:`feature_cnt > 0` - 未超过最大注册数量:`fd_feature_cnt < ACOMP_FD_MAX_RESULT_CNT` - 若当前人脸与已注册库存在比对结果(`compare_cnt > 0`),则最大相似度必须不高于阈值(避免重复注册): - `max_register_score <= CONFIG_FACE_COMPARE_SCORE_THRESHOLD / 100` ### K1 注册失败条件(任一触发即失败) - 未检测到人脸 - 人脸质量条件任一不满足(分数、关键点、姿态、活体、特征点) - 识别为“已注册同一人”(`max_register_score` 高于比对阈值) - 已达到最大可注册数量(`ACOMP_FD_MAX_RESULT_CNT`) ### 比对成功条件 - 自动模式(`CONFIG_ONLY_FACE_REGISTER=y`):每帧自动计算与已注册库的最大相似度,`max_score > CONFIG_FACE_COMPARE_SCORE_THRESHOLD / 100` 判定为成功(显示绿色) - 手动模式(`CONFIG_ONLY_FACE_REGISTER=n`):按 `K2` 后,先满足以下前置条件: - 检测到人脸且质量有效:`face_score > 0.8`、`n_align_point > 0`、姿态角在 `(-30, 30)`、`live_result.status == 1`、`feature_cnt > 0` - 存在可比对特征:`compare_cnt > 0` - 然后按阈值判断:`max_score > CONFIG_FACE_COMPARE_SCORE_THRESHOLD / 100` 为成功 ### 比对失败条件 - 不满足前置条件(未检测到人脸、质量不达标、无可比对特征) - 或者最大相似度未超过阈值:`max_score <= CONFIG_FACE_COMPARE_SCORE_THRESHOLD / 100` ## 编译运行 ```bash ./build.sh -C -S demos/face_detect -DBOARD=arcs_evb ``` ## 烧录固件 ### 1. 烧录 AP 核固件(Boot Core) ```bash cskburn -s /dev/ttyACM0 -b 3000000 -C arcs 0x00 ./res/ap.bin ``` ### 2. 烧录算法资源到 Flash ```bash # 人脸检测模型 cskburn -s /dev/ttyACM0 -b 3000000 -C arcs 0x100000 ./res/algo/face_detect_thinker.bin # 人脸对齐模型 cskburn -s /dev/ttyACM0 -b 3000000 -C arcs 0x170000 ./res/algo/face_align_thinker.bin # 活体检测模型 cskburn -s /dev/ttyACM0 -b 3000000 -C arcs 0x200000 ./res/algo/face_nir_thinker_lineart_split.bin # 人脸特征提取模型 cskburn -s /dev/ttyACM0 -b 3000000 -C arcs 0x2C0000 ./res/algo/face_verify_thinker.bin ``` ### 3. 烧录 CP 核固件 ```bash cskburn -s /dev/ttyACM0 -b 3000000 -C arcs 0x800000 ./build/arcs.bin ``` ## 预期输出 **CP 核串口输出:** ``` ********Arcs SDK 0.1.1 @ v0.0.1-860-gd85bab268324******** Running on hart-id: 1 I/lisa_adc_arcs [00:00:00.035 1 none] ADC0 initialized successfully I/lisa_i2c_arcs [00:00:00.037 1 none] I2C0 initialized successfully I/lisa_pwm_arcs [00:00:00.037 1 none] PWM0 initialized successfully (max_channels=8) I/lisa_touch_cst328 [00:00:00.039 1 none] CST328 touch device initialized successfully I/lisa_dvp [00:00:00.040 1 none] DVP driver initialized CP=======! Hard ID: 1 I/main [00:00:00.042 1 main] ic_message_init done! I/acomp_ipc [00:00:02.044 1 rpc_client] [0]acomp remote dev index 1 name acomp.fd I/acomp_fd [00:00:02.045 1 main] acomp fd init enter I/acomp_fd [00:00:02.046 1 main] acomp fd dev index 1,name:acomp.fd I/acomp_fd [00:00:02.047 1 main] acomp fd dev index 1,name:acomp.fd I/acomp_fd [00:00:02.049 1 main] acomp fd init exit I/acomp_fd [00:00:02.050 1 main] acomp fd prepare enter I/acomp_fd [00:00:02.051 1 main] acomp fd prepare:0x2895ea60, size:128 I/acomp_fd [00:00:02.053 1 main] acomp fd prepare exit I/acomp_fd [00:00:02.054 1 main] acomp fd start enter I/acomp_fd [00:00:02.254 1 main] acomp fd start exit I/acomp_fd [00:00:02.255 1 main] acomp fd add callback enter I/acomp_fd [00:00:02.256 1 main] acomp fd add callback exit I/acomp_fd [00:00:02.257 1 main] acomp_fd_live_detect_mode_set enter I/acomp_fd [00:00:02.259 1 main] acomp_fd_live_detect_mode_set exit INF:[acomp_stream_channel_create]Acomp stream channel 0x289fcbb4,0x2001b450,'stream.fd_image' created successfully ,vring phy addr=0x28966aa0, num_descs=4, buffer_size=153632, direction=M2R, role=Master I/acomp_fd [00:00:02.272 1 main] acomp_fd_stream_ch_enable chn(stream.fd_image) index(0),desc(0x2892e280) I/lvgl_heap [00:00:02.274 1 main] LVGL heap initialized: 1048576 bytes at 0x0x28825800 spi_set_bus_speed: sclk_div = 0 (sclk: 50000000), cs2sclk = 1 I/panel.st7789p3 [00:00:02.536 1 main] ST7789P3 initialized I/lisa_i2c_arcs [00:00:02.540 1 main] I2C configured: speed=100000 Hz, mode=master I/lisa_touch_cst328 [00:00:02.541 1 main] I2C auto-configured (speed: 100000 Hz) I/lisa_touch_cst328 [00:00:02.762 1 main] CST328 hardware reset completed I/lisa_touch_cst328 [00:00:02.763 1 main] CST328 interrupt pin configured I/lisa_touch_cst328 [00:00:02.764 1 main] CST328 touch bus attached successfully I/lisa_touch_cst328 [00:00:02.765 1 main] CST328 read task created with priority 2 I/lisa_touch_cst328 [00:00:02.766 1 main] CST328 interrupt mode enabled I/lisa_touch_cst328 [00:00:02.767 1 main] CST328 touch device enabled I/lvgl8_demo_widgets [00:00:02.774 1 task_ui] LVGL screen loaded and started I/sample [00:00:02.788 1 main] === LISA ADC read example === I/sample [00:00:02.789 1 main] adc0 device ready I/lisa_adc_arcs [00:00:02.790 1 main] Channel 4 configured: ref=1, 10-bit, vref_sel=0, vin_buf=1 I/sample [00:00:02.791 1 main] Channel 4 configured: 3.6V reference, 10-bit resolution I/btn [00:00:02.793 1 btn] lisa_btn_task enter I/btn [00:00:02.794 1 main] lisa btn init done I/video_camera [00:00:02.795 1 main] camera config: w:320, h:240, format:2 I/video_camera [00:00:02.796 1 main] camera device ready I/video_camera [00:00:02.797 1 main] Setting up camera... I/lisa_camera [00:00:02.799 1 main] PWDN pin initialized (pin=7) I/lisa_camera [00:00:02.800 1 main] Enabling XCLK output: 8000000 Hz I/lisa_i2c_arcs [00:00:02.802 1 main] I2C configured: speed=400000 Hz, mode=master I/lisa_camera [00:00:02.805 1 main] Sensor detected: PID=0x009D, addr=0x21 I/lisa_camera [00:00:03.308 1 main] Detected sensor frame size: 640x480 I/lisa_camera [00:00:03.311 1 main] Detected sensor pixel format: 0 I/lisa_camera [00:00:03.312 1 main] Frame buffers initialized: count=4, size=614400 I/lisa_camera [00:00:03.313 1 main] Camera setup completed I/video_camera [00:00:03.314 1 main] Camera capabilities: max_width=640, max_height=480, supported_formats=0x00000005 I/video_camera [00:00:03.316 1 main] crop:x:160, y:120, w:320, h:240 I/lisa_camera [00:00:03.325 1 main] Frame buffers re-initialized: count=4, size=153600 I/lisa_camera [00:00:03.390 1 main] Frame buffers re-initialized: count=4, size=153600 I/video_camera [00:00:03.471 1 main] camera_set_gray on success I/lisa_dvp [00:00:03.473 1 main] DVP initialized successfully with GPDMA channel 2 I/lisa_dvp [00:00:03.474 1 main] DVP clockout enabled successfully, freq: 8000000 Hz I/video_camera [00:00:03.476 1 camera_task] Capture task started I/video_camera [00:00:03.477 1 main] Camera capture started with internal task I/video_camera [00:00:03.479 1 main] Frame callback registered I/lisa_dvp [00:00:03.480 1 main] DVP capture started I/camera_bus_dvp [00:00:03.481 1 main] DVP Normal capture started I/lisa_camera [00:00:03.482 1 main] Camera started I/camera_bus_dvp [00:00:03.754 1 isr] dvp_event_callback: event:1 I/camera_bus_dvp [00:00:03.881 1 isr] dvp_event_callback: event:1 I/camera_bus_dvp [00:00:04.008 1 isr] dvp_event_callback: event:1 I/main [00:00:04.102 1 rpc_client] fd result:2732, I/camera_bus_dvp [00:00:04.135 1 isr] dvp_event_callback: event:1 I/camera_bus_dvp [00:00:04.261 1 isr] dvp_event_callback: event:1 I/main [00:00:04.361 1 rpc_client] fd result:2732, ``` **LCD 屏幕显示:** - 实时显示摄像头采集的画面 - 实时显示检测到的人脸的黄色方框 - 屏幕左上方会实时显示检测到的人脸个数、活体状态和活体得分 - 按下K1键时,如果检测到人脸符合注册条件,会注册人脸特征,并在屏幕左下角显示"Registered: "个数会增加 - 如果已注册有人脸,LCD也会显示检测到人脸的比对得分,超过对比阈值则人脸框显示为绿色(并且显示人脸ID),否则显示为红色 ## 核心 API | API | 说明 | |-----|------| | `acomp_fd_init()` | 初始化人脸识别组件 | | `acomp_fd_prepare()` | 准备人脸识别算法资源 | | `acomp_fd_start()` | 启动人脸识别算法 | | `acomp_fd_add_callback()` | 注册人脸识别结果回调函数 | | `acomp_fd_live_detect_mode_set()` | 配置活体检测模式 | | `acomp_fd_stream_ch_enable()` | 使能图像输入数据流通道 | | `acomp_fd_stream_tx_buffer_alloc()` | 分配发送缓冲区 | | `acomp_fd_stream_tx_buffer_submit()` | 提交图像数据到算法 | | `acomp_fd_features_load()` | 导入人脸特征到注册库 | ## 关键代码 ```c /* 1. 初始化 */ acomp_init(); acomp_fd_init(); acomp_fd_prepare(); acomp_fd_start(); acomp_fd_add_callback(FD_CB_EVENT_ENGINE_RLT, fd_event_handler, NULL); /* 2. 配置活体检测 */ acomp_fd_live_detect_mode_t mode = {.enable = true, .score_threshold = {0.51, 0.1}}; acomp_fd_live_detect_mode_set(&mode); /* 3. 摄像头回调 - 发送图像到算法 */ void video_camera_cb(uint8_t *data, uint32_t len) { uint8_t *buffer = acomp_fd_stream_tx_buffer_alloc(0, &buf_size, &desc_idx); acomp_fd_input_frame_t *frame = (acomp_fd_input_frame_t *)buffer; frame->format = PIX_FMT_YUV422_YUYV_PACKED; frame->width = 320; frame->height = 240; frame->length = len; memcpy(frame->data, data, len); acomp_fd_stream_tx_buffer_submit(0, buffer, buf_size, desc_idx); } /* 4. 结果回调 - 更新显示 */ void fd_event_handler(uint32_t event, void *event_data, uint32_t len, void *priv) { if (event & FD_CB_EVENT_ENGINE_RLT) { acomp_fd_result_info_t *info = (acomp_fd_result_info_t *)event_data; if (info->results_cnt > 0) { screen_update_fd_info(info, image_data, width, height, len); } } } ``` ## 配置说明 ### 算法资源地址配置 在 `prj.conf` 中配置算法模型在 Flash 中的地址: ``` CONFIG_ACOMP_FD_RES_FACE_DETECT_ADDRESS=0x30100000 CONFIG_ACOMP_FD_RES_FACE_DETECT_LENGTH=450936 CONFIG_ACOMP_FD_RES_FACE_ALIGN_ADDRESS=0x30170000 CONFIG_ACOMP_FD_RES_FACE_ALIGN_LENGTH=557304 CONFIG_ACOMP_FD_RES_FACE_LIVE_ADDRESS=0x30200000 CONFIG_ACOMP_FD_RES_FACE_LIVE_LENGTH=734752 CONFIG_ACOMP_FD_FACE_VERIFY_ADDRESS=0x302C0000 CONFIG_ACOMP_FD_FACE_VERIFY_LENGTH=1936808 ``` ### 摄像头配置 ``` CONFIG_IMAGE_WIDTH=320 CONFIG_IMAGE_HEIGHT=240 CONFIG_IMAGE_FORMAT=2 # YUYV422 CONFIG_FACE_LIVE_DETECT_ENABLE=y # 启用活体检测 CONFIG_FACE_LIVE_DETECT_THRESHOLD=50 # 活体检测阈值 CONFIG_FACE_COMPARE_SCORE_THRESHOLD=90 # 人脸比对得分阈值 CONFIG_ONLY_FACE_REGISTER=y # 只注册人脸,后续自动进行人脸识别(不需要用K2按键触发) ``` ### PSRAM 堆大小配置 ``` CONFIG_PSRAM_HEAP_SIZE=0x680000 ``` ## 注意事项 1. **算法资源烧录**: 必须先烧录 AP 核固件和 4 个算法模型资源到 Flash,否则人脸识别算法无法正常运行 2. **内存配置**: 人脸识别算法需要较大的 PSRAM 空间,建议配置 `CONFIG_PSRAM_HEAP_SIZE` 不小于 0x680000 3. **摄像头连接**: 确保 GC0328 摄像头正确连接到 DVP 接口,否则无法采集图像 4. **显示屏连接**: 确保 ST7789 LCD 正确连接到 SPI 接口,否则无法显示画面 5. **活体检测阈值**: 活体得分阈值需要根据实际应用场景调整,阈值越高误识率越低但拒识率越高 6. **双核协作**: 本示例运行在 CP 核 (HARTID=1),需要 AP 核 (HARTID=0) 提供算法支持 7. **特征注册**: 最多支持 10 个注册人脸,每个人脸特征为 384 维浮点数 8. **回调函数**: 人脸识别结果通过回调函数异步返回,不要在回调中执行耗时操作 ## 相关文档 - [ACOMP FD 组件文档](../../components/acomp/fd/README.rst) - 包含接口定义、参数说明与基础调用方式 - [Face Detect Sample 文档](../../samples/algorithms/face_detect/README.md) - 包含资源烧录地址、配置项和基础示例流程