没有钢琴也可实现弹奏自由?实时在Jetson上运行单阶段手指关键点模型

news2024/11/26 3:51:18

钢琴是人类创作音乐的经典乐器,程序是实现创意的工具之魂。今天我给大家分享用程序实现的桌上钢琴师项目。本项目基于飞桨实现一个虚拟钢琴,让大家可以在任意平面上弹奏钢琴,实现弹奏自由。

图片

该项目的原理是利用手部关键点检测模型识别手的关键点,获取指尖点在画面的坐标位置。当指尖关键点跨过虚拟钢琴键的黄色响应线,即播放该琴键的音。

图片

项目方案

1)使用摄像头获取桌面上手指的实时画面;
2)手指关键点模型识别画面中手指指尖的x方向与y方向坐标;
3)比较当前每个手指指尖的y方向坐标与校准时的y方向坐标,判定某个手指尖是否做了敲击动作;
4)根据敲击动作的指尖的x方向坐标来判定具体按了哪个键;
5)通过pygame的UI显示按键效果,并播放对应的琴键音。

项目特点

1)基于飞桨实现轻量化的单阶段关键点识别模型。由于边缘设备算力紧张,为此,在运行实时手指关键点识别模型时,将手指识别简化为只识别5个指尖,不区分左右手。https://aistudio.baidu.com/aistudio/projectdetail/4915699

2)用pygame作为主UI框架。实时显示摄像头的画面及提示UI,画面中拍到的手指在桌面上敲击,即用户敲击虚拟琴键时,立即播放对应钢琴音。

3)使用生产者-消费者模式,充分利用Python的多进程,实现高效实时的画面显示、模型推理及结果反馈,在端侧实现较好的体验。

01 模型推理

图片硬件准备—摄像头布置

  • 摄像头垂直立于桌面,拍摄角度平行于桌面

因本项目使用的是2D Hand Keypoint模型,通过识别手指在画面中的y方向和x方向的位置来判断手指尖是否有敲击动作以及敲击哪个琴键,因此,摄像头需要垂直立于桌面上,这样能最好地拍摄到敲击桌面的情况。摄像头拍摄角度水平平行于桌面,点击位置可以通过初始化校准来自动调节,这样能确保较好的交互性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qb2jy4Ho-1678171159293)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fd40b82bc0bf496180b7bdc727bf8d54~tplv-k3u1fbpfcp-zoom-1.image)]

  • 摄像头可选用普通USB网络摄像头,最好清晰度高一点

本项目选用无畸变摄像头,好处是无需处理畸变问题,缺点是画面拍摄的画幅不够大,无法对应钢琴的88键。而选用大广角的摄像头会有畸变,虽然可以通过OpenCV的四角纠正校准成几乎无畸变的画面,但运算量会增大,响应延时会增大。感兴趣的同学也可以尝试。

图片算法选型

该任务可以考虑使用3D Hand Keypoint Detection算法或2D Hand Keypoint Detection算法。如果考虑摄像头空间位置及角度,可考虑用3D Hand Keypoint Detection算法计算指尖三维空间位置,精度应该更高,受摄像头角度影响更小。使用2D Hand Keypoint Detection算法则需要通过固定摄像头位置及角度,才能实现同样的功能。

因本项目需要重点考虑算力问题,因此使用了2D Hand Keypoint Detection算法。基于2D Hand Keypoint Detection的算法也有很多。PaddleHub中本身就已集成了手部姿态模型,AI Studio上也有大佬放出了基于ResNet50直接回归手部关键点的项目。在多次尝试后,感觉速度与精确度还有提升的空间,因此我使用飞桨框架,基于CenterNet魔改了一版手指关键点模型。

下面对上述3个模型进行简单介绍。

  • hand_pose_localization模型

模型源自CMU的OpenPose开源项目,目前已经集成到PaddleHub中。

https://www.paddlepaddle.org.cn/hubdetail?name=hand_pose_localization&en_category=KeyPointDetection

在该项目的实际操作中,手指关键点识别效果比较一般,这与拍摄角度对应的训练数据比较少有关,且无法基于PaddleHub进行迁移训练。

  • 飞桨实现手部21个关键点检测模型

