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>
85 lines
2.3 KiB
Python
Executable File
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())
|