AI项目五:结印动作识别

news2025/1/18 11:43:03

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

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

恩培大佬开源地址,有兴趣的可以去复现一下。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图片。

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

from PIL import Image,ImageDraw,ImageFont

def generate(name='你好',color_label='green'):

    filename = './png_label/'+name+'.png'
   
    # 背景
    bg = Image.new("RGBA",(400,100),(0,0,0,0))
    # 添加文字
    d = ImageDraw.Draw(bg)
    font  = ImageFont.truetype('./fonts/MSYH.ttc',80,encoding="utf-8")

    if color_label == 'green':
        color = (0,255,0,255)
    else:
        color = (255,0,0,255)
        

    d.text((0,0),name,font=font,fill=color)
    # 保存
    bg.save(filename)

    print('ok: '+ name)

        
generate('火遁豪火球之术','red')

运行后,会生成PNG图片。

2、完整代码

import ctypes
import cv2
import numpy as np
import time

# 多进程
from multiprocessing import Process, Value
import threading

import torch
import sys
from playsound import playsound


class Ai_tello:
    def __init__(self):
        # ************************************ 绘制 相关 *********************************
        self.png_dict = {}
        # 获取
        self.getPngList()

        # 加载 yolov5模型
        self.model = torch.hub.load('./yolov5', 'custom', './weights/pose.pt',source='local') 
        # 置信度阈值
        self.model.conf = 0.5
        print('self.model.conf = 0.5')

        self.take_off_time = None
        # 结印动作顺序
        self.yolo_action_seq = ['ani_1', 'ani_2','ani_3', 'ani_4', 'ani_5', 'ani_6', 'ani_7']
        # 状态机,1表示当前动作已做完(击中)
        self.yolo_action_status = [0, 0, 0, 0, 0, 0, 0]

    def getPngList(self):
        '''
        读取PNG图片,追加进png_dict
        '''
        palm_action = {'ani_1': '巳', 'ani_2': '未', 'ani_3': '申',
            'ani_4': '亥', 'ani_5': '午', 'ani_6': '寅', 'ani_7':'火遁豪火球之术'}

        for name in palm_action.values():
            filename = './png_label/'+name+'.png'
            png_img = self.readPngFile(filename, 0.9)
            self.png_dict[name] = png_img

        print('PNG文字标签加载完毕')
        
    def playVoice(self, fileName,mode):
        """
        播放音乐
        """
        playsound(fileName)
        
    def backPlay(self,fileName):
        """
        后台播放
        """
        t = threading.Thread(target=self.playVoice, args=(fileName,'voice'))
        t.start()

    def readPngFile(self, fileName, scale=0.5):
        '''
        读取PNG图片
        '''
        # 解决中文路径问题
        png_img = cv2.imdecode(np.fromfile(fileName, dtype=np.uint8), -1)
        # 转为BGR,变成3通道
        png_img = cv2.cvtColor(png_img, cv2.COLOR_RGB2BGR)
        png_img = cv2.resize(png_img, (0, 0), fx=scale, fy=scale)
        return png_img

    def addOverylay(self, frame, overlay, l, t):
        '''
        添加标签png覆盖
        '''
        # 解决l、t超界
        l = max(l, 0)
        t = max(t, 0)
        # 覆盖显示
        overlay_h, overlay_w = overlay.shape[:2]
        # 覆盖范围
        overlay_l, overlay_t = l, t
        overlay_r, overlay_b = (l + overlay_w), (overlay_t+overlay_h)
        # 遮罩
        overlay_copy = cv2.addWeighted(
            frame[overlay_t:overlay_b, overlay_l:overlay_r], 1, overlay, 20, 0)
            
        frame[overlay_t:overlay_b, overlay_l:overlay_r] = overlay_copy

    
    def cameraProcess(self):
        '''
        视频流处理:动作识别、绘制等
        '''
        print('cameraProcess');
        cap = cv2.VideoCapture(0)
        # 动作
        palm_action = {'ani_1':'巳','ani_2':'未','ani_3':'申','ani_4':'亥','ani_5':'午','ani_6':'寅','ani_7':'火遁豪火球之术'}
    
        triger_time = time.time()
    
        while True:
           
            # 读取视频帧
            ret,frame = cap.read()
            if frame is None:
                continue;

            frame = cv2.flip(frame, 1)

            
            # 转为RGB
            img_cvt = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
         
            if self.take_off_time != None:  
                if time.time() - triger_time >= 2:
                    label_zh = palm_action['ani_7']
                    overlay = self.png_dict[label_zh]
                    self.addOverylay(frame,overlay,l,200)
            else:
                # 目标检测推理
                results = self.model(img_cvt)
                results_arr = results.pandas().xyxy[0].to_numpy()

                # 解析目标检测结果
                for item in results_arr:

                    # 标签ID
                    ret_label_id = item[-2]
                    # 标签名称
                    ret_label_text = item[-1]
                    # 置信度
                    ret_conf = item[-3]
                

                    # ani_1,ani_2....ani_6
                    # 结印动作,且置信度要求高一些
                    if 'ani_' in  ret_label_text and ret_conf >= 0.7:

                        l,t,r,b = item[:4].astype('int')
                        # 绘制
                        cv2.rectangle(frame,(l,t),(r,b),(0,255,20),2)
                        # 绘制动作中文png                        
                        label_zh = palm_action[ret_label_text]
                        print(ret_label_text)
                        # 拿到对应中文文字的数组图片
                        overlay = self.png_dict[label_zh]
                        # 覆盖绘制
                        self.addOverylay(frame,overlay,l,t-100)
                        cv2.putText(frame,'{}%'.format(round(ret_conf*100,2)),(l+80,t-20),cv2.FONT_ITALIC,1.5,(255,0,255),2)


                        # 状态机列表中第一个0的索引
                        first_0_index = next(i for i,x in enumerate(self.yolo_action_status) if x == 0 )
                        # 对应动作名 ['ani_1', 'ani_2','ani_3', 'ani_4', 'ani_5', 'ani_6']
                        check_action_name = self.yolo_action_seq[first_0_index]

                        # 动作匹配
                        if ret_label_text == check_action_name:
                            # 赋值1
                            self.yolo_action_status[first_0_index] = 1
                            # 检查是否完毕
                            if self.yolo_action_status == [1,1,1,1,1,1,1]:

                                self.take_off_time = time.time()
                                print('动作全部匹配完')
                                
                                self.backPlay('火遁豪火球.mp3')
                                
                                # 计时
                                triger_time = time.time()

                            else:
                                print('击中一个动作,当前列表为'+str(self.yolo_action_status))
                        else:
                            print('未击中动作,当前列表为'+str(self.yolo_action_status))
                
            cv2.imshow('demo', frame)
            if cv2.waitKey(10) & 0xFF == ord('q'):
                break
            
        cv2.destroyAllWindows()