该模型是ID为“星尘局”的同学在AI Studio上开源的模型。我测试了一下,效果比上述方案好一些,且可以继续训练或进行迁移训练。但其是基于ResNet50直接做回归,准确率和实时性还有待提升。https://aistudio.baidu.com/aistudio/projectdetail/2235290

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-We1WLhPK-1678171159293)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d1b7a47a05b14615a16642e454ae36a1~tplv-k3u1fbpfcp-zoom-1.image)]

  • 基于CenterNet的手部关键点模型
    • 基于CenterNet模型的魔改

基于上述模型情况,自己使用飞桨框架魔改了一版CenterNet关键点模型,添加了基于heatmap识别landmark的分支。本方案类似于DeepFashion2的冠军方案,如下图所示,DeepFashion2的方案基于CenterNet上添加了Keypoint识别。本方案与之类似,由于任务相对简单,并不需要求出bbox,因此删减了Object size的回归。具体代码实现将会公开在AI Studio项目。

https://aistudio.baidu.com/aistudio/projectdetail/4915699

在这里插入图片描述

 -   **手部5个指尖关键点**

为了更好地在边缘端部署,把原来手部单手21个关键点简化为只训练或推理5个手指指尖点,缩减网络训练及推理的时间。

 -  **不区分左右手**

因本项目应用于弹钢琴,左右手并不影响项目的结果,就没有区分左手或右手。

在这里插入图片描述
训练数据

训练数据集来自于Eric.Lee的handpose_datasets_v2数据集,在handpose_datasets_v1的基础上增加了左右手属性"handType": “Left” or “Right”,数据总量为38w+。

https://aistudio.baidu.com/aistudio/datasetdetail/162171/0

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gS2dorcl-1678171159294)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bc04a0224e694e54a6a13bf0245e985f~tplv-k3u1fbpfcp-zoom-1.image)]
程序运行流程

程序整体使用生产者-消费者模式,分为三个模块:输入模块、手部关键点预测模块、主显示及UI处理按键响应模块。

输入模块放在子进程是“生产者”,输入的图片加入到可跨进程读写的queue中,给到主进程的消费者。“消费者”包含关键点预测模块,预测手指关键点结果与画面及UI进行叠加,通过pygame来显示。

程序采用多进程处理,输入图片是一个进程,模型推理与UI响应是一个进程,能更高效运行,避免出现卡顿。

输入模块(生产者)

使用OpenCV的cv2.videoCapture读取视频流或摄像头画面或视频。获得的画面放入dataQueue中等待处理,获得画面frame,添加到dataQueue中。

手部关键点预测模块(消费者)

把pygame作为呈现端,摄像头画面、叠加的UI或提示、按键响应均通过pygame实现。

主UI模块

本项目使用pygame作为UI呈现端。pygame播放声音更灵活,可同时播放多个声音。

import pygamefrom pygame.localsimport *from sys import exitimport sys
pathDict={}
pathDict['hand']='../HandKeypoints/'for path in pathDict.values():
    sys.path.append(path)import cv2import timefrom collections import dequefrom PIL importImageimport tracebackfrom multiprocessing import Queue,Processfrom ModuleSound import effectDict# from ModuleHand import handKeypointsimport CVTools as CVTimport GameTools as GTfrom ModuleConsumer import FrameConsumer
from predict7 import CenterNetfrom ModuleInput import  FrameProducerimport numpy as np
pygame.init()
defframeShow(frame,screen):

#
# timeStamp = cap.get(cv2.CAP_PROP_POS_MSEC)
# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    frame=np.array(frame)[:,:,::-1]
#print('frame',frame.shape)
    frame = cv2.transpose(frame)
    frame = pygame.surfarray.make_surface(frame)
    screen.blit(frame, (0, 0))
    pygame.display.update()
# return timeStamp
defresetKeyboardPos(ftR,thresholdY):
    print('key SPACE',ftR)
iflen(ftR)>0:
        ftR=np.array(ftR)

        avrR=np.average(ftR[:,1])
        thresholdY=int(avrR)
        print('reset thresholdY',thresholdY)
return thresholdY
defkeyboardResponse(prodecer,ftR,thresholdY):

for event in pygame.event.get():
if event.type == pygame.QUIT:
            prodecer.runFlag = False
            exit()
