高通手机跑AI系列之——手部姿势跟踪
(原创作者@CSDN_伊利丹~怒风)环境准备
手机
测试手机型号:Redmi K60 Pro
处理器:第二代骁龙8移动--8gen2
运行内存:8.0GB ,LPDDR5X-8400,67.0 GB/s
摄像头:前置16MP+后置50MP+8MP+2MP
AI算力:NPU 48Tops INT8 && GPU 1536ALU x 2 x 680MHz = 2.089 TFLOPS
提示:任意手机均可以,性能越好的手机运行速度越快
软件
APP:AidLux2.0
系统环境:Ubuntu 20.04.3 LTS
提示:AidLux登录后代码运行更流畅,在代码运行时保持AidLux APP在前台运行,避免代码运行过程中被系统回收进程,另外屏幕保持常亮,一般息屏后一段时间,手机系统会进入休眠状态,如需长驻后台需要给APP权限。
算法Demo
代码功能分析
这段代码是一个基于 AI 的手部检测与识别程序,它通过摄像头实时捕捉画面,检测画面中的手部,并识别出手部的关键点位置,最终在画面上绘制出手部轮廓和关键点。
代码应用场景分析
这段代码实现了实时手部检测和关键点识别功能,可应用于多种场景:
[*]手势识别与交互系统
[*]智能家居控制:通过特定手势控制灯光、电器等设备
[*]虚拟现实 / 增强现实 (VR/AR):手部动作作为交互输入,增强沉浸感
[*]游戏控制:替代传统控制器,实现更自然的游戏操作
[*]人机协作与机器人控制
[*]工业机器人引导:通过手势指挥机器人执行任务
[*]远程操作:操作人员通过手势控制远程设备
[*]医疗与康复领域
[*]手部运动康复训练:监测患者手部动作,评估康复进度
[*]手术辅助:医生通过手势操作医疗设备或查看影像资料
[*]教育与演示
[*]互动教学:教师通过手势控制教学内容展示
[*]演示系统:演讲者通过手势控制幻灯片或其他演示内容
[*]安防监控
[*]异常行为检测:分析人员手部动作识别潜在威胁行为
[*]身份验证:结合手部特征进行身份识别
AidLite 推理引擎功能
AidLite 推理引擎是专为边缘设备优化的轻量级 AI 推理引擎,具有以下核心功能:
[*]多框架支持
[*]支持 TensorFlow Lite、ONNX 等多种模型格式
[*]代码中使用了 TensorFlow Lite 格式的模型 (.tflite)
[*]硬件加速
[*]支持 GPU、CPU 、NPU等多种加速方式
[*]手掌检测模型使用 GPU 加速,提高实时性能
[*]关键点识别模型使用 CPU 加速,保证精度
[*]轻量化设计
[*]专为资源受限的边缘设备优化
[*]低内存占用,高效的模型推理能力
[*]易用的 API 接口
[*]提供模型加载、输入设置、推理执行、输出获取等完整流程的 API
[*]代码中通过aidlite.Model、aidlite.Config和aidlite.InterpreterBuilder等类实现模型管理和推理
OpenCV (代码中的 CV) 功能
OpenCV 是计算机视觉领域的经典库,在这段代码中主要用于以下功能:
[*]图像采集与处理
[*]通过cv2.VideoCapture获取摄像头实时视频流
[*]图像预处理:颜色空间转换 (cv2.cvtColor)、缩放 (cv2.resize) 等
[*]图像显示与可视化
[*]使用cv2.imshow显示处理后的图像
[*]绘制检测框 (cv2.rectangle)、关键点 (cv2.circle) 和连接线 (cv2.line)
[*]辅助计算功能
[*]计算手掌重心:cv2.moments计算图像矩
[*]边界框计算:cv2.boundingRect计算包围关键点的最小矩形
[*]图像操作
[*]图像翻转:使用cv2.flip处理前置摄像头的镜像问题
[*]区域提取:从原始图像中提取手部区域进行单独处理
OpenCV 提供的这些功能为 AI 模型的输入准备和输出结果可视化提供了基础支持,使整个系统能够实现从图像采集到结果展示的完整流程。
AI 模型作用介绍
代码中使用了两个 AI 模型协同工作:
[*]手掌检测模型 (palm_detection.tflite)
[*]这是一个轻量级的目标检测模型,专门用于检测图像中的手掌。
[*]模型输入:128×128 像素的 RGB 图像
[*]模型输出:包含手掌位置和边界框信息
[*]作用:快速定位图像中的手掌,为后续的关键点识别提供感兴趣区域 (ROI)
[*]加速方式:使用 GPU 加速,提高检测速度
[*]手部关键点检测模型 (hand_landmark.tflite)
[*]该模型对手部区域进行更精细的分析,识别 21 个关键点
[*]模型输入:224×224 像素的 RGB 图像(通常是手掌检测模型输出的 ROI)
[*]模型输出:21 个三维关键点坐标,表示手部的详细姿态
[*]作用:精确识别手指关节、指尖等部位的位置
[*]加速方式:使用 CPU 加速,保证识别精度
这两个模型结合使用,实现了从图像中检测手掌位置,到精确识别手部 21 个关键点的完整流程,能够实时跟踪手部动作和姿态。
DEMO代码
import cv2
import time
from time import sleep
import subprocess
import math
import sys
import numpy as np
from blazeface import *# 导入BlazeFace人脸/手部检测模型相关函数
import aidlite# AidLux平台的AI推理框架
import os
# 获取摄像头设备ID,优先选择USB摄像头
def get_cap_id():
try:
# 构造命令,使用awk处理输出
cmd = "ls -l /sys/class/video4linux | awk -F ' -> ' '/usb/{sub(/.*video/, \"\", $2); print $2}'"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
output = result.stdout.strip().split()
# 转换所有捕获的编号为整数,找出最小值
video_numbers = list(map(int, output))
if video_numbers:
return min(video_numbers)
else:
return None
except Exception as e:
print(f"An error occurred: {e}")
return None
# 图像预处理函数,将图像转换为适合TFLite模型输入的格式
def preprocess_image_for_tflite32(image, model_image_size=300):
try:
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# 转换颜色空间从BGR到RGB
image = cv2.resize(image, (model_image_size, model_image_size))# 调整图像大小
image = np.expand_dims(image, axis=0)# 添加批次维度
image = (2.0 / 255.0) * image - 1.0# 归一化处理,将像素值缩放到[-1,1]范围
image = image.astype('float32')# 转换数据类型为float32
except cv2.error as e:
print(str(e))
return image
# 在图像上绘制手部检测框
def plot_detections(img, detections, with_keypoints=True):
output_img = img
print(img.shape)
x_min=# 存储两只手的最小x坐标
x_max=# 存储两只手的最大x坐标
y_min=# 存储两只手的最小y坐标
y_max=# 存储两只手的最大y坐标
hand_nums=len(detections)# 检测到的手的数量
print("Found %d hands" % hand_nums)
if hand_nums >2:
hand_nums=2# 最多处理两只手
for i in range(hand_nums):
ymin = detections[ 0] * img.shape# 计算边界框的y最小值
xmin = detections[ 1] * img.shape# 计算边界框的x最小值
ymax = detections[ 2] * img.shape# 计算边界框的y最大值
xmax = detections[ 3] * img.shape# 计算边界框的x最大值
w=int(xmax-xmin)# 计算边界框宽度
h=int(ymax-ymin)# 计算边界框高度
h=max(h,w)# 取宽高的最大值
h=h*224./128.# 调整高度尺寸
x=(xmin+xmax)/2.# 计算中心点x坐标
y=(ymin+ymax)/2.# 计算中心点y坐标
# 调整边界框大小和位置
xmin=x-h/2.
xmax=x+h/2.
ymin=y-h/2.-0.18*h
ymax=y+h/2.-0.18*h
# 存储边界框坐标
x_min=int(xmin)
y_min=int(ymin)
x_max=int(xmax)
y_max=int(ymax)
p1 = (int(xmin),int(ymin))# 边界框左上角坐标
p2 = (int(xmax),int(ymax))# 边界框右下角坐标
cv2.rectangle(output_img, p1, p2, (0,255,255),2,1)# 在图像上绘制边界框
return x_min,y_min,x_max,y_max
# 在图像上绘制手部网格关键点
def draw_mesh(image, mesh, mark_size=4, line_width=1):
"""Draw the mesh on an image"""
# The mesh are normalized which means we need to convert it back to fit
# the image size.
image_size = image.shape
mesh = mesh * image_size
for point in mesh:
cv2.circle(image, (point, point),
mark_size, (255, 0, 0), 4)
# 计算手掌的重心
def calc_palm_moment(image, landmarks):
image_width, image_height = image.shape, image.shape
palm_array = np.empty((0, 2), int)
# 收集手掌区域的关键点
for index, landmark in enumerate(landmarks):
if math.isnan(landmark):
landmark = 0
if math.isnan(landmark):
landmark = 0
landmark_x = min(int(landmark * image_width), image_width - 1)
landmark_y = min(int(landmark * image_height), image_height - 1)
landmark_point =
if index == 0:# 手腕1
palm_array = np.append(palm_array, landmark_point, axis=0)
if index == 1:# 手腕2
palm_array = np.append(palm_array, landmark_point, axis=0)
if index == 5:# 食指:根部
palm_array = np.append(palm_array, landmark_point, axis=0)
if index == 9:# 中指:根部
palm_array = np.append(palm_array, landmark_point, axis=0)
if index == 13:# 无名指:根部
palm_array = np.append(palm_array, landmark_point, axis=0)
if index == 17:# 小指:根部
palm_array = np.append(palm_array, landmark_point, axis=0)
# 计算重心
M = cv2.moments(palm_array)
cx, cy = 0, 0
if M['m00'] != 0:
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
return cx, cy
# 计算包围手部关键点的矩形框
def calc_bounding_rect(image, landmarks):
image_width, image_height = image.shape, image.shape
landmark_array = np.empty((0, 2), int)
# 收集所有关键点坐标
for _, landmark in enumerate(landmarks):
landmark_x = min(int(landmark * image_width), image_width - 1)
landmark_y = min(int(landmark * image_height), image_height - 1)
landmark_point =
landmark_array = np.append(landmark_array, landmark_point, axis=0)
# 计算包围矩形
x, y, w, h = cv2.boundingRect(landmark_array)
return
# 在图像上绘制包围矩形
def draw_bounding_rect(use_brect, image, brect):
if use_brect:
# 外接矩形
cv2.rectangle(image, (brect, brect), (brect, brect),
(0, 255, 0), 2)
return image
# 在图像上绘制手部关键点和连接线
def draw_landmarks(image, cx, cy, landmarks):
image_width, image_height = image.shape, image.shape
landmark_point = []
# 绘制关键点
for index, landmark in enumerate(landmarks):
# if landmark.visibility < 0 or landmark.presence < 0:
# continue
landmark_x = min(int(landmark * image_width), image_width - 1)
landmark_y = min(int(landmark * image_height), image_height - 1)
landmark_point.append((landmark_x, landmark_y))
# 根据关键点类型绘制不同大小和颜色的点
if index == 0:# 手腕1
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 1:# 手腕2
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 2:# 拇指:根部
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 3:# 拇指:第1关节
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 4:# 拇指:指尖
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
cv2.circle(image, (landmark_x, landmark_y), 12, (0, 255, 0), 2)
if index == 5:# 食指:根部
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 6:# 食指:第2关节
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 7:# 食指:第1关节
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 8:# 食指:指尖
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
cv2.circle(image, (landmark_x, landmark_y), 12, (0, 255, 0), 2)
if index == 9:# 中指:根部
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 10:# 中指:第2关节
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 11:# 中指:第1关节
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 12:# 中指:指尖
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
cv2.circle(image, (landmark_x, landmark_y), 12, (0, 255, 0), 2)
if index == 13:# 无名指:根部
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 14:# 无名指:第2关节
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 15:# 无名指:第1关节
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 16:# 无名指:指尖
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
cv2.circle(image, (landmark_x, landmark_y), 12, (0, 255, 0), 2)
if index == 17:# 小指:根部
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 18:# 小指:第2关节
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 19:# 小指:第1关节
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
if index == 20:# 小指:指尖
cv2.circle(image, (landmark_x, landmark_y), 5, (0, 255, 0), 2)
cv2.circle(image, (landmark_x, landmark_y), 12, (0, 255, 0), 2)
# 绘制连接线
if len(landmark_point) > 0:
# 拇指
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
# 食指
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
# 中指
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
# 无名指
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
# 小指
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
# 手掌
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
cv2.line(image, landmark_point, landmark_point, (0, 255, 0), 2)
# 绘制重心点
if len(landmark_point) > 0:
cv2.circle(image, (cx, cy), 12, (0, 255, 0), 2)
return image
# 初始化手掌检测模型
inShape =[]# 模型输入形状
outShape= [,]# 模型输出形状
model_path="models/palm_detection.tflite"# 手掌检测模型路径
# 创建Model实例对象,并设置模型相关参数
model = aidlite.Model.create_instance(model_path)
if model is None:
print("Create palm_detection model failed !")
# 设置模型属性
model.set_model_properties(inShape, aidlite.DataType.TYPE_FLOAT32, outShape,aidlite.DataType.TYPE_FLOAT32)
# 创建Config实例对象,并设置配置信息
config = aidlite.Config.create_instance()
config.implement_type = aidlite.ImplementType.TYPE_FAST# 快速推理实现类型
config.framework_type = aidlite.FrameworkType.TYPE_TFLITE# TensorFlow Lite框架类型
config.accelerate_type = aidlite.AccelerateType.TYPE_GPU# GPU加速
config.number_of_threads = 4# 线程数
# 创建推理解释器对象
fast_interpreter = aidlite.InterpreterBuilder.build_interpretper_from_model_and_config(model, config)
if fast_interpreter is None:
print("palm_detection model build_interpretper_from_model_and_config failed !")
# 完成解释器初始化
result = fast_interpreter.init()
if result != 0:
print("palm_detection model interpreter init failed !")
# 加载模型
result = fast_interpreter.load_model()
if result != 0:
print("palm_detection model interpreter load model failed !")
print("palm_detection model load success!")
# 初始化手部关键点检测模型
model_path1="models/hand_landmark.tflite"# 手部关键点检测模型路径
inShape1 =[]# 模型输入形状
outShape1= [,,]# 模型输出形状
# 创建Model实例对象,并设置模型相关参数
model1 = aidlite.Model.create_instance(model_path1)
if model1 is None:
print("Create hand_landmark model failed !")
# 设置模型属性
model1.set_model_properties(inShape1, aidlite.DataType.TYPE_FLOAT32, outShape1,
aidlite.DataType.TYPE_FLOAT32)
# 创建Config实例对象,并设置配置信息
config1 = aidlite.Config.create_instance()
config1.implement_type = aidlite.ImplementType.TYPE_FAST# 快速推理实现类型
config1.framework_type = aidlite.FrameworkType.TYPE_TFLITE# TensorFlow Lite框架类型
config1.accelerate_type = aidlite.AccelerateType.TYPE_CPU# CPU加速
config.number_of_threads = 4# 线程数
# 创建推理解释器对象
fast_interpreter1 = aidlite.InterpreterBuilder.build_interpretper_from_model_and_config(model1, config1)
if fast_interpreter1 is None:
print("hand_landmark model build_interpretper_from_model_and_config failed !")
# 完成解释器初始化
result = fast_interpreter1.init()
if result != 0:
print("hand_landmark model interpreter init failed !")
# 加载模型
result = fast_interpreter1.load_model()
if result != 0:
print("hand_landmark model interpreter load model failed !")
print("hand_landmark model load success!")
# 加载锚点数据,用于模型推理
anchors = np.load('models/anchors.npy').astype(np.float32)
# 设置Aidlux平台类型和摄像头ID
aidlux_type="basic"
# 0-后置,1-前置
camId = 1
opened = False
# 尝试打开摄像头
while not opened:
if aidlux_type == "basic":
cap=cv2.VideoCapture(camId, device='mipi')
else:
capId = get_cap_id()
print("usb camera id: ", capId)
if capId is None:
print ("no found usb camera")
# 默认用1-前置摄像头打开相机,若打开失败,请尝试修改为0-后置
cap=cv2.VideoCapture(1, device='mipi')
else:
camId = capId
cap = cv2.VideoCapture(camId)
cap.set(6, cv2.VideoWriter.fourcc('M','J','P','G'))
if cap.isOpened():
opened = True
else:
print("open camera failed")
cap.release()
time.sleep(0.5)
# 手检测标志和坐标初始化
bHand=False
x_min=
x_max=
y_min=
y_max=
fface=0.0
use_brect=True
# 主循环:持续捕获视频帧并进行手部检测和关键点识别
while True:
ret, frame=cap.read()# 读取一帧视频
if not ret:
continue
if frame is None:
continue
# 如果使用前置摄像头,水平翻转图像以获得自然的镜像效果
if camId==1:
frame=cv2.flip(frame,1)
# 图像预处理,为手掌检测模型准备输入
img = preprocess_image_for_tflite32(frame,128)
# 手部检测和关键点识别流程
if bHand==False:
# 设置输入数据
result = fast_interpreter.set_input_tensor(0, img.data)
if result != 0:
print("palm_detection model interpreter set_input_tensor() failed")
# 执行手掌检测模型推理
result = fast_interpreter.invoke()
if result != 0:
print("palm_detection model interpreter invoke() failed")
# 获取输出数据
raw_boxes = fast_interpreter.get_output_tensor(0)
if raw_boxes is None:
print("sample : palm_detection model interpreter->get_output_tensor(0) failed !")
classificators = fast_interpreter.get_output_tensor(1)
if classificators is None:
print("sample : palm_detection model interpreter->get_output_tensor(1) failed !")
# 解析检测结果
detections = blazeface(raw_boxes, classificators, anchors)
# 在图像上绘制检测框并获取边界框坐标
x_min,y_min,x_max,y_max=plot_detections(frame, detections)
# 如果检测到至少一只手,则设置标志为True,准备进行关键点识别
if len(detections)>0 :
bHand=True
# 如果已检测到手部,进行关键点识别
if bHand:
hand_nums=len(detections)
if hand_nums>2:
hand_nums=2
# 对每只检测到的手进行关键点识别
for i in range(hand_nums):
print(x_min,y_min,x_max,y_max)
# 确保边界框坐标在有效范围内
xmin=max(0,x_min)
ymin=max(0,y_min)
xmax=min(frame.shape,x_max)
ymax=min(frame.shape,y_max)
# 提取手部区域
roi_ori=frame
# 预处理手部区域图像,为关键点检测模型准备输入
roi =preprocess_image_for_tflite32(roi_ori,224)
# 设置输入数据
result = fast_interpreter1.set_input_tensor(0, roi.data)
if result != 0:
print("hand_landmark model interpreter set_input_tensor() failed")
# 执行手部关键点检测模型推理
result = fast_interpreter1.invoke()
if result != 0:
print("hand_landmark model interpreter invoke() failed")
# 获取输出数据
mesh = fast_interpreter1.get_output_tensor(0)
if mesh is None:
print("sample : hand_landmark model interpreter->get_output_tensor(0) failed !")
# 重置手检测标志,准备下一帧的检测
bHand=False
# 处理关键点数据并在图像上绘制
mesh = mesh.reshape(21, 3)/224
cx, cy = calc_palm_moment(roi_ori, mesh)
draw_landmarks(roi_ori,cx,cy,mesh)
frame=roi_ori
# 显示处理后的图像
cv2.imshow("", frame)模型位置
/opt/aidlux/app/aid-examples//hand_track模型效果
来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除
页:
[1]