Metadata-Version: 2.4
Name: mcr-metric
Version: 1.1.0
Summary: Musical Correction Rate (MCR): a format-native OMR evaluation metric for **kern, ABC, and MusicXML
Author: Tobias Hornbogen
License: MIT
Project-URL: Repository, https://github.com/tobiashornbogen/mcr-metric
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
Classifier: Intended Audience :: Science/Research
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Dynamic: license-file

# mcr-metric

Musical Correction Rate (MCR) is an evaluation metric for Optical Music Recognition. It parses **kern, ABC, and MusicXML directly into a shared canonical event representation, aligns prediction and reference at the measure level, and scores individual notes with an additive cost model calibrated to correction effort. Because each format is parsed natively, kern predictions can be scored against ABC or MusicXML references (and vice versa) without a lossy conversion step.

Introduced in *Measure for Measure: Reliable Evaluation and a New State of the Art for Full-Page Optical Music Recognition* (ISMIR 2026).

## Install

```
pip install mcr-metric
```

No dependencies. Python ≥ 3.9.

## Usage

```python
import mcr

result = mcr.score(prediction_text, reference_text)   # formats auto-detected
print(result.mcr)                                     # 0.0 = perfect, 1.0 = retype everything
```

Formats are auto-detected from content. Headerless ABC fragments look like kern to the detector, so pass the format when you know it:

```python
result = mcr.score(pred, ref, pred_format="abc", ref_format="kern")
```

`result` is a `MetricResult` with the score plus diagnostics: `content_precision` / `content_recall` / `content_f1`, `measure_alignment_score`, `staff_topology_score`, per-notation-type F1 (`notation_scores_by_type`), `confidence_flags`, and `per_measure_diagnostics` with note-level costs per aligned bar. `result.to_dict()` serializes all of it.

Parsing alone:

```python
score = mcr.parse(text, format="kern")   # -> CanonicalScore
for event in score.events:
    print(event.midi_pitch, event.duration, event.staff)
```

### Command line

```
mcr score pred.krn ref.krn                 # prints MCR
mcr score pred.abc ref.krn --json          # full result as JSON
mcr batch pairs.jsonl --output scores.jsonl
mcr parse pred.krn                         # canonical events as JSONL
```

`mcr batch` reads one JSON object per line with `prediction` and `reference` keys (override with `--pred-key` / `--ref-key`) and reports the event-weighted corpus MCR on stderr.

## How the score works

1. Parse each input into canonical note events: measure index, onset, duration, MIDI pitch, staff, grace flag, and notation features (slurs, articulations, ornaments).
2. Consolidate tied events, so kern's expanded multi-bar ties and ABC's compressed long notes reduce to the same events.
3. Align measures with dynamic programming. A structural error (extra barline, missing spine operation) costs at most one unmatched measure pair and does not cascade.
4. Within each aligned measure pair, align events sorted by (onset, staff, grace, pitch) with Needleman–Wunsch. Substitution cost is the number of differing attributes; cross-staff matches carry a +1 penalty.
5. A deleted predicted event costs 1 (click to remove), an inserted reference event costs 2 (pitch and duration must both be entered). A pair splits into deletion plus insertion when more than three attribute mismatches separate the events.

```
MCR = (matched costs + |unmatched pred| + 2 |unmatched ref|) / (|pred events| + 2 |ref events|)
```

The denominator is the cost of deleting every predicted event and re-entering the reference from a blank score, so MCR ∈ [0, 1] reads as the fraction of full-retype effort.

Key signature, meter, and clef are compared as diagnostics but do not enter the score. Beams, stem directions, dynamics, and lyrics are not evaluated. The parsers never hard-fail: malformed regions yield fewer parsed events, which are charged as unmatched, and parser warnings appear in the quality reports.

## Versioning

`mcr.__version__` is the package version. `mcr.ALGORITHM_VERSION` identifies the scoring algorithm and only changes when a release alters scores.

## Citation

See `CITATION.cff`, or cite the ISMIR 2026 paper above.
