Auto-CS 2.0

https://sourceforge.net/projects/autocs/

Module CuedSpeech.whenhand

Class sppasWhenHandTransitionPredictor

Description

Create the CS coding scheme from time-aligned phonemes.

From the time-aligned keys, this class can estimate the moments of the hand shape transitions (consonants) and the moments of the hand position transitions (vowels).

It aims to predict when the hand changes both its position and its shape. It results in two tiers indicating intervals with transitions:

  • CS-HandPositions predicting [M1,M2] intervals when position is changing;
  • CS-HandShapes predicting [D1,D2] intervals when shape is changing.

Constructor

Instantiate a CS generator.

Parameters
  • cue_rules: (CuedSpeechKeys) Rules to convert phonemes => keys
View Source
def __init__(self, predictor_version=WhenTransitionPredictor.DEFAULT, cue_rules=CuedSpeechCueingRules()):
    """Instantiate a CS generator.

    :param cue_rules: (CuedSpeechKeys) Rules to convert phonemes => keys

    """
    self.__cued = None
    self.set_cue_rules(cue_rules)
    self.__transitions = WhenTransitionPredictor()
    self.set_whenpredictor_version(predictor_version)

Public functions

set_cue_rules

Set new rules.

Parameters
  • cue_rules: (CuedSpeechKeys) Rules and codes for vowel positions and hand shapes
Raises
  • sppasTypeError: given parameter is not CuedSpeechCueingRules
View Source
def set_cue_rules(self, cue_rules: CuedSpeechCueingRules) -> None:
    """Set new rules.

        :param cue_rules: (CuedSpeechKeys) Rules and codes for vowel positions and hand shapes
        :raises: sppasTypeError: given parameter is not CuedSpeechCueingRules

        """
    if isinstance(cue_rules, CuedSpeechCueingRules) is False:
        raise sppasTypeError('cue_rules', 'CuedSpeechCueingRules')
    self.__cued = cue_rules

get_whenpredictor_version

Return the version number of the generation system.

View Source
def get_whenpredictor_version(self) -> int:
    """Return the version number of the generation system."""
    return self.__transitions.get_version_number()

get_whenpredictor_versions

Return the list of version numbers of the generation system.

View Source
@staticmethod
def get_whenpredictor_versions() -> list:
    """Return the list of version numbers of the generation system."""
    return WhenTransitionPredictor.version_numbers()

set_whenpredictor_version

Set the prediction system version.

  • 0: no time estimation.
  • 1: system based on P. Duchnowski et al. (1998)
  • 2: system based on P. Duchnowski et al. (2000)
  • 3: system based on V. Attina (2005) synchronization model
  • 4: empirical rules from B. Bigi & Datha
  • 5: revised rules by B. Bigi
Raises
  • sppasKeyError
  • TypeError
Parameters
  • value
View Source
def set_whenpredictor_version(self, value: int) -> None:
    """Set the prediction system version.

        - 0: no time estimation.
        - 1: system based on P. Duchnowski et al. (1998)
        - 2: system based on P. Duchnowski et al. (2000)
        - 3: system based on V. Attina (2005) synchronization model
        - 4: empirical rules from B. Bigi & Datha
        - 5: revised rules by B. Bigi

        :raises: sppasKeyError:
        :raises: TypeError:

        """
    value = int(value)
    self.__transitions = WhenTransitionPredictor(value)

shape_is_neutral

Return True if the given character is the neutral shape.

Parameters
  • s: (str) Character representing a shape
Returns
  • (bool) The shape is neutral
View Source
def shape_is_neutral(self, s: str) -> bool:
    """Return True if the given character is the neutral shape.

        :param s: (str) Character representing a shape
        :return: (bool) The shape is neutral

        """
    return s == self.__cued.get_neutral_consonant()

position_is_neutral

Return True if the given character is the neutral position.

Parameters
  • p: (str) Character representing a position
Returns
  • (bool) The position is neutral
View Source
def position_is_neutral(self, p: str) -> bool:
    """Return True if the given character is the neutral position.

        :param p: (str) Character representing a position
        :return: (bool) The position is neutral

        """
    return p == self.__cued.get_neutral_vowel()

get_a1a3_avg_duration

Return the average of stored [A1;A3] durations or the fixed one.

If there's not enough known [A1;A3] durations, the fixed value is returned.

View Source
def get_a1a3_avg_duration(self):
    """Return the average of stored [A1;A3] durations or the fixed one.

        If there's not enough known [A1;A3] durations, the fixed value is
        returned.

        """
    return self.__transitions.get_a1a3_avg_duration()

