spacr 0.2.32__py3-none-any.whl → 0.2.45__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
spacr/plot.py CHANGED
@@ -19,6 +19,112 @@ from IPython.display import Image as ipyimage
19
19
 
20
20
  from .logger import log_function_call
21
21
 
22
+ def plot_image_mask_overlay(file, channels, cell_channel, nucleus_channel, pathogen_channel, figuresize=10, normalize=True, thickness=3, save_pdf=True):
23
+ """Plot image and mask overlays."""
24
+
25
+ def _plot_merged_plot(image, outlines, outline_colors, figuresize, thickness):
26
+ """Plot the merged plot with overlay, image channels, and masks."""
27
+
28
+ def _normalize_image(image, percentiles=(2, 98)):
29
+ """Normalize the image to the given percentiles."""
30
+ v_min, v_max = np.percentile(image, percentiles)
31
+ image_normalized = np.clip((image - v_min) / (v_max - v_min), 0, 1)
32
+ return image_normalized
33
+
34
+ def _generate_contours(mask):
35
+ """Generate contours for the given mask using OpenCV."""
36
+ contours, _ = cv2.findContours(mask.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
37
+ return contours
38
+
39
+ def _apply_contours(image, mask, color, thickness):
40
+ """Apply the contours to the RGB image for each unique label."""
41
+ unique_labels = np.unique(mask)
42
+ for label in unique_labels:
43
+ if label == 0:
44
+ continue # Skip background
45
+ label_mask = np.where(mask == label, 1, 0).astype(np.uint8)
46
+ contours = _generate_contours(label_mask)
47
+ for contour in contours:
48
+ cv2.drawContours(image, [contour], -1, mpl.colors.to_rgb(color), thickness)
49
+ return image
50
+
51
+ num_channels = image.shape[-1]
52
+ fig, ax = plt.subplots(1, num_channels + 1, figsize=(4 * figuresize, figuresize))
53
+
54
+ # Plot each channel with its corresponding outlines
55
+ for v in range(num_channels):
56
+ channel_image = image[..., v]
57
+ channel_image_normalized = _normalize_image(channel_image)
58
+ channel_image_rgb = np.dstack((channel_image_normalized, channel_image_normalized, channel_image_normalized))
59
+
60
+ for outline, color in zip(outlines, outline_colors):
61
+ channel_image_rgb = _apply_contours(channel_image_rgb, outline, color, thickness)
62
+
63
+ ax[v].imshow(channel_image_rgb)
64
+ ax[v].set_title(f'Image - Channel {v}')
65
+
66
+ # Plot the combined RGB image with all outlines
67
+ rgb_image = np.zeros((*image.shape[:2], 3), dtype=float)
68
+ rgb_channels = min(3, num_channels)
69
+ for i in range(rgb_channels):
70
+ channel_image = image[..., i]
71
+ channel_image_normalized = _normalize_image(channel_image)
72
+ rgb_image[..., i] = channel_image_normalized
73
+
74
+ for outline, color in zip(outlines, outline_colors):
75
+ rgb_image = _apply_contours(rgb_image, outline, color, thickness)
76
+
77
+ ax[-1].imshow(rgb_image)
78
+ ax[-1].set_title('Combined RGB Image')
79
+
80
+ plt.tight_layout()
81
+
82
+ # Save the figure as a PDF
83
+ if save_pdf:
84
+ pdf_dir = os.path.join(os.path.dirname(os.path.dirname(file)), 'results', 'overlay')
85
+ os.makedirs(pdf_dir, exist_ok=True)
86
+ pdf_path = os.path.join(pdf_dir, os.path.basename(file).replace('.npy', '.pdf'))
87
+ fig.savefig(pdf_path, format='pdf')
88
+
89
+ plt.show()
90
+ return fig
91
+
92
+ stack = np.load(file)
93
+
94
+ # Convert to float for normalization and ensure correct handling of both 8-bit and 16-bit arrays
95
+ if stack.dtype == np.uint16:
96
+ stack = stack.astype(np.float32)
97
+ elif stack.dtype == np.uint8:
98
+ stack = stack.astype(np.float32)
99
+
100
+ image = stack[..., channels]
101
+ outlines = []
102
+ outline_colors = []
103
+
104
+ if pathogen_channel is not None:
105
+ pathogen_mask_dim = -1 # last dimension
106
+ outlines.append(np.take(stack, pathogen_mask_dim, axis=2))
107
+ outline_colors.append('blue')
108
+
109
+ if nucleus_channel is not None:
110
+ nucleus_mask_dim = -2 if pathogen_channel is not None else -1
111
+ outlines.append(np.take(stack, nucleus_mask_dim, axis=2))
112
+ outline_colors.append('green')
113
+
114
+ if cell_channel is not None:
115
+ if nucleus_channel is not None and pathogen_channel is not None:
116
+ cell_mask_dim = -3
117
+ elif nucleus_channel is not None or pathogen_channel is not None:
118
+ cell_mask_dim = -2
119
+ else:
120
+ cell_mask_dim = -1
121
+ outlines.append(np.take(stack, cell_mask_dim, axis=2))
122
+ outline_colors.append('red')
123
+
124
+ fig = _plot_merged_plot(image=image, outlines=outlines, outline_colors=outline_colors, figuresize=figuresize, thickness=thickness)
125
+
126
+ return
127
+
22
128
  def plot_masks(batch, masks, flows, cmap='inferno', figuresize=20, nr=1, file_type='.npz', print_object_number=True):
23
129
  """
24
130
  Plot the masks and flows for a given batch of images.