o
    s·¯i°y  ã                   @   sœ   d Z ddlZddlZddlmZ ddlZdZd#dd„Zd	d
„ Z	dd„ Z
d$dd„Zd%dd„Z	d&dd„Zd'dd„Z		d(dd„Zd)dd„Zdd „ Zd!d"„ ZdS )*aÑ  
The aim of a beat detection algorithm is to report the times at which a typical
human listener might tap their foot to a piece of music. As a result, most
metrics for evaluating the performance of beat tracking systems involve
computing the error between the estimated beat times and some reference list of
beat locations. Many metrics additionally compare the beat sequences at
different metric levels in order to deal with the ambiguity of tempo.

Based on the methods described in:
    Matthew E. P. Davies,  Norberto Degara, and Mark D. Plumbley.
    "Evaluation Methods for Musical Audio Beat Tracking Algorithms",
    Queen Mary University of London Technical Report C4DM-TR-09-06
    London, United Kingdom, 8 October 2009.

See also the Beat Evaluation Toolbox:
    https://code.soundsoftware.ac.uk/projects/beat-evaluation/

Conventions
-----------

Beat times should be provided in the form of a 1-dimensional array of beat
times in seconds in increasing order.  Typically, any beats which occur before
5s are ignored; this can be accomplished using
:func:`mir_eval.beat.trim_beats()`.

Metrics
-------

* :func:`mir_eval.beat.f_measure`: The F-measure of the beat sequence, where an
  estimated beat is considered correct if it is sufficiently close to a
  reference beat
* :func:`mir_eval.beat.cemgil`: Cemgil's score, which computes the sum of
  Gaussian errors for each beat
* :func:`mir_eval.beat.goto`: Goto's score, a binary score which is 1 when at
  least 25\% of the estimated beat sequence closely matches the reference beat
  sequence
* :func:`mir_eval.beat.p_score`: McKinney's P-score, which computes the
  cross-correlation of the estimated and reference beat sequences represented
  as impulse trains
* :func:`mir_eval.beat.continuity`: Continuity-based scores which compute the
  proportion of the beat sequence which is continuously correct
* :func:`mir_eval.beat.information_gain`: The Information Gain of a normalized
  beat error histogram over a uniform distribution

é    Né   )Úutilg     LÝ@ç      @c                 C   s   | | |k S )aQ  Remove beats before min_beat_time.  A common preprocessing step.

    Parameters
    ----------
    beats : np.ndarray
        Array of beat times in seconds.
    min_beat_time : float
        Minimum beat time to allow
        (Default value = 5.)

    Returns
    -------
    beats_trimmed : np.ndarray
        Trimmed beat array.
    © )ÚbeatsÚmin_beat_timer   r   úA/home/ubuntu/.local/lib/python3.10/site-packages/mir_eval/beat.pyÚ
trim_beats9   s   r	   c                 C   sF   | j dkr
t d¡ |j dkrt d¡ | |fD ]}t |t¡ qdS )a,  Check that the input annotations to a metric look like valid beat time
    arrays, and throws helpful errors if not.

    Parameters
    ----------
    reference_beats : np.ndarray
        reference beat times, in seconds
    estimated_beats : np.ndarray
        estimated beat times, in seconds
    r   zReference beats are empty.zEstimated beats are empty.N)ÚsizeÚwarningsÚwarnr   Úvalidate_eventsÚMAX_TIME)Úreference_beatsÚestimated_beatsr   r   r   r   ÚvalidateM   s   



