import numpy as np SPEAKER_COLORS = [ "#76B900", # Green "#49A4DE", # Blue "#E06C75", # Red "#C678DD", # Purple "#E5A84B", # Orange "#36CFC9", # Cyan "#FF7AB2", # Pink "#F5F5F5", # White ] def array_to_gradient_html(arr=None, num_frames=125, num_of_speakers=4, partitions=None): """ Convert num_of_speakers by num_frames numpy array to HTML gradient boxes. Parameters: ----------- arr : numpy.ndarray, optional num_of_speakers by num_frames array with values from 0.0 to 1.0 Shape: (num_of_speakers, num_frames) If None, creates a default random array num_frames : int, default 125 Number of frames/columns (only used if arr is None) num_of_speakers : int, default 4 Number of speakers/rows (only used if arr is None) partitions : list of dict, optional Named activity arrays rendered as proportional sections. Each dictionary contains ``name``, ``values``, ``capacity``, and optionally ``valid_frames``. Returns: -------- str : HTML string with colored gradient boxes Example: -------- >>> arr = np.array([[0.0, 0.25, 0.5, 0.75, 1.0]]) # 1 speaker, 5 frames >>> html = array_to_gradient_html(arr) >>> print(html) """ # Color map for different speakers (black background to full color) # Matching the app.py color scheme for dark theme color_map = { speaker_idx: tuple(int(color[offset : offset + 2], 16) for offset in (1, 3, 5)) for speaker_idx, color in enumerate(SPEAKER_COLORS) } def value_to_color(value, speaker_idx): """ Convert a value from 0.0 to 1.0 to a color gradient for a specific speaker. 0.0 -> #000000 (black background) 1.0 -> speaker's color (green, blue, gray, or red) """ # Clamp value between 0 and 1 value = max(0.0, min(1.0, value)) # Get speaker's target color (cycle through colors if more speakers than colors) speaker_color = color_map.get(speaker_idx % len(color_map), (0, 0, 0)) target_r, target_g, target_b = speaker_color # Gradient from black (0, 0, 0) to speaker's color # Linear interpolation between black and target color r = int(target_r * value) g = int(target_g * value) b = int(target_b * value) return f"#{r:02X}{g:02X}{b:02X}" if partitions is None: if arr is None: arr = np.random.rand(num_of_speakers, num_frames) partitions = [{"name": "DIARIZATION", "values": arr, "capacity": num_frames}] prepared_partitions = [] for partition in partitions: capacity = max(1, int(partition["capacity"])) values = partition.get("values") if values is None: values = np.zeros((num_of_speakers, 0), dtype=np.float32) values = np.asarray(values) if values.ndim != 2: raise ValueError(f"Expected a 2-D speaker activity array, but received shape {values.shape}") if values.shape[0] < num_of_speakers: values = np.pad(values, ((0, num_of_speakers - values.shape[0]), (0, 0)), mode='constant') else: values = values[:num_of_speakers] if values.shape[1] < capacity: values = np.pad(values, ((0, 0), (capacity - values.shape[1], 0)), mode='constant') else: values = values[:, -capacity:] prepared_partitions.append( { "name": partition["name"], "values": values, "capacity": capacity, "valid_frames": min(int(partition.get("valid_frames", values.shape[1])), capacity), } ) column_weights = " ".join(f"{partition['capacity']}fr" for partition in prepared_partitions) html = '