Files
Recorder-Tabber/scripts/run_oemer.py
T
Cursor Agentandchen 59d65333e3 Add a soprano recorder tab converter as a Linux AppImage.
The app turns a lead line into Baroque fingering tabs with rhythm kept, ships Twinkle and Ode to Joy, and reads photos locally with oemer when the bundled Python is present.

Co-authored-by: catpersonisme <catpersonisme@gmail.com>
2026-10-09 03:35:16 +00:00

85 lines
2.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""Read one sheet-music image with oemer and print the MusicXML path.
oemer asks ONNX Runtime for CUDA first. This wrapper forces the CPU provider
so the AppImage does not need a GPU.
"""
from __future__ import annotations
import os
import sys
import traceback
def force_cpu() -> None:
import onnxruntime as rt
original = rt.InferenceSession
def cpu_session(model, providers=None, **kwargs): # type: ignore[no-untyped-def]
kwargs.pop("providers", None)
return original(model, providers=["CPUExecutionProvider"], **kwargs)
rt.InferenceSession = cpu_session # type: ignore[method-assign]
def prepare_png(src: str, dest: str) -> None:
from PIL import Image, ImageOps
with Image.open(src) as image:
rgb = ImageOps.exif_transpose(image).convert("RGB")
if min(rgb.size) < 80:
raise ValueError("This image is too small to read as sheet music.")
rgb.thumbnail((2200, 2200))
rgb.save(dest, "PNG")
def recognize(png: str, out_dir: str) -> str:
from argparse import Namespace
from oemer.ete import clear_data, extract
def once(deskew: bool) -> str:
clear_data()
args = Namespace(
img_path=png,
output_path=out_dir,
use_tf=False,
save_cache=False,
without_deskew=not deskew,
)
return extract(args)
try:
return once(True)
except (Exception, SystemExit):
traceback.print_exc()
return once(False)
def main() -> int:
if len(sys.argv) != 3:
print("usage: run_oemer.py IMAGE OUT_DIR", file=sys.stderr)
return 2
src, out_dir = sys.argv[1], sys.argv[2]
os.makedirs(out_dir, exist_ok=True)
png = os.path.join(out_dir, "page.png")
try:
prepare_png(src, png)
force_cpu()
musicxml = recognize(png, out_dir)
except Exception as exc: # noqa: BLE001 — surface a single readable failure
print(f"{exc.__class__.__name__}: {exc}", file=sys.stderr)
traceback.print_exc()
return 1
if not musicxml or not os.path.isfile(musicxml):
print("oemer did not write MusicXML", file=sys.stderr)
return 1
print(f"MUSICXML={musicxml}")
return 0
if __name__ == "__main__":
raise SystemExit(main())