if __name__ == '__main__':

    # 实例化
    ai_tello = Ai_tello()
    ai_tello.cameraProcess()
    

代码有几个需要注意的:

一、加载 yolov5模型

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

二、识别流程

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

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

五、效果演示

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/997714.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机网路学习-time_wait过多

四次挥手 调试命令 netstat -an|awk ‘/tcp/ {print $6}’|sort|uniq -c netstat -an 列出系统中所有处于活动状态的网络连接信息,包括 IP 地址、端口号、协议等。 其中,第六列是tcp的状态。 Proto Recv-Q Send-Q Local Address Foreign Addr…

Aidlux工业视觉缺陷检测

Aidlux工业视觉缺陷检测 1. AidLux简介 AidLux是成都阿加犀智能科技有限公司自主研发的融合架构平台,提供Android/鸿蒙+Linux融合系统, 双系统既能独立使用又能相互通信。 阿加犀致力于人工智能核心技术持续创新, 独…

CSS元素浮动

概述 浮动简介 在最初,浮动是用来实现文字环绕图片效果的,现在浮动是主流的页面布局方式之一。 元素浮动后的特点 脱离文档流。不管浮动前是什么元素,浮动后,默认宽与高都是被内容撑开的(尽可能小)&am…

AKF拆分原则

在分布式软件环境下,为了保障分布式架构的可靠性、可扩展、高性能,通常会通过集群、扩容、数据分治等思想来实现,比如很多中间件的使用Redis、ZK、Kafka等,都可以通过这种设计思想来提高系统架构吞吐量。AKF是一个系统化的拓展思想…

Vue框架+Element组件库学习笔记

一、Vue框架 vue:是一款前端框架,免除原生JavaScript中的DOM操作(如document.getElementById("文本输入框名").value),简化书写。基于MVVM(Model-View-ViewModel)思想,实…

YApi 新版如何查看 http 请求数据

YApi 新版如何查看 http 请求数据 因chrome 安全策略限制,在 cross-request 升级到 3.0 后, 不再支持文件上传功能,并且需要通过以下方法查看 network:1.首先在chrome 输入 > chrome://extensions打开扩展页2.开启开发者模式3.点击 cross…

震惊:QGroupBox在linux下居然不显示边框

