• AI项目五:结印动作识别


    若该文为原创文章,转载请注明原文出处。

    感谢恩培大佬对项目进行了完整的实现,并将代码进行开源,供大家交流学习。

    恩培大佬开源地址,有兴趣的可以去复现一下。GitHub - enpeizhao/CVprojects: computer vision projects | 计算机视觉相关好玩的AI项目(Python、C++)

    一、介绍

    从恩培大佬的git上看到的小项目《火影结印识别》,发现大佬开源的代码是需要GPU的,本人电脑没有GPU,环境安装好了,没复现成功,所以取巧使用yolov5的方式实现一样的功能。

    yolov5安装及训练前面有提及,不熟悉yolov5训练可以看前面文章。

    二、训练集

    准备素材,训练的手势共有七种,每种采集30张图片,并使用lableImg标注所有图片。

    label标签

    三、训练

    训练使用的是AutoDL云端训练,样本不多,训练大概20-30分钟,用的是3090显卡。

    这有有个注意的,直接使用AutoDL提供的镜像训练出来的pt文件一直识别检测不到,所以又换回yolov5-5版本,训练出来正常。

    效果还行,想要复现就得自己训练。

    四、代码

    1、生成字幕需要的中文PNG图片

    PNG图片是为了把识别到的字显示出来,使用OPENCV不好显示中心,所以提前准备好png直接在视频内叠加PNG图片。

    1. '''
    2. 生成字幕需要的中文PNG图片
    3. '''
    4. from PIL import Image,ImageDraw,ImageFont
    5. def generate(name='你好',color_label='green'):
    6. filename = './png_label/'+name+'.png'
    7. # 背景
    8. bg = Image.new("RGBA",(400,100),(0,0,0,0))
    9. # 添加文字
    10. d = ImageDraw.Draw(bg)
    11. font = ImageFont.truetype('./fonts/MSYH.ttc',80,encoding="utf-8")
    12. if color_label == 'green':
    13. color = (0,255,0,255)
    14. else:
    15. color = (255,0,0,255)
    16. d.text((0,0),name,font=font,fill=color)
    17. # 保存
    18. bg.save(filename)
    19. print('ok: '+ name)
    20. generate('火遁豪火球之术','red')

    运行后,会生成PNG图片。

    2、完整代码

    1. import ctypes
    2. import cv2
    3. import numpy as np
    4. import time
    5. # 多进程
    6. from multiprocessing import Process, Value
    7. import threading
    8. import torch
    9. import sys
    10. from playsound import playsound
    11. class Ai_tello:
    12. def __init__(self):
    13. # ************************************ 绘制 相关 *********************************
    14. self.png_dict = {}
    15. # 获取
    16. self.getPngList()
    17. # 加载 yolov5模型
    18. self.model = torch.hub.load('./yolov5', 'custom', './weights/pose.pt',source='local')
    19. # 置信度阈值
    20. self.model.conf = 0.5
    21. print('self.model.conf = 0.5')
    22. self.take_off_time = None
    23. # 结印动作顺序
    24. self.yolo_action_seq = ['ani_1', 'ani_2','ani_3', 'ani_4', 'ani_5', 'ani_6', 'ani_7']
    25. # 状态机,1表示当前动作已做完(击中)
    26. self.yolo_action_status = [0, 0, 0, 0, 0, 0, 0]
    27. def getPngList(self):
    28. '''
    29. 读取PNG图片,追加进png_dict
    30. '''
    31. palm_action = {'ani_1': '巳', 'ani_2': '未', 'ani_3': '申',
    32. 'ani_4': '亥', 'ani_5': '午', 'ani_6': '寅', 'ani_7':'火遁豪火球之术'}
    33. for name in palm_action.values():
    34. filename = './png_label/'+name+'.png'
    35. png_img = self.readPngFile(filename, 0.9)
    36. self.png_dict[name] = png_img
    37. print('PNG文字标签加载完毕')
    38. def playVoice(self, fileName,mode):
    39. """
    40. 播放音乐
    41. """
    42. playsound(fileName)
    43. def backPlay(self,fileName):
    44. """
    45. 后台播放
    46. """
    47. t = threading.Thread(target=self.playVoice, args=(fileName,'voice'))
    48. t.start()
    49. def readPngFile(self, fileName, scale=0.5):
    50. '''
    51. 读取PNG图片
    52. '''
    53. # 解决中文路径问题
    54. png_img = cv2.imdecode(np.fromfile(fileName, dtype=np.uint8), -1)
    55. # 转为BGR,变成3通道
    56. png_img = cv2.cvtColor(png_img, cv2.COLOR_RGB2BGR)
    57. png_img = cv2.resize(png_img, (0, 0), fx=scale, fy=scale)
    58. return png_img
    59. def addOverylay(self, frame, overlay, l, t):
    60. '''
    61. 添加标签png覆盖
    62. '''
    63. # 解决l、t超界
    64. l = max(l, 0)
    65. t = max(t, 0)
    66. # 覆盖显示
    67. overlay_h, overlay_w = overlay.shape[:2]
    68. # 覆盖范围
    69. overlay_l, overlay_t = l, t
    70. overlay_r, overlay_b = (l + overlay_w), (overlay_t+overlay_h)
    71. # 遮罩
    72. overlay_copy = cv2.addWeighted(
    73. frame[overlay_t:overlay_b, overlay_l:overlay_r], 1, overlay, 20, 0)
    74. frame[overlay_t:overlay_b, overlay_l:overlay_r] = overlay_copy
    75. def cameraProcess(self):
    76. '''
    77. 视频流处理:动作识别、绘制等
    78. '''
    79. print('cameraProcess');
    80. cap = cv2.VideoCapture(0)
    81. # 动作
    82. palm_action = {'ani_1':'巳','ani_2':'未','ani_3':'申','ani_4':'亥','ani_5':'午','ani_6':'寅','ani_7':'火遁豪火球之术'}
    83. triger_time = time.time()
    84. while True:
    85. # 读取视频帧
    86. ret,frame = cap.read()
    87. if frame is None:
    88. continue;
    89. frame = cv2.flip(frame, 1)
    90. # 转为RGB
    91. img_cvt = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
    92. if self.take_off_time != None:
    93. if time.time() - triger_time >= 2:
    94. label_zh = palm_action['ani_7']
    95. overlay = self.png_dict[label_zh]
    96. self.addOverylay(frame,overlay,l,200)
    97. else:
    98. # 目标检测推理
    99. results = self.model(img_cvt)
    100. results_arr = results.pandas().xyxy[0].to_numpy()
    101. # 解析目标检测结果
    102. for item in results_arr:
    103. # 标签ID
    104. ret_label_id = item[-2]
    105. # 标签名称
    106. ret_label_text = item[-1]
    107. # 置信度
    108. ret_conf = item[-3]
    109. # ani_1,ani_2....ani_6
    110. # 结印动作,且置信度要求高一些
    111. if 'ani_' in ret_label_text and ret_conf >= 0.7:
    112. l,t,r,b = item[:4].astype('int')
    113. # 绘制
    114. cv2.rectangle(frame,(l,t),(r,b),(0,255,20),2)
    115. # 绘制动作中文png
    116. label_zh = palm_action[ret_label_text]
    117. print(ret_label_text)
    118. # 拿到对应中文文字的数组图片
    119. overlay = self.png_dict[label_zh]
    120. # 覆盖绘制
    121. self.addOverylay(frame,overlay,l,t-100)
    122. cv2.putText(frame,'{}%'.format(round(ret_conf*100,2)),(l+80,t-20),cv2.FONT_ITALIC,1.5,(255,0,255),2)
    123. # 状态机列表中第一个0的索引
    124. first_0_index = next(i for i,x in enumerate(self.yolo_action_status) if x == 0 )
    125. # 对应动作名 ['ani_1', 'ani_2','ani_3', 'ani_4', 'ani_5', 'ani_6']
    126. check_action_name = self.yolo_action_seq[first_0_index]
    127. # 动作匹配
    128. if ret_label_text == check_action_name:
    129. # 赋值1
    130. self.yolo_action_status[first_0_index] = 1
    131. # 检查是否完毕
    132. if self.yolo_action_status == [1,1,1,1,1,1,1]:
    133. self.take_off_time = time.time()
    134. print('动作全部匹配完')
    135. self.backPlay('火遁豪火球.mp3')
    136. # 计时
    137. triger_time = time.time()
    138. else:
    139. print('击中一个动作,当前列表为'+str(self.yolo_action_status))
    140. else:
    141. print('未击中动作,当前列表为'+str(self.yolo_action_status))
    142. cv2.imshow('demo', frame)
    143. if cv2.waitKey(10) & 0xFF == ord('q'):
    144. break
    145. cv2.destroyAllWindows()
    146. if __name__ == '__main__':
    147. # 实例化
    148. ai_tello = Ai_tello()
    149. ai_tello.cameraProcess()

    代码有几个需要注意的:

    一、加载 yolov5模型

    加载 yolov5模型时,有可能出错,遇到的问题是版本不同,使用的是yolov5-5版本训练的,加载的文件不是,出错。

    二、识别流程

    识别采用的状态机方式,即事先定义一下数组【0,0,0,0,0,0,0】共7个元素,

    当检测到对应动作时就置1,当数组全部为1时,就播放声音。

    五、效果演示

    《结印识别之火球之术》_哔哩哔哩_bilibili

    如有侵权,或需要完整代码,请及时联系博主。

  • 相关阅读:
    UE5 运行时生成距离场数据
    leetcode.679.24点游戏
    CTF-include
    [从零开始学习FPGA编程-52]:高阶篇 - 基于IP核的FPGA开发 - IP核使用的基本框架(以锁相环PLL为例)
    【深度学习】 Python 和 NumPy 系列教程(十七):Matplotlib详解:2、3d绘图类型(3)3D条形图(3D Bar Plot)
    vtk数据集的整合与附加_vtkAppendFilter
    上海无居住证120积分随迁子女如何求学(中考)
    动态规划 | 斐波那契数列、爬楼梯 | leecode刷题笔记
    利用pytorch自定义CNN网络(二):数据集的准备
    【前端之旅】Axios看这一篇就够了
  • 原文地址:https://blog.csdn.net/weixin_38807927/article/details/132790691