Esempio n. 1
0
def main(args):
    logging.info('Starting main...')
    _start_time = time.clock()

    # Your code goes here
    if not os.path.isfile(args.annot):
        raise ValueError('Annotation file {0} not found!'
                         ''.format(args.annot))
    nodes = read_nodes_from_file(args.annot)

    pitch_inference_engine = PitchInferenceEngine()
    time_inference_engine = OnsetsInferenceEngine(nodes=nodes)

    logging.info('Running pitch inference.')
    pitches, pitch_names = pitch_inference_engine.infer_pitches(
        nodes, with_names=True)

    # Export
    logging.info('Adding pitch information to <Data> attributes.')
    for node in nodes:
        if node.id in pitches:
            midi_pitch_code = pitches[node.id]
            pitch_step, pitch_octave = pitch_names[node.id]
            if node.data is None:
                node.data = dict()
            node.data['midi_pitch_code'] = midi_pitch_code
            node.data['normalized_pitch_step'] = pitch_step
            node.data['pitch_octave'] = pitch_octave

    logging.info('Adding duration info to <Data> attributes.')
    durations = time_inference_engine.durations(nodes)
    logging.info('Total durations: {0}'.format(len(durations)))
    for node in nodes:
        if node.id in durations:
            node.data['duration_beats'] = durations[node.id]

    logging.info('Some durations: {0}'.format(sorted(durations.items())[:10]))

    logging.info('Adding onset info to <Data> attributes.')
    onsets = time_inference_engine.onsets(nodes)
    logging.info('Total onsets: {0}'.format(len(onsets)))
    for node in nodes:
        if node.id in onsets:
            node.data['onset_beats'] = onsets[node.id]

    if args.export is not None:
        with open(args.export, 'w') as hdl:
            hdl.write(export_node_list(nodes))
            hdl.write('\n')
    else:
        print(export_node_list(nodes))

    if args.midi is not None:
        mf = build_midi(pitches, durations, onsets)
        with open(args.midi, 'wb') as hdl:
            mf.writeFile(hdl)

    _end_time = time.clock()
    logging.info('infer_pitches.py done in {0:.3f} s'.format(_end_time -
                                                             _start_time))
Esempio n. 2
0
def main(args):
    logging.info('Starting main...')
    _start_time = time.clock()

    ##########################################################################
    logging.info('Import the Node list')
    if not os.path.isfile(args.annot):
        raise ValueError('Annotation file {0} not found!'
                         ''.format(args.annot))
    nodes = read_nodes_from_file(args.annot)

    output_nodes = add_staff_relationships(
        nodes,
        notehead_staffspace_threshold=args.notehead_staffspace_threshold)

    ##########################################################################
    logging.info('Export the combined list.')
    nodes_string = export_node_list(output_nodes)

    if args.export is not None:
        with open(args.export, 'w') as hdl:
            hdl.write(nodes_string)
    else:
        print(nodes_string)

    _end_time = time.clock()
    logging.info('add_staff_reationships.py done in {0:.3f} s'
                 ''.format(_end_time - _start_time))
Esempio n. 3
0
def main(args):
    logging.info('Starting main...')
    _start_time = time.clock()

    # Your code goes here
    ##########################################################################
    logging.info('Import the Node list')
    if not os.path.isfile(args.annot):
        raise ValueError('Annotation file {0} not found!'
                         ''.format(args.annot))
    nodes = read_nodes_from_file(args.annot)

    ##########################################################################
    staff_id_to_node_mapping = {
        node.id: node
        for node in nodes if node.class_name in _CONST.STAFF_CLASS_NAMES
    }

    output_nodes = []
    for node in nodes:
        if node.id in staff_id_to_node_mapping:
            continue
        new_c = copy.deepcopy(node)
        new_c.inlinks = [
            i for i in node.inlinks if i not in staff_id_to_node_mapping
        ]
        new_c.outlinks = [
            o for o in node.outlinks if o not in staff_id_to_node_mapping
        ]
        output_nodes.append(new_c)

    ##########################################################################
    logging.info('Export the stripped list.')
    nodes_string = export_node_list(output_nodes)

    if args.export is not None:
        with open(args.export, 'w') as hdl:
            hdl.write(nodes_string)
    else:
        print(nodes_string)

    _end_time = time.clock()
    logging.info(
        'strip_staffline_symbols.py done in {0:.3f} s'.format(_end_time -
                                                              _start_time))