has_nil_pos

Return True if the given string does not contain a vowel.

Parameters
  • phns: (str) Phonemes of a key in the form "C-V" or "V" or "C"
Returns
  • (bool) True if the phns are of "C" structure
View Source
def has_nil_pos(self, phns: str) -> bool:
    """Return True if the given string does not contain a vowel.

        :param phns: (str) Phonemes of a key in the form "C-V" or "V" or "C"
        :return: (bool) True if the phns are of "C" structure

        """
    return self.__cued.get_class(phns) == 'C' if '-' not in phns else False

has_nil_shape

Return True if the given string does not contain a consonant.

Parameters
  • phns: (str) Phonemes of a key in the form "C-V" or "V" or "C"
Returns
  • (bool) True if the phns are of "V" structure
View Source
def has_nil_shape(self, phns: str) -> bool:
    """Return True if the given string does not contain a consonant.

        :param phns: (str) Phonemes of a key in the form "C-V" or "V" or "C"
        :return: (bool) True if the phns are of "V" structure

        """
    return self.__cued.get_class(phns) == 'V' if '-' not in phns else False

asset_a1a3

Reset then store all [a1;a3] values in the transitions model.

Parameters
  • tier_keys: (sppasTier)
View Source
def asset_a1a3(self, tier_keys: sppasTier) -> None:
    """Reset then store all [a1;a3] values in the transitions model.

        :param tier_keys: (sppasTier)

        """
    self.__transitions.reset_key_intervals()
    for ii in range(len(tier_keys)):
        ann = tier_keys[ii]
        interval = ann.get_location()
        labels = ann.get_labels()
        if len(labels) != 2:
            logging.error('Expected 2 labels of annotation: {:s}'.format(str(ann)))
            raise sppasCuedRulesValueError(serialize_labels(labels))
        a1 = interval.get_lowest_localization().get_midpoint()
        a3 = interval.get_highest_localization().get_midpoint()
        cur_pos = labels[1].copy()
        is_neutral = self.position_is_neutral(cur_pos.get_best().get_content())
        self.__transitions.set_a(a1, a3, store=not is_neutral)

when_hands

Create two tiers with the transition periods of the hand.

Parameters
  • tier_keys: (sppasTier)
  • tier_segments: (sppasTier) Tier with name 'CS-PhonSegments', phonemes of each key
Returns
  • (sppasTier, sppasTier) Position transitions and Shape transitions
Raises

sppasCuedRulesValueError

View Source
def when_hands(self, tier_keys: sppasTier, tier_segments: sppasTier) -> tuple:
    """Create two tiers with the transition periods of the hand.

        :param tier_keys: (sppasTier)
        :param tier_segments: (sppasTier) Tier with name 'CS-PhonSegments', phonemes of each key
        :return: (sppasTier, sppasTier) Position transitions and Shape transitions
        :raises: sppasCuedRulesValueError

        """
    if len(tier_keys) * len(tier_segments) == 0:
        cs_pos = sppasTier('CS-HandPositions')
        cs_shapes = sppasTier('CS-HandShapes')
    else:
        (positions_moves, shapes_moves) = self.predict_transitions(tier_keys, tier_segments)
        cs_pos = self.predicted_to_tier(positions_moves)
        cs_pos.set_name('CS-HandPositions')
        cs_shapes = self.predicted_to_tier(shapes_moves)
        cs_shapes.set_name('CS-HandShapes')
    cs_pos.set_meta('cued_speech_position_of_keys_tier', tier_keys.get_name())
    cs_shapes.set_meta('cued_speech_shape_of_keys_tier', tier_keys.get_name())
    cs_pos.set_meta('cued_speech_position_of_phns_tier', tier_segments.get_name())
    cs_shapes.set_meta('cued_speech_shape_of_phns_tier', tier_segments.get_name())
    return (cs_pos, cs_shapes)

predict_transitions

Return the predicted position transitions and shape transitions.

The two returned lists contain:

  • the estimated m1 and m2 values (in seconds), or d1 and d2;
  • the transition position in a tuple with origin and target; and
  • the origin/target annotation identifiers.
Parameters
  • tier_keys: (sppasTier)
  • tier_segments: (sppasTier) Tier with name 'CS-PhonSegments', phonemes of each key
Returns
  • tuple(PredictedWhenHand, PredictedWhenHand)
Raises

sppasCuedRulesValueError

