Spaces:
Running on Zero
Running on Zero
| 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 = '<div class="diar-partition-labels">' | |
| for partition in prepared_partitions: | |
| html += ( | |
| f'<span class="diar-partition-label">{partition["name"]} ' | |
| f'{partition["valid_frames"]}/{partition["capacity"]}</span>' | |
| ) | |
| html += '</div>' | |
| html += '<div class="diar-map">' | |
| for speaker_idx in range(num_of_speakers): | |
| html += ( | |
| f'<div class="diar-row" style="grid-template-columns:{column_weights};" ' | |
| f'title="Speaker {speaker_idx + 1}">' | |
| ) | |
| for partition in prepared_partitions: | |
| html += ( | |
| f'<div class="diar-partition" title="{partition["name"]}" ' | |
| f'style="grid-template-columns:repeat({partition["capacity"]}, minmax(0, 1fr));">' | |
| ) | |
| for val in partition["values"][speaker_idx]: | |
| color = value_to_color(val, speaker_idx) | |
| html += f'<span class="diar-cell" style="background-color:{color};"></span>' | |
| html += '</div>' | |
| html += '</div>' | |
| html += '</div>' | |
| return html | |
| if __name__ == "__main__": | |
| # Example 3: Default parameters | |
| print("Example 3: Using default parameters (4 speakers x 125 frames)") | |
| html3 = array_to_gradient_html() | |
| # print(html3[:500] + "...") # Print first 500 chars | |
| print(html3) | |
| print(f"\nTotal HTML length: {len(html3)} characters") | |
| # Example 4: Show the 20-step gradient like in the original example | |
| # print("\n" + "="*50 + "\n") | |
| # print("Example 4: 20-step gradient (like the original) - 1 speaker x 20 frames") | |
| # gradient_arr = np.linspace(0, 1, 20).reshape(1, 20) # Shape: (1, 20) | |
| # html4 = array_to_gradient_html(gradient_arr) | |
| # print(html4) | |