def __init__(self, source_image_path): # Constants self.LEDGER_LINE_MAX_THICKNESS = 2 self.whole_image = SheetImageSlice(Image.open(source_image_path)) #self.staffs = self.find_staffs() # list of StaffImages representing staff positions self.notes = [] # list of Notes self.header = None # if non-None, the main header text (usually title) self.subheader = None # if non-None, the sub header text (usually author) self.other_texts = [] # list of other texts found self.ledger_dict = {} # y-values -> pitch values self.ledger_lines = [] self.build_ledger_dict() self.erase_ledger_lines() self.get_notes()
class SheetImageParser: def __init__(self, source_image_path): # Constants self.LEDGER_LINE_MAX_THICKNESS = 2 self.whole_image = SheetImageSlice(Image.open(source_image_path)) #self.staffs = self.find_staffs() # list of StaffImages representing staff positions self.notes = [] # list of Notes self.header = None # if non-None, the main header text (usually title) self.subheader = None # if non-None, the sub header text (usually author) self.other_texts = [] # list of other texts found self.ledger_dict = {} # y-values -> pitch values self.ledger_lines = [] self.build_ledger_dict() self.erase_ledger_lines() self.get_notes() def __ascii_image(self): for y in range(self.whole_image.get_height()): line = "" for x in range(self.whole_image.get_width()): line += "_" if self.whole_image.get_pixel(x, y) else "1" def get_ledger_lines(self): # returns a list of y-positions where there are black, horizontal lines # we'll assume a line is anything that is 20% of the width of the image, # is located at the center, and has a spacing of at least 3 pixels from # the last line examined. horizontal_lines = [] midpoint = self.whole_image.get_midpoint() iterator_y = range(self.whole_image.get_height()).__iter__() for y in iterator_y: # if the center pixel is not black, it's not a line if self.whole_image.get_pixel(midpoint, y): # pixel is white continue current_row = self.whole_image.get_row(y) x_begin = int(self.whole_image.get_midpoint() - int(self.whole_image.get_width()*.1)) x_end = int(self.whole_image.get_midpoint() + int(self.whole_image.get_width()*.1)) expected_width = int(x_end-x_begin) if current_row[x_begin:x_end].count(False) == expected_width: horizontal_lines.append(y) collections.deque(itertools.islice(iterator_y, self.LEDGER_LINE_MAX_THICKNESS)) self.ledger_lines = horizontal_lines return horizontal_lines def build_ledger_dict(self): ledger_lines = self.get_ledger_lines() # get distance between ledger lines ledger_distance = ledger_lines[1] - ledger_lines[0] half_distance = int(ledger_distance/2) for i, line in enumerate(ledger_lines): if (i+1) % 5 == 1: self.ledger_dict[line - half_distance] = 'G3' self.ledger_dict[line] = 'F3' self.ledger_dict[line + half_distance] = 'E3' elif (i+1) % 5 == 2: self.ledger_dict[line] = 'D3' self.ledger_dict[line + half_distance] = 'C3' elif (i+1) % 5 == 3: self.ledger_dict[line] = 'B3' self.ledger_dict[line + half_distance] = 'A4' elif (i+1) % 5 == 4: self.ledger_dict[line] = 'G4' self.ledger_dict[line + half_distance] = 'F4' elif (i+1) % 5 == 0: self.ledger_dict[line] = 'E4' self.ledger_dict[line + half_distance] = 'D4' self.ledger_dict[line + half_distance*2] = 'C4' def erase_ledger_lines(self): # how many are there counts = [] for x in range(self.whole_image.get_width()): count = 0 for y in range(self.whole_image.get_height()): if not self.whole_image.get_pixel(x, y): count += 1 counts.append(count) total = collections.Counter(counts).most_common(1)[0][0] # where are they locations = [] for x in range(self.whole_image.get_width()): count = 0 blacks_found = [] for y in range(self.whole_image.get_height()): if not self.whole_image.get_pixel(x, y): count += 1 blacks_found.append(y) if count == total: locations = blacks_found break # erase for y in locations: for x in range(self.whole_image.get_width()): if self.whole_image.get_pixel(x, y-2) or self.whole_image.get_pixel(x, y+2): self.whole_image.erase(x, y) # testing self.whole_image.image.save("test.gif") def get_notes(self): # returns list of note images checked = [] # list of rectangle tuples of areas we've already examined notes = [] # list of NoteImage notes ledgers = sorted(self.ledger_lines) top_y = ledgers[0] bottom_y = ledgers[-1] left_x = 3 right_x = self.whole_image.get_width()-3 head_size = ledgers[1] - ledgers[0] imaginary_borders = [] begin = None end = None for i, y in enumerate(ledgers): if (i+1) % 5 == 1: begin = y if (i+1) % 5 == 0: end = y distance = end - begin buffer = distance/2 imaginary_borders.append((begin-buffer, end+buffer)) for borders in imaginary_borders: for x in range(left_x, right_x): for y in range(borders[0], borders[1]): # make sure we didn't check this pixel already already_checked = False for rect in checked: if x >= rect[0] and x <= rect[0]+rect[2]: if y >= rect[1] and y <= rect[1] + rect[3]: already_checked = True break if already_checked: continue if not self.whole_image.get_pixel(x, y): # begin boxing out note top_left = {"x": x, "y": y} bottom_right = {"x": x+1, "y": y+1} search_over = False while not search_over: search_over = True # look up for xi in range(top_left["x"]-1, bottom_right["x"]+1): if not self.whole_image.get_pixel(xi, top_left["y"]-1): search_over = False top_left["y"] -= 1 break # look down for xi in range(top_left["x"]-1, bottom_right["x"]+1): if not self.whole_image.get_pixel(xi, bottom_right["y"]+1): search_over = False bottom_right["y"] += 1 break # look left for yi in range(top_left["y"]-1, bottom_right["y"]+1): if not self.whole_image.get_pixel(top_left["x"]-1, yi): search_over = False top_left["x"] -= 1 break # look right for yi in range(top_left["y"]-1, bottom_right["y"]+1): if not self.whole_image.get_pixel(bottom_right["x"]+1, yi): search_over = False bottom_right["x"] += 1 break # search is over note_x_pos = top_left["x"] note_y_pos = top_left["y"] note_width = abs(bottom_right["x"] - top_left["x"]) note_height = abs(bottom_right["y"] - top_left["y"]) checked.append([note_x_pos, note_y_pos, note_width, note_height]) if note_width > 3 and note_height > 3: note = NoteImage(self.whole_image.slice(note_x_pos, note_y_pos, note_width+1, note_height+1), head_size, note_x_pos + note_width, note_y_pos + note_height - head_size/2) notes.append(note) self.notes = notes def get_pitch(self, y): # get closest line lines = self.ledger_dict.keys() smallest_difference = None note = None for line in lines: diff = abs(line - y) if smallest_difference == None or diff < smallest_difference: smallest_difference = diff note = self.ledger_dict[line] return note
class SheetImageParser: def __init__(self, source_image_path): # Constants self.LEDGER_LINE_MAX_THICKNESS = 2 self.whole_image = SheetImageSlice(Image.open(source_image_path)) #self.staffs = self.find_staffs() # list of StaffImages representing staff positions self.notes = [] # list of Notes self.header = None # if non-None, the main header text (usually title) self.subheader = None # if non-None, the sub header text (usually author) self.other_texts = [] # list of other texts found self.ledger_dict = {} # y-values -> pitch values self.ledger_lines = [] self.build_ledger_dict() self.erase_ledger_lines() self.get_notes() def __ascii_image(self): for y in range(self.whole_image.get_height()): line = "" for x in range(self.whole_image.get_width()): line += "_" if self.whole_image.get_pixel(x, y) else "1" def get_ledger_lines(self): # returns a list of y-positions where there are black, horizontal lines # we'll assume a line is anything that is 20% of the width of the image, # is located at the center, and has a spacing of at least 3 pixels from # the last line examined. horizontal_lines = [] midpoint = self.whole_image.get_midpoint() iterator_y = range(self.whole_image.get_height()).__iter__() for y in iterator_y: # if the center pixel is not black, it's not a line if self.whole_image.get_pixel(midpoint, y): # pixel is white continue current_row = self.whole_image.get_row(y) x_begin = int(self.whole_image.get_midpoint() - int(self.whole_image.get_width() * .1)) x_end = int(self.whole_image.get_midpoint() + int(self.whole_image.get_width() * .1)) expected_width = int(x_end - x_begin) if current_row[x_begin:x_end].count(False) == expected_width: horizontal_lines.append(y) collections.deque( itertools.islice(iterator_y, self.LEDGER_LINE_MAX_THICKNESS)) self.ledger_lines = horizontal_lines return horizontal_lines def build_ledger_dict(self): ledger_lines = self.get_ledger_lines() # get distance between ledger lines ledger_distance = ledger_lines[1] - ledger_lines[0] half_distance = int(ledger_distance / 2) for i, line in enumerate(ledger_lines): if (i + 1) % 5 == 1: self.ledger_dict[line - half_distance] = 'G3' self.ledger_dict[line] = 'F3' self.ledger_dict[line + half_distance] = 'E3' elif (i + 1) % 5 == 2: self.ledger_dict[line] = 'D3' self.ledger_dict[line + half_distance] = 'C3' elif (i + 1) % 5 == 3: self.ledger_dict[line] = 'B3' self.ledger_dict[line + half_distance] = 'A4' elif (i + 1) % 5 == 4: self.ledger_dict[line] = 'G4' self.ledger_dict[line + half_distance] = 'F4' elif (i + 1) % 5 == 0: self.ledger_dict[line] = 'E4' self.ledger_dict[line + half_distance] = 'D4' self.ledger_dict[line + half_distance * 2] = 'C4' def erase_ledger_lines(self): # how many are there counts = [] for x in range(self.whole_image.get_width()): count = 0 for y in range(self.whole_image.get_height()): if not self.whole_image.get_pixel(x, y): count += 1 counts.append(count) total = collections.Counter(counts).most_common(1)[0][0] # where are they locations = [] for x in range(self.whole_image.get_width()): count = 0 blacks_found = [] for y in range(self.whole_image.get_height()): if not self.whole_image.get_pixel(x, y): count += 1 blacks_found.append(y) if count == total: locations = blacks_found break # erase for y in locations: for x in range(self.whole_image.get_width()): if self.whole_image.get_pixel( x, y - 2) or self.whole_image.get_pixel(x, y + 2): self.whole_image.erase(x, y) # testing self.whole_image.image.save("test.gif") def get_notes(self): # returns list of note images checked = [ ] # list of rectangle tuples of areas we've already examined notes = [] # list of NoteImage notes ledgers = sorted(self.ledger_lines) top_y = ledgers[0] bottom_y = ledgers[-1] left_x = 3 right_x = self.whole_image.get_width() - 3 head_size = ledgers[1] - ledgers[0] imaginary_borders = [] begin = None end = None for i, y in enumerate(ledgers): if (i + 1) % 5 == 1: begin = y if (i + 1) % 5 == 0: end = y distance = end - begin buffer = distance / 2 imaginary_borders.append((begin - buffer, end + buffer)) for borders in imaginary_borders: for x in range(left_x, right_x): for y in range(borders[0], borders[1]): # make sure we didn't check this pixel already already_checked = False for rect in checked: if x >= rect[0] and x <= rect[0] + rect[2]: if y >= rect[1] and y <= rect[1] + rect[3]: already_checked = True break if already_checked: continue if not self.whole_image.get_pixel(x, y): # begin boxing out note top_left = {"x": x, "y": y} bottom_right = {"x": x + 1, "y": y + 1} search_over = False while not search_over: search_over = True # look up for xi in range(top_left["x"] - 1, bottom_right["x"] + 1): if not self.whole_image.get_pixel( xi, top_left["y"] - 1): search_over = False top_left["y"] -= 1 break # look down for xi in range(top_left["x"] - 1, bottom_right["x"] + 1): if not self.whole_image.get_pixel( xi, bottom_right["y"] + 1): search_over = False bottom_right["y"] += 1 break # look left for yi in range(top_left["y"] - 1, bottom_right["y"] + 1): if not self.whole_image.get_pixel( top_left["x"] - 1, yi): search_over = False top_left["x"] -= 1 break # look right for yi in range(top_left["y"] - 1, bottom_right["y"] + 1): if not self.whole_image.get_pixel( bottom_right["x"] + 1, yi): search_over = False bottom_right["x"] += 1 break # search is over note_x_pos = top_left["x"] note_y_pos = top_left["y"] note_width = abs(bottom_right["x"] - top_left["x"]) note_height = abs(bottom_right["y"] - top_left["y"]) checked.append( [note_x_pos, note_y_pos, note_width, note_height]) if note_width > 3 and note_height > 3: note = NoteImage( self.whole_image.slice(note_x_pos, note_y_pos, note_width + 1, note_height + 1), head_size, note_x_pos + note_width, note_y_pos + note_height - head_size / 2) notes.append(note) self.notes = notes def get_pitch(self, y): # get closest line lines = self.ledger_dict.keys() smallest_difference = None note = None for line in lines: diff = abs(line - y) if smallest_difference == None or diff < smallest_difference: smallest_difference = diff note = self.ledger_dict[line] return note