elif event.type == pygame.MOUSEBUTTONUP:
            thresholdY=resetKeyboardPos(ftR,thresholdY)

elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
                prodecer.runFlag = False
                exit()                
elif event.key == pygame.K_SPACE:
                thresholdY=resetKeyboardPos(ftR,thresholdY)
return thresholdYdefloopRun(dataQueue,wSize,hSize,prodecer,consumer,thresholdY,movieDict,skipFrame):
# tip position of hand down
    ftDown1={}
    ftDown2={}
# tip position for now
    ft1={}
    ft2={}
# tip position of hand up
    ftUp1={}
    ftUp2={}
#
    stageR=-1
    stageL=-1
    resXR=-1
    resXL=-1
    idsR=-1
    idsL=-1
#
    biasDict1={}
    biasDict2={}
    screen = pygame.display.set_mode((wSize,hSize))
# cap = cv2.VideoCapture(path)
    num=-1
    keyNums=12
    biasy=20
    result={}
whileTrue:
##

        FPS=prodecer.fps/skipFrame
if FPS >0:
            videoFlag = True
else:
            videoFlag = False
##

##
# print('ppp', len(dataDeque), len(result))
if  dataQueue.qsize()==0 :
            time.sleep(0.1)
continue
# print('FPS',FPS)


elif dataQueue.qsize()>0:
##
            image=dataQueue.get()
## flir left right:
            image=image[:,::-1,:]
            result=consumer.process(image,thresholdY)
            resImage=result['image']
            ftR=result['fringerTip1']
            ftL=result['fringerTip2']
#print('resImage',resImage.size)

        thresholdY=keyboardResponse(prodecer, ftR,thresholdY)

if videoFlag:

            num += 1
if num == 0:
                T0 = time.time()
                print('T0',T0,num*(1./FPS))

try:           
            resImage = GT.uiProcess(resImage,ftR,ftL,biasy)

except Exception as e:
            traceback.print_exc()

try:
            fringerR,keyIndexR,stageR=GT.pressDetect(ftR,stageR,thresholdY,biasy,wSize,keyNums)
            fringerL,keyIndexL,stageL=GT.pressDetect(ftL,stageL,thresholdY,biasy,wSize,keyNums)

#print('resR',idsR,resR,idsL,resL)
except Exception as e:
            traceback.print_exc()
#

        GT.soundPlay(effectDict,keyIndexR)
        GT.soundPlay(effectDict,keyIndexL)
#
        resImage=GT.moviePlay(movieDict,keyIndexR,resImage,thresholdY)
        resImage=GT.moviePlay(movieDict,keyIndexL,resImage,thresholdY)

        frameShow(resImage, screen)
#clear result
        result={}

if __name__=='__main__':

    link=0
    wSize=640
    hSize=480

    skipFrmae=2

    dataQueue = Queue(maxsize=2)
    resultDeque = Queue()
    thresholdY=250
    producer = FrameProducer(dataQueue, link)
##
    frontPIL=Image.open('pianoPic/pianobg.png')
    handkeypoint=CenterNet(folderPath='/home/sig/sig_dir/program/HandKeypoints/')
    consumer=FrameConsumer(dataQueue,resultDeque,handkeypoint,frontPIL)
    producer.start()
#
    moviePicPath='pianoPic/'
    movieDict=GT.loadMovieDict(moviePicPath)
#
    loopRun(dataQueue, wSize, hSize, producer,consumer,thresholdY,movieDict,skipFrmae)

02 部署

在这里插入图片描述
下载模型代码

下载本项目数据集的Piano.zip压缩包(或data/data181662/文件夹中的压缩包) 到本地并解压,可以根据不同具体情况选择对应的版本开始部署。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RwnqY7mS-1678171159294)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf54b8169ed04ab6854d214752a7a0f5~tplv-k3u1fbpfcp-zoom-1.image)]
X86 / X64系统上运行

  • 安装飞桨及pygame等所需的库;
  • 解压data中的压缩包到目录中;
  • 进入HandPiano文件夹,运行python main.py程序即可。

图片在ARM设备,如Jetson NX上运行

  • 从0开始

如果从0开始部署飞桨到Jetson NX可参考“ゞ灰酱”的项目:https://aistudio.baidu.com/aistudio/projectdetail/969585?channelType=0&channel=0

  • 下载 Paddle Inference库并安装

