def append_guitar_data(self, tuning, capo): ''' Append meta data about the guitar the transcriber is using ''' mei_path = self.get_abs_path() mei_doc = XmlImport.documentFromFile(mei_path) staff_def = mei_doc.getElementsByName('staffDef')[0] g = Guitar(tuning=str(tuning), capo=capo) sounding_pitches = g.strings # From the MEI guidelines: # "this is given using the written pitch, not the sounding pitch. # For example, the Western 6-string guitar, in standard tuning, sounds an octave below written pitch." written_pitches = [s.pname + str(s.oct + 1) for s in sounding_pitches] staff_def.addAttribute('lines', str(len(sounding_pitches))) staff_def.addAttribute('tab.strings', " ".join(written_pitches)) # Capo could be implicitly encoded by setting the pitches of the open strings # but I really don't like this solution. Instructions are lost about how to tune # and perform the piece on the guitar. # TODO: this attribute doesn't exist in MEI, make a custom build if capo > 0: staff_def.addAttribute('tab.capo', str(capo)) XmlExport.meiDocumentToFile(mei_doc, mei_path)
def append_guitar_data(self, tuning, capo): ''' Append meta data about the guitar the transcriber is using ''' mei_path = self.get_abs_path() mei_doc = XmlImport.documentFromFile(mei_path) staff_def = mei_doc.getElementsByName('staffDef')[0] g = Guitar(tuning=str(tuning), capo=capo) sounding_pitches = g.strings # From the MEI guidelines: # "this is given using the written pitch, not the sounding pitch. # For example, the Western 6-string guitar, in standard tuning, sounds an octave below written pitch." written_pitches = [s.pname + str(s.oct+1) for s in sounding_pitches] staff_def.addAttribute('lines', str(len(sounding_pitches))) staff_def.addAttribute('tab.strings', " ".join(written_pitches)) # Capo could be implicitly encoded by setting the pitches of the open strings # but I really don't like this solution. Instructions are lost about how to tune # and perform the piece on the guitar. # TODO: this attribute doesn't exist in MEI, make a custom build if capo > 0: staff_def.addAttribute('tab.capo', str(capo)) XmlExport.meiDocumentToFile(mei_doc, mei_path)
def _get_symbol_widths(self, pages): ''' Calculate the average pixel width of each symbol from a set of pages. PARAMETERS ---------- pages (list): a list of pages ''' # TODO: make this a global var, since it is used in more than one function now symbol_types = ['clef', 'neume', 'custos', 'division'] # dict of symbol_name -> [cumulative_width_sum, num_occurences] symbol_widths = {} for p in pages: meidoc = XmlImport.documentFromFile(p['mei']) num_systems = len(meidoc.getElementsByName('system')) flat_tree = meidoc.getFlattenedTree() sbs = meidoc.getElementsByName('sb') # for each system # important: need to iterate system by system because the class labels depend on the acting clef for staff_index in range(num_systems): try: labels = self._get_symbol_labels(staff_index, meidoc) except IndexError: continue # retrieve list of MeiElements that correspond to glyphs between system breaks start_sb_pos = meidoc.getPositionInDocument(sbs[staff_index]) if staff_index+1 < len(sbs): end_sb_pos = meidoc.getPositionInDocument(sbs[staff_index+1]) else: end_sb_pos = len(flat_tree) symbols = [s for s in flat_tree[start_sb_pos+1:end_sb_pos] if s.getName() in symbol_types] # get bounding box information for each symbol belonging this system symbol_zones = [meidoc.getElementById(s.getAttribute('facs').value) for s in symbols if s.hasAttribute('facs')] for l, z in zip(labels, symbol_zones): ulx = int(z.getAttribute('ulx').value) lrx = int(z.getAttribute('lrx').value) if l in symbol_widths: symbol_widths[l][0] += (lrx - ulx) symbol_widths[l][1] += 1 else: symbol_widths[l] = [lrx - ulx, 1] # calculate average symbol widths across all training pages # rounding to the nearest pixel for s in symbol_widths: symbol_widths[s] = int(round(symbol_widths[s][0] / symbol_widths[s][1])) return symbol_widths
def parse_file(self, mei_path): ''' Read an mei file and fill the score model ''' from pymei import XmlImport self.doc = XmlImport.documentFromFile(str(mei_path)) self.parse_input()
def sanitize_mei_file(self, mei_path, output_mei_path=None, prune=True): self.mei_doc = XmlImport.documentFromFile(mei_path) self._sanitize_mei(prune) if output_mei_path: XmlExport.meiDocumentToFile(self.mei_doc, str(output_mei_path)) else: return XmlExport.meiDocumentToText(self.mei_doc)
def processMeiFile(ffile, solr_server, shortest_gram, longest_gram, page_number, project_id): solrconn = solr.SolrConnection(solr_server) print '\nProcessing ' + str(ffile) + '...' try: meifile = XmlImport.documentFromFile(str(ffile)) except Exception, e: print "E: ",e lg.debug("Could not process file {0}. Threw exception: {1}".format(ffile, e)) print "Whoops!"
def processMeiFile(ffile, solr_server, shortest_gram, longest_gram, page_number, project_id): solrconn = solr.SolrConnection(solr_server) print '\nProcessing ' + str(ffile) + '...' try: meifile = XmlImport.documentFromFile(str(ffile)) except Exception, e: print "E: ", e lg.debug("Could not process file {0}. Threw exception: {1}".format( ffile, e)) print "Whoops!"
def __init__(self, image_path, mei_path): ''' Creates a GlyphGen object. PARAMETERS ---------- image_path: path to the image to generate glyphs for. mei_path: path to the mei file containing glyph bounding box information. ''' self.page_image = Image.open(image_path) self.meidoc = XmlImport.documentFromFile(mei_path)
def analyze(MEI_filename): MEI_doc = XmlImport.documentFromFile(MEI_filename) MEI_tree = MEI_doc.getRootElement() has_editor_element_ = editorial.has_editor_element(MEI_tree) has_arranger_element_ = editorial.has_arranger_element(MEI_tree) editor_name_ = editorial.editor_name(MEI_tree) staff_list_ = staves.staff_list(MEI_tree) alternates_list_ = staves.alternates_list(staff_list_) return AnalyzeData(has_editor_element_, has_arranger_element_, editor_name_, staff_list_, alternates_list_, )
def __init__(self, input_mei_paths, output_mei_path): ''' PARAMETERS ---------- input_mei_paths {list}: list of mei paths to combine output_mei_path {String}: output file path of type .mei ''' self._input_mei_paths = input_mei_paths self._output_mei_path = output_mei_path if len(self._input_mei_paths): self._meidoc = XmlImport.documentFromFile(self._input_mei_paths[0]) else: self._meidoc = None
def massage_mei(in_file, out_file): try: analysis = make_analysis(in_file) MEI_instructions = TransformData( arranger_to_editor=True, obliterate_incipit=analysis.first_measure_empty, replace_longa=True, editorial_resp=analysis.has_arranger_element, alternates_list=analysis.alternates_list) old_MEI_doc = XmlImport.documentFromFile(in_file) new_MEI_doc = transform_mei(old_MEI_doc, MEI_instructions) XmlExport.meiDocumentToFile(new_MEI_doc, out_file) except Exception as ex: logging.critical(ex) logging.critical("Error during massaging " + in_file)
def mei_append_metamusic(self): ''' Append meta data for the musical work to the mei document ''' mei_path = self.get_abs_path() mei_doc = XmlImport.documentFromFile(mei_path) mei = mei_doc.getRootElement() mei_head = MeiElement('meiHead') music = mei.getChildrenByName('music')[0] file_desc = MeiElement('fileDesc') # title title_stmt = MeiElement('titleStmt') title = MeiElement('title') title.setValue(str(self.fk_mid.title)) # contributers resp_stmt = MeiElement('respStmt') pers_name_artist = MeiElement('persName') pers_name_artist.addAttribute('role', 'artist') pers_name_artist.setValue(str(self.fk_mid.artist)) pers_name_tabber = MeiElement('persName') pers_name_tabber.addAttribute('role', 'tabber') pers_name_tabber.setValue(str(self.fk_mid.copyright)) # encoding information encoding_desc = MeiElement('encodingDesc') app_info = MeiElement('appInfo') application = MeiElement('application') application.setValue('Robotaba') mei_head.addChild(file_desc) file_desc.addChild(title_stmt) title_stmt.addChild(title) title_stmt.addChild(resp_stmt) resp_stmt.addChild(pers_name_artist) resp_stmt.addChild(pers_name_tabber) title_stmt.addChild(encoding_desc) encoding_desc.addChild(app_info) app_info.addChild(application) # attach mei metadata to the document mei.addChildBefore(music, mei_head) XmlExport.meiDocumentToFile(mei_doc, mei_path)
def OnLoadRects(self, event): ''' Loads rectangles from an mei file. Only loads bar boxes and not staff boxes yet. ''' fdlg = wx.FileDialog(self) if fdlg.ShowModal() == wx.ID_OK: print "File loaded: " + fdlg.GetPath() meidoc = XmlImport.documentFromFile(str(fdlg.GetPath())) # get all the measure elements measures = meidoc.getElementsByName('measure') # the measures have their coordinates stored in zones zones = meidoc.getElementsByName('zone') for m in measures: # the id of the zone that has the coordinates is stored in 'facs' facs = m.getAttribute('facs') print facs.getName(), facs.getValue() # there's a # sign preceding the id stored in the facs # attribute, remove it zone = meidoc.getElementById(facs.getValue()[1:]) # the coordinates stored in zone ulx = int(zone.getAttribute('ulx').getValue()) uly = int(zone.getAttribute('uly').getValue()) lrx = int(zone.getAttribute('lrx').getValue()) lry = int(zone.getAttribute('lry').getValue()) print ulx, uly, lrx, lry # make a new panel # Rect is looking for top (x,y) coordinates and length and # height, so we need to subtract the two corners self.scrolledwin.barpanels.append(\ Rect(ulx, uly, lrx - ulx, lry - uly)) self.scrolledwin.Refresh()
def Run(self): old_filename = self.mei_file if (len(old_filename) < EXT_LENGTH or old_filename[-EXT_LENGTH:] not in EXT): logging.info("No file extension provided; " + EXT[0] + " used.") old_filename += EXT[0] old_MEI_doc = XmlImport.documentFromFile(old_filename) logging.info('running test case ' + self.name + ' Input: ' + old_filename) new_MEI_doc = transform(old_MEI_doc, self.transform_data) new_filename = (old_filename[:-EXT_LENGTH] + self.outsuffix + '_' + old_filename[-EXT_LENGTH:]) status = XmlExport.meiDocumentToFile(new_MEI_doc, new_filename) if status: logging.info("Done. Transformed file saved as " + new_filename) pass else: logging.error("Transformation failed") return new_MEI_doc
def combine(self): if self._meidoc and len(input_mei_paths) > 1: base_facsimile = self._meidoc.getElementsByName('facsimile')[0] base_section = self._meidoc.getElementsByName('section')[0] for f in self._input_mei_paths[1:]: mei = XmlImport.documentFromFile(f) # combine surface surface = mei.getElementsByName('surface') if len(surface): # have to remove the child from the old document in memory # or else pymei segfaults ... surface[0].getParent().removeChild(surface[0]) base_facsimile.addChild(surface[0]) # combine measures pb = MeiElement('pb') base_section.addChild(pb) # get last measure number measures = base_section.getChildrenByName('measure') last_measure_n = int(measures[-1].getAttribute('n').value) new_section = mei.getElementsByName('section')[0] music_elements = new_section.getChildren() for e in music_elements: if e.getName() == 'measure': last_measure_n += 1 e.addAttribute('n', str(last_measure_n)) base_section.addChild(e) # remove all musical elements from the old document or else pymei segfaults new_section.getParent().deleteAllChildren() self._add_revision()
def test_malformedexception(self): with self.assertRaises(MalformedFileException) as cm: XmlImport.documentFromFile(os.path.join("test", "testdocs", "malformed.mei")) self.assertTrue(isinstance(cm.exception, MalformedFileException))
def test_noversionexception(self): with self.assertRaises(NoVersionFoundException) as cm: XmlImport.documentFromFile(os.path.join("test", "testdocs", "noversion.mei")) self.assertTrue(isinstance(cm.exception, NoVersionFoundException))
def test_badversionexception(self): with self.assertRaises(VersionMismatchException) as cm: XmlImport.documentFromFile(os.path.join("test", "testdocs", "badversion.mei")) self.assertTrue(isinstance(cm.exception, VersionMismatchException))
def test_readlargefile(self): doc = XmlImport.documentFromFile(os.path.join("test", "testdocs", "beethoven_no5.mei")) self.assertNotEqual(None, doc)
def test_readfile_with_procinst(self): procinst = XmlInstructions() doc = XmlImport.documentFromFile(os.path.join("test", "testdocs", "test-procinst.mei"), procinst) self.assertEqual(2, len(procinst)) self.assertEqual("xml-model", procinst[0].name)
def test_readfile(self): doc = XmlImport.documentFromFile(os.path.join("test", "testdocs", "beethoven.mei")) self.assertNotEqual(None, doc) el = doc.getElementById("d1e41") self.assertEqual("c", el.getAttribute("pname").value) self.assertEqual("4", el.getAttribute("oct").value)
def test_readfile_with_frbr(self): doc = XmlImport.documentFromFile(os.path.join("test", "testdocs", "test-itemlist.mei")) self.assertNotEqual(None, doc)
from pymei import XmlImport from gamera.core import * import PIL, os init_gamera() import pdb from optparse import OptionParser if __name__ == "__main__": usage = "usage: %prog [options] input_mei_file input_image_file output_folder" opts = OptionParser(usage) (options, args) = opts.parse_args() input_file = args[0] output_folder = args[2] mdoc = XmlImport.documentFromFile(input_file) neumes = mdoc.getElementsByName('neume') clefs = mdoc.getElementsByName('clef') divisions = mdoc.getElementsByName('division') custos = mdoc.getElementsByName('custos') systems = mdoc.getElementsByName('system') img = load_image(args[1]) if img.pixel_type_name != "OneBit": img = img.to_onebit() rgb = Image(img, RGB) neumecolour = RGBPixel(255, 0, 0)
This function ignores this possibility, which is unlikely to occur at the last tone (and would then be notable!). If the bass is not present at all at the last tone, then the function will return None, which of course bears looking into regardless. """ all_measures = MEI_tree.getDescendantsByName('measure') last_measure = all_measures[-1] # Gets all the staves in the last measure last_staves = last_measure.getChildrenByName('staff') try: bass_staff = last_staves[-1] last_bass_note = bass_staff.getChildrenByName('note')[-1] last_bass_pname = last_bass_note.getAttribute('pname').getValue() if last_bass_note.getAttribute('accid'): last_bass_accid = last_bass_note.getAttribute('accid').getValue() else: last_bass_accid = 'n' # for natural return PITCH_CLASSES[last_bass_pname] + ACCIDENTALS[last_bass_accid] except IndexError: # Error because no final bass tone? return None if __name__ == "__main__": MEI_filename = raw_input('Enter a file name: ') MEI_doc = XmlImport.documentFromFile(MEI_filename) MEI_tree = MEI_doc.getRootElement() print last_bass_tone(MEI_tree)
def test_exportwithcomments(self): docf = XmlImport.documentFromFile(os.path.join("test", "testdocs", "campion.mei")) status = XmlExport.meiDocumentToFile(docf, os.path.join(self.tempdir,"filename.mei")) self.assertTrue(status)
def test_basicexport(self): docf = XmlImport.documentFromFile(os.path.join("test", "testdocs", "beethoven.mei")) status = XmlExport.meiDocumentToFile(docf, os.path.join(self.tempdir,"filename.mei")) self.assertTrue(status)
def write_transformation(file_path, data=TransformData()): old_MEI_doc = XmlImport.documentFromFile(file_path) new_MEI_doc = transform_mei(old_MEI_doc, data) XmlExport.meiDocumentToFile(new_MEI_doc, file_path)
def _extract_staves(self, pages, staff_data_path, bb_padding_in=0.4): ''' Extracts the staves from the image given the bounding boxes encoded in the corresponding mei document. The staff images are saved on the HDD to accomodate large datasets, though, this could easily be modified by storing each staff image in main mem. PARAMETERS ---------- pages (list): a list of pages staff_data_path (string): path to output the staff images bb_padding_in (float): number of inches to pad system bounding boxes in the y plane ''' staves = [] for p in pages: image = load_image(p['image']) if np.allclose(image.resolution, 0): # set a default image dpi of 72 image_dpi = 72 else: image_dpi = image.resolution image_name = os.path.splitext(os.path.split(p['image'])[1])[0] page_name = image_name.split('_')[0] # calculate number of pixels the system padding should be bb_padding_px = int(bb_padding_in * image_dpi) # get staff bounding boxes from mei document meidoc = XmlImport.documentFromFile(p['mei']) gt_system_zones = [meidoc.getElementById(s.getAttribute('facs').value) for s in meidoc.getElementsByName('system') if s.hasAttribute('facs')] s_bb = [{ 'ulx': int(z.getAttribute('ulx').value), 'uly': int(z.getAttribute('uly').value) - bb_padding_px, 'lrx': int(z.getAttribute('lrx').value), 'lry': int(z.getAttribute('lry').value) + bb_padding_px } for z in gt_system_zones] # obtain an image of the staff, scaled to be 100px tall num_errors = 0 for i, bb in enumerate(s_bb): try: staff_image = image.subimage(Point(bb['ulx'], bb['uly']), Point(bb['lrx'], bb['lry'])) # binarize and despeckle staff_image = staff_image.to_onebit() staff_image.despeckle(100) # scale to be 100px tall, maintaining aspect ratio scale_factor = 100 / staff_image.nrows staff_image = staff_image.scale(scale_factor, 1) # create staff data directory if it does not already exist if not os.path.exists(staff_data_path): os.makedirs(staff_data_path) staff_path = os.path.join(staff_data_path, '%s_s%d.tiff' % (page_name, len(staves))) staff_image.save_image(staff_path) transcription = self._get_symbol_labels(i, meidoc) except: num_errors += 1 continue staves.append({'path': staff_path, 'symbols': transcription}) if self.verbose: print "\tNumber of staves extracted: %d" % len(staves) print "\tNumber of errors: %d" % num_errors return staves
def _evaluate_output(self, mei_path, gt_mei_path, bb_padding_px): ''' Evaluate the output of the measure finding algorithm against the manual measure annotations. PARAMETERS ---------- mei_path (String): path to mei document created by algorithm gt_mei_path (String): path to mei document created by annotator bb_padding_px (int): number of pixels to pad the ground-truth measure bounding boxes ''' meidoc = XmlImport.documentFromFile(mei_path) gtmeidoc = XmlImport.documentFromFile(gt_mei_path) p = 0.0 # precision r = 0.0 # recall f = 0.0 # f-measure # get bounding boxes of ground-truth measures gt_measure_zones = gtmeidoc.getElementsByName('zone') gt_bb = [{ 'ulx': int(z.getAttribute('ulx').value), 'uly': int(z.getAttribute('uly').value), 'lrx': int(z.getAttribute('lrx').value), 'lry': int(z.getAttribute('lry').value) } for z in gt_measure_zones] num_gt_measures = len(gt_measure_zones) # get bounding boxes of algorithm measures alg_measure_zones = [meidoc.getElementById(m.getAttribute('facs').value[1:]) for m in meidoc.getElementsByName('measure') if m.hasAttribute('facs')] alg_bb = [{ 'ulx': int(z.getAttribute('ulx').value), 'uly': int(z.getAttribute('uly').value), 'lrx': int(z.getAttribute('lrx').value), 'lry': int(z.getAttribute('lry').value) } for z in alg_measure_zones] num_alg_measures = len(alg_bb) # compare each measure bounding box estimate to the ground truth # deleting after to ensure no double counting. for abb in alg_bb: for i in range(len(gt_bb)-1,-1,-1): # check if it is nearby a ground-truth measure bounding box if (abs(abb['ulx']-gt_bb[i]['ulx']) <= bb_padding_px and abs(abb['uly']-gt_bb[i]['uly']) <= bb_padding_px and abs(abb['lrx']-gt_bb[i]['lrx']) <= bb_padding_px and abs(abb['lry']-gt_bb[i]['lry']) <= bb_padding_px): r += 1 del gt_bb[i] break if num_alg_measures > 0: p = r / num_alg_measures else: if self.verbose: print '[WARNING]: no algorithm output measures found' p = 0 if num_gt_measures > 0: r /= num_gt_measures else: if self.verbose: print '[WARNING]: no ground-truth measures found' r = 1 f = calc_fmeasure(p,r) return p, r, f, num_gt_measures