---
title: utils
---

<div class="python-reference">
<a id="fastvideo.eval.metrics.physics_iq.utils-functions"></a>
<a id="fastvideo.eval.metrics.physics_iq.utils"></a>

<h2 id="fastvideo.eval.metrics.physics_iq.utils.PreparedPhysicsIQPair"><code>fastvideo.eval.metrics.physics_iq.utils.PreparedPhysicsIQPair</code></h2>
<pre class="doc-signature"><code>PreparedPhysicsIQPair(generated_quarter: np.ndarray, reference_quarter: np.ndarray, generated_masks: np.ndarray, reference_masks: np.ndarray)</code></pre>


<details><summary>Source</summary><pre><code>@dataclass(frozen=True)
class PreparedPhysicsIQPair:
    generated_quarter: np.ndarray
    reference_quarter: np.ndarray
    generated_masks: np.ndarray
    reference_masks: np.ndarray</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.PreparedPhysicsIQTriplet"><code>fastvideo.eval.metrics.physics_iq.utils.PreparedPhysicsIQTriplet</code></h2>
<pre class="doc-signature"><code>PreparedPhysicsIQTriplet(generated_quarter: np.ndarray, reference_quarter: np.ndarray, reference_take2_quarter: np.ndarray, generated_masks: np.ndarray, reference_masks: np.ndarray, reference_take2_masks: np.ndarray)</code></pre>


<details><summary>Source</summary><pre><code>@dataclass(frozen=True)
class PreparedPhysicsIQTriplet:
    generated_quarter: np.ndarray
    reference_quarter: np.ndarray
    reference_take2_quarter: np.ndarray
    generated_masks: np.ndarray
    reference_masks: np.ndarray
    reference_take2_masks: np.ndarray</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.tensor_to_uint8_frames"><code>fastvideo.eval.metrics.physics_iq.utils.tensor_to_uint8_frames</code></h2>
<pre class="doc-signature"><code>tensor_to_uint8_frames(video: torch.Tensor) -&gt; np.ndarray</code></pre>

<details><summary>Source</summary><pre><code>def tensor_to_uint8_frames(video: torch.Tensor) -&gt; np.ndarray:
    arr = video.detach().cpu().float().clamp(0, 1).permute(0, 2, 3, 1).numpy()
    return np.clip(np.rint(arr * 255.0), 0, 255).astype(np.uint8)</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.read_video_frames"><code>fastvideo.eval.metrics.physics_iq.utils.read_video_frames</code></h2>
<pre class="doc-signature"><code>read_video_frames(source: str | Path, *, start_frame: int = 0, end_frame: int | None = None) -&gt; np.ndarray</code></pre>

