def start_bundle(self): # TODO(ringw): Expose a cleaner way to set this value. # The image is too small for the default min staffline distance score. # pylint: disable=protected-access staffline_distance._MIN_STAFFLINE_DISTANCE_SCORE = 100 self.omr = engine.OMREngine()
def testIMSLP00823_008_mergeStandardAndBeginRepeatBars(self): page = engine.OMREngine().run( _get_imslp_path('IMSLP00823-008.png')).page[0] self.assertEqual(len(page.system), 6) self.assertEqual(len(page.system[0].staff), 2) self.assertEqual(len(page.system[0].bar), 6) self.assertEqual(len(page.system[1].staff), 2) self.assertEqual(len(page.system[1].bar), 6) self.assertEqual(len(page.system[2].staff), 2) self.assertEqual(len(page.system[2].bar), 7) self.assertEqual(len(page.system[3].staff), 2) self.assertEqual(len(page.system[3].bar), 6) self.assertEqual(len(page.system[4].staff), 2) self.assertEqual(len(page.system[4].bar), 6) # TODO(ringw): Detect BEGIN_REPEAT_BAR here. self.assertEqual(page.system[4].bar[0].type, musicscore_pb2.StaffSystem.Bar.END_BAR) self.assertEqual(page.system[4].bar[1].type, musicscore_pb2.StaffSystem.Bar.STANDARD_BAR) self.assertEqual(len(page.system[5].staff), 2) self.assertEqual(len(page.system[5].bar), 7)
def testIMSLP39661_keySignature_CSharpMinor(self): page = engine.OMREngine().run( _get_imslp_path('IMSLP39661-000.png')).page[0] score_reader = reader.ScoreReader() score_reader.read_system(page.system[0]) treble_sig = score_reader.score_state.staves[0].get_key_signature() self.assertEqual(treble_sig.get_type(), musicscore_pb2.Glyph.SHARP) self.assertEqual(len(treble_sig), 4) bass_sig = score_reader.score_state.staves[1].get_key_signature() self.assertEqual(bass_sig.get_type(), musicscore_pb2.Glyph.SHARP)
def testIMSLP01963_106_multipleStaffSizes(self): page = engine.OMREngine().run( _get_imslp_path('IMSLP01963-106.png')).page[0] self.assertEqual(len(page.system), 3) for system in page.system: self.assertEqual(len(system.staff), 4) self.assertEqual(system.staff[0].staffline_distance, 14) self.assertEqual(system.staff[1].staffline_distance, 14) self.assertEqual(system.staff[2].staffline_distance, 22) self.assertEqual(system.staff[3].staffline_distance, 22)
def testIMSLP00023_015_doubleNoteDots(self): """Tests note dots in system[1].staff[1] of the image.""" page = engine.OMREngine().run( _get_imslp_path('IMSLP00023-015.png')).page[0] self.assertEqual(len(page.system), 6) system = page.system[1] system_measures = measures.Measures(system) staff = system.staff[1] # All dotted notes in the first measure belong to one chord, and are # double-dotted. double_dotted_notes = [ glyph for glyph in staff.glyph if system_measures.get_measure(glyph) == 0 and len(glyph.dot) == 2 ] for note in double_dotted_notes: self.assertEqual(len(note.beam), 1) # Double-dotted eighth note duration. self.assertEqual(note.note.end_time - note.note.start_time, .5 + .25 + .125) double_dotted_note_ys = [ glyph.y_position for glyph in double_dotted_notes ] self.assertIn(-6, double_dotted_note_ys) self.assertIn(-1, double_dotted_note_ys) self.assertIn(-3, double_dotted_note_ys) self.assertIn(3, double_dotted_note_ys) # TODO(ringw): Fix 3 dots detected at y position +4. The dots from y # position +3 are too close, and we should only consider a single row of # horizontally adjacent dots. For now, assert that there are no other notes # in the measure with 2 dots. self.assertTrue( set(double_dotted_note_ys).issubset([-6, -3, -1, 3, 4]), 'No unexpected noteheads') # All dotted notes in the second measure belong to one chord, and are # single-dotted. single_dotted_notes = [ glyph for glyph in staff.glyph if system_measures.get_measure(glyph) == 1 and len(glyph.dot) == 1 ] for note in single_dotted_notes: self.assertEqual(len(note.beam), 1) # Single-dotted eighth note duration. self.assertEqual(note.note.end_time - note.note.start_time, .75) single_dotted_note_ys = [ glyph.y_position for glyph in single_dotted_notes ] self.assertIn(-5, single_dotted_note_ys) self.assertIn(-3, single_dotted_note_ys)
def testGetPage_x_scale(self): # Random staffline images matching the dimensions of PREDICTIONS. dummy_stafflines = np.random.random((2, 3, 5, 6)) classifier = glyphs_testing.DummyGlyphClassifier(glyphs_testing.PREDICTIONS) image = np.random.randint(0, 255, (30, 20), dtype=np.uint8) staves = staves_testing.FakeStaves( image_t=image, staves_t=np.asarray([[[0, 10], [19, 10]], [[0, 20], [19, 20]]], np.int32), staffline_distance_t=np.asarray([5, 20], np.int32), staffline_thickness_t=np.asarray(1, np.int32)) structure = structure_module.create_structure(image, lambda unused_image: staves) class DummyStafflineExtractor(object): """A placeholder for StafflineExtractor. It only contains the constants necessary to scale the x coordinates. """ staffline_distance_multiple = 2 target_height = 10 omr = engine.OMREngine(lambda _: classifier) page = omr.process_image( # Feed in a dummy image. It doesn't matter because FakeStaves has # hard-coded staff values. np.random.randint(0, 255, (100, 100)), process_structure=False) page = staff_processor.StaffProcessor(structure, DummyStafflineExtractor()).apply(page) self.assertEqual(len(page.system[0].staff), 2) # The first staff has a staffline distance of 5. # The extracted staffline slices have an original height of # staffline_distance * staffline_distance_multiple (10), which equals # target_height here, so there is no scaling. self.assertEqual( musicscore_pb2.Staff(glyph=page.system[0].staff[0].glyph), glyphs_testing.GLYPHS_PAGE.system[0].staff[0]) # Glyphs in the second staff have a scaled x coordinate. self.assertEqual( len(page.system[0].staff[1].glyph), len(glyphs_testing.GLYPHS_PAGE.system[0].staff[1].glyph)) for glyph in glyphs_testing.GLYPHS_PAGE.system[0].staff[1].glyph: expected_glyph = copy.deepcopy(glyph) # The second staff has a staffline distance of 20. The extracted staffline # slice would be 4 times the size of the scaled staffline, so x # coordinates are scaled by 4. Also, the glyphs may be in a different # order. expected_glyph.x *= 4 self.assertIn(expected_glyph, page.system[0].staff[1].glyph)
def run(input_pngs, glyphs_saved_model=None, output_notesequence=False): """Runs OMR over a list of input images. Args: input_pngs: A list of PNG filenames to process. glyphs_saved_model: Optional saved model dir to override the included model. output_notesequence: Whether to return a NoteSequence, as opposed to a Score containing Pages with Glyphs. Returns: A NoteSequence message, or a Score message holding Pages for each input image (with their detected Glyphs). """ return engine.OMREngine( saved_classifier_fn.build_classifier_fn(glyphs_saved_model)).run( input_pngs, output_notesequence=output_notesequence)
def testEndToEnd(self): with tempfile.TemporaryDirectory() as tmpdir: with engine.get_included_labels_file() as centroids: export_dir = os.path.join(tmpdir, 'export') export_kmeans_centroids.run(centroids.name, export_dir) # Now load the saved model. omr = engine.OMREngine( glyph_classifier_fn=saved_classifier. SavedConvolutional1DClassifier.glyph_classifier_fn(export_dir)) filename = os.path.join(tf.resource_loader.get_data_files_path(), '../testdata/IMSLP00747-000.png') notes = omr.run(filename, output_notesequence=True) # TODO(ringw): Fix the extra note that is detected before the actual # first eighth note. self.assertEqual(librosa.note_to_midi('C4'), notes.notes[1].pitch) self.assertEqual(librosa.note_to_midi('D4'), notes.notes[2].pitch) self.assertEqual(librosa.note_to_midi('E4'), notes.notes[3].pitch)
def main(argv): pages = argv[1:] assert pages, 'Pass one or more PNG files' omr = engine.OMREngine() for i, filename in enumerate(pages): escaped_filename = re.sub(r'([\'\\])', r'\\\0', filename) page = omr.run(filename).page[0] # TODO(ringw): Use a real templating system (e.g. jinja or mako). if i > 0: print('') print(' def test%s_structure(self):' % _sanitized_basename(filename)) print(' page = engine.OMREngine().run(') print(' \'%s\').page[0]' % escaped_filename) print(' self.assertEqual(len(page.system), %d)' % len(page.system)) for i, system in enumerate(page.system): print('') print(' self.assertEqual(len(page.system[%d].staff), %d)' % (i, len(system.staff))) print(' self.assertEqual(len(page.system[%d].bar), %d)' % (i, len(system.bar)))
def testIMSLP00304_038_MultipleStaffSizes(self): # Image has staff systems with different numbers of staves, some of which # contain staves slightly smaller than the others. page = engine.OMREngine().run( _get_imslp_path('IMSLP00304-038.png')).page[0] self.assertEqual(len(page.system), 3) self.assertEqual(len(page.system[0].staff), 4) self.assertEqual(page.system[0].staff[0].staffline_distance, 19) self.assertEqual(page.system[0].staff[1].staffline_distance, 19) self.assertEqual(page.system[0].staff[2].staffline_distance, 16) self.assertEqual(page.system[0].staff[3].staffline_distance, 16) self.assertEqual(len(page.system[1].staff), 2) self.assertEqual(page.system[1].staff[0].staffline_distance, 19) self.assertEqual(page.system[1].staff[1].staffline_distance, 19) self.assertEqual(len(page.system[2].staff), 4) self.assertEqual(page.system[2].staff[0].staffline_distance, 19) self.assertEqual(page.system[2].staff[1].staffline_distance, 19) self.assertEqual(page.system[2].staff[2].staffline_distance, 16) self.assertEqual(page.system[2].staff[3].staffline_distance, 16)
def testIMSLP00823_000_structure(self): page = engine.OMREngine().run( _get_imslp_path('IMSLP00823-000.png')).page[0] self.assertEqual(len(page.system), 6) self.assertEqual(len(page.system[0].staff), 2) self.assertEqual(len(page.system[0].bar), 7) self.assertEqual(len(page.system[1].staff), 2) self.assertEqual(len(page.system[1].bar), 7) self.assertEqual(len(page.system[2].staff), 2) # TODO(ringw): Detect thick repeat barlines correctly. # page.system[2] should have 6 bars. self.assertEqual(len(page.system[3].staff), 2) self.assertEqual(len(page.system[3].bar), 6) self.assertEqual(len(page.system[4].staff), 2) # TODO(ringw): Fix. page.system[4] should have 6 bars. self.assertEqual(len(page.system[5].staff), 2) self.assertEqual(len(page.system[5].bar), 6)
def testIMSLP00823_000_structure(self): page = engine.OMREngine().run( _get_imslp_path('IMSLP00823-000.png')).page[0] self.assertEqual(len(page.system), 6) self.assertEqual(len(page.system[0].staff), 2) self.assertEqual(len(page.system[0].bar), 7) self.assertEqual(len(page.system[1].staff), 2) self.assertEqual(len(page.system[1].bar), 7) self.assertEqual(len(page.system[2].staff), 2) self.assertEqual(len(page.system[2].bar), 6) self.assertEqual(len(page.system[3].staff), 2) self.assertEqual(len(page.system[3].bar), 6) self.assertEqual(len(page.system[4].staff), 2) # TODO(ringw): Fix barline detection here. # self.assertEqual(len(page.system[4].bar), 6) self.assertEqual(len(page.system[5].staff), 2) self.assertEqual(len(page.system[5].bar), 6)
def testIMSLP00023_015_doubleNoteDots(self): """Tests note dots in system[1].staff[1] of the image.""" page = engine.OMREngine().run( _get_imslp_path('IMSLP00023-015.png')).page[0] self.assertEqual(len(page.system), 6) system = page.system[1] system_measures = measures.Measures(system) staff = system.staff[1] # All dotted notes in the first measure belong to one chord, and are # double-dotted. double_dotted_notes = [ glyph for glyph in staff.glyph if system_measures.get_measure(glyph) == 0 and len(glyph.dot) == 2 ] for note in double_dotted_notes: self.assertEqual(len(note.beam), 1) # Double-dotted eighth note duration. self.assertEqual(note.note.end_time - note.note.start_time, .5 + .25 + .125) double_dotted_note_ys = [ glyph.y_position for glyph in double_dotted_notes ] self.assertIn(-6, double_dotted_note_ys) self.assertIn(-3, double_dotted_note_ys) self.assertIn(-1, double_dotted_note_ys) self.assertTrue( set(double_dotted_note_ys).issubset([-6, -3, -1, +3, +4]), 'No unexpected double-dotted noteheads') # TODO(ringw): Notehead at +4 picks up extra dots (4 total). The dots # should be in a horizontal line, and we should discard other dots. # There should only be one notehead at +4 with 2 or more dots. self.assertEqual( len([ glyph for glyph in staff.glyph if system_measures.get_measure(glyph) == 0 and glyph.type == NOTEHEAD_FILLED and glyph.y_position == +4 and len(glyph.dot) >= 2 ]), 1) # All dotted notes in the second measure belong to one chord, and are # single-dotted. single_dotted_notes = [ glyph for glyph in staff.glyph if system_measures.get_measure(glyph) == 1 and len(glyph.dot) == 1 ] for note in single_dotted_notes: if note.y_position == +2: # TODO(ringw): Detect the beam for this notehead. Its stem is too # short. continue self.assertEqual(len(note.beam), 1) # Single-dotted eighth note duration. self.assertEqual(note.note.end_time - note.note.start_time, .75) single_dotted_note_ys = [ glyph.y_position for glyph in single_dotted_notes ] self.assertIn(-5, single_dotted_note_ys) self.assertIn(-3, single_dotted_note_ys) self.assertIn(0, single_dotted_note_ys) self.assertIn(+2, single_dotted_note_ys) # TODO(ringw): Detect the dot for the note at y position +4. self.assertTrue( set(single_dotted_note_ys).issubset([-5, -3, 0, +2, +4]))
def setUp(self): self.engine = engine.OMREngine()
def __init__(self, **kwargs): self.omr = engine.OMREngine(**kwargs)