ÿr   c                 C   sd   t  d| jd d d¡}t  d| jd ¡}t  ||| ¡}| |ddd… || ddd… | ddd… fS )aý  Return metric variations of the reference beats

    Parameters
    ----------
    reference_beats : np.ndarray
        beat locations in seconds

    Returns
    -------
    reference_beats : np.ndarray
        Original beat locations
    off_beat : np.ndarray
        180 degrees out of phase from the original beat locations
    double : np.ndarray
        Beats at 2x the original tempo
    half_odd : np.ndarray
        Half tempo, odd beats
    half_even : np.ndarray
        Half tempo, even beats
    r   ç      à?r   Né   )ÚnpÚarangeÚshapeÚinterp)r   Úinterpolated_indicesÚoriginal_indicesÚdouble_reference_beatsr   r   r   Ú_get_reference_beat_variationsb   s   ÿûr   çìQ¸…ë±?c                 C   sd   t | |ƒ |jdks| jdkrdS t | ||¡}tt|ƒƒt|ƒ }tt|ƒƒt| ƒ }t ||¡S )a‚  Compute the F-measure of correct vs incorrectly predicted beats.
    "Correctness" is determined over a small window.

    Examples
    --------
    >>> reference_beats = mir_eval.io.load_events('reference.txt')
    >>> reference_beats = mir_eval.beat.trim_beats(reference_beats)
    >>> estimated_beats = mir_eval.io.load_events('estimated.txt')
    >>> estimated_beats = mir_eval.beat.trim_beats(estimated_beats)
    >>> f_measure = mir_eval.beat.f_measure(reference_beats,
                                            estimated_beats)

    Parameters
    ----------
    reference_beats : np.ndarray
        reference beat times, in seconds
    estimated_beats : np.ndarray
        estimated beat times, in seconds
    f_measure_threshold : float
        Window size, in seconds
        (Default value = 0.07)

    Returns
    -------
    f_score : float
        The computed F-measure score

    r   ç        )r   r
   r   Úmatch_eventsÚfloatÚlenÚ	f_measure)r   r   Úf_measure_thresholdÚmatchingÚ	precisionÚrecallr   r   r   r!   ˆ   s   
r!   ç{®Gáz¤?c              	   C   s®   t | |ƒ |jdks| jdkrdS g }t| ƒD ]6} d}| D ]}t t || ¡¡}|t |d  d|d   ¡7 }q|d|jd | jd    }| |¡ q|d t 	|¡fS )a  Cemgil's score, computes a gaussian error of each estimated beat.
    Compares against the original beat times and all metrical variations.

    Examples
    --------
    >>> reference_beats = mir_eval.io.load_events('reference.txt')
    >>> reference_beats = mir_eval.beat.trim_beats(reference_beats)
    >>> estimated_beats = mir_eval.io.load_events('estimated.txt')
    >>> estimated_beats = mir_eval.beat.trim_beats(estimated_beats)
    >>> cemgil_score, cemgil_max = mir_eval.beat.cemgil(reference_beats,
                                                        estimated_beats)

    Parameters
    ----------
    reference_beats : np.ndarray
        reference beat times, in seconds
    estimated_beats : np.ndarray
        query beat times, in seconds
    cemgil_sigma : float
        Sigma parameter of gaussian error windows
        (Default value = 0.04)

    Returns
    -------
    cemgil_score : float
        Cemgil's score for the original reference beats
    cemgil_max : float
        The best Cemgil score for all metrical variations
    r   )r   r   r   g       @r   )
r   r
   r   r   ÚminÚabsÚexpr   ÚappendÚmax)r   r   Úcemgil_sigmaÚ
