Пример #1
0
def main(args):
    logging.info('Starting main...')
    _start_time = time.clock()

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

    output_cropobjects = add_staff_relationships(
        cropobjects,
        notehead_staffspace_threshold=args.notehead_staffspace_threshold)

    ##########################################################################
    logging.info('Export the combined list.')
    cropobject_string = export_cropobject_list(output_cropobjects)

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

    _end_time = time.clock()
    logging.info('add_staff_reationships.py done in {0:.3f} s'
                 ''.format(_end_time - _start_time))
Пример #2
0
def main(args):
    logging.info('Starting main...')
    _start_time = time.clock()

    mungs = parse_cropobject_list(args.mung)

    img = imread(args.image, mode='L')

    output_mungs = []
    for m in mungs:
        if (args.classes is not None) and (m.clsname not in args.classes):
            output_mungs.append(m)
            continue
        if (not args.force_binarize) and (m.mask.nonzero()[0].shape[0] == 0):
            output_mungs.append(m)
            continue
        output_mungs.append(binarize_mask(m, img, inplace=True))

    xml = export_cropobject_list(output_mungs)
    with open(args.output_mung, 'w') as hdl:
        hdl.write(xml)
        hdl.write('\n')

    _end_time = time.clock()
    logging.info('binarize_masks.py done in {0:.3f} s'.format(_end_time - _start_time))
Пример #3
0
def main(args):
    logging.info('Starting main...')
    _start_time = time.clock()

    cropobjects = parse_cropobject_list(args.input_mung)

    output_cropobjects = build_SEILS_composites(cropobjects)

    xml = export_cropobject_list(output_cropobjects)
    with open(args.output_mung, 'w') as hdl:
        hdl.write(xml)
        hdl.write('\n')

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

    logging.warning('Merging CropObject lists is now very dangerous,'
                    ' becaues of the uid situation.')

    inputs = [parse_cropobject_list(f) for f in args.inputs]
    merged = merge_cropobject_lists(*inputs)
    with codecs.open(args.output, 'w', 'utf-8') as hdl:
        hdl.write(export_cropobject_list(merged))
        hdl.write('\n')

    _end_time = time.clock()
    logging.info('merge_cropobject_lists.py done in {0:.3f} s'.format(_end_time - _start_time))
Пример #5
0
def main(args):
    logging.info('Starting main...')
    _start_time = time.clock()

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

    _cropobjects_dict = {c.objid: c for c in cropobjects}

    ##########################################################################
    staff_cropobjects_dict = {c.objid: c for c in cropobjects
                              if c.clsname in STAFF_CLSNAMES}

    output_cropobjects = []
    for c in cropobjects:
        if c.objid in staff_cropobjects_dict:
            continue
        new_c = copy.deepcopy(c)
        new_c.inlinks = [i for i in c.inlinks
                         if i not in staff_cropobjects_dict]
        new_c.outlinks = [o for o in c.outlinks
                          if o not in staff_cropobjects_dict]
        output_cropobjects.append(new_c)

    ##########################################################################
    logging.info('Export the stripped list.')
    cropobject_string = export_cropobject_list(output_cropobjects)

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



    _end_time = time.clock()
    logging.info('[XXXX] done in {0:.3f} s'.format(_end_time - _start_time))
