python语言手势控制音乐播放器代码QZQ

# pip install opencv-python mediapipe pygame numpy pillow
# pip install pyinstaller
# pyinstaller --onefile --icon=1.ico main.py

import cv2
import mediapipe as mp
import pygame
import os
import numpy as np
import time
from PIL import Image, ImageDraw, ImageFont # 添加PIL库支持中文显示

# 初始化pygame混音器
pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=512)


# 手势控制音乐播放器类
class GestureMusicPlayer:
def __init__(self, music_folder):
self.music_folder = music_folder
self.songs = self._load_valid_songs() # 加载验证通过的音乐
self.current_song_index = 0
self.volume = 0.5
self.is_playing = False
self.last_gesture_time = 0
self.gesture_cooldown = 1 # 手势冷却时间(秒)

# 加载中文字体
self.font_path = self._find_chinese_font()
if not self.font_path:
print("警告: 找不到中文字体,将使用英文显示")

# 加载第一首歌(如果有有效歌曲)
if self.songs:
self._load_current_song()
pygame.mixer.music.set_volume(self.volume)

# 初始化MediaPipe手势识别
self.mp_hands = mp.solutions.hands
self.hands = self.mp_hands.Hands(
max_num_hands=1,
min_detection_confidence=0.7,
min_tracking_confidence=0.5)
self.mp_draw = mp.solutions.drawing_utils

# 手势定义
self.GESTURES = {
"THUMBS_UP": "👍 播放/暂停",
"THUMBS_DOWN": "👎 停止",
"INDEX_UP": "👆 音量增加",
"INDEX_DOWN": "👇 音量减小",
"PALM_LEFT": "👈 上一首",
"PALM_RIGHT": "👉 下一首"
}

def _find_chinese_font(self):
"""查找可用的中文字体"""
# 常见中文字体路径
font_paths = [
"simhei.ttf", # 当前目录
"C:/Windows/Fonts/simhei.ttf", # Windows
"C:/Windows/Fonts/msyh.ttc", # Windows 微软雅黑
"/System/Library/Fonts/PingFang.ttc", # macOS
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", # Linux
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", # Linux
]

for path in font_paths:
if os.path.exists(path):
return path
return None

def put_chinese_text(self, image, text, position, font_size=30, color=(0, 255, 0)):
"""在图像上绘制中文文本"""
if self.font_path:
try:
# 转换OpenCV图像到PIL图像
img_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img_pil)

# 加载字体
font = ImageFont.truetype(self.font_path, font_size)

# 绘制文本
draw.text(position, text, font=font, fill=color)

# 转换回OpenCV格式
return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
except Exception as e:
print(f"字体渲染错误: {e}")
# 失败时使用英文显示
cv2.putText(image, text, position, cv2.FONT_HERSHEY_SIMPLEX,
1, color, 2, cv2.LINE_AA)
return image
else:
# 如果没有中文字体,使用英文显示
cv2.putText(image, text, position, cv2.FONT_HERSHEY_SIMPLEX,
1, color, 2, cv2.LINE_AA)
return image

def _load_valid_songs(self):
"""加载并验证文件夹中可播放的音乐文件"""
valid_songs = []
if not os.path.exists(self.music_folder):
return valid_songs

# 遍历文件夹中的音乐文件
for f in os.listdir(self.music_folder):
if f.endswith(('.mp3', '.wav')):
file_path = os.path.join(self.music_folder, f)
# 尝试加载文件验证是否可播放
try:
pygame.mixer.music.load(file_path)
valid_songs.append(f)
print(f"已加载音乐: {f}")
except pygame.error:
print(f"跳过无效/损坏的文件: {f}")
return valid_songs

def _load_current_song(self):
"""加载当前索引对应的歌曲"""
if self.songs:
song_path = os.path.join(self.music_folder, self.songs[self.current_song_index])
pygame.mixer.music.load(song_path)

def detect_gesture(self, landmarks):
# 获取关键点坐标
thumb_tip = landmarks[self.mp_hands.HandLandmark.THUMB_TIP]
index_tip = landmarks[self.mp_hands.HandLandmark.INDEX_FINGER_TIP]
middle_tip = landmarks[self.mp_hands.HandLandmark.MIDDLE_FINGER_TIP]
ring_tip = landmarks[self.mp_hands.HandLandmark.RING_FINGER_TIP]
pinky_tip = landmarks[self.mp_hands.HandLandmark.PINKY_TIP]
wrist = landmarks[self.mp_hands.HandLandmark.WRIST]