<details><summary>Source</summary><pre><code>def read_video_frames(
    source: str | Path,
    *,
    start_frame: int = 0,
    end_frame: int | None = None,
) -&gt; np.ndarray:
    cap = cv2.VideoCapture(str(source))
    if not cap.isOpened():
        raise FileNotFoundError(f&quot;Could not open video: &#123;source&#125;&quot;)

    frames: list&#91;np.ndarray&#93; = &#91;&#93;
    frame_idx = 0
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break
        if frame_idx &gt;= start_frame and (end_frame is None or frame_idx &lt; end_frame):
            frames.append(frame)
        if end_frame is not None and frame_idx &gt;= end_frame:
            break
        frame_idx += 1
    cap.release()

    if not frames:
        return np.zeros((0, 0, 0, 3), dtype=np.uint8)
    return np.stack(frames, axis=0)</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.as_numpy_video"><code>fastvideo.eval.metrics.physics_iq.utils.as_numpy_video</code></h2>
<pre class="doc-signature"><code>as_numpy_video(source: Any) -&gt; tuple&#91;np.ndarray, str&#93;</code></pre>

<details><summary>Source</summary><pre><code>def as_numpy_video(source: Any) -&gt; tuple&#91;np.ndarray, str&#93;:
    if isinstance(source, torch.Tensor):
        if source.ndim != 4:
            raise ValueError(f&quot;Expected 4D video tensor (T,C,H,W), got shape &#123;tuple(source.shape)&#125;&quot;)
        return tensor_to_uint8_frames(source), &quot;rgb&quot;
    if isinstance(source, np.ndarray):
        if source.ndim != 4:
            raise ValueError(f&quot;Expected 4D ndarray video, got shape &#123;source.shape&#125;&quot;)
        if source.shape&#91;-1&#93; == 3:
            return source.astype(np.uint8), &quot;rgb&quot;
        if source.shape&#91;1&#93; == 3:
            return np.transpose(source, (0, 2, 3, 1)).astype(np.uint8), &quot;rgb&quot;
        raise ValueError(f&quot;Unsupported ndarray video shape: &#123;source.shape&#125;&quot;)
    if isinstance(source, str | Path):
        return read_video_frames(source), &quot;bgr&quot;
    raise TypeError(f&quot;Unsupported Physics-IQ video source type: &#123;type(source)!r&#125;&quot;)</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.prepare_pair"><code>fastvideo.eval.metrics.physics_iq.utils.prepare_pair</code></h2>
<pre class="doc-signature"><code>prepare_pair(sample: dict&#91;str, Any&#93;, *, prep_kwargs: dict&#91;str, Any&#93; | None = None) -&gt; PreparedPhysicsIQPair</code></pre>
<p>Resolve a sample into a prepared (gen, ref) pair.</p>
<p>Caches the result on <code>sample['_physics_iq_pair']</code> so other physics_iq
sub-metrics on the same sample reuse it instead of re-decoding.</p>
<details><summary>Source</summary><pre><code>def prepare_pair(
    sample: dict&#91;str, Any&#93;,
    *,
    prep_kwargs: dict&#91;str, Any&#93; | None = None,
) -&gt; PreparedPhysicsIQPair:
    &quot;&quot;&quot;Resolve a sample into a prepared (gen, ref) pair.

    Caches the result on ``sample&#91;&#x27;_physics_iq_pair&#x27;&#93;`` so other physics_iq
    sub-metrics on the same sample reuse it instead of re-decoding.
    &quot;&quot;&quot;
    prepared = sample.get(&quot;_physics_iq_pair&quot;)
    if prepared is not None:
        return prepared

    if &quot;reference&quot; not in sample:
        raise KeyError(&quot;Physics-IQ pair metrics require sample&#91;&#x27;reference&#x27;&#93;.&quot;)

    prepared = prepare_pair_inputs(
        sample&#91;&quot;video&quot;&#93;,
        sample&#91;&quot;reference&quot;&#93;,
        generated_mask=sample.get(&quot;video_mask&quot;),
        reference_mask=sample.get(&quot;reference_mask&quot;),
        **(prep_kwargs or &#123;&#125;),
    )
    sample&#91;&quot;_physics_iq_pair&quot;&#93; = prepared
    return prepared</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.select_window"><code>fastvideo.eval.metrics.physics_iq.utils.select_window</code></h2>
<pre class="doc-signature"><code>select_window(frames: np.ndarray, *, target_frames: int, selection: str = &#x27;first&#x27;) -&gt; np.ndarray</code></pre>

<details><summary>Source</summary><pre><code>def select_window(frames: np.ndarray, *, target_frames: int, selection: str = &quot;first&quot;) -&gt; np.ndarray:
    if selection != &quot;first&quot;:
        start = max(frames.shape&#91;0&#93; - target_frames, 0)
        return frames&#91;start:start + target_frames&#93;
    return frames&#91;:target_frames&#93;</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.resize_frames"><code>fastvideo.eval.metrics.physics_iq.utils.resize_frames</code></h2>
<pre class="doc-signature"><code>resize_frames(frames: np.ndarray, target_size: tuple&#91;int, int&#93;) -&gt; np.ndarray</code></pre>

<details><summary>Source</summary><pre><code>def resize_frames(frames: np.ndarray, target_size: tuple&#91;int, int&#93;) -&gt; np.ndarray:
    if frames.size == 0:
        return frames
    resized = &#91;cv2.resize(frame, target_size) for frame in frames&#93;
    return np.stack(resized, axis=0)</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.rebinarize_masks"><code>fastvideo.eval.metrics.physics_iq.utils.rebinarize_masks</code></h2>
<pre class="doc-signature"><code>rebinarize_masks(mask_frames: np.ndarray) -&gt; np.ndarray</code></pre>

<details><summary>Source</summary><pre><code>def rebinarize_masks(mask_frames: np.ndarray) -&gt; np.ndarray:
    if mask_frames.ndim == 4 and mask_frames.shape&#91;-1&#93; == 3:
        mask_frames = mask_frames&#91;..., 0&#93;
    return (mask_frames &gt; 127).astype(np.uint8)</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.load_mask_frames"><code>fastvideo.eval.metrics.physics_iq.utils.load_mask_frames</code></h2>
<pre class="doc-signature"><code>load_mask_frames(mask_source: Any, *, target_frames: int, target_size: tuple&#91;int, int&#93;) -&gt; np.ndarray</code></pre>

<details><summary>Source</summary><pre><code>def load_mask_frames(
    mask_source: Any,
    *,
    target_frames: int,
    target_size: tuple&#91;int, int&#93;,
) -&gt; np.ndarray:
    if mask_source is None:
        raise ValueError(&quot;mask_source cannot be None when loading mask frames&quot;)
    mask_frames, _ = as_numpy_video(mask_source)
    mask_frames = select_window(mask_frames, target_frames=target_frames, selection=&quot;first&quot;)
    mask_frames = resize_frames(mask_frames, target_size)
    return rebinarize_masks(mask_frames)</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.roundtrip_mask_frames"><code>fastvideo.eval.metrics.physics_iq.utils.roundtrip_mask_frames</code></h2>
<pre class="doc-signature"><code>roundtrip_mask_frames(mask_frames: np.ndarray, *, fps: int) -&gt; np.ndarray</code></pre>

<details><summary>Source</summary><pre><code>def roundtrip_mask_frames(mask_frames: np.ndarray, *, fps: int) -&gt; np.ndarray:
    if mask_frames.size == 0:
        return mask_frames
    fd, tmp_path = tempfile.mkstemp(suffix=&quot;.mp4&quot;)
    os.close(fd)
    output_path = Path(tmp_path)
    output_path.unlink(missing_ok=True)
    try:
        writer = cv2.VideoWriter(
            str(output_path),
            cv2.VideoWriter_fourcc(*&quot;mp4v&quot;),
            fps,
            (mask_frames.shape&#91;2&#93;, mask_frames.shape&#91;1&#93;),
            isColor=False,
        )
        for frame in mask_frames:
            writer.write(frame)
        writer.release()
        return read_video_frames(output_path)
    finally:
        output_path.unlink(missing_ok=True)</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.infer_real_mask_path"><code>fastvideo.eval.metrics.physics_iq.utils.infer_real_mask_path</code></h2>
<pre class="doc-signature"><code>infer_real_mask_path(video_source: Any) -&gt; str | None</code></pre>

<details><summary>Source</summary><pre><code>def infer_real_mask_path(video_source: Any) -&gt; str | None:
    if not isinstance(video_source, str | Path):
        return None

    video_path = Path(video_source)
    filename = video_path.name
    if &quot;_testing-videos_&quot; not in filename:
        return None

    mask_name = filename.replace(&quot;_testing-videos_&quot;, &quot;_video-masks_&quot;)
    candidates: list&#91;Path&#93; = &#91;&#93;
    fps_dir = video_path.parent.name
    for ancestor in video_path.parents:
        if ancestor.name == &quot;split-videos&quot;:
            candidates.extend(&#91;
                ancestor.parent / &quot;video-masks&quot; / &quot;real&quot; / fps_dir / mask_name,
                ancestor.parent / &quot;video_masks&quot; / &quot;real&quot; / fps_dir / mask_name,
            &#93;)
            break

    for candidate in candidates:
        if candidate.exists():
            return str(candidate)
    return None</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.prepare_grayscale_frame"><code>fastvideo.eval.metrics.physics_iq.utils.prepare_grayscale_frame</code></h2>
<pre class="doc-signature"><code>prepare_grayscale_frame(frame: np.ndarray, *, color_order: str) -&gt; np.ndarray</code></pre>

<details><summary>Source</summary><pre><code>def prepare_grayscale_frame(frame: np.ndarray, *, color_order: str) -&gt; np.ndarray:
    if frame.ndim == 2:
        gray = frame
    elif color_order == &quot;bgr&quot;:
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    elif color_order == &quot;rgb&quot;:
        gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
    else:
        raise ValueError(f&quot;Unsupported color order: &#123;color_order&#125;&quot;)
    return cv2.GaussianBlur(gray, (5, 5), 0)</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.generate_motion_mask"><code>fastvideo.eval.metrics.physics_iq.utils.generate_motion_mask</code></h2>
<pre class="doc-signature"><code>generate_motion_mask(video_frames: np.ndarray, *, threshold: int = 10, alpha: float = 0.3, color_order: str = &#x27;rgb&#x27;) -&gt; np.ndarray</code></pre>

<details><summary>Source</summary><pre><code>def generate_motion_mask(
    video_frames: np.ndarray,
    *,
    threshold: int = 10,
    alpha: float = 0.3,
    color_order: str = &quot;rgb&quot;,
) -&gt; np.ndarray:
    if video_frames.size == 0:
        return np.zeros((0, 0, 0), dtype=np.uint8)

    first_gray = prepare_grayscale_frame(video_frames&#91;0&#93;, color_order=color_order)
    avg_frame = first_gray.astype(&quot;float&quot;)
    masks = &#91;np.zeros_like(first_gray, dtype=np.uint8)&#93;
    kernel = np.ones((5, 5), np.uint8)

    for frame in video_frames&#91;1:&#93;:
        gray_frame = prepare_grayscale_frame(frame, color_order=color_order)
        cv2.accumulateWeighted(gray_frame, avg_frame, alpha)
        avg_gray_frame = cv2.convertScaleAbs(avg_frame)
        frame_diff = cv2.absdiff(gray_frame, avg_gray_frame)
        _, binary_frame = cv2.threshold(frame_diff, threshold, 255, cv2.THRESH_BINARY)
        binary_frame = cv2.morphologyEx(binary_frame, cv2.MORPH_OPEN, kernel)
        binary_frame = cv2.morphologyEx(binary_frame, cv2.MORPH_CLOSE, kernel)
        masks.append(binary_frame)
    return np.stack(masks, axis=0)</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.compute_iou"><code>fastvideo.eval.metrics.physics_iq.utils.compute_iou</code></h2>
<pre class="doc-signature"><code>compute_iou(mask1: np.ndarray, mask2: np.ndarray) -&gt; float</code></pre>

<details><summary>Source</summary><pre><code>def compute_iou(mask1: np.ndarray, mask2: np.ndarray) -&gt; float:
    intersection = np.logical_and(mask1, mask2).sum()
    union = np.logical_or(mask1, mask2).sum()
    if union == 0:
        return 1.0
    return float(intersection / union)</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.compute_mse"><code>fastvideo.eval.metrics.physics_iq.utils.compute_mse</code></h2>
<pre class="doc-signature"><code>compute_mse(video1_frames: np.ndarray, video2_frames: np.ndarray) -&gt; list&#91;float&#93;</code></pre>

<details><summary>Source</summary><pre><code>def compute_mse(video1_frames: np.ndarray, video2_frames: np.ndarray) -&gt; list&#91;float&#93;:
    if len(video1_frames) != len(video2_frames):
        raise ValueError(&quot;Videos must have the same number of frames.&quot;)
    frame_mses: list&#91;float&#93; = &#91;&#93;
    for frame1, frame2 in zip(video1_frames, video2_frames, strict=False):
        if frame1.shape != frame2.shape:
            raise ValueError(&quot;Frames must have the same dimensions.&quot;)
        mse = np.mean((frame1.astype(np.float32) - frame2.astype(np.float32))**2)
        frame_mses.append(round(float(mse), 4))
    return frame_mses</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.compute_spatiotemporal_iou"><code>fastvideo.eval.metrics.physics_iq.utils.compute_spatiotemporal_iou</code></h2>
<pre class="doc-signature"><code>compute_spatiotemporal_iou(mask1_frames: np.ndarray, mask2_frames: np.ndarray) -&gt; list&#91;float&#93;</code></pre>

<details><summary>Source</summary><pre><code>def compute_spatiotemporal_iou(mask1_frames: np.ndarray, mask2_frames: np.ndarray) -&gt; list&#91;float&#93;:
    values: list&#91;float&#93; = &#91;&#93;
    for mask1, mask2 in zip(mask1_frames, mask2_frames, strict=False):
        values.append(round(compute_iou(mask1, mask2), 4))
    return values</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.compute_spatial_iou"><code>fastvideo.eval.metrics.physics_iq.utils.compute_spatial_iou</code></h2>
<pre class="doc-signature"><code>compute_spatial_iou(mask1_frames: np.ndarray, mask2_frames: np.ndarray) -&gt; float</code></pre>

<details><summary>Source</summary><pre><code>def compute_spatial_iou(mask1_frames: np.ndarray, mask2_frames: np.ndarray) -&gt; float:
    spatial_mask1 = (np.max(mask1_frames, axis=0) &gt; 0).astype(np.uint8) * 255
    spatial_mask2 = (np.max(mask2_frames, axis=0) &gt; 0).astype(np.uint8) * 255
    return compute_iou(spatial_mask1, spatial_mask2)</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.compute_weighted_spatial_iou"><code>fastvideo.eval.metrics.physics_iq.utils.compute_weighted_spatial_iou</code></h2>
<pre class="doc-signature"><code>compute_weighted_spatial_iou(mask1_frames: np.ndarray, mask2_frames: np.ndarray) -&gt; float</code></pre>

<details><summary>Source</summary><pre><code>def compute_weighted_spatial_iou(mask1_frames: np.ndarray, mask2_frames: np.ndarray) -&gt; float:
    weighted_spatial_1 = np.sum(mask1_frames, axis=0, dtype=np.uint16) / len(mask1_frames)
    weighted_spatial_2 = np.sum(mask2_frames, axis=0, dtype=np.uint16) / len(mask2_frames)
    intersection = np.minimum(weighted_spatial_1, weighted_spatial_2)
    union = np.maximum(weighted_spatial_1, weighted_spatial_2)
    valid_pixels = union &gt; 0
    if np.sum(valid_pixels) == 0:
        return 1.0
    return float(np.sum(intersection&#91;valid_pixels&#93;) / np.sum(union&#91;valid_pixels&#93;))</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.mean"><code>fastvideo.eval.metrics.physics_iq.utils.mean</code></h2>
<pre class="doc-signature"><code>mean(values: list&#91;float&#93; | tuple&#91;float, ...&#93;) -&gt; float</code></pre>

<details><summary>Source</summary><pre><code>def mean(values: list&#91;float&#93; | tuple&#91;float, ...&#93;) -&gt; float:
    arr = np.asarray(list(values), dtype=np.float64)
    if arr.size == 0:
        raise ValueError(&quot;Cannot aggregate empty Physics-IQ values.&quot;)
    return float(arr.mean())</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.quarter_resolution_target"><code>fastvideo.eval.metrics.physics_iq.utils.quarter_resolution_target</code></h2>
<pre class="doc-signature"><code>quarter_resolution_target(reference_frames: np.ndarray) -&gt; tuple&#91;int, int&#93;</code></pre>

<details><summary>Source</summary><pre><code>def quarter_resolution_target(reference_frames: np.ndarray) -&gt; tuple&#91;int, int&#93;:
    return (
        max(reference_frames&#91;0&#93;.shape&#91;1&#93; // 4, 1),
        max(reference_frames&#91;0&#93;.shape&#91;0&#93; // 4, 1),
    )</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.prepare_pair_inputs"><code>fastvideo.eval.metrics.physics_iq.utils.prepare_pair_inputs</code></h2>
<pre class="doc-signature"><code>prepare_pair_inputs(generated: Any, reference: Any, *, generated_mask: Any | None = None, reference_mask: Any | None = None, target_fps: int = DEFAULT_TARGET_FPS, duration_seconds: int = DEFAULT_DURATION_SECONDS, video_time_selection: str = &#x27;first&#x27;, threshold: int = 10, alpha: float = 0.3, roundtrip_generated_masks: bool = True) -&gt; PreparedPhysicsIQPair</code></pre>

<details><summary>Source</summary><pre><code>def prepare_pair_inputs(
    generated: Any,
    reference: Any,
    *,
    generated_mask: Any | None = None,
    reference_mask: Any | None = None,
    target_fps: int = DEFAULT_TARGET_FPS,
    duration_seconds: int = DEFAULT_DURATION_SECONDS,
    video_time_selection: str = &quot;first&quot;,
    threshold: int = 10,
    alpha: float = 0.3,
    roundtrip_generated_masks: bool = True,
) -&gt; PreparedPhysicsIQPair:
    generated_frames, generated_color = as_numpy_video(generated)
    reference_frames, reference_color = as_numpy_video(reference)
    consider_frames = target_fps * duration_seconds

    generated_frames = select_window(
        generated_frames,
        target_frames=consider_frames,
        selection=video_time_selection,
    )
    reference_frames = reference_frames&#91;:consider_frames&#93;
    if not len(generated_frames) or not len(reference_frames):
        raise ValueError(&quot;Physics-IQ pair metrics require non-empty generated and reference videos.&quot;)

    target_size = quarter_resolution_target(reference_frames)
    reference_mask = reference_mask or infer_real_mask_path(reference)

    generated_quarter = resize_frames(generated_frames, target_size).astype(np.float32) / 255.0
    reference_quarter = resize_frames(reference_frames, target_size).astype(np.float32) / 255.0

    generated_masks = (load_mask_frames(generated_mask, target_frames=consider_frames, target_size=target_size)
                       if generated_mask is not None else rebinarize_masks(
                           resize_frames(
                               roundtrip_mask_frames(
                                   generate_motion_mask(
                                       generated_frames,
                                       threshold=threshold,
                                       alpha=alpha,
                                       color_order=generated_color,
                                   ),
                                   fps=target_fps,
                               ) if roundtrip_generated_masks else generate_motion_mask(
                                   generated_frames,
                                   threshold=threshold,
                                   alpha=alpha,
                                   color_order=generated_color,
                               ),
                               target_size,
                           )))
    reference_masks = (load_mask_frames(reference_mask, target_frames=consider_frames, target_size=target_size)
                       if reference_mask is not None else rebinarize_masks(
                           resize_frames(
                               generate_motion_mask(
                                   reference_frames,
                                   threshold=threshold,
                                   alpha=alpha,
                                   color_order=reference_color,
                               ),
                               target_size,
                           )))
    return PreparedPhysicsIQPair(
        generated_quarter=generated_quarter,
        reference_quarter=reference_quarter,
        generated_masks=generated_masks,
        reference_masks=reference_masks,
    )</code></pre></details>
<h2 id="fastvideo.eval.metrics.physics_iq.utils.prepare_triplet_inputs"><code>fastvideo.eval.metrics.physics_iq.utils.prepare_triplet_inputs</code></h2>
<pre class="doc-signature"><code>prepare_triplet_inputs(generated: Any, reference: Any, reference_take2: Any, *, generated_mask: Any | None = None, reference_mask: Any | None = None, reference_take2_mask: Any | None = None, target_fps: int = DEFAULT_TARGET_FPS, duration_seconds: int = DEFAULT_DURATION_SECONDS, video_time_selection: str = &#x27;first&#x27;, threshold: int = 10, alpha: float = 0.3, roundtrip_generated_masks: bool = True) -&gt; PreparedPhysicsIQTriplet</code></pre>

<details><summary>Source</summary><pre><code>def prepare_triplet_inputs(
    generated: Any,
    reference: Any,
    reference_take2: Any,
    *,
    generated_mask: Any | None = None,
    reference_mask: Any | None = None,
    reference_take2_mask: Any | None = None,
    target_fps: int = DEFAULT_TARGET_FPS,
    duration_seconds: int = DEFAULT_DURATION_SECONDS,
    video_time_selection: str = &quot;first&quot;,
    threshold: int = 10,
    alpha: float = 0.3,
    roundtrip_generated_masks: bool = True,
) -&gt; PreparedPhysicsIQTriplet:
    pair = prepare_pair_inputs(
        generated,
        reference,
        generated_mask=generated_mask,
        reference_mask=reference_mask,
        target_fps=target_fps,
        duration_seconds=duration_seconds,
        video_time_selection=video_time_selection,
        threshold=threshold,
        alpha=alpha,
        roundtrip_generated_masks=roundtrip_generated_masks,
    )
    reference_take2_frames, reference_take2_color = as_numpy_video(reference_take2)
    consider_frames = target_fps * duration_seconds
    reference_take2_frames = reference_take2_frames&#91;:consider_frames&#93;
    if not len(reference_take2_frames):
        raise ValueError(&quot;Physics-IQ requires a non-empty take-2 reference video.&quot;)

    target_size = (pair.reference_quarter.shape&#91;2&#93;, pair.reference_quarter.shape&#91;1&#93;)
    reference_take2_mask = reference_take2_mask or infer_real_mask_path(reference_take2)
    reference_take2_quarter = resize_frames(reference_take2_frames, target_size).astype(np.float32) / 255.0
    reference_take2_masks = (load_mask_frames(
        reference_take2_mask, target_frames=consider_frames, target_size=target_size)
                             if reference_take2_mask is not None else rebinarize_masks(
                                 resize_frames(
                                     generate_motion_mask(
                                         reference_take2_frames,
                                         threshold=threshold,
                                         alpha=alpha,
                                         color_order=reference_take2_color,
                                     ),
                                     target_size,
                                 )))
    return PreparedPhysicsIQTriplet(
        generated_quarter=pair.generated_quarter,
        reference_quarter=pair.reference_quarter,
        reference_take2_quarter=reference_take2_quarter,
        generated_masks=pair.generated_masks,
        reference_masks=pair.reference_masks,
        reference_take2_masks=reference_take2_masks,
    )</code></pre></details>
</div>