Пример #6
0
    runner = MunglinkerRunner(model=model,
                              config=config,
                              runtime_batch_iterator=runtime_batch_iterator)

    for i, (image_file, input_mung_file, output_mung_file,
            ground_truth_mung_file) in enumerate(
                zip(image_files, input_mung_files, output_mung_files,
                    ground_truth_mung_files)):
        input_mungos = parse_cropobject_list(input_mung_file)
        input_mung = NotationGraph(input_mungos)

        print('Running Munglinker: {} / {}'.format(i, len(image_files)))
        output_mung = runner.run(image_file, input_mung)
        with open(output_mung_file, 'w') as file:
            file.write(export_cropobject_list(output_mung.cropobjects))

        precision, recall, f1_score, true_positives, false_positives, false_negatives = \
            evaluate_result(ground_truth_mung_file, output_mung_file)

        results.append((input_mung_file, precision, recall, f1_score,
                        true_positives, false_positives, false_negatives))

        if args.play:
            mf = build_midi(nodes=output_mung.cropobjects)
            with open("output.midi", 'wb') as stream_out:
                mf.writeFile(stream_out)

    results = pd.DataFrame(results,
                           columns=[
                               "Filename", "Precision", "Recall", "F1-Score",
Пример #7
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 = CVC_MUSCIMA(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.
    cc, 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 CropObjects for stafflines and staffs:
    #  - Load corresponding annotation, to which the stafflines and
    #    staves should be added. (This is needed to correctly set docname
    #    and objids.)
    if not args.annot:
        cropobjects = []
        next_objid = 0
        dataset_namespace = 'FCNOMR'
        docname = 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 cropobjects...')
        cropobjects = parse_cropobject_list(args.annot)
        logging.info('Non-staffline cropobjects: {0}'.format(len(cropobjects)))
        next_objid = max([c.objid for c in cropobjects]) + 1
        dataset_namespace = cropobjects[0].dataset
        docname = cropobjects[0].doc

    #  - Create the staffline CropObjects
    staffline_cropobjects = []
    for sl_bb, sl_m in zip(staffline_bboxes, staffline_masks):
        uid = CropObject.build_uid(dataset_namespace, docname, next_objid)
        t, l, b, r = sl_bb
        c = CropObject(objid=next_objid,
                       clsname=STAFFLINE_CLSNAME,
                       top=t, left=l, height=b - t, width=r - l,
                       mask=sl_m,
                       uid=uid)
        staffline_cropobjects.append(c)
        next_objid += 1

    if not args.stafflines_only:

        #  - Create the staff CropObjects
        staff_cropobjects = []
        for s_bb, s_m in zip(staff_bboxes, staff_masks):
            uid = CropObject.build_uid(dataset_namespace, docname, next_objid)
            t, l, b, r = s_bb
            c = CropObject(objid=next_objid,
                           clsname=STAFF_CLSNAME,
                           top=t, left=l, height=b - t, width=r - l,
                           mask=s_m,
                           uid=uid)
            staff_cropobjects.append(c)
            next_objid += 1

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

        # Add the staffspaces.
        staffspace_cropobjects = []
        for i, staff in enumerate(staff_cropobjects):
            current_stafflines = [sc for sc in staffline_cropobjects if sc.objid in staff.outlinks]
            sorted_stafflines = sorted(current_stafflines, key=lambda x: x.top)

            current_staffspace_cropobjects = []

            # 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

                uid = CropObject.build_uid(dataset_namespace, docname, next_objid)

                staffspace = CropObject(next_objid, STAFFSPACE_CLSNAME,
                                        top=ss_top, left=ss_left,
                                        height=ss_bottom - ss_top,
                                        width=ss_right - ss_left,
                                        mask=staffspace_mask,
                                        uid=uid)

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

                current_staffspace_cropobjects.append(staffspace)

                next_objid += 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_staffspace_cropobjects[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

            uid = CropObject.build_uid(dataset_namespace, docname, next_objid)
            staffspace = CropObject(next_objid, STAFFSPACE_CLSNAME,
                                    top=uss_top, left=uss_left,
                                    height=uss_height,
                                    width=uss_width,
                                    mask=uss_mask,
                                    uid=uid)
            current_staffspace_cropobjects.append(staffspace)
            staff.outlinks.append(staffspace.objid)
            staffspace.inlinks.append(staff.objid)
            next_objid += 1

            # Lower staffspace
            bss = current_staffspace_cropobjects[-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

            uid = CropObject.build_uid(dataset_namespace, docname, next_objid)
            staffspace = CropObject(next_objid, STAFFSPACE_CLSNAME,
                                    top=lss_top, left=lss_left,
                                    height=lss_height,
                                    width=lss_width,
                                    mask=lss_mask,
                                    uid=uid)
            current_staffspace_cropobjects.append(staffspace)
            staff.outlinks.append(staffspace.objid)
            staffspace.inlinks.append(staff.objid)
            next_objid += 1

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

            # Add to current list
            staffspace_cropobjects += current_staffspace_cropobjects

        # - Join the lists together
        cropobjects_with_staffs = cropobjects \
                                  + staffline_cropobjects \
                                  + staffspace_cropobjects \
                                  + staff_cropobjects

    else:
        cropobjects_with_staffs = cropobjects + staffline_cropobjects

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

    _end_time = time.clock()
    logging.info('add_staffline_symbols.py done in {0:.3f} s'
                    ''.format(_end_time - _start_time))
Пример #8
0
def write_crop_objects_to_disk(crop_objects: List[CropObject], output_directory: str, output_filename: str) -> None:
    os.makedirs(output_directory, exist_ok=True)
    cropobject_xml_string = export_cropobject_list(crop_objects)
    with open(os.path.join(output_directory, output_filename), "w") as file:
        file.write(cropobject_xml_string)
Пример #9
0
def main(args):
    logging.info('Starting main...')
    _start_time = time.clock()

    ##########################################################################
    logging.info('Converting to absolute paths...')
    root = None
    if args.root is not None:
        root = os.path.abspath(args.root)
    output_dir = os.path.abspath(args.output_dir)
    input_files = [os.path.abspath(f) for f in args.input_files]
    logging.info('Root: {0}'.format(root))
    logging.info('Output: {0}'.format(output_dir))
    logging.info('Example input: {0}'.format(input_files[0]))

    ##########################################################################
    # Get output filenames,
    # fail on non-corresponding input file and root.
    logging.info('Inferring output pathnames...')
    if args.inplace:
        output_files = input_files
    else:
        if args.root is None:
            relative_files = input_files
        else:
            len_root = len(root)
            relative_files = []
            for f in input_files:
                if not os.path.samefile(os.path.commonpath([f, root]), root):
                    raise ValueError('Input file {0} does not seem to'
                                     ' come from the root directory {1}.'
                                     ''.format(f, root))
                relative_files.append(f[len_root + 1:])

        # Ensure output dir exists
        if not os.path.isdir(output_dir):
            os.mkdir(output_dir)

        logging.debug(
            'Making output file names. Output dir: {0}'.format(output_dir))
        logging.debug('Example rel file: {0}'.format(relative_files[0]))
        logging.debug('Ex. output: {0}'.format(
            os.path.join(output_dir, relative_files[0])))
        output_files = [os.path.join(output_dir, f) for f in relative_files]
        logging.debug('Local Example output file: {0}'.format(output_files[0]))

    logging.info('Example output file: {0}'.format(output_files[0]))

    ##########################################################################
    # Parse cropobjects
    logging.info('Parsing cropobject files ({0} total)...'.format(
        len(input_files)))
    cropobjects_for_files = []
    for i, f in enumerate(input_files):
        cropobjects_for_files.append(parse_cropobject_list(f))
        if (i > 0) and (i % 10 == 0):
            logging.info('Parsed {0} files.'.format(i))

        if args.recode_uids:
            dataset_namespace = args.uid_dataset_namespace
            document_namespace = get_document_namespace(filename=f,
                                                        root=root,
                                                        output_dir=output_dir)
            recoded_cropobjects = recode_ids(
                cropobjects_for_files[-1],
                document_namespace=document_namespace,
                dataset_namespace=dataset_namespace)
            cropobjects_for_files[-1] = recoded_cropobjects

    ##########################################################################
    logging.info('Exporting cropobjects...')
    _i = 0
    for output_file, c in zip(output_files, cropobjects_for_files):
        s = export_cropobject_list(c)
        with open(output_file, 'w') as hdl:
            hdl.write(s)
            hdl.write('\n')

        _i += 1
        if (_i % 10) == 0:
            logging.info('Done: {0} files'.format(_i))

    _end_time = time.clock()
    logging.info(
        'recode_xy_to_topleft.py done in {0:.3f} s'.format(_end_time -
                                                           _start_time))
Пример #10
0
    def call(self, request):

        logging.info(
            'ObjectDetectionHandler: Calling with input bounding box {0}'
            ''.format(self.input_bounding_box))

        self.current_request = request

        # Format request for client
        #  (=pickle it, plus pickle-within-pickle for image array)
        f_request = self._format_request(request)

        _rstring = str(uuid.uuid4())
        temp_basename = 'MUSCIMarker.omrapp-request.' + _rstring + '.pkl'
        request_fname = os.path.join(self.tmp_dir, temp_basename)
        with open(request_fname, 'w') as fh:
            pickle.dump(f_request, fh, protocol=0)

        # Send to ObjectDetectionOMRAppClient
        # We didn't want to introduce "mhr" as a dependency,
        # so we wrote our own client for omrapp.

        response_basename = 'MUSCIMarker.omrapp-response.' + _rstring + '.xml'
        response_fname = os.path.join(self.tmp_dir, response_basename)

        client = ObjectDetectionOMRAppClient(host=HOST,
                                             port=self.port,
                                             request_file=request_fname,
                                             response_file=response_fname)
        client.call()
        #   ...this happens in ObjectDetectionOMRAppClient...
        # Open socket according to conf
        # Send request to server
        # Collect raw result
        # Close connection

        # Convert raw result (XML) to output representation (CropObjects)
        if not os.path.isfile(response_fname):
            raise OSError('ObjectDetectionHandler: Did not receive'
                          ' response file {0}'.format(response_fname))

        try:
            cropobjects = parse_cropobject_list(response_fname)
            print(export_cropobject_list(cropobjects))
            # Verify that result is valid (re-request on failure?)
            if os.path.isfile(response_fname):
                os.unlink(response_fname)

        except:
            logging.warn('ObjectDetectionHandler: Could not parse'
                         ' response file {0}'.format(response_fname))
            cropobjects = []
        # finally:
        #     # Cleanup files.
        #     logging.info('Cleaning up files.')
        #     if os.path.isfile(request_fname):
        #         os.unlink(request_fname)

        # Bind output representation to self.result to fire bindings
        #  - Subsequent processing means adding the CropObjects
        #    into the current annotation, in this case.
        #  - This can also trigger auto-parse.
        self.response_cropobjects = cropobjects
Пример #11
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))
    cropobjects = parse_cropobject_list(args.annot)

    pitch_inference_engine = PitchInferenceEngine()
    time_inference_engine = OnsetsInferenceEngine(cropobjects=cropobjects)

    logging.info('Running pitch inference.')
    pitches, pitch_names = pitch_inference_engine.infer_pitches(
        cropobjects, with_names=True)
    # durations = inference_engine.durations_beats

    # Logging
    #pitch_names = {objid: midi2pitch_name(midi_code)
    #               for objid, midi_code in pitches.items()}

    # Export
    logging.info('Adding pitch information to <Data> attributes.')
    for c in cropobjects:
        if c.objid in pitches:
            midi_pitch_code = pitches[c.objid]
            pitch_step, pitch_octave = pitch_names[c.objid]
            # beats = durations[c.objid]
            # if len(beats) > 1:
            #     logging.warn('Notehead {0}: multiple possible beats: {1}'
            #                  ''.format(c.uid, beats))
            #     b = beats[0]
            # else:
            #     b = beats[0]
            if c.data is None:
                c.data = dict()
            c.data['midi_pitch_code'] = midi_pitch_code
            c.data['normalized_pitch_step'] = pitch_step
            c.data['pitch_octave'] = pitch_octave

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

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

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

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

    if args.midi is not None:
        midi_builder = MIDIBuilder()
        mf = midi_builder.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))