Spaces:
Runtime error
Runtime error
File size: 5,906 Bytes
742e266 0020ddc 742e266 0020ddc 742e266 0020ddc 742e266 0020ddc 742e266 0020ddc 742e266 0020ddc 742e266 0020ddc 742e266 0020ddc 742e266 0020ddc 742e266 0020ddc 742e266 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | # Created: 2026-02-18
# Purpose: P(AI) per-segment timeline bar chart with waveform (plotly)
# Dependencies: plotly, numpy
"""Per-segment (chunk) AI probability timeline visualization with waveform."""
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from config import CHUNK_SEC, SR, CHUNK_SAMPLES
def plot_timeline(
chunk_probs: list[float],
waveform: np.ndarray = None,
chunk_metadata: list[dict] = None,
weighted_median: float = None
) -> go.Figure:
"""Per-chunk P(AI) timeline bar chart with optional waveform.
Args:
chunk_probs: P(AI) list for each 4-second chunk
waveform: Optional mono waveform array for envelope visualization
chunk_metadata: Optional metadata with start_sample info
weighted_median: Energy-weighted median P(AI) for reference line
Returns:
plotly Figure with waveform (top) + P(AI) bars (bottom)
"""
n = len(chunk_probs)
times = [f"{i * CHUNK_SEC:.0f}-{(i + 1) * CHUNK_SEC:.0f}s" for i in range(n)]
colors = ['#ff4757' if p >= 0.5 else '#2ed573' for p in chunk_probs]
# ํํ์ด ์์ผ๋ฉด subplot, ์์ผ๋ฉด ๋จ์ bar chart
if waveform is not None and len(waveform) > 0:
fig = make_subplots(
rows=2, cols=1,
row_heights=[0.3, 0.7],
vertical_spacing=0.08,
subplot_titles=("Waveform Envelope", "Segment-level AI Probability"),
)
# Waveform envelope (unipolar - ์ ๋๊ฐ์ ์๋จ๋ง)
time_axis = np.arange(len(waveform)) / SR
envelope = np.abs(waveform)
# Downsample for plotting (๋งค 100 ์ํ๋ง๋ค)
downsample_factor = 100
time_ds = time_axis[::downsample_factor]
envelope_ds = envelope[::downsample_factor]
fig.add_trace(
go.Scatter(
x=time_ds,
y=envelope_ds,
mode='lines',
line=dict(color='#5f9ea0', width=0.5),
fill='tozeroy',
fillcolor='rgba(95, 158, 160, 0.3)',
name='Envelope',
hovertemplate="Time: %{x:.2f}s<br>Amplitude: %{y:.3f}<extra></extra>",
),
row=1, col=1
)
# ์ธ๊ทธ๋จผํธ ๊ฒฝ๊ณ์ ํ์ (chunk metadata ์ฌ์ฉ)
if chunk_metadata:
for meta in chunk_metadata:
start_sec = meta['start_sample'] / SR
fig.add_vline(
x=start_sec,
line=dict(color='#ffa502', width=1, dash='dot'),
opacity=0.5,
row=1, col=1
)
# P(AI) bar chart
fig.add_trace(
go.Bar(
x=list(range(n)),
y=chunk_probs,
marker_color=colors,
text=[f"{p:.2f}" for p in chunk_probs],
textposition='outside',
textfont=dict(size=10, color='white'),
hovertemplate="<b>%{customdata}</b><br>P(AI): %{y:.3f}<extra></extra>",
customdata=times,
name='P(AI)',
),
row=2, col=1
)
# Energy-weighted median reference line
if weighted_median is not None:
fig.add_hline(
y=weighted_median, line_dash="dash", line_color="#00d2ff",
annotation_text=f"Weighted Median ({weighted_median:.2f})",
annotation_position="top right",
annotation_font_color="#00d2ff",
annotation_font_size=10,
row=2, col=1
)
# Layout
fig.update_xaxes(title_text="Time (s)", row=1, col=1)
fig.update_yaxes(title_text="Amplitude", row=1, col=1)
fig.update_xaxes(
title_text="Segment",
tickvals=list(range(n)),
ticktext=times,
tickangle=-45,
tickfont=dict(size=9),
row=2, col=1
)
fig.update_yaxes(title_text="P(AI)", range=[0, 1.05], row=2, col=1)
fig.update_layout(
plot_bgcolor='#1a1a2e',
paper_bgcolor='#1a1a2e',
font=dict(color='white'),
margin=dict(l=50, r=20, t=60, b=60),
height=500,
showlegend=False,
)
else:
# Fallback: ๊ธฐ์กด ๋จ์ bar chart
fig = go.Figure()
fig.add_trace(go.Bar(
x=list(range(n)),
y=chunk_probs,
marker_color=colors,
text=[f"{p:.2f}" for p in chunk_probs],
textposition='outside',
textfont=dict(size=10, color='white'),
hovertemplate="<b>%{customdata}</b><br>P(AI): %{y:.3f}<extra></extra>",
customdata=times,
))
if weighted_median is not None:
fig.add_hline(y=weighted_median, line_dash="dash", line_color="#00d2ff",
annotation_text=f"Weighted Median ({weighted_median:.2f})",
annotation_position="top right",
annotation_font_color="#00d2ff")
fig.update_layout(
title=dict(text="Segment-level AI Probability", font=dict(size=14)),
xaxis=dict(
title="Segment",
tickvals=list(range(n)),
ticktext=times,
tickangle=-45,
tickfont=dict(size=9),
),
yaxis=dict(title="P(AI)", range=[0, 1.05]),
plot_bgcolor='#1a1a2e',
paper_bgcolor='#1a1a2e',
font=dict(color='white'),
margin=dict(l=50, r=20, t=40, b=60),
height=300,
showlegend=False,
)
return fig
|