Module emblaze.frame_colors
A helper module to compute HSV colors for each frame in an animated DR plot. The colors are chosen such that the perceptual distance between colors corresponds to the difference between the frames, with respect to some set of points of interest.
Functions
def compute_colors(frames, ids_of_interest=None, scale_factor=1.0)
-
Expand source code
def compute_colors(frames, ids_of_interest=None, scale_factor=1.0): """ Computes HSV colors for each frame. Args: frames: A list of Embeddings. ids_of_interest: A list of IDs to limit distance calculation to. If None, uses the full contents of each frame. scale_factor: Amount by which to scale the color wheel. Values larger than 1 effectively make the colors more saturated and appear more different. Returns: A list of HSV colors, expressed as tuples of (hue, saturation, value). """ distance_sample = ids_of_interest or frames[0].ids.tolist() if len(distance_sample) > 1000: distance_sample = np.random.choice(distance_sample, size=1000, replace=False).tolist() # First compute a distance matrix for the IDs for each frame outer_jaccard_distances = np.zeros((len(frames), len(frames))) inner_jaccard_distances = np.zeros((len(frames), len(frames))) for i in range(len(frames)): frame_1_neighbors = frames[i].get_recent_neighbors()[distance_sample] for j in range(len(frames)): frame_2_neighbors = frames[j].get_recent_neighbors()[distance_sample] # If the id set is the entire frame, there will be no outer neighbors # so we can just leave this at zero if ids_of_interest is not None and len(ids_of_interest): outer_jaccard_distances[i,j] = np.mean(inverse_intersection(frame_1_neighbors, frame_2_neighbors, List(distance_sample), True)) inner_jaccard_distances[i,j] = np.mean(inverse_intersection(frame_1_neighbors, frame_2_neighbors, List(distance_sample), False)) if ids_of_interest is not None and len(ids_of_interest): if len(ids_of_interest) == 1: distances = outer_jaccard_distances else: distances = 0.5 * (outer_jaccard_distances + inner_jaccard_distances) else: distances = inner_jaccard_distances # Compute clusteredness in each frame (only used to determine offset of colors) neighbor_dists = [np.log(1 + frame.distances(distance_sample, distance_sample).flatten()) for frame in frames] clusteredness = np.array([np.abs(ndists - np.mean(ndists)).mean() / np.maximum(np.max(ndists), 1e-3) for ndists in neighbor_dists]) # Compute an ordering using hierarchical clustering ordering_indexes = _clustered_ordering(distances) # Put the most cluster-y embedding first first_index = np.argmax(clusteredness) ordering_position = np.argmax(ordering_indexes == first_index) ordering_indexes = np.concatenate([ordering_indexes[ordering_position:], ordering_indexes[:ordering_position]]).astype(int) # Arrange the colors around a color wheel in the L*a*b* color space. offset = clusteredness[first_index] reduced = _arrange_around_circle(distances, offset, ordering_indexes) #, max_dist=np.array(neighbor_dists).mean()) # Generate colors in L*a*b* space and convert to HSL/HSV colors = [] for point in reduced: scaled_point = np.array([point[0] * 100.0 * scale_factor, point[1] * 100.0 * scale_factor]) lab = LabColor(70.0, scaled_point[1], scaled_point[0]) rgb = convert_color(lab, HSLColor) colors.append((int(rgb.hsl_h), int(rgb.hsl_s * 100.0), int(rgb.hsl_l * 100.0))) return colors
Computes HSV colors for each frame.
Args
frames
- A list of Embeddings.
ids_of_interest
- A list of IDs to limit distance calculation to. If None, uses the full contents of each frame.
scale_factor
- Amount by which to scale the color wheel. Values larger than 1 effectively make the colors more saturated and appear more different.
Returns
A list of HSV colors, expressed as tuples of (hue, saturation, value).