https://www.paddlepaddle.org.cn/inference/v2.4/guides/install/download_lib.html

  • 解压data中的压缩包到目录中
  • 进入 HandPiano 文件夹,python main.py运行程序即可

03 运行

按硬件配置设置好,按C部署,启动程序,看到如下UI。

在这里插入图片描述

  • 效果展示

https://www.bilibili.com/video/BV1wT411g7MU/

  • 一只手的五指放于桌面上,当5个手指点的圆形都出现后,点击鼠标左键进行“点击位置校准”。

  • “点击位置校准”后,会调整琴键UI位置,黄线会在指尖所成的直线处。

  • 当指尖的点越过黄线进入琴键位置后即触发该琴键的声音。

  • 标示有C1的就表示C大调的do,之后的2、3、4、5、6、7 就是对应C大调的简谱的 2(re)、3(mi)、 4(fa)、 5(sol)、 6(la)、 7(si)。C1左边的是降一个调的简谱的5、6、7。

    一起开始弹钢琴吧!

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

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

相关文章

金三银四,我猜你需要这套网络安全工程师面试题合集【全网首发】

以下为信息安全/网络安全各个方向涉及的面试题,星数越多代表问题出现的几率越大,祝各位都能找到满意的工作~ 注:做这个List的目标不是很全,因为无论如何都不可能覆盖所有的面试问题,更多的还是希望由点达面&#xff0…

h264编码之SPS解析

一、概念 SPS即Sequence Paramater Set,又称作序列参数集。SPS中保存了一组编码视频序列(Coded video sequence)的全局参数。 二、定义 H.264标准协议中规定的SPS格式位于文档的7.3.2.1.1,如下图所示: 1、profile_idc 根据《T-REC-H.264-2…

数据库的备份与恢复

文章目录前言备份恢复概述故障的种类数据库备份数据库的恢复日志文件前言 提示:这里可以添加本文要记录的大概内容: 备份与恢复是为了防止数据库运行出现故障时造成数据丢失、损坏的一个重要手段 提示:以下是本篇文章正文内容,…

【SAP PO】X-DOC:SAP PO 接口配置完整教程之三REST服务发布报文转换

X-DOC:SAP PO 接口配置完整教程之三REST服务发布&报文转换1、SAP端RFC开发2、PO端接口配置2.1、PO端SLD配置2.2、PO端ESB配置2.3、PO端IB配置2.4、Postman测试3、报文转换配置1、SAP端RFC开发 开发一个支持远程访问的RFC: FUNCTION yfm_xl_rest_de…

水库大坝安全监测及水情自动测报系统(设备清单)

解决方案水库大坝安全监测及水情自动测报系统方案,系统主要由降雨量监测站、水库水位监测站、大坝安全监测中的渗流量、渗流压力和变形监测站及视频和图像监测站等站点组成,同时建立规范、统一的监测平台,集数据传输、信息共享、数据储存于一…

springBoot健康检查与k8s探针

从springBoot健康检查到k8s探针的问题定位 环境: k8s、springboot、mysql、flyway、Spring boot 2.0 Actuator健康检查组件等 问题: 项目中集成了flyway,当项目第一次启动时,都会初始化flyway文件,再加上各种组件的…

水文模型有哪些?SWAT模型、VIC模型、HEC模型、HSPF模型、HYPE模型、SWMM模型、FVCOM模型、Delft3D模型等应用

目录 ㈠ 从小白到精通SWAT模型学习建模方法、实例应用、高级进阶 ㈡ RVIC模型融合实践技术应用及未来气候变化模型预测 ㈢ HEC-RAS一维、二维建模方法及实践技术应用 ㈣ HEC-HMS水文模型实践技术应用 ㈤ HSPF 模型应用 ㈥ HYPE分布式水文模型建模方法与案例分析 ㈦ 基于…

raspberry安装ffmpeg以及扩展了解

文章目录ffmpeg简介使用ffmpegffplay播放控制命令播放滤镜处理参数选项ffprobeffmpeg sudo apt-get install ffmpeg简介 FFmpeg的名称来自MPEG视频编码标准,前面的“FF”代表“Fast Forward”,FFmpeg是一套可以用来记录、转换数字音频、视频&#xff0…

