Spaces:
Sleeping
Sleeping
color change pt2
Browse files
utils.py
CHANGED
|
@@ -39,60 +39,51 @@ TRANSPARENT_BG = dict(
|
|
| 39 |
# Speaker sample helpers
|
| 40 |
# ---------------------------------------------------------------------------
|
| 41 |
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
-
Strategy: generate a large pre-built palette of 30 colors using the full
|
| 46 |
-
360-degree hue wheel in two interleaved saturation/brightness tiers.
|
| 47 |
-
Tier 1 (even indices): high saturation, medium-high brightness — vivid colors.
|
| 48 |
-
Tier 2 (odd indices): medium saturation, high brightness — lighter pastels.
|
| 49 |
-
Interleaving means adjacent speakers in the list always come from different
|
| 50 |
-
tiers, maximizing perceptual distance even with many speakers.
|
| 51 |
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
-
|
|
|
|
| 57 |
"""
|
| 58 |
if n == 0:
|
| 59 |
return []
|
| 60 |
-
|
| 61 |
-
PALETTE_SIZE = 30
|
| 62 |
-
# Build the full palette at a neutral starting point
|
| 63 |
-
palette = []
|
| 64 |
-
tiers = [
|
| 65 |
-
(220, 200), # tier 1: vivid — HSV saturation=220, value=200
|
| 66 |
-
(140, 230), # tier 2: pastel — HSV saturation=140, value=230
|
| 67 |
-
]
|
| 68 |
-
for i in range(PALETTE_SIZE):
|
| 69 |
-
hue = int(i * 360 / PALETTE_SIZE) % 360
|
| 70 |
-
# OpenCV HSV: hue is 0-179 (half degrees), so divide by 2
|
| 71 |
-
h_cv = hue // 2
|
| 72 |
-
sat, val = tiers[i % 2]
|
| 73 |
-
hsv = np.uint8([[[h_cv, sat, val]]])
|
| 74 |
-
bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
|
| 75 |
-
b = f'{bgr[0][0][0].item():02x}'
|
| 76 |
-
g = f'{bgr[0][0][1].item():02x}'
|
| 77 |
-
r = f'{bgr[0][0][2].item():02x}'
|
| 78 |
-
palette.append('#' + b + g + r)
|
| 79 |
-
|
| 80 |
-
# Apply rotation from startingHue so sessions get color variety
|
| 81 |
-
offset = 0
|
| 82 |
-
if startingHue is not None:
|
| 83 |
-
offset = int(startingHue / 180 * PALETTE_SIZE) % PALETTE_SIZE
|
| 84 |
-
|
| 85 |
-
# Pick n colors evenly spaced from the rotated palette
|
| 86 |
-
result = []
|
| 87 |
-
for i in range(min(n, PALETTE_SIZE)):
|
| 88 |
-
idx = (offset + int(i * PALETTE_SIZE / min(n, PALETTE_SIZE))) % PALETTE_SIZE
|
| 89 |
-
result.append(palette[idx])
|
| 90 |
-
|
| 91 |
-
# If n > PALETTE_SIZE (very unlikely), cycle
|
| 92 |
-
while len(result) < n:
|
| 93 |
-
result.extend(palette[:n - len(result)])
|
| 94 |
-
|
| 95 |
-
return result
|
| 96 |
|
| 97 |
|
| 98 |
def extract_clip_bytes(waveform, sample_rate, seg_start, seg_end):
|
|
@@ -345,6 +336,17 @@ def build_fig_sunburst(df5, catTypeColors, speakerColors, get_display_name_fn, c
|
|
| 345 |
df5 = df5.copy()
|
| 346 |
df5["labels"] = df5["labels"].apply(lambda s: get_display_name_fn(s, currFile))
|
| 347 |
df5["parentNames"] = df5["parentNames"].apply(lambda s: get_display_name_fn(s, currFile))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
fig = px.sunburst(
|
| 349 |
df5,
|
| 350 |
branchvalues="total",
|
|
@@ -353,7 +355,7 @@ def build_fig_sunburst(df5, catTypeColors, speakerColors, get_display_name_fn, c
|
|
| 353 |
custom_data=["labels", "valueStrings", "percentiles", "parentNames", "parentPercentiles"],
|
| 354 |
color="labels",
|
| 355 |
title="Percentage of each Voice Category with Speakers",
|
| 356 |
-
|
| 357 |
)
|
| 358 |
fig.update_traces(hovertemplate="<br>".join([
|
| 359 |
"<b>%{customdata[0]}</b>",
|
|
@@ -371,6 +373,16 @@ def build_fig_treemap(df5, catTypeColors, speakerColors, get_display_name_fn, cu
|
|
| 371 |
df5 = df5.copy()
|
| 372 |
df5["labels"] = df5["labels"].apply(lambda s: get_display_name_fn(s, currFile))
|
| 373 |
df5["parentNames"] = df5["parentNames"].apply(lambda s: get_display_name_fn(s, currFile))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 374 |
fig = px.treemap(
|
| 375 |
df5,
|
| 376 |
branchvalues="total",
|
|
@@ -379,7 +391,7 @@ def build_fig_treemap(df5, catTypeColors, speakerColors, get_display_name_fn, cu
|
|
| 379 |
custom_data=["labels", "valueStrings", "percentiles", "parentNames", "parentPercentiles"],
|
| 380 |
color="labels",
|
| 381 |
title="Division of Speakers in each Voice Category",
|
| 382 |
-
|
| 383 |
)
|
| 384 |
fig.update_traces(hovertemplate="<br>".join([
|
| 385 |
"<b>%{customdata[0]}</b>",
|
|
|
|
| 39 |
# Speaker sample helpers
|
| 40 |
# ---------------------------------------------------------------------------
|
| 41 |
|
| 42 |
+
# ---------------------------------------------------------------------------
|
| 43 |
+
# Fixed 24-color palette: 6 hues × 4 saturation/brightness tiers.
|
| 44 |
+
# Hue order: red, yellow, green, cyan, blue, magenta — the 6 perceptually
|
| 45 |
+
# distinct primaries. Cycling through all 6 hues before repeating a tier
|
| 46 |
+
# means no two adjacent speakers ever share a similar hue.
|
| 47 |
+
# Tiers (OpenCV HSV sat, val):
|
| 48 |
+
# 1 — vivid saturated (255, 200)
|
| 49 |
+
# 2 — medium bright (180, 230)
|
| 50 |
+
# 3 — deep dark (255, 140)
|
| 51 |
+
# 4 — pale tint (100, 240)
|
| 52 |
+
# The palette is interleaved hue-first so index 0=red-vivid, 1=yellow-vivid,
|
| 53 |
+
# 2=green-vivid ... 6=red-medium, 7=yellow-medium, etc.
|
| 54 |
+
# ---------------------------------------------------------------------------
|
| 55 |
+
def _build_palette():
|
| 56 |
+
# OpenCV HSV hue is 0-179 (half-degrees)
|
| 57 |
+
hues_deg = [0, 30, 60, 90, 120, 150] # red, yellow, green, cyan, blue, magenta
|
| 58 |
+
tiers = [(255, 200), (180, 230), (255, 140), (100, 240)]
|
| 59 |
+
palette = []
|
| 60 |
+
for sat, val in tiers:
|
| 61 |
+
for h_deg in hues_deg:
|
| 62 |
+
h_cv = h_deg # already 0-179 range
|
| 63 |
+
hsv = np.uint8([[[h_cv, sat, val]]])
|
| 64 |
+
bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
|
| 65 |
+
b = f'{bgr[0][0][0].item():02x}'
|
| 66 |
+
g = f'{bgr[0][0][1].item():02x}'
|
| 67 |
+
r = f'{bgr[0][0][2].item():02x}'
|
| 68 |
+
palette.append('#' + b + g + r)
|
| 69 |
+
return palette # 24 colors
|
| 70 |
+
|
| 71 |
+
_PALETTE = _build_palette()
|
| 72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
+
def colorsCSS(n, startingHue=None, pool=None):
|
| 75 |
+
"""Return n CSS hex colors drawn from the fixed 24-color palette.
|
| 76 |
+
|
| 77 |
+
Colors are taken in palette order (red→yellow→green→cyan→blue→magenta,
|
| 78 |
+
then repeating across brightness tiers) so adjacent speakers always have
|
| 79 |
+
maximally different hues. Cycles if n > 24.
|
| 80 |
|
| 81 |
+
startingHue and pool are accepted for backwards compatibility but ignored —
|
| 82 |
+
the palette is fixed so colors are always distinct and deterministic.
|
| 83 |
"""
|
| 84 |
if n == 0:
|
| 85 |
return []
|
| 86 |
+
return [_PALETTE[i % len(_PALETTE)] for i in range(n)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
|
| 88 |
|
| 89 |
def extract_clip_bytes(waveform, sample_rate, seg_start, seg_end):
|
|
|
|
| 336 |
df5 = df5.copy()
|
| 337 |
df5["labels"] = df5["labels"].apply(lambda s: get_display_name_fn(s, currFile))
|
| 338 |
df5["parentNames"] = df5["parentNames"].apply(lambda s: get_display_name_fn(s, currFile))
|
| 339 |
+
|
| 340 |
+
# Build an explicit label->color map so every node gets a guaranteed color
|
| 341 |
+
# regardless of encounter order. color_discrete_sequence is position-based
|
| 342 |
+
# and can silently drop nodes when label count exceeds sequence length.
|
| 343 |
+
top_labels = ["No Voice", "Single Voice", "Multi Voice"]
|
| 344 |
+
speaker_labels = [l for l in df5["labels"] if l not in top_labels]
|
| 345 |
+
color_map = {lbl: catTypeColors[i % len(catTypeColors)]
|
| 346 |
+
for i, lbl in enumerate(top_labels)}
|
| 347 |
+
for i, lbl in enumerate(speaker_labels):
|
| 348 |
+
color_map[lbl] = speakerColors[i % len(speakerColors)]
|
| 349 |
+
|
| 350 |
fig = px.sunburst(
|
| 351 |
df5,
|
| 352 |
branchvalues="total",
|
|
|
|
| 355 |
custom_data=["labels", "valueStrings", "percentiles", "parentNames", "parentPercentiles"],
|
| 356 |
color="labels",
|
| 357 |
title="Percentage of each Voice Category with Speakers",
|
| 358 |
+
color_discrete_map=color_map,
|
| 359 |
)
|
| 360 |
fig.update_traces(hovertemplate="<br>".join([
|
| 361 |
"<b>%{customdata[0]}</b>",
|
|
|
|
| 373 |
df5 = df5.copy()
|
| 374 |
df5["labels"] = df5["labels"].apply(lambda s: get_display_name_fn(s, currFile))
|
| 375 |
df5["parentNames"] = df5["parentNames"].apply(lambda s: get_display_name_fn(s, currFile))
|
| 376 |
+
|
| 377 |
+
# Same explicit color map as sunburst — avoids silent node drops from
|
| 378 |
+
# position-based color_discrete_sequence running out of colors.
|
| 379 |
+
top_labels = ["No Voice", "Single Voice", "Multi Voice"]
|
| 380 |
+
speaker_labels = [l for l in df5["labels"] if l not in top_labels]
|
| 381 |
+
color_map = {lbl: catTypeColors[i % len(catTypeColors)]
|
| 382 |
+
for i, lbl in enumerate(top_labels)}
|
| 383 |
+
for i, lbl in enumerate(speaker_labels):
|
| 384 |
+
color_map[lbl] = speakerColors[i % len(speakerColors)]
|
| 385 |
+
|
| 386 |
fig = px.treemap(
|
| 387 |
df5,
|
| 388 |
branchvalues="total",
|
|
|
|
| 391 |
custom_data=["labels", "valueStrings", "percentiles", "parentNames", "parentPercentiles"],
|
| 392 |
color="labels",
|
| 393 |
title="Division of Speakers in each Voice Category",
|
| 394 |
+
color_discrete_map=color_map,
|
| 395 |
)
|
| 396 |
fig.update_traces(hovertemplate="<br>".join([
|
| 397 |
"<b>%{customdata[0]}</b>",
|