Source code for audiomate.annotations.label

import copy
from functools import total_ordering


[docs]@total_ordering class Label: """ Represents a label that describes some part of an utterance. Args: value (str): The text of the label. start (float): Start of the label within the utterance in seconds. (default: 0) end (float): End of the label within the utterance in seconds. (default: inf) (inf defines the end of the utterance) meta (dict): A dictionary containing additional information for the label. Attributes: label_list (LabelList): The label-list this label is belonging to. """ __slots__ = ['value', 'start', 'end', 'label_list', 'meta'] def __init__(self, value, start=0, end=float('inf'), meta=None): self.value = value self.start = start self.end = end self.meta = meta or {} self.label_list = None def __eq__(self, other): return ( self.start == other.start and self.end == other.end and self.value.lower() == other.value.lower() ) def __lt__(self, other): data_this = (self.start, self.end, self.value.lower()) data_other = (other.start, other.end, other.value.lower()) return data_this < data_other def __repr__(self) -> str: return 'Label({}, {}, {})'.format(self.value, self.start, self.end) def __copy__(self): return Label( self.value, start=self.start, end=self.end, meta=self.meta ) def __deepcopy__(self, memo): return Label( self.value, start=self.start, end=self.end, meta=copy.deepcopy(self.meta, memo) ) @property def start_abs(self): """ Return the absolute start of the label in seconds relative to the signal. If the label isn't linked to any utterance via label-list, it is assumed ``self.start`` is relative to the start of the signal, hence ``self.start`` == ``self.start_abs``. """ if self.label_list is None or self.label_list.utterance is None: return self.start return self.label_list.utterance.start + self.start @property def end_abs(self): """ Return the absolute end of the label in seconds relative to the signal. If the label isn't linked to any utterance via label-list, it is assumed ``self.end`` is relative to the start of the signal, hence ``self.end`` == ``self.end_abs``. """ if self.label_list is None or self.label_list.utterance is None: return self.end elif self.end == float('inf'): return self.label_list.utterance.end_abs else: return self.end + self.label_list.utterance.start @property def duration(self): """ Return the duration of the label in seconds. """ return self.end_abs - self.start_abs @property def length(self): """ Return the length of the label (Number of characters). """ return len(self.value)
[docs] def read_samples(self, sr=None): """ Read the samples of the utterance. Args: sr (int): If None uses the sampling rate given by the track, otherwise resamples to the given sampling rate. Returns: np.ndarray: A numpy array containing the samples as a floating point (numpy.float32) time series. """ duration = None if self.end != float('inf') or self.label_list.utterance.end >= 0: duration = self.duration track = self.label_list.utterance.track return track.read_samples( sr=sr, offset=self.start_abs, duration=duration )
[docs] def tokenized(self, delimiter=' '): """ Return a list with tokens from the value of the label. Tokens are extracted by splitting the string using ``delimiter`` and then trimming any whitespace before and after splitted strings. Args: delimiter (str): The delimiter used to split into tokens. (default: space) Return: list: A list of tokens in the order they occur in the label. Examples: >>> label = Label('as is oh') >>> label.tokenized() ['as', 'is', 'oh'] Using a different delimiter (whitespace is trimmed anyway): >>> label = Label('oh hi, as, is ') >>> label.tokenized(delimiter=',') ['oh hi', 'as', 'is'] """ tokens = self.value.split(sep=delimiter) tokens = [t.strip() for t in tokens] while '' in tokens: tokens.remove('') return tokens
[docs] def do_overlap(self, other_label, adjacent=True): """ Determine whether ``other_label`` overlaps with this label. If ``adjacent==True``, adjacent labels are also considered as overlapping. Args: other_label (Label): Another label. adjacent (bool): If ``True``, adjacent labels are considered as overlapping. Returns: bool: ``True`` if the two labels overlap, ``False`` otherwise. """ this_end = self.end other_end = other_label.end if this_end == float('inf'): this_end = self.end_abs if other_end == float('inf'): other_end = other_label.end_abs if adjacent: first_ends_before = this_end < other_label.start second_ends_before = other_end < self.start else: first_ends_before = this_end <= other_label.start second_ends_before = other_end <= self.start return not (first_ends_before or second_ends_before)
[docs] def overlap_duration(self, other_label): """ Return the duration of the overlapping part between this label and ``other_label``. Args: other_label(Label): Another label to check. Return: float: The duration of overlap in seconds. Example: >>> label_a = Label('a', 3.4, 5.6) >>> label_b = Label('b', 4.8, 6.2) >>> label_a.overlap_duration(label_b) 0.8 """ this_end = self.end other_end = other_label.end if this_end == float('inf'): this_end = self.end_abs if other_end == float('inf'): other_end = other_label.end_abs start_overlap = max(self.start, other_label.start) end_overlap = min(this_end, other_end) return max(0, end_overlap - start_overlap)