Godot Engine 4.0横空出世,Vulkan大怪兽加持,画质提升简直亮瞎眼

【CSDN 编者按】经历了漫长的等待,万众瞩目的 Godot Engine 4.0 正式版在其 3.0 版本发布 5 年以后,终于带着海量令人兴奋的新功能横空出世! 整理 | 开发游戏的老王 责编 | 王子彧 出品 | CSDN(ID:CSDNnews&#xff09…

优思学院|盘点,精益生产25个工具!【必需收藏】

精益生产方法需要一种全面的方法才能有效实施。精益这个概念是每个接触产品供应链的人都要实践的,无论是在计划方面还是在分析方面。 精益生产工具有助于持续改进生产效率和产品或服务质量。精益工具是要减少 Muda (浪费),从生产过…

内存清理、动画制作、CPU检测等五款实用软件推荐

人类与99%的动物之间最大差别在于是否会运用工具,借助好的工具,能提升几倍的工作效率。 1.内存清理软件——MemReduct MemReduct是一款内存清理软件,现在越来越多的软件由于硬件的普遍发展,对内存的使用都开始肆无忌惮起来&…

【C++】list的使用和基本迭代器框架的实现 vs和g++下string结构的说明

真正的成熟应该并不是追求完美,而是直面自己的缺憾,这才是生活的本质。 文章目录一、初见list1.list的迭代器失效和基本使用2.list的operations操作接口(看起来挺不错的接口,但可惜不怎么实用)3.vector和list的排序性能…

Linux系统基础命令(二)

一、浏览和切换目录 ls命令:列出文件和目录,主要用于列出文件和目录 CentOS的终端默认是有颜色标注的。一般来说:蓝色--->目录;绿色-->可执行文件;红色--->压缩文件;浅蓝色--->链接文件&#…

Java并发类库提供的线程池有哪几种? 分别有什么特点?

第21讲 | Java并发类库提供的线程池有哪几种? 分别有什么特点? 我在专栏第 17 讲中介绍过线程是不能够重复启动的,创建或销毁线程存在一定的开销,所以利用线程池技术来提高系统资源利用效率,并简化线程管理&#xff0c…

计算机网络总结

第一章 互联网 互连网:局部范围互连起来的计算机网络,与网络相连的计算机常称为主机。 互联网:当今世界上最大的计算机网络,Internet 两个基本特点 连通性,资源共享(信息,软件,硬件) 计算…

Ubuntu18安装新版本PCL-1.13,并和ROS自带PCL-1.8共存

文章目录1.安装新版本PCL-1.132.在工程中使用新版本的PCL-1.133.pcl-1.13误装到/usr/local/下如何卸载1.安装新版本PCL-1.13 下载PCL-1.13代码: 修改CMakeLists.txt文件,不编译vtk相关的代码:vtk的问题比较难解决,但是一般我们安…

科技大势怎么看 2023怎么干?

2023年,科技的走向依旧是世界各国的关注重点,各国在纷纷设立自己的科技战略目标外,还在潜心研究不同技术领域的科技趋势,试图通过科技占据国际竞争的制高点。 随着我国深入实施创新驱动发展战略,推动产业结构优化升级&…

数据结构1——概念与程序复杂度

翻陈年老底翻到了之前的数据结构笔记,由于当时刚开始使用电脑记笔记,markdown语法用的还是不是很熟,以及技术上比较欠缺,望多多海涵。 来都来了,点个赞呗~ 数据结构大体框架 早期>>数值计算>>计算机元素…

配置主机名与ip的映射关系

本次进行简单的小实验 通过在windows上配置主机名与IP地址的映射关系,达到我们在xshell或其他远程连接设备上,不用IP地址登陆,只需要用主机名就能实现登陆的效果 配置 首先 需要查看自己虚拟机的IP地址,找到ens33或者ens160…

[项目] Boost搜索引擎

目录 1.项目相关背景 2.项目宏观原理 3.技术栈和项目环境 4.正排索引&&倒排索引 5.去标签与数据清洗 6.构建索引模块Index 6.1正排索引 6.2 建立倒排 jiebacpp使用 建立分词 7.搜索引擎模块Searcher Jsoncpp -- 通过jsoncpp进行序列化和反序列化 处理Cont…