# 计算手指是否伸直
def is_finger_extended(finger_tip, finger_mcp):
return finger_tip.y < finger_mcp.y

# 大拇指竖起 (👍)
thumb_extended = thumb_tip.y < landmarks[self.mp_hands.HandLandmark.THUMB_IP].y
other_fingers_closed = all(
landmarks[self.mp_hands.HandLandmark(i)].y > landmarks[self.mp_hands.HandLandmark(i - 2)].y
for i in [self.mp_hands.HandLandmark.INDEX_FINGER_TIP,
self.mp_hands.HandLandmark.MIDDLE_FINGER_TIP,
self.mp_hands.HandLandmark.RING_FINGER_TIP,
self.mp_hands.HandLandmark.PINKY_TIP]
)
if thumb_extended and other_fingers_closed:
return "THUMBS_UP"

# 拇指向下 (👎)
thumb_down = thumb_tip.y > landmarks[self.mp_hands.HandLandmark.THUMB_IP].y
if thumb_down and other_fingers_closed:
return "THUMBS_DOWN"

# 食指向上 (👆)
index_extended = is_finger_extended(index_tip, landmarks[self.mp_hands.HandLandmark.INDEX_FINGER_MCP])
other_fingers_closed = all(
not is_finger_extended(landmarks[self.mp_hands.HandLandmark(i)],
landmarks[self.mp_hands.HandLandmark(i - 2)])
for i in [self.mp_hands.HandLandmark.MIDDLE_FINGER_TIP,
self.mp_hands.HandLandmark.RING_FINGER_TIP,
self.mp_hands.HandLandmark.PINKY_TIP]
)
if index_extended and other_fingers_closed:
return "INDEX_UP"

# 食指向下 (👇)
index_down = index_tip.y > landmarks[self.mp_hands.HandLandmark.INDEX_FINGER_PIP].y
if index_down and other_fingers_closed:
return "INDEX_DOWN"

# 手掌向左 (👈)
fingers_extended = all(
is_finger_extended(landmarks[self.mp_hands.HandLandmark(i)],
landmarks[self.mp_hands.HandLandmark(i - 2)])
for i in [self.mp_hands.HandLandmark.INDEX_FINGER_TIP,
self.mp_hands.HandLandmark.MIDDLE_FINGER_TIP,
self.mp_hands.HandLandmark.RING_FINGER_TIP,
self.mp_hands.HandLandmark.PINKY_TIP]
)
palm_orientation = wrist.x - index_tip.x
if fingers_extended and palm_orientation > 0.1:
return "PALM_LEFT"

# 手掌向右 (👉)
if fingers_extended and palm_orientation < -0.1:
return "PALM_RIGHT"

return None

def handle_gesture(self, gesture):
current_time = time.time()
if current_time - self.last_gesture_time < self.gesture_cooldown:
return

self.last_gesture_time = current_time

if not self.songs:
print("没有可用的音乐文件")
return

if gesture == "THUMBS_UP":
if self.is_playing:
pygame.mixer.music.pause()
self.is_playing = False
else:
pygame.mixer.music.unpause()
if not pygame.mixer.music.get_busy():
pygame.mixer.music.play()
self.is_playing = True
print("播放/暂停")

elif gesture == "THUMBS_DOWN":
pygame.mixer.music.stop()
self.is_playing = False
print("停止")

elif gesture == "INDEX_UP":
self.volume = min(1.0, self.volume + 0.1)
pygame.mixer.music.set_volume(self.volume)
print(f"音量增加: {self.volume:.1f}")

elif gesture == "INDEX_DOWN":
self.volume = max(0.0, self.volume - 0.1)
pygame.mixer.music.set_volume(self.volume)
print(f"音量减小: {self.volume:.1f}")

elif gesture == "PALM_LEFT":
self.current_song_index = (self.current_song_index - 1) % len(self.songs)
self._load_current_song()
if self.is_playing:
pygame.mixer.music.play()
print(f"上一首: {self.songs[self.current_song_index]}")

elif gesture == "PALM_RIGHT":
self.current_song_index = (self.current_song_index + 1) % len(self.songs)
self._load_current_song()
if self.is_playing:
pygame.mixer.music.play()
print(f"下一首: {self.songs[self.current_song_index]}")

def run(self):
if not self.songs:
print("没有可播放的音乐文件,请检查music文件夹")
# 显示提示信息
cap = cv2.VideoCapture(0)
while True:
success, image = cap.read()
if not success:
continue

