music21_rs/omr/
evaluators.rs1use crate::error::{Error, Result};
6use crate::stream::Stream;
7
8use super::correctors::ScoreCorrector;
9
10pub fn min_edit_distance(target: &[String], source: &[String]) -> usize {
14 let (n, m) = (target.len(), source.len());
15 let mut distance = vec![vec![0; m + 1]; n + 1];
16 for i in 1..=n {
17 distance[i][0] = distance[i - 1][0] + 1;
18 }
19 for j in 1..=m {
20 distance[0][j] = distance[0][j - 1] + 1;
21 }
22 for i in 1..=n {
23 for j in 1..=m {
24 let substitution = if source[j - 1] == target[i - 1] { 0 } else { 2 };
25 distance[i][j] = (distance[i - 1][j] + 1)
26 .min(distance[i][j - 1] + 1)
27 .min(distance[i - 1][j - 1] + substitution);
28 }
29 }
30 distance[n][m]
31}
32
33#[derive(Clone, Debug)]
36pub struct OmrGroundTruthPair {
37 omr: ScoreCorrector,
38 ground: ScoreCorrector,
39}
40
41impl OmrGroundTruthPair {
42 pub fn new(omr: Stream, ground: Stream) -> Result<Self> {
48 Ok(Self {
49 omr: ScoreCorrector::new(omr)?,
50 ground: ScoreCorrector::new(ground)?,
51 })
52 }
53
54 pub fn omr(&self) -> &ScoreCorrector {
56 &self.omr
57 }
58
59 pub fn omr_mut(&mut self) -> &mut ScoreCorrector {
61 &mut self.omr
62 }
63
64 pub fn ground(&self) -> &ScoreCorrector {
66 &self.ground
67 }
68
69 pub fn differences(&self) -> Result<usize> {
77 let theirs = self.ground.all_hashes();
78 let mut total = 0;
79 for (part, ours) in self.omr.all_hashes().into_iter().enumerate() {
80 let truth = theirs
81 .get(part)
82 .ok_or_else(|| Error::Omr("list index out of range".to_string()))?;
83 total += min_edit_distance(ours, truth);
84 }
85 Ok(total)
86 }
87}
88
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
93pub struct Evaluation {
94 pub original_edit_distance: usize,
96 pub new_edit_distance: usize,
98 pub number_of_flagged_measures: usize,
100 pub total_number_of_measures: usize,
102}
103
104pub fn evaluate_correcting_model(
114 omr: Stream,
115 ground: Stream,
116 original_differences: Option<usize>,
117) -> Result<Evaluation> {
118 let mut pair = OmrGroundTruthPair::new(omr, ground)?;
119 let original_edit_distance = match original_differences {
120 Some(differences) => differences,
121 None => pair.differences()?,
122 };
123 let corrector = pair.omr_mut();
124 let mut horizontal = Vec::new();
125 let (mut flagged, mut total) = (0, 0);
126 for part in 0..corrector.single_parts().len() {
127 flagged += corrector.find_incorrect_measures(part, false)?.len();
128 horizontal.push(
129 corrector
130 .single_part_mut(part)
131 .run_horizontal_correction_model(),
132 );
133 total += corrector.single_parts()[part].hashed_notes().len();
134 }
135 let vertical = corrector.run_vertical_correction_model()?;
136 corrector.generate_corrected_score(&horizontal, &vertical)?;
137 Ok(Evaluation {
138 original_edit_distance,
139 new_edit_distance: pair.differences()?,
140 number_of_flagged_measures: flagged,
141 total_number_of_measures: total,
142 })
143}
144
145pub fn auto_correlation_best_measure(score: Stream) -> Result<(usize, usize)> {
156 let mut corrector = ScoreCorrector::new(score)?;
157 let hashes: Vec<Vec<String>> = corrector
158 .all_hashes()
159 .into_iter()
160 .map(<[String]>::to_vec)
161 .collect();
162 let (mut measures, mut matches) = (0, 0);
163 for (part, own) in hashes.iter().enumerate() {
164 let incorrect = corrector.find_incorrect_measures(part, false)?.to_vec();
165 for (i, hash) in own.iter().enumerate() {
166 if incorrect.contains(&i) {
167 continue;
168 }
169 measures += 1;
170 let mut found = own
171 .iter()
172 .enumerate()
173 .any(|(j, other)| i != j && other == hash);
174 if !found {
175 for (other_part, theirs) in hashes.iter().enumerate() {
176 if other_part == part {
177 continue;
178 }
179 let other = theirs
180 .get(i)
181 .ok_or_else(|| Error::Omr("list index out of range".to_string()))?;
182 if other == hash {
183 found = true;
184 break;
185 }
186 }
187 }
188 if found {
189 matches += 1;
190 }
191 }
192 }
193 Ok((measures, matches))
194}