def pitch_class(data, method, sound_ids=None):
    """
    Compares the estimated note with the reference note expecting an exact match or an octave error (pitch class)
    :param data: sound data with annotations and analysis
    :param method: analysis algorithm name to compare with annotation
    :param sound_ids: ids of the sounds to include in comparison (default=None, all found in data)
    :return: list with 1 for sounds that match and 0 for sounds that do not match
    """
    if sound_ids is None:
        sound_ids = data.keys()
    output = list()
    for sound_id in sound_ids:
        ground_truth_value = data[sound_id]['annotations']['midi_note']
        try:
            estimated_value = vfkp(data[sound_id]['analysis'], method + '.midi_note', ignore_non_existing=False)
        except KeyError:
            try:
                estimated_value = vfkp(data[sound_id]['analysis'], method + '.note_midi', ignore_non_existing=False)
            except KeyError:
                estimated_value = None

        if estimated_value is not None:
            if (int(estimated_value) - int(ground_truth_value)) % 12 == 0:
                output.append(1)
            else:
                output.append(0)
        else:
            output.append(0)

    return output
Exemple #2
0
def filter_data(data, filters=None, condition=None):
    """
    Returns a dictionary with all keys and values in data that passes one of the given filters (OR).
    Filters are given as a list of tuples in the filters parameter. Each tuple contains a filter_term and
    filter_values. Filter terms contain a key_path that will be used to get the corresponding value from
    each elements' dict in data, and the operation that will be performed against the given filter values.
    Key path and operation are separated by '__', and filter_values contain all values required to carry out
    the operation. For example:
    >> filtered_data = filter_data(data, filters=[('analysis.RhythmExtractor2013.bpm__>=' , 120)])

    Alternatively, the filter_data also accepts a function as a condition parameter. This function will be
    passed the key and current item being filtered as well as the rest of the data as a third argument. The
    function must return True or False to decide whether to add or not the item to the filtered data.
    >> def condition_func(key, item, data):
    >>     return item['property1'] == item['property2']
    >> filtered_data = filter_data(data, condition=condition_func)

    Remember that if any of the filters or the condition passes, the element is added to the output.
    To get AND filtering you should call this method in "cascade".

    :param data: data dictionary
    :param filters: list of tuples specifying filter_term and filter_values (optional)
    :param condition: function for which current item must return True in order to be returned
    :return: filtered dictionary
    """
    out_data = dict()
    for key, item in data.items():
        passes_filter = False
        if filters is not None:
            for filter_term, filter_values in filters:
                key_path, operation = filter_term.split('__')
                if operation == '>':
                    if vfkp(item, filter_term.replace('__' + operation, '')) > filter_values:
                        passes_filter = True
                elif operation == '>=':
                    if vfkp(item, filter_term.replace('__' + operation, '')) >= filter_values:
                        passes_filter = True
                elif operation == '<':
                    if vfkp(item, filter_term.replace('__' + operation, '')) < filter_values:
                        passes_filter = True
                elif operation == '<=':
                    if vfkp(item, filter_term.replace('__' + operation, '')) <= filter_values:
                        passes_filter = True
                elif operation == '==':
                    if vfkp(item, filter_term.replace('__' + operation, '')) == filter_values:
                        passes_filter = True
                else:
                    raise Exception("Filter term could not be parsed correctly")
        if condition is not None:
            passes_filter = condition(key, item, data)
        if passes_filter:
            out_data[key] = item
    return out_data
Exemple #3
0
 def get_data(self, key_path, format=list, ignore_non_existing=True):
     if format == list:
         return vfkp(self.data.values(), key_path, ignore_non_existing=ignore_non_existing)
     elif format == dict:
         out_dict = dict()
         for key, item in self.data.items():
             value = vfkp(item, key_path, ignore_non_existing=ignore_non_existing)
             if value is not None:
                 out_dict[key] = value
         return out_dict
     else:
         raise Exception(u'Unknown format \'%s\'' % format)
def accuracy1e(data, method, sound_ids=None, skip_zeroed_values=False):
    """
    Compares estimated bpm with annotated bpm of provided data. Considers as good matches values that, after rounder,
     are exatcly that of the ground truth.
    :param data: sound data with annotations and analysis
    :param method: analysis algorithm name to compare with annotation
    :param sound_ids: ids of the sounds to include in comparison (default=None, all found in data)
    :param skip_zeroed_values: whether to take into account sounds annotated with ground truth bpm=0.0
    :return: list with 1 for sounds that match and 0 for sounds that do not match
    """
    if sound_ids is None:
        sound_ids = data.keys()
    output = list()
    for sound_id in sound_ids:
        ground_truth_value = data[sound_id]['annotations']['bpm']
        if ground_truth_value is None:
            continue
        try:
            estimated_value = vfkp(data[sound_id]['analysis'], method + '.bpm', ignore_non_existing=False)
            if skip_zeroed_values and estimated_value == 0.0:
                continue
        except KeyError:
            # Method produced no estimation
            if not skip_zeroed_values:
                output.append(0)
            continue

        if int(round(estimated_value)) == int(round(ground_truth_value)):
            output.append(1)
        else:
            output.append(0)

    return output
