Annotations¶
The annotations submodule manipulates segment-based annotations: gloss/lemma boundaries, sign
activity, or any other labeling of a video's timeline.
A set of segments is represented as a numpy array of shape (M, 2) ([start, end]) or (M, 3)
([start, end, label]), with an inclusive end: [start, end] covers frames start, ..., end.
This is the same convention used by VideoPlayer.attach_segments.
Transforms¶
sign_language_tools.annotations.transforms converts and manipulates segments: merging adjacent or
overlapping spans, rendering them as a dense per-frame label vector (and back), scaling/moving them
for data augmentation, or building BIO tags for sequence labeling.
import numpy as np
from sign_language_tools.annotations.transforms import (
MergeSegments, SegmentsToFrameLabels, FrameLabelsToSegments,
)
segments = np.array([
[2, 5, 1],
[6, 12, 2],
[14, 17, 0],
[18, 25, 1],
]) # (M, 3): [start, end, label]
segments = MergeSegments()(segments)
frame_labels = SegmentsToFrameLabels()(segments) # (T,) dense label vector
segments_back = FrameLabelsToSegments()(frame_labels)
Utilities¶
sign_language_tools.annotations.utils provides temporal IoU (intersection-over-union) and
non-maximum suppression, useful to evaluate or post-process segment predictions.
from sign_language_tools.annotations.utils.iou import pairwise_temporal_intersection_over_union
from sign_language_tools.annotations.utils.nms import non_maximum_suppression
tiou_map = pairwise_temporal_intersection_over_union(target_segments, predicted_segments)
kept = non_maximum_suppression(predicted_segments, iou_threshold=0.5)
Visualization¶
sign_language_tools.annotations.visualization plots segments as colored spans on a matplotlib
timeline.
import numpy as np
from sign_language_tools.annotations.visualization import plot_segments_on_timeline
segments = np.array([[0, 10], [15, 25]])
plot_segments_on_timeline(segments, labels=["bonjour", "merci"])
Reference¶
Transforms¶
sign_language_tools.annotations.transforms.scale.ScaleSegments ¶
ScaleSegments(
factor: float = 0.8,
location: Literal["center", "start", "end"] = "center",
min_length: float = 1.0,
max_length: float = 1000.0,
)
Bases: Transform
Scales the length of segments by a fixed factor.
Each segment's length is multiplied by factor and clipped to
[min_length, max_length]. The resized segment is then repositioned
according to location, so that either its center, its start, or its
end stays fixed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
factor
|
float
|
Multiplicative factor applied to each segment's length. Values below 1.0 shrink segments, values above 1.0 grow them. |
0.8
|
location
|
Literal['center', 'start', 'end']
|
Which point of the segment stays fixed while resizing:
|
'center'
|
min_length
|
float
|
Lower bound applied to the new segment length after scaling. |
1.0
|
max_length
|
float
|
Upper bound applied to the new segment length after scaling. |
1000.0
|
Note
If segments has an integer dtype, the repositioned start/end
values are truncated (not rounded) to that dtype. Pass a float
array if fractional precision matters.
Example
import numpy as np from sign_language_tools.annotations.transforms import ScaleSegments segments = np.array([[10., 20.], [30., 60.]]) # (M, 2) transform = ScaleSegments(factor=0.5, location="center") transform(segments) array([[12.5, 17.5], [37.5, 52.5]])
__call__ ¶
__call__(segments: ndarray) -> np.ndarray
Scales the segments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The scaled segments, with the same shape as |
sign_language_tools.annotations.transforms.scale.RandomRelativeScaleSegments ¶
RandomRelativeScaleSegments(
scale_std: float = 0.5,
location: Literal["center", "start", "end"] = "center",
min_length: float = 1.0,
max_length: float = 1000.0,
)
Bases: ScaleSegments
Scales the length of segments by a random factor drawn per segment.
Each segment's length is multiplied by an independent factor sampled as
1 + scale_std * N(0, 1), then clipped to [min_length, max_length] and
repositioned according to location, exactly like ScaleSegments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scale_std
|
float
|
Standard deviation of the random scaling factor around 1.0. |
0.5
|
location
|
Literal['center', 'start', 'end']
|
Which point of the segment stays fixed while resizing:
|
'center'
|
min_length
|
float
|
Lower bound applied to the new segment length after scaling. |
1.0
|
max_length
|
float
|
Upper bound applied to the new segment length after scaling. |
1000.0
|
Example
import numpy as np from sign_language_tools.annotations.transforms import RandomRelativeScaleSegments segments = np.array([[10., 20.], [30., 60.]]) # (M, 2) transform = RandomRelativeScaleSegments(scale_std=0.3) scaled = transform(segments) scaled.shape (2, 2)
sign_language_tools.annotations.transforms.boundaries.SegmentsToBoundaries ¶
SegmentsToBoundaries(
width: int | None = None,
relative_width: float | None = None,
min_width: int = 1,
exclude_start: bool = False,
exclude_end: bool = False,
min_start: int | None = 0,
max_end: int | None = None,
boundary_labels: tuple[int, int] = (1, 1),
)
Bases: Transform
Transform segments into boundary regions centered on their transitions.
A segment is a [start, end] interval (both inclusive, in frames) marking
an event in a sequence — for example, a sign in a sign-language video. A
boundary is a short interval centered on a transition (the point where a
segment starts or ends) that can be used as a training target for models
that detect segment onsets/offsets rather than segment content.
For each input segment, this transform can emit up to two boundaries:
* a start-boundary centered on the segment's first frame, and
* an end-boundary centered on the frame immediately after the
segment's last frame (i.e. on end + 1).
Centering the end-boundary on end + 1 rather than on end means the
boundary marks the transition out of the segment (between the last
in-segment frame and the first out-of-segment frame), which is symmetric
with the start-boundary marking the transition into the segment.
Boundary placement uses integer arithmetic when width is given as an
integer: a boundary of width w centered on transition t covers
[t - w // 2, t - w // 2 + w - 1]. For even widths this is asymmetric
by one frame (more on the right of the transition than the left), which
keeps everything on the frame grid without rounding artifacts.
The emitted boundaries are [b_start, b_end, label] triples. The output
is sorted by b_start and may contain overlapping or duplicate
boundaries when segments are close together — downstream code should
handle this if needed. Boundaries that fall entirely outside
[min_start, max_end] after clamping are dropped.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
width
|
int | None
|
Fixed boundary width in frames. Exactly one of |
None
|
relative_width
|
float | None
|
Boundary width as a fraction of each segment's
length, rounded to the nearest integer. Exactly one of
|
None
|
min_width
|
int
|
Lower bound on the boundary width, applied after
|
1
|
exclude_start
|
bool
|
If True, do not emit start-boundaries. |
False
|
exclude_end
|
bool
|
If True, do not emit end-boundaries. |
False
|
min_start
|
int | None
|
If not None, boundary starts are clamped to at least this value. Typically 0 to keep boundaries inside the timeline. |
0
|
max_end
|
int | None
|
If not None, boundary ends are clamped to at most this value. Set this to the sequence length minus one to keep boundaries inside the timeline. |
None
|
boundary_labels
|
tuple[int, int]
|
Labels assigned to (start-boundaries,
end-boundaries) respectively. Using distinct values (e.g.
|
(1, 1)
|
Example
import numpy as np from sign_language_tools.annotations.transforms import SegmentsToBoundaries segments = np.array([[0, 6], [7, 9], [13, 16]]) # (M, 2) transform = SegmentsToBoundaries(width=4, boundary_labels=(1, 2)) transform(segments) array([[ 0, 1, 1], [ 5, 8, 1], [ 5, 8, 2], [ 8, 11, 2], [11, 14, 1], [15, 18, 2]])
__call__ ¶
__call__(segments: ndarray) -> np.ndarray
Transforms segments into their transition boundaries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Array of shape |
ndarray
|
|
ndarray
|
|
sign_language_tools.annotations.transforms.bio_tags.BioTags ¶
BioTags(
width: int | None = None,
relative_width: float | None = None,
b_label: int = 1,
i_label: int = 2,
)
Bases: Transform
Split each segment into a Beginning tag followed by an Inside tag.
Segments use inclusive-end convention: [start, end] covers frames
start, ..., end. Each segment of length L produces:
* a B-tag covering [start, start + w - 1] with label b_label, and
* an I-tag covering [start + w, end] with label i_label,
where w is the B-tag width (either width or round(relative_width * L),
minimum 1). If w >= L the I-tag is omitted, so single-frame segments
produce a B-tag only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
width
|
int | None
|
Fixed B-tag width, in frames. Exactly one of |
None
|
relative_width
|
float | None
|
B-tag width as a fraction of each segment's
length, rounded to the nearest integer (minimum 1). Exactly
one of |
None
|
b_label
|
int
|
Label assigned to the B-tag (beginning) of each segment. |
1
|
i_label
|
int
|
Label assigned to the I-tag (inside) of each segment. |
2
|
Example
import numpy as np from sign_language_tools.annotations.transforms import BioTags segments = np.array([[0, 6], [8, 13]]) # (M, 2) transform = BioTags(width=2) transform(segments) array([[ 0, 1, 1], [ 2, 6, 2], [ 8, 9, 1], [10, 13, 2]])
__call__ ¶
__call__(segments: ndarray) -> np.ndarray
Splits each segment into B/I tags.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Array of shape |
ndarray
|
of the resulting B/I tags, sorted by start. |
sign_language_tools.annotations.transforms.offset.SegmentsToBoundaryOffsets ¶
SegmentsToBoundaryOffsets(
sequence_length: int | None = None,
background_value: float = -1.0,
dtype: dtype = np.float32,
)
Bases: Transform
Render segments as a per-frame (start_offset, end_offset) series.
Segments use inclusive-end convention: [start, end] covers frames
start, ..., end. For each frame t inside a segment:
* start_offset[t] = t - start (frames since segment began)
* end_offset[t] = end - t (frames until segment ends)
Both offsets are 0 at the boundary frames and positive in between.
Frames not covered by any segment get background_value.
When segments overlap, later segments overwrite earlier ones for the
overlapping frames (sorted by start). To avoid surprises, run
RemoveOverlapping upstream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sequence_length
|
int | None
|
Length of the output series. If None, inferred
from the segments as |
None
|
background_value
|
float
|
Value for frames outside any segment, written to both columns. |
-1.0
|
dtype
|
dtype
|
Output dtype. Float because offsets are commonly used as regression targets, and the background sentinel may be non-integer. |
float32
|
Example
import numpy as np from sign_language_tools.annotations.transforms import SegmentsToBoundaryOffsets segments = np.array([[2, 5]]) # (M, 2) transform = SegmentsToBoundaryOffsets(sequence_length=8) transform(segments) array([[-1., -1.], [-1., -1.], [ 0., 3.], [ 1., 2.], [ 2., 1.], [ 3., 0.], [-1., -1.], [-1., -1.]], dtype=float32)
__call__ ¶
__call__(
segments: ndarray, sequence_length: int | None = None
) -> np.ndarray
Renders the segments as a per-frame offset series.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
sequence_length
|
int | None
|
Overrides the |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Array of shape |
ndarray
|
|
sign_language_tools.annotations.transforms.silence.CloseShortSilences ¶
CloseShortSilences(max_silence: int)
Bases: Transform
Close short silences between consecutive segments by extending them.
Segments use inclusive-end convention: [start, end] covers frames
start, ..., end. The silence between two segments [a, b] and [c, d]
(with c > b) is c - b - 1 empty frames.
For each adjacent pair whose silence is at most max_silence frames,
the boundary is placed near the midpoint of the gap. When the silence
has odd length, the extra frame is absorbed into the earlier segment.
Like RemoveOverlapping, this is a single-pass transform: cascading
effects (where closing one gap might shift a boundary into another
nearby gap) are not handled. In practice this only matters when
max_silence is large relative to segment lengths.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_silence
|
int
|
Maximum number of empty frames between two segments for the gap to be closed. Must be non-negative. |
required |
Example
import numpy as np from sign_language_tools.annotations.transforms import CloseShortSilences segments = np.array([[0, 6], [8, 13], [25, 27], [30, 41]]) # (M, 2) transform = CloseShortSilences(max_silence=3) transform(segments) array([[ 0, 7], [ 8, 13], [25, 28], [29, 41]])
__call__ ¶
__call__(segments: ndarray) -> np.ndarray
Closes short silences between consecutive segments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The segments with short silences closed, sorted by start. |
sign_language_tools.annotations.transforms.frame_labels.SegmentsToFrameLabels ¶
SegmentsToFrameLabels(
vector_size: int | None = None,
background_label: int = 0,
fill_label: int = 1,
dtype: dtype = np.int64,
)
Bases: Transform
Renders segments as a dense per-frame label vector.
Segments use inclusive-end convention: [start, end] covers frames
start, ..., end. For (M, 2) segments, all frames within a segment
get fill_label. For (M, 3) segments, the third column is used as
the per-segment label. Frames not covered by any segment get
background_label. When segments overlap, later segments (in array
order) overwrite earlier ones for the overlapping frames.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector_size
|
int | None
|
Length of the output label vector. If None, inferred
from the segments as |
None
|
background_label
|
int
|
Label assigned to frames outside any segment. |
0
|
fill_label
|
int
|
Label assigned to frames inside a segment when
|
1
|
dtype
|
dtype
|
Output dtype of the label vector. |
int64
|
Example
import numpy as np from sign_language_tools.annotations.transforms import SegmentsToFrameLabels segments = np.array([[2, 4, 1], [6, 8, 2]]) # (M, 3) transform = SegmentsToFrameLabels() transform(segments) array([0, 0, 1, 1, 1, 0, 2, 2, 2], dtype=int64)
__call__ ¶
__call__(
segments: ndarray, vector_size: int | None = None
) -> np.ndarray
Renders the segments as a label vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
vector_size
|
int | None
|
Overrides the |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Label vector of shape |
sign_language_tools.annotations.transforms.frame_labels.FrameLabelsToSegments ¶
FrameLabelsToSegments(
background_classes: tuple[int, ...] = (0,),
include_labels: bool = True,
)
Bases: Transform
Inverse of SegmentsToFrameLabels: groups a label vector into segments.
Contiguous runs of identical labels become [start, end, label]
segments, using the same inclusive-end convention as the rest of the
module ([start, end] covers frames start, ..., end). Runs whose
label is in background_classes are excluded from the output.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
background_classes
|
tuple[int, ...]
|
Labels considered background; segments with one of these labels are dropped from the output. |
(0,)
|
include_labels
|
bool
|
If True, the output segments include the label as
a third column. If False, only |
True
|
Example
import numpy as np from sign_language_tools.annotations.transforms import FrameLabelsToSegments labels = np.array([0, 0, 1, 1, 1, 0, 2, 2, 2]) transform = FrameLabelsToSegments() transform(labels) array([[2, 4, 1], [6, 8, 2]])
__call__ ¶
__call__(labels: ndarray) -> np.ndarray
Groups the label vector into segments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
labels
|
ndarray
|
Label vector of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Array of shape |
ndarray
|
False, containing the |
sign_language_tools.annotations.transforms.merge.MergeSegmentsOnTransition ¶
MergeSegmentsOnTransition(
transitions: list[tuple[int, int]], new_value: int = 1
)
Bases: Transform
Merges adjacent segment pairs whose labels match a given transition.
Segments are sorted by start. For each pair of adjacent segments (the
end of one immediately followed by the start of the next, with no
gap), if their (label_a, label_b) pair is listed in transitions,
the pair is merged into a single segment spanning both, labeled
new_value.
Note
This is a single-pass, pairwise transform: a chain of 3 or more consecutive segments that all match a transition is merged as overlapping pairs rather than collapsed into one segment. In practice this only matters when the same transition can repeat back-to-back.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transitions
|
list[tuple[int, int]]
|
List of |
required |
new_value
|
int
|
Label assigned to the merged segments. |
1
|
Example
import numpy as np from sign_language_tools.annotations.transforms import MergeSegmentsOnTransition segments = np.array([[2, 5, 1], [6, 10, 2], [12, 15, 3]]) # (M, 3) transform = MergeSegmentsOnTransition(transitions=[(1, 2)], new_value=9) transform(segments) array([[ 2, 10, 9], [12, 15, 3]])
__call__ ¶
__call__(segments: ndarray) -> np.ndarray
Merges matching adjacent segment pairs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The segments after merging, sorted by start. |
sign_language_tools.annotations.transforms.merge.MergeSegments ¶
MergeSegments()
Bases: Transform
Merges overlapping or touching segments into single segments.
Segments are sorted by start, then greedily merged: a segment is
folded into the current run whenever its start falls at or before
current_end + 1 (i.e. it overlaps or touches the run with no gap).
If segments has a label column, the merged segment keeps the label
of the first segment in each run.
Example
import numpy as np from sign_language_tools.annotations.transforms import MergeSegments segments = np.array([ ... [2, 5, 1], ... [6, 12, 2], ... [14, 17, 0], ... [18, 25, 1], ... [67, 78, 3], ... ]) # (M, 3) transform = MergeSegments() transform(segments) array([[ 2, 12, 1], [14, 25, 0], [67, 78, 3]])
__call__ ¶
__call__(segments: ndarray) -> np.ndarray
Merges the overlapping or touching segments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The merged segments, sorted by start. |
sign_language_tools.annotations.transforms.move.MoveSegments ¶
MoveSegments(dx: float = 0.0)
Bases: Transform
Shifts segments in time by a fixed offset.
Only the start and end columns are shifted; any extra column (e.g. a per-segment label) is left untouched.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dx
|
float
|
Offset added to the start and end of every segment. Negative values shift segments earlier. |
0.0
|
Example
import numpy as np from sign_language_tools.annotations.transforms import MoveSegments segments = np.array([[10, 20, 1], [30, 50, 2]]) # (M, 3) transform = MoveSegments(dx=5) transform(segments) array([[15, 25, 1], [35, 55, 2]])
__call__ ¶
__call__(segments: ndarray) -> np.ndarray
Shifts the segments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The shifted segments, with the same shape as |
sign_language_tools.annotations.transforms.move.RandomRelativeMoveSegments ¶
RandomRelativeMoveSegments(dx_std: float = 0.4)
Bases: Transform
Shifts segments in time by a random offset proportional to their length.
Each segment is shifted independently by dx_std * N(0, 1) * length,
where length is that segment's duration. Only the start and end
columns are shifted; any extra column (e.g. a per-segment label) is
left untouched.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dx_std
|
float
|
Standard deviation of the random shift, expressed as a fraction of each segment's length. |
0.4
|
Example
import numpy as np from sign_language_tools.annotations.transforms import RandomRelativeMoveSegments segments = np.array([[10, 20, 1], [30, 50, 2]]) # (M, 3) transform = RandomRelativeMoveSegments(dx_std=0.4) moved = transform(segments) moved.shape (2, 3)
__call__ ¶
__call__(segments: ndarray) -> np.ndarray
Shifts the segments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The shifted segments, with the same shape and dtype as |
ndarray
|
|
sign_language_tools.annotations.transforms.overlapping.RemoveOverlapping ¶
RemoveOverlapping(min_gap: int = 0)
Bases: Transform
Remove overlap between segments and enforce a minimum frame gap.
Segments use inclusive-end convention: [start, end] covers frames start, start+1, ..., end, so length = end - start + 1.
Two segments A, B (sorted by start) are considered too close when B.start - A.end <= min_gap. The boundary is placed near the midpoint of the overlap/touch region; when min_gap is odd, the extra empty frame falls on B's side.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
min_gap
|
int
|
Minimum number of empty frames required between two consecutive segments. Must be non-negative. |
0
|
Example
import numpy as np from sign_language_tools.annotations.transforms import RemoveOverlapping segments = np.array([[2, 5], [6, 14], [12, 17]]) # (M, 2) transform = RemoveOverlapping(min_gap=1) transform(segments) array([[ 2, 5], [ 7, 13], [15, 17]])
__call__ ¶
__call__(segments: ndarray) -> np.ndarray
Removes overlap between the segments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Segments with overlaps resolved, sorted by start. |
sign_language_tools.annotations.transforms.fill_between.FillBetween ¶
FillBetween(
start_value: int = 1,
end_value: int = 1,
fill_value: int = 1,
max_width: int | None = None,
bidirectional: bool = False,
)
Bases: Transform
Fills the gap between consecutive segments that form a transition.
Segments are sorted by start, then each adjacent pair is checked: if the
first segment's label equals start_value and the second's label
equals end_value (or the reverse, when bidirectional is True), a new
segment is created to cover the gap between them, labeled fill_value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_value
|
int
|
The label of the segment where the filling starts. |
1
|
end_value
|
int
|
The label of the segment where the filling ends. |
1
|
fill_value
|
int
|
The label to use for the newly created segments that fill the gaps. |
1
|
max_width
|
int | None
|
The maximum width (duration) of a gap to be filled. If None, all identified gaps will be filled. |
None
|
bidirectional
|
bool
|
If True, also considers gaps between an |
False
|
Example
import numpy as np from sign_language_tools.annotations.transforms import FillBetween segments = np.array([[2, 5, 1], [14, 17, 1], [30, 34, 1]]) # (M, 3) transform = FillBetween(start_value=1, end_value=1, fill_value=1) transform(segments) array([[ 2, 5, 1], [ 6, 13, 1], [14, 17, 1], [18, 29, 1], [30, 34, 1]])
__call__ ¶
__call__(segments: ndarray) -> np.ndarray
Fills the gaps between matching consecutive segments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The segments with filling segments inserted, sorted by start. |
Utilities¶
sign_language_tools.annotations.utils.iou.temporal_intersection_over_union ¶
temporal_intersection_over_union(
target_segment: Union[tuple[float, float], ndarray],
segments: ndarray,
) -> np.ndarray
Computes the temporal intersection-over-union (tIoU) between a target segment and K segments. The IoU is also called the Jaccard index or the Jaccard similarity coefficient.
The tIoU between a segment A and a segment B is: $\frac{|A \cup B|}{|A \cap B|}$.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_segment
|
Union[tuple[float, float], ndarray]
|
A target segment which is a tuple (start, end) or a Numpy array of shape (2). |
required |
segments
|
ndarray
|
A Numpy array of shape (K, 2) containing K segments (start, end). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
iou_scores |
ndarray
|
The IoU score between the interval and each of the K segments. This is a Numpy array of shape (K). |
sign_language_tools.annotations.utils.iou.pairwise_temporal_intersection_over_union ¶
pairwise_temporal_intersection_over_union(
target_segments: ndarray, candidate_segments: ndarray
) -> np.ndarray
Computes the temporal intersection-over-union (tIoU) between all pairs of target and candidate segments. The IoU is also called the Jaccard index or the Jaccard similarity coefficient.
The tIoU between a segment A and a segment B is: $\frac{|A \cup B|}{|A \cap B|}$. Args: target_segments: A Numpy array of shape (N, 2) containing N target segments (start, end). candidate_segments: A Numpy array of shape (M, 2) containing M candidate segments (start, end).
Returns:
| Name | Type | Description |
|---|---|---|
iou_map |
ndarray
|
A Numpy array of size (N, M) containing the IoU score for all pairs of target and candidate segments. |
sign_language_tools.annotations.utils.iou.get_segment_positive_negative_count ¶
get_segment_positive_negative_count(
tiou_map: ndarray, tiou_threshold: float
)
sign_language_tools.annotations.utils.iou.calculate_segment_prediction_metrics ¶
calculate_segment_prediction_metrics(
tiou_map: ndarray, tiou_threshold: float = 0.5
)
sign_language_tools.annotations.utils.nms.non_maximum_suppression ¶
non_maximum_suppression(
proposals: ndarray, iou_threshold: float = 0.8
) -> np.ndarray
Filter out a set of segment proposals using the Non-Maximum Suppression (NMS) algorithm. A high scoring proposal is selected, then only proposals with a IoU score less than a given threshold are kept. This operation removes proposals that overlap too much.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
proposals
|
ndarray
|
A Numpy array of shape (K, 3) containing K proposals with scores associated to them. Each proposal is in the form (start, end, score). |
required |
iou_threshold
|
float
|
The IoU threshold used to filter out proposals that overlap too much. |
0.8
|
Returns:
| Type | Description |
|---|---|
ndarray
|
A numpy array of shape (L, 3) that is a subset of the K initial proposals. |
sign_language_tools.annotations.utils.nms.soft_nms ¶
soft_nms(
proposals: ndarray,
alpha: float = 0.4,
t1: float = 0.5,
t2: float = 0.9,
n_proposals: int = 100,
) -> np.ndarray
Filter out a set of segment proposals using the Soft Non-Maximum Suppression (soft NMS) algorithm with a gaussian decay.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
proposals
|
ndarray
|
A Numpy array of shape (K, 3) containing K proposals with scores associated to them. Each proposal is in the form (start, end, score). |
required |
alpha
|
float
|
Alpha value of Gaussian decaying function. Default = 0.4. |
0.4
|
t1
|
float
|
Minimum thresholds for soft NMS. Default = 0.5. |
0.5
|
t2
|
float
|
Maximum thresholds for soft NMS. Default = 0.9. |
0.9
|
n_proposals
|
int
|
Maximum number of retained proposals. Default = 100. |
100
|
Returns:
| Type | Description |
|---|---|
ndarray
|
A numpy array of shape (L, 3) that is a subset of the K initial proposals. |
Visualization¶
sign_language_tools.annotations.visualization.timeline.plot_segments_on_timeline ¶
plot_segments_on_timeline(
segments: ndarray,
labels: list[str] = None,
y_lim: tuple[float, float] = (0, 0.5),
ax: Axes | None = None,
alpha: float = 0.5,
colors: Any = None,
cmap: str = "tab20",
**kwargs: Any
)
Plot segments as colored spans on a timeline.
Each segment is drawn as a vertical span between its start and end
frame. If labels is given, the label of each segment is drawn
centered in its span. Colors can be assigned automatically (one
color per unique label), given as a single color applied to every
segment, or given explicitly per segment.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segments
|
ndarray
|
Array of shape |
required |
labels
|
list[str]
|
Optional list of |
None
|
y_lim
|
tuple[float, float]
|
Tuple |
(0, 0.5)
|
ax
|
Axes | None
|
Matplotlib axes to draw on. Defaults to the current axes. |
None
|
alpha
|
float
|
Opacity of the segment spans. |
0.5
|
colors
|
Any
|
Colors to use for the segments. Can be:
|
None
|
cmap
|
str
|
Name of the matplotlib colormap used for automatic
per-label coloring. Ignored if |
'tab20'
|
**kwargs
|
Any
|
Additional keyword arguments forwarded to
|
{}
|
Example
import numpy as np segments = np.array([[0, 10], [15, 25]]) plot_segments_on_timeline(segments, labels=["a", "b"])