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>
This commit is contained in:
55 files changed
+17536
No files matched your search
Executable
+84
@@ -0,0 +1,84 @@
|
||||
#!/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())
|
||||
Reference in new issue
Block a user