accuraciesÚaccuracyÚbeatÚ	beat_diffr   r   r   Úcemgil±   s   
"r1   çffffffÖ?çš™™™™™É?c                 C   s  t | |ƒ |jdks| jdkrdS t | jd ¡}t | jd ¡}d}td| jd d ƒD ]f}d| | | |d    }	| | |	 }
d| |d  | |   }| | | }t ||
k||k ¡}| ¡ dksi| ¡ dkrrd||< d||< q-d||< || | |  }|dk r‹|d |	 ||< q-|d | ||< q-t 	t 
|¡|k¡}|jd dk r¶||d d |d d … }d}n5t t |¡¡}t 	t |¡|k¡d }|d d| jd d  krëd}|| }||d  }|||d … }|rt t 
|¡¡|k rtj|dd	|k rd}d
|dk S )a‘  Calculate Goto's score, a binary 1 or 0 depending on some specific
    heuristic criteria

    Examples
    --------
    >>> reference_beats = mir_eval.io.load_events('reference.txt')
    >>> reference_beats = mir_eval.beat.trim_beats(reference_beats)
    >>> estimated_beats = mir_eval.io.load_events('estimated.txt')
    >>> estimated_beats = mir_eval.beat.trim_beats(estimated_beats)
    >>> goto_score = mir_eval.beat.goto(reference_beats, estimated_beats)

    Parameters
    ----------
    reference_beats : np.ndarray
        reference beat times, in seconds
    estimated_beats : np.ndarray
        query beat times, in seconds
    goto_threshold : float
        Threshold of beat error for a beat to be "correct"
        (Default value = 0.35)
    goto_mu : float
        The mean of the beat errors in the continuously correct
        track must be less than this
        (Default value = 0.2)
    goto_sigma : float
        The std of the beat errors in the continuously correct track must
        be less than this
        (Default value = 0.2)

    Returns
    -------
    goto_score : float
        Either 1.0 or 0.0 if some specific criteria are met
    r   r   r   r   é   éÿÿÿÿg      Ð?r   )Úddofç      ð?)r   r
   r   Úonesr   ÚzerosÚrangeÚlogical_andÚsumÚflatnonzeror(   r+   ÚdiffÚmeanÚstd)r   r   Úgoto_thresholdÚgoto_muÚ
goto_sigmaÚ
beat_errorÚpairedÚgoto_criteriaÚnÚprevious_intervalÚ
window_minÚnext_intervalÚ
window_maxÚbeats_in_windowÚoffsetÚincorrect_beatsÚtrackÚ	track_lenÚtrack_startÚ
start_beatÚend_beatr   r   r   Úgotoæ   sJ   
%ÿ
*rT   c              
   C   s’  t | |ƒ | jdkrt d¡ |jdkrt d¡ |jdks#| jdkr%dS tdƒ}t| ¡ |  ¡ ƒ}t || ¡}t | | ¡} t t 	t 
t 
|¡t 
| ¡g¡¡¡}t || d ¡}t 	| | ¡ tj¡}d||< t || d ¡}t 	|| ¡ tj¡}d||< t t |¡¡}	tt |t |	¡ ¡ƒ}
t ||d¡}|jd d	 }||
 }||
 d }|||… }t 
|jd | jd g¡}t |¡| S )
an  Get McKinney's P-score.
    Based on the autocorrelation of the reference and estimated beats

    Examples
    --------
    >>> reference_beats = mir_eval.io.load_events('reference.txt')
    >>> reference_beats = mir_eval.beat.trim_beats(reference_beats)
    >>> estimated_beats = mir_eval.io.load_events('estimated.txt')
    >>> estimated_beats = mir_eval.beat.trim_beats(estimated_beats)
    >>> p_score = mir_eval.beat.p_score(reference_beats, estimated_beats)

    Parameters
    ----------
    reference_beats : np.ndarray
        reference beat times, in seconds
    estimated_beats : np.ndarray
        query beat times, in seconds
    p_score_threshold : float
        Window size will be
        ``p_score_threshold*np.median(inter_annotation_intervals)``,
        (Default value = 0.2)

    Returns
    -------
    correlation : float
        McKinney's P-score

    r   úKOnly one reference beat was provided, so beat intervals cannot be computed.úKOnly one estimated beat was provided, so beat intervals cannot be computed.r   g      Y@r7   Úfullr   r   )r   r
   r   r   Úintr'   r   ÚarrayÚint64Úceilr+   r9   Úastyper>   r=   ÚroundÚmedianÚ	correlater   r<   )r   r   Úp_score_thresholdÚsampling_raterM   Ú	end_pointÚreference_trainÚbeat_indicesÚestimated_trainÚannotation_intervalsÚwin_sizeÚtrain_correlationÚ
middle_lagÚstartÚendÚn_beatsr   r   r   Úp_scoreJ  sB   

