[bar image] → model → [{duration, drums}, {duration, drums}, ...]1[
2 {"duration": "eighth", "drums": ["kick", "hi_hat_closed"]},
3 {"duration": "eighth", "drums": ["hi_hat_closed"]},
4 {"duration": "quarter", "drums": ["snare", "hi_hat_closed"]},
5 {"duration": "eighth", "drums": ["kick", "hi_hat_closed"]}
6]| File | Purpose |
|---|---|
omr_finetuned.pt | PyTorch checkpoint — use this to resume training or fine-tune |
omr.onnx | ONNX export — use this for inference (no PyTorch needed) |
1import onnxruntime as ort
2import numpy as np
3from PIL import Image
4import torchvision.transforms as T
5
6DRUMS = [
7 'hi_hat_closed', 'snare', 'kick', 'ride', 'crash',
8 'hi_hat_open_half', 'hi_hat_open_full', 'hi_hat_pedal',
9 'floor_tom_1', 'floor_tom_2', 'tom_mid', 'tom_hi',
10 'ride_bell', 'snare_rim', 'snare_rimshot',
11 'cowbell', 'clap', 'choked_crash', 'china',
12]
13DURATIONS = [
14 'whole', 'half', 'dotted_quarter', 'quarter',
15 'dotted_eighth', 'eighth', 'sixteenth', 'thirty_second',
16 'triplet_eighth', 'triplet_sixteenth',
17]
18N_BEATS, N_DRUMS, N_DURATIONS = 32, 19, 10
19THRESHOLD = 0.5
20
21sess = ort.InferenceSession('omr.onnx')
22
23transform = T.Compose([
24 T.Resize((128, 384)),
25 T.Grayscale(num_output_channels=3),
26 T.ToTensor(),
27 T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
28])
29
30def predict_bar(image_path: str) -> list:
31 img = Image.open(image_path).convert('L')
32 x = transform(img).unsqueeze(0).numpy()
33
34 drum_logits, dur_logits = sess.run(None, {'image': x})
35
36 drum_probs = 1 / (1 + np.exp(-drum_logits[0]))
37 drum_grid = drum_probs.reshape(N_BEATS, N_DRUMS)
38 dur_preds = dur_logits[0].reshape(N_BEATS, N_DURATIONS).argmax(axis=1)
39
40 notes = []
41 for bi in range(N_BEATS):
42 drums = [DRUMS[di] for di in range(N_DRUMS) if drum_grid[bi, di] > THRESHOLD]
43 if drums:
44 notes.append({
45 'duration': DURATIONS[dur_preds[bi]],
46 'drums': drums,
47 })
48 return notes| Category | Drums |
|---|---|
| Core | kick, snare, hi_hat_closed |
| Hi-hat | hi_hat_open_half, hi_hat_open_full, hi_hat_pedal |
| Cymbals | ride, crash, china, ride_bell, choked_crash |
| Toms | tom_hi, tom_mid, floor_tom_1, floor_tom_2 |
| Articulations | snare_rim, snare_rimshot |
| Percussion | cowbell, clap |
whole, half, dotted_quarter, quarter, dotted_eighth, eighth, sixteenth, thirty_second, triplet_eighth, triplet_sixteenth| Drum | F1 | Notes |
|---|---|---|
| kick | 0.929 | Excellent |
| snare | 0.890 | Excellent |
| hi_hat_closed | 0.810 | Good |
| hi_hat_open_half | 0.708 | Good |
| hi_hat_open_full | 0.550 | Moderate |
| crash | 0.532 | Moderate |
| tom_hi | 0.462 | Moderate |
| snare_rim | 0.458 | Moderate |
| ride | 0.431 | Weak — visually similar to crash |
| floor_tom_1 | 0.424 | Weak |
| ride_bell | 0.424 | Weak |
| tom_mid | 0.264 | Weak |
| hi_hat_pedal | 0.245 | Weak |
| floor_tom_2 | 0.131 | Very weak — hard to distinguish from floor_tom_1 |
| snare_rimshot | 0.018 | Too few training examples |
| cowbell | 0.000 | Too few training examples |
| clap | 0.000 | Too few training examples |
| choked_crash | 0.000 | Too few training examples |
| china | 0.000 | Too few training examples |
| Metric | Value |
|---|---|
| Overall duration accuracy | 90.9% |
| triplet_eighth | 98% |
| eighth | 94% |
| quarter | 92% |
| sixteenth | 87% |
| dotted_eighth | 11% — too few examples |
| Metric | Value |
|---|---|
| Cell accuracy (per slot) | 97.4% |
| Exact-bar accuracy | 14.7% |
Note: exact-bar accuracy requires every single prediction in a 608-slot grid to be correct — it is an extremely strict metric. Cell accuracy and per-drum F1 are more meaningful for practical use.
parse_gp7.py / parse_gp5.py