View Source
def predict_transitions(self, tier_keys: sppasTier, tier_segments: sppasTier) -> tuple:
    """Return the predicted position transitions and shape transitions.

        The two returned lists contain:

        - the estimated m1 and m2 values (in seconds), or d1 and d2;
        - the transition position in a tuple with origin and target; and
        - the origin/target annotation identifiers.

        :param tier_keys: (sppasTier)
        :param tier_segments: (sppasTier) Tier with name 'CS-PhonSegments', phonemes of each key
        :return: tuple(PredictedWhenHand, PredictedWhenHand)
        :raises: sppasCuedRulesValueError

        """
    pos_moves = PredictedWhenHand()
    shp_moves = PredictedWhenHand()
    self.asset_a1a3(tier_keys)
    ann = tier_keys[0]
    labels = ann.get_labels()
    prev_shape = labels[0].copy()
    prev_pos = labels[1].copy()
    interval = ann.get_location()
    key_rank_ipu = 1
    prev_phns = self.__get_phones(tier_segments, interval)
    for ii in range(1, len(tier_keys)):
        ann = tier_keys[ii]
        labels = ann.get_labels()
        cur_shape = labels[0].copy()
        cur_pos = labels[1].copy()
        cur_phns = self.__get_phones(tier_segments, ann.get_location())
        interval = ann.get_location()
        a1 = interval.get_lowest_localization().get_midpoint()
        a3 = interval.get_highest_localization().get_midpoint()
        if self.position_is_neutral(cur_pos.get_best().get_content()) is True:
            key_rank_ipu = 0
        self.__transitions.set_a(a1, a3, store=False)
        if prev_pos != cur_pos or (prev_pos == cur_pos and prev_pos != 's') or (prev_pos == cur_pos and prev_pos == 's' and (prev_shape == cur_shape)):
            (m1, m2) = self.__transitions.predict_m(rank=key_rank_ipu, is_nil_shape=self.has_nil_shape(cur_phns), is_nil_pos=self.has_nil_pos(cur_phns))
            prev_pos.set_key(prev_phns)
            cur_pos.set_key(cur_phns)
            pos_moves.append(m1, m2, (prev_pos, cur_pos), tier_keys[ii - 1].get_id(), ann.get_id())
        if cur_shape != prev_shape:
            (d1, d2) = self.__transitions.predict_d(rank=key_rank_ipu, is_nil_shape=self.has_nil_shape(cur_phns), is_nil_pos=self.has_nil_pos(cur_phns))
            shp_moves.append(d1, d2, (prev_shape, cur_shape), tier_keys[ii - 1].get_id(), ann.get_id())
        prev_shape = cur_shape
        prev_pos = cur_pos
        prev_phns = cur_phns
        key_rank_ipu += 1
    return (pos_moves, shp_moves)

predicted_to_tier

Turn the given values into sppasPoint.

Parameters
  • predicted: (PredictedWhenHand) Result of the prediction model.
Returns
  • (sppasTier) Predicted values turned into a sppasTier
View Source
@staticmethod
def predicted_to_tier(predicted: PredictedWhenHand) -> sppasTier:
    """Turn the given values into sppasPoint.

        :param predicted: (PredictedWhenHand) Result of the prediction model.
        :return: (sppasTier) Predicted values turned into a sppasTier

        """
    tier = sppasTier('CS-Predicted')
    prev_end = sppasPoint(0.0)
    for i in range(len(predicted)):
        (start, end, tags, source_id, target_id) = predicted[i]
        if prev_end >= start:
            p1 = prev_end.copy()
        else:
            p1 = sppasPoint(start)
        next_start = None if i + 1 == len(predicted) else predicted[i + 1][0]
        radius = None
        if next_start is not None and end > next_start:
            radius = (end - next_start) / 2.0
            if end - radius > start:
                end = end - radius
            else:
                radius = (end - start) / 2.0
        p2 = sppasPoint(end, radius)
        if p2 > p1:
            p2 = sppasPoint(end)
        if p2 > p1:
            tier.create_annotation(sppasLocation(sppasInterval(p1, p2)), list(tags))
            tier.set_meta('from_id', source_id)
            tier.set_meta('to_id', target_id)
            prev_end = p2
        else:
            prev_end = p1
    return tier

Protected functions

__get_phones

Return the serialized best label of the tier in the given interval.

Parameters
  • tier
  • interval
View Source
@staticmethod
def __get_phones(tier, interval) -> str:
    """Return the serialized best label of the tier in the given interval."""
    begin = interval.get_lowest_localization()
    end = interval.get_highest_localization()
    anns = tier.find(begin, end)
    if len(anns) > 0:
        return serialize_labels(anns[0].get_labels(), separator='-')
    return ''