1、linux系统下GroupBox默认无边框,如需添加,需要通过style来修改 下面介绍几个例子: QGroupBox {background: transparent;border: 1px solid rgb(130, 130, 130);border-radius: 6px;}QGroupBox::title {subcontrol-origin: margin;subcont…

RegShot – 注册表比较工具

RegShot 是一种注册表比较工具,它通过两次抓取注册表而快速地比较出答案。它还可以将您的注册表以纯文本方式记录下来,便于浏览;还可以监察 Win.ini,System.ini 中的键值;还可以监察您Windows目录和 System 目录中文件…

比较聚合模型实战文本匹配

引言 本文我们采用比较聚合模型来实现文本匹配任务。 数据准备 数据准备包括 构建词表(Vocabulary)构建数据集(Dataset) 本次用的是LCQMC通用领域问题匹配数据集,它已经分好了训练、验证和测试集。 我们通过pandas来加载一下。 import pandas as pdtrain_df …

diskqueue第五篇 - 追尾检测,错误处理,如何正常关闭

diskqueue是nsq消息持久化的核心,内容较多,故分为多篇 1. diskqueue第一篇 - 是什么,为什么需要它,整体架构图,对外接口 2. diskqueue第二篇 - 元数据文件,数据文件,启动入口,元数…

熟悉Redis6

NoSQL数据库简介 技术发展 技术的分类 1、解决功能性的问题:Java、Jsp、RDBMS、Tomcat、HTML、Linux、JDBC、SVN 2、解决扩展性的问题:Struts、Spring、SpringMVC、Hibernate、Mybatis 3、解决性能的问题:NoSQL、Java线程、Hadoop、Nginx…

嵌入式-Linux基本操作 pwd cd ls touch clear mkdir rm cp mv

目录 一.Linux文件系统 二.Linux目录结构 三.Linux基本命令 3.1shell脚本 3.2pwd命令 3.3cd命令 3.4ls命令 3.5touch命令 3.6clear命令 3.7mkdir命令 3.8rm命令 3.9cp命令 3.10mv命令 一.Linux文件系统 Linux文件系统是Linux操作系统中用于组织和管理文件和目录的…

【算法训练-链表 六】【查找】:链表中倒数第k个节点

废话不多说,喊一句号子鼓励自己:程序员永不失业,程序员走向架构!本篇Blog的主题是【查找链表】,使用【链表】这个基本的数据结构来实现,这个高频题的站点是:CodeTop,筛选条件为&…

C盘清理教程

C盘清理教程 首先使用space Sniffer 扫一下c盘,然后看一下到底是哪个文件这么大 第二步,创建软链接。 首先将我们需要移动的文件的当前路径拷贝下来:C:\Users\Tom\Desktop\test-link\abc\ghi.txt 然后假设剪切到D盘下:D:\ghi.…

MOOC软件系统外包开发

MOOC(大规模开放在线课程)系统是用于创建、管理和交付在线教育课程的软件平台。这些系统通常具有多种功能,旨在支持大规模的在线学习。以下是MOOC系统主要实现的功能以及一些常见的开源系统,希望对大家有所帮助。北京木奇移动技术…

vite项目框架搭建

vite项目框架搭建 1. 使用vite初始化项目 开始 | Vite 官方中文文档 (vitejs.dev) pnpm create vite # 依次设置项目名称、选择框架【vue】、选择语言【typescript】 √ Project name: ... vite-project √ Select a framework: Vue √ Select a variant: TypeScript2. ele…

【strtok函数和strerror函数的介绍和使用以及扩展】

strtok函数和strerror函数的介绍和使用以及扩展 一.strtok函数 1.strtok函数介绍 资源来源于cplusplus网站 它的作用: 对此函数的一系列调用将 str 拆分为标记,这些标记是由分隔符中的任何字符分隔的连续字符序列。 在第一次调用时,该函数…

vue学习之Javascript 表达式内容渲染和属性绑定

Javascript 表达式内容渲染和属性绑定 创建 demo4.html,内容如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">…

Vuex核心概念 - actions 和 getters

文章目录 actions 和 getters一、actions作用使用目的&#xff1a; 二、actions的使用执行原理代码示例&#xff1a; 三、actions中的辅助函数mapActions代码示例&#xff1a; 四、核心-getters1. 什么是getters&#xff1f;2. getters的作用&#xff1a;3. 访问 getters 的两种…

Compose的一些小Tips - 可组合项的生命周期

系列文章 Compose的一些小Tips - 可组合项的生命周期&#xff08;本文&#xff09; 前言 本系列介绍Compose的一些常识&#xff0c;了解这些tips并不会让人摇身一变成为大佬&#xff0c;但可以帮助到一些学习Compose的安卓开发者避免一些误区&#xff0c;也是对入门详解中遗漏…