Source code for cyto.postprocessing.interaction_detection

import math
from typing import Any
from tqdm import tqdm
from skimage import (
    color, feature, filters, measure, morphology, segmentation, util, exposure, data, io
)

[docs] def distance(p1, p2): return math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)
[docs] class DetectInteractions(): def __init__(self, verbose=True) -> None: """ Detect interactions between two masked images Args: verbose (bool): Turn on or off the processing printout """ self.verbose = verbose def __call__(self, data_target, data_other) -> Any: """ Args: data_target: Target cells data_other: Interacting cells (e.g. T cells) """ labels_target = data_target["labels"] labels_other = data_other["labels"] df_target = data_target["features"] df_other = data_other["features"] touchingCol = [] for frameNum in tqdm(range(len(labels_target))): df_target_frame = df_target[df_target["frame"] == frameNum] df_other_frame = df_other[df_other["frame"] == frameNum] labels_target_frame = labels_target[frameNum] labels_other_frame = labels_other[frameNum] detected_target_cells = measure.regionprops(labels_target_frame) for index, row in df_target_frame.iterrows(): y, x, shape = row["y"], row["x"], row["shape"] centerX, centerY = row["bbox_xsize"]//2, row["bbox_ysize"]//2 left, right = row["bbox_xstart"], row["bbox_xstart"] + row["bbox_xsize"] bottom, top = row["bbox_ystart"], row["bbox_ystart"] + row["bbox_ysize"] labels_other_zone = labels_other_frame[bottom:top, left:right] labels_target_zone = labels_target_frame[bottom:top, left:right] labels_combined = labels_other_zone + labels_target_zone detected_cells_prior = measure.regionprops(labels_other_zone) detected_cells_post = measure.regionprops(labels_combined) before_cell_features = [] for p in detected_cells_prior: yP, xP = d.centroid before_cell_features.append((p.area, xP, yP)) after_cell_features = [] for a in detected_cells_post: yA, xA = a.centroid after_cell_features.append((a.area, xA, yA)) touching_features, touching_cells = [], [] for b in before_cell_features: if b not in after_cell_features: # Interaction Detected # touching_features.append(b) for t in touching_features: a, xT, yT = t # area, x, y label_touching = labels_other_zone[int(yT), int(xT)] for index, row in df_other_frame.iterrows(): if labels_other_frame[int(row["y"]), int(row["x"])] == label_touching: touching_cells.append(row["label"]) # check this is actually the cell's "Id" touchingCol.append(touching_cells) df_target["touching"] = touchingCol
[docs] class VideoInteractions(): def __init__(self, verbose=True) -> None: """ Export GIF of two cells' interactions Args: verbose (bool): Turn on or off the processing printout """ self.verbose = verbose def __call__(self, data_target, image_target, target_cell_label, data_interacting, image_interacting, interacting_cell_label, buffer_frames = 30, output_name = "", buffer_pixels = 20, fps = 15) -> Any: """ Args: data_target (dataframe): Target cells image_target (numpy array): Image of the Target Cells (across all frames) target_cell_label (int): label of the target cell of interest data_interacting: Interacting cells (e.g. T cells) image_interacting (numpy array): Image of the Target Cells (across all frames) interacting_cell_label (int): label of the cell interacting with the target cell buffer_frames (int): How many frames before and after the first interaction to show in the GIF output_name (string): Where to write the GIF to (exclude .gif extension) buffer_pixels (int): How many pixels to leave around the interaction fps (int): Frames per second in the GIF """ if output_name == "": output_name = f'Target={target_cell_label}_Interacting={interacting_cell_label}' labels_target = data_target["labels"] labels_interacting = data_interacting["labels"] df_target = data_target["features"] df_interacting = data_interacting["features"] interacting_cell_df = df_interacting[df_interacting['label'] == interacting_cell_label] target_cell_df = df_target[df_target['label'] == target_cell_label] y_interacting, x_interacting = list(interacting_cell_df['y']), list(interacting_cell_df['x']) xlims = [max(0, min(x_interacting) - buffer_pixels), min(image_interacting.shape[2], max(x_interacting) + buffer_pixels)] ylims = [max(0, min(y_interacting) - buffer_pixels), min(image_interacting.shape[1], max(y_interacting) + buffer_pixels)] target_dead_frames = [] for index, row in target_cell_df.iterrows(): if not row['alive']: target_dead_frames.append(row['frame']) snapshots = [] # frames for index, row in interacting_cell_df.iterrows(): y, x, shape, frame, touching = row["y"], row["x"], (row["bbox_ysize"], row["bbox_ysize"]), row["frame"], row['touching'] target_region = image_target[frame][ylims[0]:ylims[1], xlims[0]:xlims[1]] blank = np.zeros((target_region.shape)) interacting_region = blank.copy() bottom, top = y - ylims[0] - shape[0]//2, y - ylims[0] + shape[0]//2 left, right = x - xlims[0] - shape[1]//2, x - xlims[0] + shape[1]//2 interacting_region[bottom:top, left:right] = image_interacting[frame][y - shape[0]//2: y + shape[0]//2, x - shape[1]//2: x + shape[1]//2] snap = [target_region/np.max(target_region), interacting_region/np.max(interacting_region), blank.copy()] # RGB if len(touching[0]) > 0: snap[0] += snap[1] / np.max(snap[1]) # Color Transformation if int(frame) in [int(f) for f in target_cell_df['frame']]: target_cell_df_frame = target_cell_df[target_cell_df['frame'] == frame] y, x = int(target_cell_df_frame['y']), int(target_cell_df_frame['x']) maskValue = labels_target[frame][y, x] labels_target_frame = labels_target[frame][:] labels_target_frame = (labels_target_frame == maskValue).astype(int) labels_target_frame = labels_target_frame[ylims[0]:ylims[1], xlims[0]:xlims[1]] if frame in target_dead_frames: snap[0] += labels_target_frame/np.max(labels_target_frame)/2 snap[1] += labels_target_frame/np.max(labels_target_frame)/2 snap[2] += labels_target_frame/np.max(labels_target_frame)/2 else: snap[2] += labels_target_frame/np.max(labels_target_frame)/2 snapshots.append(np.transpose(snap, (1, 2, 0))) fig = plt.figure(figsize=(8,8)) plt.axis('off') a = snapshots[0] im = plt.imshow(a) def animate_func(i): if i % fps == 0: print( '.', end ='' ) im.set_array(snapshots[i]) return [im] fps=15 anim = animation.FuncAnimation( fig, animate_func, frames = len(snapshots) ) anim.save(f'{output_name}.gif', fps=fps, dpi=100, savefig_kwargs={'transparent': True, 'facecolor': 'none'})