ÿ
ÿÿrm   çffffffÆ?c                 C   s  t | |ƒ | jdkrt d¡ |jdkrt d¡ |jdks#| jdkr%dS g }g }t| ƒD ]C} t | jd |jd g¡}t |¡}t |¡}t	|jd ƒD ]à}	d}
t 
||	 |  ¡}t |¡}|| }|| dkr*|	dkss|dkrì|d | jd k r‡| |d  | |  }n
| | | |d   }|dkr |dkrœd}ntj}nt 
|| ¡}|	d |jd k r»||	d  ||	  }n
||	 ||	d   }|dkrÔ|dkrÐd}ntj}n	t 
d||  ¡}||k rë||k rëd||< d}
n>| | | |d   }t 
|| ¡}||	 ||	d   }| | | |d   }t 
d||  ¡}||k r*||k r*d||< d}
|
||	< qNt t d|¡d¡}t |dk¡d }|dd… }t t |¡¡d }|d|jd   }| |¡ t |¡d|jd   }| |¡ q-|d |d t |¡t |¡fS )af  Get metrics based on how much of the estimated beat sequence is
    continually correct.

    Examples
    --------
    >>> reference_beats = mir_eval.io.load_events('reference.txt')
    >>> reference_beats = mir_eval.beat.trim_beats(reference_beats)
    >>> estimated_beats = mir_eval.io.load_events('estimated.txt')
    >>> estimated_beats = mir_eval.beat.trim_beats(estimated_beats)
    >>> CMLc, CMLt, AMLc, AMLt = mir_eval.beat.continuity(reference_beats,
                                                          estimated_beats)

    Parameters
    ----------
    reference_beats : np.ndarray
        reference beat times, in seconds
    estimated_beats : np.ndarray
        query beat times, in seconds
    continuity_phase_threshold : float
        Allowable ratio of how far is the estimated beat
        can be from the reference beat
        (Default value = 0.175)
    continuity_period_threshold : float
        Allowable distance between the inter-beat-interval
        and the inter-annotation-interval
        (Default value = 0.175)

    Returns
    -------
    CMLc : float
        Correct metric level, continuous accuracy
    CMLt : float
        Correct metric level, total accuracy (continuity not required)
    AMLc : float
        Any metric level, continuous accuracy
    AMLt : float
        Any metric level, total accuracy (continuity not required)
    r   rU   rV   )r   r   r   r   r   r5   r7   )r   r
   r   r   r   r   r+   r   r9   r:   r(   ÚargminÚinfr*   Únonzeror>   r<   )r   r   Úcontinuity_phase_thresholdÚcontinuity_period_thresholdÚcontinuous_accuraciesÚtotal_accuraciesÚn_annotationsÚused_annotationsÚbeat_successesÚmÚbeat_successÚbeat_differencesÚnearestÚmin_differenceÚreference_intervalÚphaseÚestimated_intervalÚperiodÚbeat_failuresÚlongest_trackÚcontinuous_accuracyÚtotal_accuracyr   r   r   Ú
continuityš  s   
,
ÿ
ÿ


ÿÿ€ÿÿ



ür†   é)   c                 C   s¦   t | |ƒ |d st d¡ | jdkrt d¡ |jdkr"t d¡ |jdks,| jdkr.dS t| ||ƒ}t|| |ƒ}t |¡}||krK|| | }|S || | }|S )a–  Get the information gain - K-L divergence of the beat error histogram
    to a uniform histogram

    Examples
    --------
    >>> reference_beats = mir_eval.io.load_events('reference.txt')
    >>> reference_beats = mir_eval.beat.trim_beats(reference_beats)
    >>> estimated_beats = mir_eval.io.load_events('estimated.txt')
    >>> estimated_beats = mir_eval.beat.trim_beats(estimated_beats)
    >>> information_gain = mir_eval.beat.information_gain(reference_beats,
                                                          estimated_beats)

    Parameters
    ----------
    reference_beats : np.ndarray
        reference beat times, in seconds
    estimated_beats : np.ndarray
        query beat times, in seconds
    bins : int
        Number of bins in the beat error histogram
        (Default value = 41)

    Returns
    -------
    information_gain_score : float
        Entropy of beat error histogram
    r   zDbins parameter is even, so there will not be a bin centered at zero.r   rU   rV   r   )r   r   r   r
   Ú_get_entropyr   Úlog2)r   r   ÚbinsÚforward_entropyÚbackward_entropyÚnormÚinformation_gain_scorer   r   r   Úinformation_gainJ  s.   