Esempio n. 4
0
def main(args):
    logging.info('Starting main...')
    _start_time = time.clock()

    ########################################################
    # Load gt image.
    logging.info('Loading staffline image.')
    #  - Initialize Dataset. This checks for the root.

    if args.staff_imfile is None:
        cvc_dataset = CvcMuscimaDataset(root=args.root)
        args.staff_imfile = cvc_dataset.imfile(page=args.number,
                                               writer=args.writer,
                                               distortion='ideal',
                                               mode='staff_only')

    # - Load the image.
    gt = (imread(args.staff_imfile, as_grey=True) * 255).astype('uint8')

    # - Cast as binary mask.
    gt[gt > 0] = 1

    ########################################################
    # Locate stafflines in gt image.
    logging.info('Getting staffline connected components.')

    #  - Get connected components in gt image.
    connected_components, labels, bboxes = compute_connected_components(gt)

    #  - Use vertical dimension of CCs to determine which ones belong together
    #    to form stafflines. (Criterion: row overlap.)
    n_rows, n_cols = gt.shape
    intervals = [[] for _ in range(n_rows)
                 ]  # For each row: which CCs have pxs on that row?
    for label, (t, l, b, r) in list(bboxes.items()):
        if label == 0:
            continue
        # Ignore very short staffline segments that can easily be artifacts
        # and should not affect the vertical range of the staffline anyway.
        if (r - l) < 8:
            continue
        for row in range(t, b):
            intervals[row].append(label)

    logging.info('Grouping staffline connected components into stafflines.')
    staffline_components = [
    ]  # For each staffline, we collect the CCs that it is made of
    _in_staffline = False
    _current_staffline_components = []
    for r_labels in intervals:
        if not _in_staffline:
            # Last row did not contain staffline components.
            if len(r_labels) == 0:
                # No staffline component on current row
                continue
            else:
                _in_staffline = True
                _current_staffline_components += r_labels
        else:
            # Last row contained staffline components.
            if len(r_labels) == 0:
                # Current staffline has no more rows.
                staffline_components.append(set(_current_staffline_components))
                _current_staffline_components = []
                _in_staffline = False
                continue
            else:
                # Current row contains staffline components: the current
                # staffline continues.
                _current_staffline_components += r_labels

    logging.info('No. of stafflines, with component groups: {0}'
                 ''.format(len(staffline_components)))

    # Now: merge the staffline components into one bbox/mask.
    logging.info(
        'Merging staffline components into staffline bboxes and masks.')
    staffline_bboxes = []
    staffline_masks = []
    for sc in sorted(staffline_components,
                     key=lambda c: min([bboxes[cc][0]
                                        for cc in c])):  # Sorted top-down
        st, sl, sb, sr = n_rows, n_cols, 0, 0
        for component in sc:
            t, l, b, r = bboxes[component]
            st, sl, sb, sr = min(t, st), min(l, sl), max(b, sb), max(r, sr)
        _sm = gt[st:sb, sl:sr]
        staffline_bboxes.append((st, sl, sb, sr))
        staffline_masks.append(_sm)

    # Check if n. of stafflines is divisible by 5
    n_stafflines = len(staffline_bboxes)
    logging.info('\tTotal stafflines: {0}'.format(n_stafflines))
    if n_stafflines % 5 != 0:
        import matplotlib.pyplot as plt
        stafllines_mask_image = numpy.zeros(gt.shape)
        for i, (_sb, _sm) in enumerate(zip(staffline_bboxes, staffline_masks)):
            t, l, b, r = _sb
            stafllines_mask_image[t:b, l:r] = min(255, (i * 333) % 255 + 40)
        plt.imshow(stafllines_mask_image, cmap='jet', interpolation='nearest')
        plt.show()
        raise ValueError('No. of stafflines is not divisible by 5!')

    logging.info('Creating staff bboxes and masks.')

    #  - Go top-down and group the stafflines by five to get staves.
    #    (The staffline bboxes are already sorted top-down.)
    staff_bboxes = []
    staff_masks = []

    for i in range(n_stafflines // 5):
        _sbb = staffline_bboxes[5 * i:5 * (i + 1)]
        _st = min([bb[0] for bb in _sbb])
        _sl = min([bb[1] for bb in _sbb])
        _sb = max([bb[2] for bb in _sbb])
        _sr = max([bb[3] for bb in _sbb])
        staff_bboxes.append((_st, _sl, _sb, _sr))
        staff_masks.append(gt[_st:_sb, _sl:_sr])

    logging.info('Total staffs: {0}'.format(len(staff_bboxes)))

    ##################################################################
    # (Optionally fill in missing pixels, based on full image.)
    logging.info('SKIP: fill in missing pixels based on full image.')
    #  - Load full image
    #  - Find gap regions
    #  - Obtain gap region masks from full image
    #  - Add gap region mask to staffline mask.

    # Create the Nodes for stafflines and staffs:
    #  - Load corresponding annotation, to which the stafflines and
    #    staves should be added. (This is needed to correctly set docname
    #    and node_ids.)
    if not args.annot:
        nodes = []
        next_node_id = 0
        dataset = 'FCNOMR'
        document = os.path.splitext(os.path.basename(args.staff_imfile))[0]
    else:
        if not os.path.isfile(args.annot):
            raise ValueError('Annotation file {0} does not exist!'.format(
                args.annot))

        logging.info('Creating Nodes...')
        nodes = read_nodes_from_file(args.annot)
        logging.info('Non-staffline Nodes: {0}'.format(len(nodes)))
        next_node_id = max([c.id for c in nodes]) + 1
        dataset = nodes[0].dataset
        document = nodes[0].document

    #  - Create the staffline Nodes
    stafflines = []
    for sl_bb, sl_m in zip(staffline_bboxes, staffline_masks):
        t, l, b, r = sl_bb
        c = Node(id_=next_node_id,
                 class_name=_CONST.STAFFLINE_CLASS_NAME,
                 top=t,
                 left=l,
                 height=b - t,
                 width=r - l,
                 mask=sl_m,
                 dataset=dataset,
                 document=document)
        stafflines.append(c)
        next_node_id += 1

    if not args.stafflines_only:

        #  - Create the staff Nodes
        staffs = []
        for s_bb, s_m in zip(staff_bboxes, staff_masks):
            t, l, b, r = s_bb
            c = Node(id_=next_node_id,
                     class_name=_CONST.STAFF_CLASS_NAME,
                     top=t,
                     left=l,
                     height=b - t,
                     width=r - l,
                     mask=s_m,
                     dataset=dataset,
                     document=document)
            staffs.append(c)
            next_node_id += 1

        #  - Add the inlinks/outlinks
        for i, sc in enumerate(staffs):
            sl_from = 5 * i
            sl_to = 5 * (i + 1)
            for sl in stafflines[sl_from:sl_to]:
                sl.inlinks.append(sc.id)
                sc.outlinks.append(sl.id)

        # Add the staffspaces.
        staffspaces = []
        for i, staff in enumerate(staffs):
            current_stafflines = [
                sc for sc in stafflines if sc.id in staff.outlinks
            ]
            sorted_stafflines = sorted(current_stafflines, key=lambda x: x.top)

            current_staffspaces = []

            # Percussion single-line staves do not have staffspaces.
            if len(sorted_stafflines) == 1:
                continue

            # Internal staffspace
            for s1, s2 in zip(sorted_stafflines[:-1], sorted_stafflines[1:]):
                # s1 is the UPPER staffline, s2 is the LOWER staffline
                # Left and right limits: to simplify things, we take the column
                # *intersection* of (s1, s2). This gives the invariant that
                # the staffspace is limited from top and bottom in each of its columns.
                l = max(s1.left, s2.left)
                r = min(s1.right, s2.right)

                # Shift s1, s2 to the right by this much to have the cols. align
                # All of these are non-negative.
                dl1, dl2 = l - s1.left, l - s2.left
                dr1, dr2 = s1.right - r, s2.right - r

                # The stafflines are not necessarily straight,
                # so top is given for the *topmost bottom edge* of the top staffline + 1

                # First create mask
                canvas = numpy.zeros((s2.bottom - s1.top, r - l),
                                     dtype='uint8')

                # Paste masks into canvas.
                # This assumes that the top of the bottom staffline is below
                # the top of the top staffline... and that the bottom
                # of the top staffline is above the bottom of the bottom
                # staffline. This may not hold in very weird situations,
                # but it's good for now.
                logging.debug(s1.bounding_box, s1.mask.shape)
                logging.debug(s2.bounding_box, s2.mask.shape)
                logging.debug(canvas.shape)
                logging.debug(
                    'l={0}, dl1={1}, dl2={2}, r={3}, dr1={4}, dr2={5}'
                    ''.format(l, dl1, dl2, r, dr1, dr2))
                # canvas[:s1.height, :] += s1.mask[:, dl1:s1.width-dr1]
                # canvas[-s2.height:, :] += s2.mask[:, dl2:s2.width-dr2]

                # We have to deal with staffline interruptions.
                # One way to do this
                # is watershed fill: put markers along the bottom and top
                # edge, use mask * 10000 as elevation

                s1_above, s1_below = staffline_surroundings_mask(s1)
                s2_above, s2_below = staffline_surroundings_mask(s2)

                # Get bounding boxes of the individual stafflines' masks
                # that intersect with the staffspace bounding box, in terms
                # of the staffline bounding box.
                s1_t, s1_l, s1_b, s1_r = 0, dl1, \
                                         s1.height, s1.width - dr1
                s1_h, s1_w = s1_b - s1_t, s1_r - s1_l
                s2_t, s2_l, s2_b, s2_r = canvas.shape[0] - s2.height, dl2, \
                                         canvas.shape[0], s2.width - dr2
                s2_h, s2_w = s2_b - s2_t, s2_r - s2_l

                logging.debug(s1_t, s1_l, s1_b, s1_r, (s1_h, s1_w))

                # We now take the intersection of s1_below and s2_above.
                # If there is empty space in the middle, we fill it in.
                staffspace_mask = numpy.ones(canvas.shape)
                staffspace_mask[s1_t:s1_b, :] -= (
                    1 - s1_below[:, dl1:s1.width - dr1])
                staffspace_mask[s2_t:s2_b, :] -= (
                    1 - s2_above[:, dl2:s2.width - dr2])

                ss_top = s1.top
                ss_bottom = s2.bottom
                ss_left = l
                ss_right = r

                staffspace = Node(next_node_id,
                                  _CONST.STAFFSPACE_CLASS_NAME,
                                  top=ss_top,
                                  left=ss_left,
                                  height=ss_bottom - ss_top,
                                  width=ss_right - ss_left,
                                  mask=staffspace_mask,
                                  dataset=dataset,
                                  document=document)

                staffspace.inlinks.append(staff.id)
                staff.outlinks.append(staffspace.id)

                current_staffspaces.append(staffspace)

                next_node_id += 1

            # Add top and bottom staffspace.
            # These outer staffspaces will have the width
            # of their bottom neighbor, and height derived
            # from its mask columns.
            # This is quite approximate, but it should do.

            # Upper staffspace
            tsl = sorted_stafflines[0]
            tsl_heights = tsl.mask.sum(axis=0)
            tss = current_staffspaces[0]
            tss_heights = tss.mask.sum(axis=0)

            uss_top = max(0, tss.top - max(tss_heights))
            uss_left = tss.left
            uss_width = tss.width
            # We use 1.5, so that large noteheads
            # do not "hang out" of the staffspace.
            uss_height = int(tss.height / 1.2)
            # Shift because of height downscaling:
            uss_top += tss.height - uss_height
            uss_mask = tss.mask[:uss_height, :] * 1

            staffspace = Node(next_node_id,
                              _CONST.STAFFSPACE_CLASS_NAME,
                              top=uss_top,
                              left=uss_left,
                              height=uss_height,
                              width=uss_width,
                              mask=uss_mask,
                              dataset=dataset,
                              document=document)
            current_staffspaces.append(staffspace)
            staff.outlinks.append(staffspace.id)
            staffspace.inlinks.append(staff.id)
            next_node_id += 1

            # Lower staffspace
            bss = current_staffspaces[-1]
            bss_heights = bss.mask.sum(axis=0)
            bsl = sorted_stafflines[-1]
            bsl_heights = bsl.mask.sum(axis=0)

            lss_top = bss.bottom  # + max(bsl_heights)
            lss_left = bss.left
            lss_width = bss.width
            lss_height = int(bss.height / 1.2)
            lss_mask = bss.mask[:lss_height, :] * 1

            staffspace = Node(next_node_id,
                              _CONST.STAFFSPACE_CLASS_NAME,
                              top=lss_top,
                              left=lss_left,
                              height=lss_height,
                              width=lss_width,
                              mask=lss_mask,
                              dataset=dataset,
                              document=document)
            current_staffspaces.append(staffspace)
            staff.outlinks.append(staffspace.id)
            staffspace.inlinks.append(staff.id)
            next_node_id += 1

            # ################ End of dealing with upper/lower staffspace ######

            # Add to current list
            staffspaces += current_staffspaces

        # - Join the lists together
        nodes_with_staffs = nodes \
                            + stafflines \
                            + staffspaces \
                            + staffs

    else:
        nodes_with_staffs = nodes + stafflines

    logging.info('Exporting the new Node list: {0} objects'
                 ''.format(len(nodes_with_staffs)))
    # - Export the combined list.
    nodes_string = export_node_list(nodes_with_staffs)
    if args.export is not None:
        with open(args.export, 'w') as hdl:
            hdl.write(nodes_string)
    else:
        print(nodes_string)

    _end_time = time.clock()
    logging.info('add_staffline_symbols.py done in {0:.3f} s'
                 ''.format(_end_time - _start_time))
Esempio n. 5
0
def main(args):
    logging.info('Starting main...')
    _start_time = time.clock()

    ###############################################################
    # Preparation: loading the parsing apparatus

    with open(args.vectorizer) as hdl:
        vectorizer = pickle.load(hdl)
    feature_extractor = PairwiseClassificationFeatureExtractor(
        vectorizer=vectorizer)

    with open(args.parser) as hdl:
        classifier = pickle.load(hdl)

    mlclass_list = parse_node_classes(args.mlclasses)
    mlclasses = {m.name for m in mlclass_list}

    grammar = DependencyGrammar(grammar_filename=args.grammar,
                                alphabet=mlclasses)

    parser = PairwiseClassificationParser(grammar=grammar,
                                          classifier=classifier,
                                          feature_extractor=feature_extractor)

    #################################################################
    logging.info('Load graph')
    nodes = read_nodes_from_file(args.input_mung)

    logging.info('Filter very small')
    very_small_nodes = find_very_small_nodes(nodes,
                                             bbox_threshold=40,
                                             mask_threshold=35)
    very_small_nodes = set(very_small_nodes)
    nodes = [c for c in nodes if c not in very_small_nodes]

    logging.info('Parsing')
    nodes = do_parse(nodes, parser=parser)

    # Filter contained here.
    if args.filter_contained:
        logging.info('Finding contained Nodes...')
        contained = find_contained_nodes(nodes, mask_threshold=0.95)
        NEVER_DISCARD_CLASSES = ['key_signature', 'time_signature']
        contained = [
            c for c in contained if c.class_name not in NEVER_DISCARD_CLASSES
        ]

        _contained_counts = collections.defaultdict(int)
        for c in contained:
            _contained_counts[c.class_name] += 1
        logging.info('Found {} contained Nodes'.format(len(contained)))
        logging.info('Contained counts:\n{0}'.format(
            pprint.pformat(dict(_contained_counts))))
        nodes = remove_contained_nodes(nodes, contained)
        logging.info('Removed contained Nodes: {}...'.format(
            [m.id for m in contained]))

    logging.info('Inferring staffline & staff objects, staff relationships')
    nodes = process_stafflines(nodes)

    if args.add_key_signatures:
        nodes = add_key_signatures(nodes)

    logging.info('Filter invalid edges')
    graph = NotationGraph(nodes)
    # Operatng on the graph changes the Nodes
    #  -- the graph only keeps a pointer
    wrong_edges = find_wrong_edges(nodes, grammar)
    for f, t in wrong_edges:
        graph.remove_edge(f, t)

    logging.info('Add precedence relationships, factored only by staff')
    prec_edges = infer_precedence_edges(nodes)
    nodes = add_precedence_edges(nodes, prec_edges)

    logging.info('Ensuring MIDI can be built')
    mf = build_midi(nodes,
                    retain_pitches=True,
                    retain_durations=True,
                    retain_onsets=True,
                    tempo=180)

    logging.info('Save output')
    docname = os.path.splitext(os.path.basename(args.output_mung))[0]
    xml = export_node_list(nodes, document=docname, dataset='FNOMR_results')
    with open(args.output_mung, 'w') as out_stream:
        out_stream.write(xml)
        out_stream.write('\n')

    _end_time = time.clock()
    logging.info(
        'baseline_process_symbols.py done in {0:.3f} s'.format(_end_time -
                                                               _start_time))