duongthienz commited on
Commit
6c31615
·
verified ·
1 Parent(s): b18daa3

color change pt2

Browse files
Files changed (1) hide show
  1. utils.py +62 -50
utils.py CHANGED
@@ -39,60 +39,51 @@ TRANSPARENT_BG = dict(
39
  # Speaker sample helpers
40
  # ---------------------------------------------------------------------------
41
 
42
- def colorsCSS(n, startingHue=None, pool=None):
43
- """Creates n visually distinct CSS hex colors for up to ~30 speakers.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- The palette is built once at a fixed seed for stability, then a random
53
- rotation is applied via startingHue so different sessions get variety
54
- while within a session all color pools stay coordinated.
 
 
 
55
 
56
- pool parameter is accepted but ignored — kept for backwards compatibility.
 
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
- color_discrete_sequence=catTypeColors + speakerColors,
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
- color_discrete_sequence=catTypeColors + speakerColors,
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>",