feat(exercise): 优化死虫式训练姿态检测算法

- 调整视频处理频率从每帧处理改为每2帧处理
- 添加膝角趋势平滑算法减少单帧抖动误判
- 改进对角伸展检测逻辑支持准备位手臂上举
- 优化状态机确保严格回到准备姿态才计数
- 添加姿态丢失时的候选帧清理机制
- 更新音频文件生成路径至resources目录
- 改进macOS音频生成使用AIFF格式提高质量
- 添加详细的帧处理日志输出间隔配置
This commit is contained in:
2026-06-10 22:57:35 +08:00
parent ea0c007441
commit 6dee2a2ff3
9 changed files with 281 additions and 39 deletions
+29 -24
View File
@@ -21,27 +21,27 @@ def generate_rep_audio_files(
默认生成到:
app/audio/reps/0.wav
app/audio/reps/1.wav
resources/audio/reps/0.aiff # macOS
resources/audio/reps/0.wav # Windows / Linux
...
app/audio/reps/200.wav
resources/audio/reps/200.aiff 或 200.wav
服务启动时调用一次即可。
"""
output_dir.mkdir(parents=True, exist_ok=True)
system = platform.system().lower()
suffix = ".aiff" if system == "darwin" else ".wav"
missing_counts = [
count
for count in range(0, max_count + 1)
if overwrite or not _audio_path(output_dir, count).exists()
if overwrite or not _audio_path(output_dir, count, suffix=suffix).exists()
]
if not missing_counts:
logger.info("Rep audio files already prepared: {}", output_dir)
return
system = platform.system().lower()
logger.info(
"Preparing rep audio files, system={}, count={}, output_dir={}",
system,
@@ -79,22 +79,27 @@ def _generate_with_macos_say(
raise RuntimeError("macOS say command not found")
for count in counts:
audio_file = _audio_path(output_dir, count)
audio_file = _audio_path(output_dir, count, suffix=".aiff")
subprocess.run(
[
"say",
"-r",
str(rate),
"--file-format=WAVE",
"-o",
str(audio_file),
str(count),
],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=True,
)
try:
subprocess.run(
[
"say",
"-r",
str(rate),
"--file-format=AIFF",
"-o",
str(audio_file),
str(count),
],
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
text=True,
check=True,
)
except subprocess.CalledProcessError as exc:
message = exc.stderr.strip() or f"exit status {exc.returncode}"
raise RuntimeError(f"Failed to generate {audio_file}: {message}") from exc
def _generate_with_pyttsx3(
@@ -114,11 +119,11 @@ def _generate_with_pyttsx3(
engine.setProperty("volume", 1.0)
for count in counts:
audio_file = _audio_path(output_dir, count)
audio_file = _audio_path(output_dir, count, suffix=".wav")
engine.save_to_file(str(count), str(audio_file))
engine.runAndWait()
def _audio_path(output_dir: Path, count: int) -> Path:
return output_dir / f"{count}.wav"
def _audio_path(output_dir: Path, count: int, *, suffix: str) -> Path:
return output_dir / f"{count}{suffix}"