【CanMV K210】AI 视觉 68 点人脸关键点检测与轮廓定位

在 CanMV K210 入门和综合项目中,68 点人脸关键点检测是一个很适合拆解的 AI 视觉实验。它不是单纯判断"有没有人脸",而是先找到人脸位置,再在人脸区域内定位眼睛、鼻子、嘴巴和脸部轮廓等关键点。通过这个实验,可以理解摄像头采集、KPU 推理、人脸框裁剪、坐标还原和 LCD 可视化之间的完整关系。

本实验使用 CanMV K210 运行 4.11_face_detect_68lm.py。程序先加载人脸检测模型 /sd/yolo_face_detect.kmodel,检测画面中的人脸框;再加载 68 点关键点模型 /sd/landmark68.kmodel,对裁剪后的人脸区域进行关键点推理。程序会在 LCD 上绘制人脸检测框和 68 个关键点,同时在画面左上角显示 FPS,便于观察识别效果和运行速度。

学习目标 说明
理解双模型视觉流程 认识人脸检测模型和 68 点关键点模型之间的衔接关系
掌握人脸框裁剪 根据检测框扩大人脸区域,并裁剪出单独的人脸图像
理解模型输入尺寸 人脸检测模型使用 320×256 输入,关键点模型使用 128×128 人脸图像
学会坐标还原 将关键点模型输出的归一化坐标还原到原始画面位置
建立 AI 视觉调试思路 通过 LCD 显示框线、关键点和 FPS,观察模型运行状态

文章目录

理论基础

68 点人脸关键点检测并不是一步完成的。程序通常先进行人脸检测,找到画面中人脸的大致位置;再把人脸区域裁剪出来,缩放到关键点模型需要的输入尺寸;最后由关键点模型输出 68 组坐标。这样做的原因是关键点模型需要关注的是人脸局部区域,而不是整张图像中的所有背景。

本实验中使用了两个 KPU 模型。第一个模型 /sd/yolo_face_detect.kmodel 用于检测人脸框,输出人脸在画面中的位置和大小;第二个模型 /sd/landmark68.kmodel 用于检测人脸关键点,输出 68 个关键点的相对坐标。程序再根据裁剪区域的位置和尺寸,把这些相对坐标还原到原始图像中,并用圆点绘制在 LCD 画面上。

人脸检测模型和关键点模型的输入尺寸不同。摄像头采集的是 QVGA 图像,也就是 320×240;代码中额外创建了 od_img = image.Image(size=(320,256), copy_to_fb=False),用于适配人脸检测模型的 320×256 输入。检测到人脸后,程序从原图中裁剪人脸区域,并通过 resize(128, 128) 转换成关键点模型需要的 128×128 输入。

下面这张流程图可以从视觉数据链路理解本实验。摄像头负责采集画面,人脸检测模型负责找到人脸框,关键点模型负责定位 68 个点,绘图函数负责把框和点画回原始图像,LCD 负责显示最终结果。
摄像头采集

sensor.snapshot()
原始图像

320×240 QVGA
检测输入图

od_img 320×256
人脸检测模型

yolo_face_detect.kmodel
人脸框结果

x / y / w / h
扩大人脸框

extend_box()
裁剪人脸区域

img.cut()
缩放到 128×128

face_cut.resize()
68点关键点模型

landmark68.kmodel
关键点输出

68组相对坐标
坐标还原

映射回原图位置
绘制结果

人脸框 / 关键点 / FPS
LCD 显示

lcd.display(img)
SD卡模型文件

