コード例 #1
0
 def with_alignment_from_ctm(self, ctm_file: Pathlike, type: str = 'word', match_channel: bool = False) -> 'SupervisionSet':
     """
     Add alignments from CTM file to the supervision set.
     
     :param ctm: Path to CTM file.
     :param type: Alignment type (optional, default = `word`).
     :param match_channel: if True, also match channel between CTM and SupervisionSegment
     :return: A new SupervisionSet with AlignmentItem objects added to the segments.
     """
     ctm_words = []
     with open(ctm_file) as f:
         for line in f:
             reco_id, channel, start, duration, symbol = line.strip().split()
             ctm_words.append((reco_id, int(channel), float(start), float(duration), symbol))
     ctm_words = sorted(ctm_words, key=lambda x:(x[0], x[2]))
     reco_to_ctm = defaultdict(list, {k: list(v) for k,v in groupby(ctm_words, key=lambda x:x[0])})
     segments = []
     num_total = len(ctm_words)
     num_overspanned = 0
     for reco_id in set([s.recording_id for s in self]):
         if reco_id in reco_to_ctm:
             for seg in self.find(recording_id=reco_id):
                 alignment = [AlignmentItem(symbol=word[4], start=word[2], duration=word[3]) for word in reco_to_ctm[reco_id] 
                                 if overspans(seg, TimeSpan(word[2], word[2] + word[3]))
                                 and (seg.channel == word[1] or not match_channel)
                             ]
                 num_overspanned += len(alignment)
                 segments.append(fastcopy(seg, alignment={type: alignment}))
         else:
             segments.append([s for s in self.find(recording_id=reco_id)])
     logging.info(f"{num_overspanned} alignments added out of {num_total} total. If there are several"
         " missing, there could be a mismatch problem.")
     return SupervisionSet.from_segments(segments)
コード例 #2
0
ファイル: cut.py プロジェクト: popcornell/lhotse
    def truncate(self,
                 *,
                 offset: Seconds = 0.0,
                 duration: Optional[Seconds] = None,
                 keep_excessive_supervisions: bool = True,
                 preserve_id: bool = False) -> 'Cut':
        """
        Returns a new Cut that is a sub-region of the current Cut.

        Note that no operation is done on the actual features - it's only during the call to load_features()
        when the actual changes happen (a subset of features is loaded).

        :param offset: float (seconds), controls the start of the new cut relative to the current Cut's start.
            E.g., if the current Cut starts at 10.0, and offset is 2.0, the new start is 12.0.
        :param duration: optional float (seconds), controls the duration of the resulting Cut.
            By default, the duration is (end of the cut before truncation) - (offset).
        :param keep_excessive_supervisions: bool. Since trimming may happen inside a SupervisionSegment,
            the caller has an option to either keep or discard such supervisions.
        :param preserve_id: bool. Should the truncated cut keep the same ID or get a new, random one.
        :return: a new Cut instance.
        """
        new_start = self.start + offset
        until = offset + (duration if duration is not None else self.duration)
        new_duration = self.duration - new_start if duration is None else until - offset
        assert new_duration > 0.0
        assert new_start + new_duration <= self.start + self.duration + 1e-5
        new_time_span = TimeSpan(start=new_start, end=new_start + new_duration)
        criterion = overlaps if keep_excessive_supervisions else overspans
        return Cut(id=self.id if preserve_id else str(uuid4()),
                   start=new_start,
                   duration=new_duration,
                   supervisions=[
                       segment for segment in self.supervisions
                       if criterion(new_time_span, segment)
                   ],
                   features=self.features)
コード例 #3
0
from pathlib import Path
from tempfile import NamedTemporaryFile

import pytest

from lhotse.utils import overlaps, TimeSpan, overspans, save_to_yaml, load_yaml


@pytest.mark.parametrize(['lhs', 'rhs', 'expected'], [
    (TimeSpan(0, 1), TimeSpan(0, 1), True),
    (TimeSpan(0.5, 1), TimeSpan(0, 1), True),
    (TimeSpan(0, 0.5), TimeSpan(0, 1), True),
    (TimeSpan(0.1, 0.9), TimeSpan(0, 1), True),
    (TimeSpan(1, 2), TimeSpan(0, 1), False),
    (TimeSpan(2, 3), TimeSpan(0, 1), False),
    (TimeSpan(1, 1), TimeSpan(0, 1), False),
    (TimeSpan(0, 0), TimeSpan(0, 1), False),
])
def test_overlaps(lhs, rhs, expected):
    assert overlaps(lhs, rhs) == expected
    assert overlaps(rhs, lhs) == expected


@pytest.mark.parametrize(['lhs', 'rhs', 'expected'], [
    (TimeSpan(0, 1), TimeSpan(0, 1), True),
    (TimeSpan(0.5, 1), TimeSpan(0, 1), False),
    (TimeSpan(0, 1), TimeSpan(0.5, 1), True),
    (TimeSpan(0, 0.5), TimeSpan(0, 1), False),
    (TimeSpan(0, 1), TimeSpan(0, 0.5), True),
    (TimeSpan(0.1, 0.9), TimeSpan(0, 1), False),
    (TimeSpan(0, 1), TimeSpan(0.1, 0.9), True),