image = self.put_chinese_text(image, "没有可播放的音乐文件", (50, 100), font_size=40, color=(0, 0, 255))
image = self.put_chinese_text(image, "请将音乐文件放入music文件夹", (50, 150), font_size=30, color=(255, 255, 255))
image = self.put_chinese_text(image, "按ESC退出", (50, 200), font_size=25, color=(255, 255, 255))

cv2.imshow('Gesture Music Player', image)
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
return

cap = cv2.VideoCapture(0)

while cap.isOpened():
success, image = cap.read()
if not success:
continue

# 转换图像颜色空间
image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)
image.flags.writeable = False
results = self.hands.process(image)

# 绘制手势识别结果
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

current_gesture = None

if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
self.mp_draw.draw_landmarks(
image, hand_landmarks, self.mp_hands.HAND_CONNECTIONS)
current_gesture = self.detect_gesture(hand_landmarks.landmark)

# 处理手势
if current_gesture:
self.handle_gesture(current_gesture)
# 使用新方法绘制中文文本
gesture_text = self.GESTURES.get(current_gesture, "")
image = self.put_chinese_text(image, gesture_text, (10, 50))

# 显示当前歌曲和状态
song_name = self.songs[self.current_song_index] if self.songs else '无'
# 截断过长的文件名
if len(song_name) > 30:
song_name = song_name[:27] + "..."

status_text = f"歌曲: {song_name} | 状态: {'播放中' if self.is_playing else '暂停/停止'} | 音量: {int(self.volume * 100)}%"

# 使用新方法绘制中文状态文本
image = self.put_chinese_text(image, status_text, (10, image.shape[0] - 30),
font_size=20, color=(255, 255, 255))

cv2.imshow('Gesture Music Player', image)

if cv2.waitKey(5) & 0xFF == 27: # ESC键退出
break

cap.release()
cv2.destroyAllWindows()


if __name__ == "__main__":
music_folder = "music" # 音乐文件夹路径

if not os.path.exists(music_folder):
os.makedirs(music_folder)
print(f"已创建音乐文件夹,请将音乐文件放入: {os.path.abspath(music_folder)}")
# 等待用户放入文件
input("按回车键继续...")

player = GestureMusicPlayer(music_folder)
player.run()

 

 

 

软件截图:屏幕截图 2025-09-21 100701

 

 

屏幕截图 2025-09-21 100630

 

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

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

相关文章

地方门户网站建设方案秦皇岛市海港区建设局网站

