niobures's picture
Pyannote (models, models_onnx)
8c838e7 verified
#!/usr/bin/env python
# encoding: utf-8
# The MIT License (MIT)
# Copyright (c) 2012-2019 CNRS
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# AUTHORS
# Hervé BREDIN - http://herve.niderb.fr
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 = [], []
# build list of collars if needed
if collar > 0.:
# iterate over all segments in reference
for segment in reference.itersegments():
# add collar centered on start time
t = segment.start
collars.append(Segment(t - .5 * collar, t + .5 * collar))
# add collar centered on end time
t = segment.end
collars.append(Segment(t - .5 * collar, t + .5 * collar))
# build list of overlap regions if needed
if skip_overlap:
# iterate over pair of intersecting segments
for (segment1, track1), (segment2, track2) in reference.co_iter(reference):
if segment1 == segment2 and track1 == track2:
continue
# add their intersection
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)
"""
# when uem is not provided, use the union of reference and hypothesis
# extents -- and warn the user about that.
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.")
# extrude collars (and overlap regions) from uem
uem = self.extrude(uem, reference, collar=collar,
skip_overlap=skip_overlap)
# extrude regions outside of uem
reference = reference.crop(uem, mode='intersection')
hypothesis = hypothesis.crop(uem, mode='intersection')
# project reference and hypothesis on common timeline
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