ÿ
ÿ
ÿ
ÿr   c                 C   sP  t  |jd ¡}t|jd ƒD ]c}|| |  }t  t  |¡¡}|| }|dkr1d| d | d   }|| jd d krEd| d | d   }n%|dk rZ| | }	| |d  }
d|	|
  }n| |d  }	| | }
d|	|
  }d| | ||< qt  |d d¡d }t  dd|d ¡}t  ||¡d }|dt  	|¡  }d||dk< t  	|t  
|¡ ¡ S )aý  Compute the entropy of the beat error histogram.

    This is a helper function for the information gain
    metric, and needs to be run twice: once backwards, once
    forwards.

    Parameters
    ----------
    reference_beats : np.ndarray
        reference beat times, in seconds
    estimated_beats : np.ndarray
        query beat times, in seconds
    bins : int
        Number of bins in the beat error histogram

    Returns
    -------
    entropy : float
        Entropy of beat error histogram
    r   r   r   r5   éþÿÿÿg      à¿r7   )r   r9   r   r:   ro   r(   ÚmodÚlinspaceÚ	histogramr<   r‰   )r   r   rŠ   rD   rG   Úbeat_distancesÚclosest_beatÚabsolute_errorÚintervalrj   rk   Úhistogram_bin_edgesÚraw_bin_valuesr   r   r   rˆ   ‹  s.   rˆ   c                 K   sì   t jt| fi |¤Ž} t jt|fi |¤Ž}t ¡ }t jt| |fi |¤Ž|d< t jt| |fi |¤Ž\|d< |d< t jt| |fi |¤Ž|d< t jt| |fi |¤Ž|d< t jt	| |fi |¤Ž\|d< |d< |d< |d	< t jt
| |fi |¤Ž|d
< |S )a  Compute all metrics for the given reference and estimated annotations.

    Examples
    --------
    >>> reference_beats = mir_eval.io.load_events('reference.txt')
    >>> estimated_beats = mir_eval.io.load_events('estimated.txt')
    >>> scores = mir_eval.beat.evaluate(reference_beats, estimated_beats)

    Parameters
    ----------
    reference_beats : np.ndarray
        Reference beat times, in seconds
    estimated_beats : np.ndarray
        Query beat times, in seconds
    **kwargs
        Additional keyword arguments which will be passed to the
        appropriate metric or preprocessing functions.

    Returns
    -------
    scores : dict
        Dictionary of scores, where the key is the metric name (str) and
        the value is the (float) score achieved.

    z	F-measureÚCemgilzCemgil Best Metric LevelÚGotozP-scorezCorrect Metric Level ContinuouszCorrect Metric Level TotalzAny Metric Level ContinuouszAny Metric Level TotalzInformation gain)r   Úfilter_kwargsr	   ÚcollectionsÚOrderedDictr!   r1   rT   rm   r†   r   )r   r   ÚkwargsÚscoresr   r   r   ÚevaluateË  sF   ÿ
ÿÿÿÿ
ÿÿ
ÿ
ûÿ
ÿr¡   )r   )r   )r&   )r2   r3   r3   )r3   )rn   rn   )r‡   )Ú__doc__Únumpyr   r   Ú r   r   r   r	   r   r   r!   r1   rT   rm   r†   r   rˆ   r¡   r   r   r   r   Ú<module>   s*    .

&
)6
ÿ
dS
ü 
1A@