|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import warnings |
|
|
from typing import Optional, Tuple, Union |
|
|
|
|
|
from pyannote_audio_utils.core import Timeline, Segment, Annotation |
|
|
|
|
|
|
|
|
class UEMSupportMixin: |
|
|
"""Provides 'uemify' method with optional (à la NIST) collar""" |
|
|
|
|
|
def extrude(self, |
|
|
uem: Timeline, |
|
|
reference: Annotation, |
|
|
collar: float = 0.0, |
|
|
skip_overlap: bool = False) -> Timeline: |
|
|
"""Extrude reference boundary collars from uem |
|
|
|
|
|
reference |----| |--------------| |-------------| |
|
|
uem |---------------------| |-------------------------------| |
|
|
extruded |--| |--| |---| |-----| |-| |-----| |-----------| |-----| |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
uem : Timeline |
|
|
Evaluation map. |
|
|
reference : Annotation |
|
|
Reference annotation. |
|
|
collar : float, optional |
|
|
When provided, set the duration of collars centered around |
|
|
reference segment boundaries that are extruded from both reference |
|
|
and hypothesis. Defaults to 0. (i.e. no collar). |
|
|
skip_overlap : bool, optional |
|
|
Set to True to not evaluate overlap regions. |
|
|
Defaults to False (i.e. keep overlap regions). |
|
|
|
|
|
Returns |
|
|
------- |
|
|
extruded_uem : Timeline |
|
|
""" |
|
|
|
|
|
if collar == 0. and not skip_overlap: |
|
|
return uem |
|
|
|
|
|
collars, overlap_regions = [], [] |
|
|
|
|
|
|
|
|
if collar > 0.: |
|
|
|
|
|
for segment in reference.itersegments(): |
|
|
|
|
|
t = segment.start |
|
|
collars.append(Segment(t - .5 * collar, t + .5 * collar)) |
|
|
|
|
|
|
|
|
t = segment.end |
|
|
collars.append(Segment(t - .5 * collar, t + .5 * collar)) |
|
|
|
|
|
|
|
|
if skip_overlap: |
|
|
|
|
|
for (segment1, track1), (segment2, track2) in reference.co_iter(reference): |
|
|
if segment1 == segment2 and track1 == track2: |
|
|
continue |
|
|
|
|
|
overlap_regions.append(segment1 & segment2) |
|
|
|
|
|
segments = collars + overlap_regions |
|
|
|
|
|
return Timeline(segments=segments).support().gaps(support=uem) |
|
|
|
|
|
def common_timeline(self, reference: Annotation, hypothesis: Annotation) \ |
|
|
-> Timeline: |
|
|
"""Return timeline common to both reference and hypothesis |
|
|
|
|
|
reference |--------| |------------| |---------| |----| |
|
|
hypothesis |--------------| |------| |----------------| |
|
|
timeline |--|-----|----|---|-|------| |-|---------|----| |----| |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
reference : Annotation |
|
|
hypothesis : Annotation |
|
|
|
|
|
Returns |
|
|
------- |
|
|
timeline : Timeline |
|
|
""" |
|
|
timeline = reference.get_timeline(copy=True) |
|
|
timeline.update(hypothesis.get_timeline(copy=False)) |
|
|
return timeline.segmentation() |
|
|
|
|
|
def project(self, annotation: Annotation, timeline: Timeline) -> Annotation: |
|
|
"""Project annotation onto timeline segments |
|
|
|
|
|
reference |__A__| |__B__| |
|
|
|____C____| |
|
|
|
|
|
timeline |---|---|---| |---| |
|
|
|
|
|
projection |_A_|_A_|_C_| |_B_| |
|
|
|_C_| |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
annotation : Annotation |
|
|
timeline : Timeline |
|
|
|
|
|
Returns |
|
|
------- |
|
|
projection : Annotation |
|
|
""" |
|
|
projection = annotation.empty() |
|
|
timeline_ = annotation.get_timeline(copy=False) |
|
|
for segment_, segment in timeline_.co_iter(timeline): |
|
|
for track_ in annotation.get_tracks(segment_): |
|
|
track = projection.new_track(segment, candidate=track_) |
|
|
projection[segment, track] = annotation[segment_, track_] |
|
|
return projection |
|
|
|
|
|
def uemify(self, |
|
|
reference: Annotation, |
|
|
hypothesis: Annotation, |
|
|
uem: Optional[Timeline] = None, |
|
|
collar: float = 0., |
|
|
skip_overlap: bool = False, |
|
|
returns_uem: bool = False, |
|
|
returns_timeline: bool = False) \ |
|
|
-> Union[ |
|
|
Tuple[Annotation, Annotation], |
|
|
Tuple[Annotation, Annotation, Timeline], |
|
|
Tuple[Annotation, Annotation, Timeline, Timeline], |
|
|
]: |
|
|
"""Crop 'reference' and 'hypothesis' to 'uem' support |
|
|
|
|
|
Parameters |
|
|
---------- |
|
|
reference, hypothesis : Annotation |
|
|
Reference and hypothesis annotations. |
|
|
uem : Timeline, optional |
|
|
Evaluation map. |
|
|
collar : float, optional |
|
|
When provided, set the duration of collars centered around |
|
|
reference segment boundaries that are extruded from both reference |
|
|
and hypothesis. Defaults to 0. (i.e. no collar). |
|
|
skip_overlap : bool, optional |
|
|
Set to True to not evaluate overlap regions. |
|
|
Defaults to False (i.e. keep overlap regions). |
|
|
returns_uem : bool, optional |
|
|
Set to True to return extruded uem as well. |
|
|
Defaults to False (i.e. only return reference and hypothesis) |
|
|
returns_timeline : bool, optional |
|
|
Set to True to oversegment reference and hypothesis so that they |
|
|
share the same internal timeline. |
|
|
|
|
|
Returns |
|
|
------- |
|
|
reference, hypothesis : Annotation |
|
|
Extruded reference and hypothesis annotations |
|
|
uem : Timeline |
|
|
Extruded uem (returned only when 'returns_uem' is True) |
|
|
timeline : Timeline: |
|
|
Common timeline (returned only when 'returns_timeline' is True) |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
if uem is None: |
|
|
r_extent = reference.get_timeline().extent() |
|
|
h_extent = hypothesis.get_timeline().extent() |
|
|
extent = r_extent | h_extent |
|
|
uem = Timeline(segments=[extent] if extent else [], |
|
|
uri=reference.uri) |
|
|
warnings.warn( |
|
|
"'uem' was approximated by the union of 'reference' " |
|
|
"and 'hypothesis' extents.") |
|
|
|
|
|
|
|
|
uem = self.extrude(uem, reference, collar=collar, |
|
|
skip_overlap=skip_overlap) |
|
|
|
|
|
|
|
|
reference = reference.crop(uem, mode='intersection') |
|
|
hypothesis = hypothesis.crop(uem, mode='intersection') |
|
|
|
|
|
|
|
|
if returns_timeline: |
|
|
timeline = self.common_timeline(reference, hypothesis) |
|
|
reference = self.project(reference, timeline) |
|
|
hypothesis = self.project(hypothesis, timeline) |
|
|
|
|
|
result = (reference, hypothesis) |
|
|
if returns_uem: |
|
|
result += (uem,) |
|
|
|
|
|
if returns_timeline: |
|
|
result += (timeline,) |
|
|
|
|
|
return result |
|
|
|