文章目录 1、什么是DES2、DES的基本概念3、DES的加密流程4、DES算法步骤详解4.1 初始置换(Initial Permutation&#xff0c;IP置换)4.2 加密轮次4.3 F轮函数4.3.1 拓展R到48位4.3.2 子密钥K的生成4.3.3 当前轮次的子密钥与拓展的48位R进行异或运算4.3.4 S盒替换&#xff08;Sub…

做网站关键词加到什么位置企业网企业网站制作

夏天天气炎热&#xff0c;电脑机箱内温度也较高&#xff0c;温度过高会影响电脑性能出现死机等问题&#xff0c;甚至影响硬件寿命。所以给机箱装风扇来散热是非常重要的。那么&#xff0c;机箱风扇怎么装合理呢?机箱风扇的电源线怎么接呢?下面分享一下机箱风...夏天天气炎热&…

新服务器做网站如何配置聊天直播软件开发

简介上篇文章我们介绍了Spring boot的fat jar/war包&#xff0c;jar/war包都可以使用 java -jar 命令来运行&#xff0c;而maven也提供了mvn spring-boot:run 命令来运行应用程序&#xff0c;下面我们看看两者有什么不同。Spring Boot Maven Plugin上篇文章我们提到了Spring Bo…

Python语言自动玩游戏的数字拼图游戏程序代码ZXQMQZQ

import pygameimport sysimport randomimport timefrom queue import PriorityQueuefrom pygame.locals import * # 初始化pygamepygame.init() # 游戏常量WIDTH, HEIGHT = 400, 500GRID_SIZE = 3TILE_SIZE = 100MARGI…

如何找出集合的两个子集使得和相等?

给定一个大小为 \(n\) 的整数集合 \(S\subseteq [0,V]\),找出他的两个子集 \(s_1,s_2\) 使得其元素的和相等,或报告无解。对于所有的 \(T\subseteq S\),\(T\) 中元素的和满足 \(0\le \sum_{x\in T} x\le V|T|\)。 所…

Python语言自动玩游戏的俄罗斯方块游戏程序代码QZQ

import randomimport mathimport sysimport numpy as npimport pygame # -------------------- 常量 --------------------CELL = 30 # 像素COLS, ROWS = 10, 20SCREEN_W, SCREEN_H = 20 * CELL, ROWS * CELLFPS = 60…

Spring AI(七)Spring AI 的RAG搭建集合火山向量模型+阿里云Tair(企业版)

Spring AI(七)Spring AI 的RAG搭建集合火山向量模型+阿里云Tair(企业版)pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-fami…

sw网站建设合肥网站seo服务

前言-什么是数据幻觉&#xff1f;它到底有什么危害呢 我们直接来举例&#xff1a; 我是金银花一区的&#xff0c;附近有什么小学&#xff1f; 此时RAG带出如下信息&#xff1a; 金银花小区一区、二区、三区附近教育资源有&#xff1a;银树大学、建设小学金银花校区、金树高…

Python语言自动玩游戏的数字拼图游戏程序代码QZQ

import sysimport randomimport pygame # 初始化pygamepygame.init() # 游戏常量SIZE = 3 # 3x3 拼图CELL = 120 # 每个格子的像素大小WIDTH = CELL * SIZEHEIGHT = CELL * SIZEEMPTY = SIZE * SIZE # 空格的表示值…

温州网站开发培训北京网站制作外包

http://blog.csdn.net/dandelion_gong/article/details/51673085 Unix下可用的I/O模型一共有五种&#xff1a;阻塞I/O 、非阻塞I/O 、I/O复用 、信号驱动I/O 、异步I/O。此处我们主要介绍第三种I/O符复用。 I/O复用的功能&#xff1a;如果一个或多个I/O条件满足&#xff08;输…

看动漫什么网站好企业网站的分类有哪三种

目录&#xff1a;系统学习 Java IO---- 目录&#xff0c;概览 PipedInputStream 类使得可以作为字节流读取管道的内容。 管道是同一 JVM 内的线程之间的通信通道。 使用两个已连接的管道流时&#xff0c;要为每个流操作创建一个线程&#xff0c; read() 和 write() 都是阻塞方法…

浙江自己如何做网站鹰潭手机网站建设

xin3721网络学院为广大学员&#xff0c;准备了丰富了教学视频。为了更好的让大学配合视频进行学习&#xff0c;拓展学员的知识面&#xff0c;我站特整理了大量的&#xff0c;技术文章&#xff0c;供学员参考。因此本教案需配合视频教程学习&#xff0c;视频教程地址为&#xff…

赛前训练4 字符串哈希

A 显然长度具有单调性,于是二分长度+map存储哈希值判断即可。实现 #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <string> #include <…

wordpress企业网站建设智慧农业项目方案

EventBus是android 下高效的发布/订阅事件总线机制&#xff0c;可以代替传统的Intent&#xff0c;Handler&#xff0c;BroadCast 或者Fragment&#xff0c;Activity&#xff0c;Service&#xff0c;线程之间传递数据&#xff0c;是一种发布订阅设计模式&#xff08;观察者模式&…

上海著名的网站制作公司杭州网站商场开发

美术教案第三课:曲曲直直(三年级美术下册教案)教学目标:认知目标:能够认识生活中的各种曲线和直线,说说曲线、直线给自己的感受.能力目标:能够用绘画、剪贴等方法表现曲线和直线的画面,培养学生自主探究的能力和创造能力.情感目标:通过收集不同直线、曲线材料进行创作,培养学生…

处处吻

你爱热吻却永不爱人 练习为乐但是怕熟人 你爱路过去索取见闻 陌路人变得必有份好感 你热爱别离 再合再离 似花瓣献技 叫花粉遍地 噢噢 你在播弄这穿线游戏 跟他结束 他与她再一起 你小心 一吻便颠倒众生 一吻便救一个人…

ThreadLocal原理与使用详解

ThreadLocal原理与使用详解 一、ThreadLocal 介绍 1.1 定义与核心特性定义:Java 官方文档描述,ThreadLocal 类用于提供线程内部的局部变量,多线程环境下通过 get() 和 set() 方法访问时,能保证各线程变量相对独立于…

WinCC监控框架实战解析:打通物联网网关的关键步骤

WinCC监控框架实战解析:打通物联网网关的关键步骤pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas"…