Files
skills/musicXML-ocr/scripts/audiveris_to_musescore.py
hmo 04db423416 Initial commit: skills library
- 70 skills with code and documentation
- Add .gitignore (ignore __pycache__, output/, temp/, venv/)
- Clean up test intermediates and caches
2026-04-26 19:27:40 +08:00

203 lines
5.7 KiB
Python

#!/usr/bin/env python3
"""
audiveris_to_musescore.py
使用 Audiveris 进行五线谱 OCR 识别的完整流水线。
功能:
1. 调用 Audiveris 识别五线谱图片/PDF
2. 输出 .mxl 格式的 MusicXML 文件
Usage:
python audiveris_to_musescore.py <input_image_or_pdf> [output_dir]
Examples:
python audiveris_to_musescore.py "D:/scores/sheet.png" "D:/output"
# 处理 PDF
python audiveris_to_musescore.py "D:/scores/piece.pdf" "D:/output"
Dependencies:
- Audiveris (https://github.com/Audiveris/audiveris)
- Python 3.8+
Installation:
# Windows
winget install --id Audiveris.Audiveris --accept-package-agreements --accept-source-agreements
# 或手动下载 MSI: https://github.com/Audiveris/audiveris/releases
"""
import sys
import os
import subprocess
import tempfile
import shutil
import zipfile
import argparse
# Audiveris 安装路径
AUDIVERIS_PATH = r"C:\Program Files\Audiveris\Audiveris.exe"
def find_audiveris() -> str:
"""查找 Audiveris 可执行文件路径。"""
if os.path.exists(AUDIVERIS_PATH):
return AUDIVERIS_PATH
# 尝试常见安装位置
possible_paths = [
r"C:\Program Files\Audiveris\Audiveris.exe",
r"C:\Program Files (x86)\Audiveris\Audiveris.exe",
os.path.expanduser(r"~\AppData\Local\Programs\Audiveris\Audiveris.exe"),
]
for path in possible_paths:
if os.path.exists(path):
return path
raise FileNotFoundError(
"Audiveris not found. Please install from:\n"
" winget install --id Audiveris.Audiveris\n"
" or download from: https://github.com/Audiveris/audiveris/releases"
)
def extract_musicxml_from_mxl(mxl_path: str, output_dir: str) -> str:
"""从 .mxl 文件中提取 MusicXML 内容。"""
with zipfile.ZipFile(mxl_path, 'r') as z:
for name in z.namelist():
if name.endswith('.xml'):
xml_path = os.path.join(output_dir, os.path.basename(name))
with z.open(name) as src:
content = src.read()
with open(xml_path, 'wb') as dst:
dst.write(content)
return xml_path
return None
def process_with_audiveris(input_path: str, output_dir: str) -> dict:
"""
使用 Audiveris 处理输入文件。
Returns:
dict with keys:
- mxl_path: path to .mxl output
- xml_path: path to extracted .xml (if available)
- omr_path: path to .omr project file
"""
if not os.path.exists(input_path):
raise FileNotFoundError(f"Input file not found: {input_path}")
os.makedirs(output_dir, exist_ok=True)
audiveris_exe = find_audiveris()
print(f"[Audiveris] Found at: {audiveris_exe}")
# 构建命令:-batch -export -output <dir> <input>
cmd = [
audiveris_exe,
'-batch',
'-export',
'-output', output_dir,
input_path
]
print(f"[Audiveris] Processing: {input_path}")
print(f"[Audiveris] Command: {' '.join(cmd)}")
# 运行 Audiveris
result = subprocess.run(
cmd,
capture_output=True,
text=True,
encoding='utf-8',
errors='replace'
)
if result.returncode != 0:
print(f"[Audiveris] STDERR:\n{result.stderr}")
raise RuntimeError(f"Audiveris failed with code {result.returncode}")
print(f"[Audiveris] Processing complete!")
print(f"[Audiveris] Output directory: {output_dir}")
# 查找生成的输出文件
base_name = os.path.splitext(os.path.basename(input_path))[0]
# 可能的输出文件
mxl_path = os.path.join(output_dir, f"{base_name}.mxl")
omr_path = os.path.join(output_dir, f"{base_name}.omr")
# 列出输出目录内容
print(f"[Audiveris] Files in output directory:")
for f in os.listdir(output_dir):
print(f" - {f}")
return {
'mxl_path': mxl_path if os.path.exists(mxl_path) else None,
'xml_path': None, # 稍后提取
'omr_path': omr_path if os.path.exists(omr_path) else None,
'output_dir': output_dir,
'base_name': base_name
}
def main():
parser = argparse.ArgumentParser(
description="OMR using Audiveris - Convert sheet music images to MusicXML",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python audiveris_to_musescore.py "D:\\scores\\sheet.png" "D:\\output"
python audiveris_to_musescore.py "D:\\scores\\piece.pdf"
"""
)
parser.add_argument('input', help="Input image (PNG/JPG) or PDF file")
parser.add_argument('output_dir', nargs='?', default=None,
help="Output directory (default: same as input)")
args = parser.parse_args()
input_path = os.path.abspath(args.input)
if args.output_dir:
output_dir = os.path.abspath(args.output_dir)
else:
# 默认输出到输入文件同目录
output_dir = os.path.join(os.path.dirname(input_path), 'audiveris_output')
try:
result = process_with_audiveris(input_path, output_dir)
print()
print("=" * 50)
print("Processing complete!")
print("=" * 50)
if result['mxl_path']:
print(f"MusicXML (.mxl): {result['mxl_path']}")
# 提取 XML 以便查看
xml_path = extract_musicxml_from_mxl(
result['mxl_path'],
output_dir
)
if xml_path:
print(f"MusicXML (.xml): {xml_path}")
if result['omr_path']:
print(f"Project (.omr): {result['omr_path']}")
print()
print("Open .mxl file in MuseScore to verify and edit.")
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == '__main__':
main()