/sd/*.kmodel

这条链路的关键在于坐标关系。人脸检测模型得到的是原始画面中的人脸框,关键点模型输出的是裁剪后人脸区域中的相对坐标。代码中使用 KPU.sigmoid(out[2 * j])KPU.sigmoid(out[2 * j + 1]) 得到归一化后的横纵坐标,再乘以裁剪区域的宽高,并加上裁剪区域左上角坐标 x1y1,最终得到关键点在原图中的位置。

硬件设施

本实验属于 AI 视觉类实验,核心硬件是 CanMV K210 开发板、摄像头模块、LCD 显示屏和 SD 卡中的模型文件。代码没有使用 GPIO、蜂鸣器、按键、电机或普通传感器,因此硬件检查重点不在排针接线,而在摄像头、LCD、模型文件路径和 KPU 推理环境。

硬件 / 软件 作用 说明
CanMV K210 开发板 实验运行平台 负责运行 MicroPython 程序,并通过 KPU 执行 AI 模型推理
摄像头模块 图像采集设备 通过 sensor.snapshot() 获取实时图像
LCD 显示屏 结果显示设备 通过 lcd.display(img) 显示人脸框、关键点和 FPS
SD 卡 模型文件存储 存放 /sd/yolo_face_detect.kmodel/sd/landmark68.kmodel
KPU 神经网络加速器 AI 推理单元 加载 kmodel 模型并执行人脸检测和关键点推理
sensor 模块 摄像头控制模块 用于摄像头初始化、图像格式设置和图像采集
image 模块 图像处理模块 用于创建图像、裁剪人脸、缩放图像和绘制图形
lcd 模块 显示控制模块 用于初始化 LCD 并显示处理后的图像
time 模块 帧率统计模块 通过 time.clock() 统计 FPS
gc 模块 内存管理模块 主循环中调用 gc.collect() 回收临时图像对象占用的内存

实验检查时,重点关注摄像头是否能正常采集图像,LCD 是否能正常显示画面,SD 卡中是否存在两个模型文件。KPU 模型路径必须和代码中的 load_kmodel() 参数一致,否则程序会在模型加载阶段失败。

资源 / 接口 代码对象 对应作用 检查重点
摄像头接口 sensor.reset() / sensor.snapshot() 初始化摄像头并采集图像 确认摄像头连接正常,画面能稳定输出
LCD 接口 lcd.init() / lcd.display(img) 显示图像和识别结果 确认 LCD 能显示摄像头画面
人脸检测模型 /sd/yolo_face_detect.kmodel 检测画面中的人脸框 文件必须存在于 SD 卡指定路径
关键点模型 /sd/landmark68.kmodel 输出 68 个人脸关键点 文件名和路径必须与代码一致
检测输入图像 od_img 提供 320×256 的人脸检测模型输入 用于适配模型输入尺寸
人脸裁剪图像 face_cut 从原图中裁剪人脸区域 裁剪范围由 extend_box() 计算
关键点输入图像 face_cut_128 提供 128×128 的关键点模型输入 裁剪后人脸图像需要缩放到模型尺寸

从硬件和资源关系看,本实验的核心不是接几根线,而是保证"摄像头输入、模型文件、KPU 推理、LCD 输出"这一条链路完整。如果 LCD 有画面但没有关键点,通常需要检查模型文件、检测阈值、光照和人脸距离;如果程序在启动阶段报错,通常需要优先检查模型路径和 SD 卡文件。

实验现象 正常表现 异常提示
程序启动 串口打印 ready load model 如果启动后卡住,检查模型文件是否存在
摄像头画面 LCD 显示实时画面 如果无画面,检查摄像头和 LCD 初始化
检测到人脸 人脸区域出现绿色矩形框 如果没有框,检查人脸距离、光照和检测阈值
关键点显示 人脸上出现红色圆点 如果有框无点,检查关键点模型是否加载成功
FPS 显示 左上角显示当前帧率 FPS 偏低时减少打印、减少绘制或优化画面条件
多次检测 程序持续刷新人脸框和关键点 如果运行一段时间后异常,注意内存回收和临时图像释放

软件代码

本实验的软件部分以 4.11_face_detect_68lm.py 为核心。程序导入 sensorimagetimelcdKPUgc,随后完成 LCD 初始化、摄像头初始化、人脸检测模型加载、68 点关键点模型加载,并在主循环中持续执行图像采集、模型推理、坐标还原和结果绘制。

软件环境 作用 检查重点
CanMV IDE 编辑、运行和调试 K210 程序 能识别开发板串口,并能运行基础摄像头示例
CanMV 固件 提供 sensorimagelcdKPU 等模块 固件环境需要支持 KPU 模型加载和图像处理
SD 卡 存放 kmodel 模型文件 /sd/yolo_face_detect.kmodel/sd/landmark68.kmodel 必须存在
摄像头环境 提供实时画面 sensor.snapshot() 能正常返回图像
LCD 显示环境 显示检测结果 lcd.display(img) 能正常刷新画面
串口终端 查看模型加载和检测输出 能看到 ready load modeldect: 等调试信息
python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ----湖南创乐博智能科技有限公司----
#  文件名:4.11_face_detect_68lm.py
#  版本:V2.0
#  author: zhulin
#  说明:CanMv K210 基于68点模型人脸检测实验
#####################################################
import sensor, image, time, lcd
from maix import KPU
import gc

lcd.init()                          # 初始化LCD显示屏
sensor.reset()                      # 复位并初始化摄像头
sensor.set_pixformat(sensor.RGB565) # 设置摄像头输出格式为 RGB565
sensor.set_framesize(sensor.QVGA)   # 设置摄像头输出大小为 QVGA (320x240)
sensor.skip_frames(time = 500)      # 等待摄像头稳定
clock = time.clock()                # 创建一个clock对象,用来计算帧率

#人脸检测模型需要320*256图输入,这里初始化一个image
od_img = image.Image(size=(320,256), copy_to_fb=False)

anchor = (0.893, 1.463, 0.245, 0.389, 1.55, 2.58, 0.375, 0.594, 3.099, 5.038, 0.057, 0.090, 0.567, 0.904, 0.101, 0.160, 0.159, 0.255)
# 创建一个kpu对象,用于人脸检测
kpu = KPU()
print("ready load model")
# 加载模型
kpu.load_kmodel("/sd/yolo_face_detect.kmodel")
# yolo2初始化
kpu.init_yolo2(anchor, anchor_num=9, img_w=320, img_h=240, net_w=320 , net_h=256 ,layer_w=10 ,layer_h=8, threshold=0.7, nms_value=0.2, classes=1)

# 创建一个kpu对象,用于人脸68关键点检测
lm68_kpu = KPU()
print("ready load model")
# 加载模型
lm68_kpu.load_kmodel("/sd/landmark68.kmodel")

RATIO = 0.08
def extend_box(x, y, w, h, scale):
    x1_t = x - scale*w
    x2_t = x + w + scale*w
    y1_t = y - scale*h
    y2_t = y + h + scale*h
    x1 = int(x1_t) if x1_t>1 else 1
    x2 = int(x2_t) if x2_t<320 else 319
    y1 = int(y1_t) if y1_t>1 else 1
    y2 = int(y2_t) if y2_t<256 else 255
    cut_img_w = x2-x1+1
    cut_img_h = y2-y1+1
    return x1, y1, cut_img_w, cut_img_h

while 1:
    gc.collect()
    #print("mem free:",gc.mem_free())
    clock.tick()                            # 更新计算帧率的clock
    img = sensor.snapshot()                 # 拍照,获取一张图像
    a =  od_img.draw_image(img, 0,0)        # 将img图像写到od_img图像的坐标(0,0)位置处
    od_img.pix_to_ai()                      # 对rgb565的image生成ai运算需要的r8g8b8格式存储
    kpu.run_with_output(od_img)             # 对输入图像进行kpu运算
    dect = kpu.regionlayer_yolo2()          # yolo2后处理
    fps = clock.fps()                       # 获取帧率
    if len(dect) > 0:
        print("dect:",dect)
        for l in dect :
            x1, y1, cut_img_w, cut_img_h = extend_box(l[0], l[1], l[2], l[3], scale=RATIO) # 扩大人脸框
            face_cut = img.cut(x1, y1, cut_img_w, cut_img_h) # 从img中裁剪出人脸图
            a = img.draw_rectangle(l[0],l[1],l[2],l[3], color=(0, 255, 0))
            face_cut_128 = face_cut.resize(128, 128)
            face_cut_128.pix_to_ai()
            out = lm68_kpu.run_with_output(face_cut_128, getlist=True)
            #print("out:",len(out))
            for j in range(68):
                x = int(KPU.sigmoid(out[2 * j])*cut_img_w + x1)
                y = int(KPU.sigmoid(out[2 * j + 1])*cut_img_h + y1)
                #a = img.draw_cross(x, y, size=1, color=(0, 0, 255))
                a = img.draw_circle(x, y, 2, color=(0, 0, 255), fill=True)
            del (face_cut_128)
            del (face_cut)

    a = img.draw_string(0, 0, "%2.1ffps" %(fps), color=(0, 60, 255), scale=2.0)
    lcd.display(img)
    gc.collect()

# 创建的kpu对象去初始化,释放模型内存
kpu.deinit()
lm68_kpu.deinit()

这段代码可以分成初始化、模型加载、人脸检测、人脸裁剪、关键点推理、坐标还原、结果绘制和 LCD 显示几个部分。初始化阶段完成 LCD 和摄像头设置,模型加载阶段分别创建 kpulm68_kpu 两个对象,主循环阶段不断获取图像并执行双模型推理。

od_img 是人脸检测模型的输入缓冲图像。摄像头采集到的 img 是 320×240,而人脸检测模型需要 320×256 的输入,因此代码创建了一个 320×256 的图像对象,并通过 od_img.draw_image(img, 0, 0) 把当前摄像头画面写入其中。随后调用 od_img.pix_to_ai(),把图像转换为 KPU 推理需要的数据格式。

人脸检测由 kpu.run_with_output(od_img)kpu.regionlayer_yolo2() 完成。regionlayer_yolo2() 返回的人脸检测结果保存在 dect 中,每个检测结果包含人脸框的位置和大小。代码用 draw_rectangle() 在原图上绘制绿色人脸框,方便观察模型是否找到了正确的人脸区域。

extend_box() 用于扩大检测框。人脸检测框通常只覆盖主要人脸区域,如果直接送入关键点模型,边缘轮廓可能不完整。代码通过 RATIO = 0.08 向四周略微扩大框,再使用 img.cut() 裁剪人脸图像。裁剪后的人脸图像被缩放为 128×128,并送入 landmark68.kmodel 执行关键点检测。

关键点模型输出的是 68 个点的相对位置,每个点由两个数值表示,分别对应 x 和 y。程序通过 KPU.sigmoid() 把输出结果转换到 0~1 范围,再乘以裁剪区域宽高,并加上裁剪区域左上角坐标,最终得到关键点在原始图像中的坐标。每个关键点通过 draw_circle() 绘制为红色小圆点。

函数 / 对象 功能 对应现象
lcd.init() 初始化 LCD 显示屏 LCD 准备显示摄像头画面
sensor.reset() 初始化摄像头 摄像头进入可采集状态
sensor.set_pixformat(sensor.RGB565) 设置图像格式 摄像头输出 RGB565 彩色图像
sensor.set_framesize(sensor.QVGA) 设置图像尺寸 图像分辨率为 320×240
KPU() 创建 KPU 推理对象 分别用于人脸检测和关键点检测
load_kmodel() 加载模型文件 /sd/ 路径读取 kmodel
init_yolo2() 初始化 YOLO2 后处理 设置 anchor、阈值、输入尺寸和类别数
extend_box() 扩大人脸检测框 裁剪时保留更多脸部边缘信息
img.cut() 裁剪人脸区域 从原图中得到单独的人脸图像
resize(128, 128) 调整关键点模型输入尺寸 人脸图像适配 68 点模型
KPU.sigmoid() 还原关键点相对坐标 将模型输出转换到可计算范围
draw_rectangle() 绘制人脸框 LCD 上出现绿色矩形框
draw_circle() 绘制关键点 人脸上出现红色关键点
lcd.display(img) 显示处理后的图像 LCD 实时显示检测结果

主循环中多次调用 gc.collect(),用于回收临时图像对象占用的内存。关键点检测每一帧都会创建 face_cutface_cut_128,如果不及时释放,长时间运行可能出现内存不足。代码中使用 del 删除临时对象,并配合垃圾回收,适合这类持续推理的视觉实验。

扩展应用

68 点人脸关键点检测实验常见问题集中在模型文件路径、摄像头画面、检测阈值、人脸距离、内存占用和 LCD 显示几个方面。排查时应先确认模型能否加载,再确认摄像头和 LCD 是否正常,最后再调整检测条件和显示效果。

问题现象 可能原因 处理思路
程序启动时报模型加载错误 kmodel 文件不存在、路径不一致、SD 卡未识别 确认 /sd/yolo_face_detect.kmodel/sd/landmark68.kmodel 已放到 SD 卡根目录
LCD 没有画面 摄像头或 LCD 初始化失败 先运行基础摄像头显示示例,确认 sensor.snapshot()lcd.display() 正常
能显示画面但没有人脸框 人脸太远、角度偏差大、光照不足、阈值过高 让人脸正对摄像头,保持适当距离和稳定光照,必要时降低 threshold=0.7
有人脸框但没有关键点 关键点模型未正常加载,或裁剪区域不合适 检查 landmark68.kmodel 路径,确认裁剪框中包含完整人脸
关键点位置偏移 裁剪框坐标、缩放比例或坐标还原计算不匹配 检查 extend_box() 返回的 x、y、宽高是否合理
识别结果闪烁 光照变化、人脸移动过快、检测框不稳定 固定摄像头和人脸距离,增强光照,减少背景干扰
FPS 偏低 双模型推理和绘制关键点耗时较高 减少串口打印,减少额外绘制,保持 QVGA 输入
运行一段时间后异常 临时图像对象占用内存,垃圾回收不及时 保留 delgc.collect(),减少主循环中不必要的对象创建
检测到多张人脸后变慢 每张人脸都会执行一次 68 点模型推理 可限制只处理最大人脸,减少关键点推理次数

68 点人脸关键点检测的价值不只在于把点画出来,更重要的是获得了面部结构数据。眼睛、鼻子、嘴角、下巴和脸部轮廓点都可以进一步参与计算,例如眼睛开合程度、嘴巴开合程度、脸部方向变化和人脸对齐等。当前程序已经完成了从图像采集到关键点显示的基础链路,后续可以围绕这些关键点继续扩展应用。

应用场景 实现思路 可扩展能力
表情分析 根据嘴角、眼睛和眉部关键点的相对位置判断表情变化 可扩展为笑脸检测、张嘴检测或简单表情分类
人脸对齐 根据眼睛和鼻梁关键点计算人脸角度并进行校正 可用于后续人脸识别或人脸特征提取预处理
美颜特效 根据关键点位置绘制装饰图形或贴纸 可扩展为眼镜、胡子、帽子等简易 AR 效果
疲劳检测 根据眼睛关键点计算闭眼状态和持续时间 可扩展为驾驶员疲劳提醒实验
姿态估计 根据鼻子、眼睛、嘴巴和脸部轮廓位置估计头部朝向 可用于人机交互或视线方向粗略判断
人脸跟踪云台 根据人脸框中心或关键点中心控制舵机方向 可结合舵机或步进电机,实现目标跟随
AI 视觉教学 用双模型流程讲解检测、裁剪、推理和绘制 适合解释边缘 AI 项目中的模块分层
课堂互动项目 根据关键点状态触发屏幕提示、蜂鸣器或 LED 效果 可扩展为互动教具或趣味识别实验

从工程角度看,建议保持"采集、检测、裁剪、推理、还原、显示"的分层思路。后续如果要增加串口通信、保存图片、按键切换模式、蜂鸣器提示或舵机跟随,只需要在检测结果和关键点结果之后增加动作逻辑,不需要重写摄像头和模型加载部分。

总结

本实验通过 CanMV K210 完成了基于 68 点模型的人脸关键点检测,核心内容包括摄像头初始化、LCD 显示、人脸检测模型加载、YOLO2 后处理、人脸框扩大、人脸区域裁剪、关键点模型推理、坐标还原、关键点绘制和 FPS 显示。程序先找到人脸,再定位 68 个关键点,完整展示了边缘 AI 视觉实验中"双模型串联"的典型流程。

这类实验非常适合作为 CanMV K210 AI 视觉课程中的进阶案例。它比单纯人脸检测多了裁剪、缩放、坐标映射和关键点绘制,也为后续表情分析、人脸对齐、疲劳检测、美颜特效、姿态估计和人脸跟踪云台提供了基础。掌握这条流程后,更多基于目标检测和局部模型推理的视觉项目,都可以按照类似思路继续扩展。

相关推荐
才兄说1 小时前
机器人二次开发机器狗巡检?多源传感器融合建图
人工智能·机器人
xinshu5271 小时前
2026企业联系方式查询平台对比:哪个能查到详细电话?
人工智能·技术分享
PNP机器人1 小时前
斯坦福 HOMER 技术解析:移动操作机器人如何用 20 次演示实现家庭场景自主作业
人工智能
辰同学ovo1 小时前
从 LLM 到 Agent Skill:理解 AI 应用的底层链路
人工智能
呆萌很1 小时前
CULane数据集详解
人工智能
Leo.yuan1 小时前
企业数字化转型选型指南:FineBI如何助力数据驱动决策?
数据仓库·人工智能·信息可视化
Mr数据杨1 小时前
【CanMV K210】AI 视觉 MNIST 手写数字识别与分类推理
人工智能·硬件开发·canmv k210
TENSORTEC腾视科技1 小时前
让安全驾驶有“AI”相伴|腾视科技DMS视频监控一体机,守护每一次出行
大数据·人工智能·科技·安全·ai·零售·无人叉车及智能调度系统解决方案
日月新著1 小时前
仙踪问道·爱马仕助手:Mac 本地 AI Agent 零门槛部署实战
人工智能·macos