Exemple #5
0
def accuracy1e(data, method, sound_ids=None, skip_zeroed_values=False):
    """
    Compares estimated bpm with annotated bpm of provided data. Considers as good matches values that, after rounder,
     are exatcly that of the ground truth.
    :param data: sound data with annotations and analysis
    :param method: analysis algorithm name to compare with annotation
    :param sound_ids: ids of the sounds to include in comparison (default=None, all found in data)
    :param skip_zeroed_values: whether to take into account sounds annotated with ground truth bpm=0.0
    :return: list with 1 for sounds that match and 0 for sounds that do not match
    """
    if sound_ids is None:
        sound_ids = data.keys()
    output = list()
    for sound_id in sound_ids:
        ground_truth_value = data[sound_id]['annotations']['bpm']
        if ground_truth_value is None:
            continue
        try:
            estimated_value = vfkp(data[sound_id]['analysis'],
                                   method + '.bpm',
                                   ignore_non_existing=False)
            if skip_zeroed_values and estimated_value == 0.0:
                continue
        except KeyError:
            # Method produced no estimation
            if not skip_zeroed_values:
                output.append(0)
            continue

        if int(round(estimated_value)) == int(round(ground_truth_value)):
            output.append(1)
        else:
            output.append(0)

    return output
Exemple #6
0
def accuracy2(data,
              method,
              sound_ids=None,
              tolerance=0.04,
              skip_zeroed_values=False,
              allowed_multiples=[1.0, 1.0 / 3, 0.5, 1.0, 2.0, 3.0]):
    """
    Compares estimated bpm with annotated bpm of provided data. Considers as good matches values that are either the
    same or multiples as listed in 'allowed_multiples' parameter.
    See: Gouyon, F., Klapuri, A., andM. Alonso, S. D., Tzanetakis, G., & Uhle, C. (2006). An Experimental Comparison
    of Audio Tempo Induction Algorithms. IEEE Transactions on Audio, Speech and Language Processing, 14(5), 1832-1844.
    :param data: sound data with annotations and analysis
    :param method: analysis algorithm name to compare with annotation
    :param sound_ids: ids of the sounds to include in comparison (default=None, all found in data)
    :param tolerance: max % deviation of bpm to consider a good match (default=0.04)
    :param skip_zeroed_values: whether to take into account sounds annotated with ground truth bpm=0.0
    :param allowed_multiples: multiples of the ground truth bpm value which are to be accepted as good estimates
    :return: list with 1 for sounds that match and 0 for sounds that do not match
    """
    if sound_ids is None:
        sound_ids = data.keys()
    output = list()
    for sound_id in sound_ids:
        ground_truth_value = data[sound_id]['annotations']['bpm']
        if ground_truth_value is None:
            continue
        try:
            estimated_value = vfkp(data[sound_id]['analysis'],
                                   method + '.bpm',
                                   ignore_non_existing=False)
            if skip_zeroed_values and estimated_value == 0.0:
                continue
        except KeyError:
            # Method produced no estimation
            if not skip_zeroed_values:
                output.append(0)
            continue

        threshold = tolerance * float(ground_truth_value)
        found_match = 0
        for multiple in allowed_multiples:
            delta = abs(float(ground_truth_value) * multiple - estimated_value)
            if delta <= threshold:
                found_match = 1
                break
        output.append(found_match)

    return output
def accuracy2(data, method, sound_ids=None, tolerance=0.04, skip_zeroed_values=False,
                         allowed_multiples=[1.0, 1.0/3, 0.5, 1.0, 2.0, 3.0]):
    """
    Compares estimated bpm with annotated bpm of provided data. Considers as good matches values that are either the
    same or multiples as listed in 'allowed_multiples' parameter.
    See: Gouyon, F., Klapuri, A., andM. Alonso, S. D., Tzanetakis, G., & Uhle, C. (2006). An Experimental Comparison
    of Audio Tempo Induction Algorithms. IEEE Transactions on Audio, Speech and Language Processing, 14(5), 1832-1844.
    :param data: sound data with annotations and analysis
    :param method: analysis algorithm name to compare with annotation
    :param sound_ids: ids of the sounds to include in comparison (default=None, all found in data)
    :param tolerance: max % deviation of bpm to consider a good match (default=0.04)
    :param skip_zeroed_values: whether to take into account sounds annotated with ground truth bpm=0.0
    :param allowed_multiples: multiples of the ground truth bpm value which are to be accepted as good estimates
    :return: list with 1 for sounds that match and 0 for sounds that do not match
    """
    if sound_ids is None:
        sound_ids = data.keys()
    output = list()
    for sound_id in sound_ids:
        ground_truth_value = data[sound_id]['annotations']['bpm']
        if ground_truth_value is None:
            continue
        try:
            estimated_value = vfkp(data[sound_id]['analysis'], method + '.bpm', ignore_non_existing=False)
            if skip_zeroed_values and estimated_value == 0.0:
                continue
        except KeyError:
            # Method produced no estimation
            if not skip_zeroed_values:
                output.append(0)
            continue

        threshold = tolerance * float(ground_truth_value)
        found_match = 0
        for multiple in allowed_multiples:
            delta = abs(float(ground_truth_value) * multiple - estimated_value)
            if delta <= threshold:
                found_match = 1
                break
        output.append(found_match)

    return output