当前位置: 首页 > news >正文

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.jsqmd.com/news/7340/

相关文章:

  • Python语言自动玩游戏的数字拼图游戏程序代码ZXQMQZQ
  • 如何找出集合的两个子集使得和相等?
  • Python语言自动玩游戏的俄罗斯方块游戏程序代码QZQ
  • Spring AI(七)Spring AI 的RAG搭建集合火山向量模型+阿里云Tair(企业版)
  • Python语言自动玩游戏的数字拼图游戏程序代码QZQ
  • 赛前训练4 字符串哈希
  • 处处吻
  • ThreadLocal原理与使用详解
  • WinCC监控框架实战解析:打通物联网网关的关键步骤
  • 2025国庆Day1
  • 2025 编码器厂家 TOP 企业品牌推荐排行榜,无磁,光学,脉冲,绝对型,伺服,机械多圈,工业,二进制,拉线编码器公司推荐
  • 2025 年玻璃钢水箱厂家 TOP 企业品牌推荐排行榜,30 吨,订做,消防,专业,方形,拼装式,屋顶,大型玻璃钢水箱推荐这十家公司!
  • Java 大视界 -- Java 大数据在智能安防视频监控系统中的视频语义理解与智能检索进阶 - 实践
  • Spark专题-第三部分:性能监控与实战优化(1)-认识spark ui - 指南
  • 禁止DataGridView自动根据数据源的结构生成列
  • 2025 年压球机厂家 TOP 企业品牌推荐排行榜,新型,高压,节能,双螺旋干粉,对辊,煤粉,超低压压球机公司推荐!
  • 2025 年磨粉机厂家 TOP 企业品牌推荐排行榜,新型磨粉机,超细磨粉机,立式双动力磨粉机,节能磨粉机公司推荐!
  • 2025 年等离子清洗机厂家 TOP 企业品牌推荐排行榜,大气,真空,宽幅,微波,自动化,常压,低温,大腔体,射频,DBD,介质阻挡放电等离子清洗机公司推荐!
  • 基于Java+SSM+Django宠物医院信息管理系统(源码+LW+调试文档+讲解等)/宠物医院软件/宠物医疗管理系统/宠物诊所信息系统/动物医院管理软件/宠物医院信息管理/宠物健康记录系统 - 详解
  • 实用指南:Coze源码分析-资源库-删除数据库-后端源码-基础设施/数据存储层
  • 完整教程:如何优雅的布局,height: 100% 的使用和 flex-grow: 1 的 min-height 陷阱
  • MyBatis缓存架构深度拆解:从PerpetualCache的LRU陷阱到Redis分布式二级缓存防穿透实战 - 详解
  • 2025柔版印刷机厂家 TOP 企业品牌推荐排行榜,塑编袋,编织袋,阀口袋,重包膜,机组式,卫星式,不换版,FFS 重载膜,水泥袋柔版印刷机公司推荐!
  • 9 30 -
  • 2025/10/2
  • Spring 基础核心 - SpringMVC 入门与请求流程
  • (数据结构)链表OJ——刷题练习 - 实践
  • 重测序数据fastp数据质控及fastQC质量评估
  • 8. Spring AI tools/function-call - 教程
  • electron 安装失败