miblab-plot 0.0.1__py3-none-any.whl → 0.0.2__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.
miblab_plot/__init__.py CHANGED
@@ -1 +1,5 @@
1
- from miblab_plot.image_3d import volume_to_mosaic, mosaic_overlay
1
+ from miblab_plot.image_3d import (
2
+ volume_to_mosaic,
3
+ mosaic_overlay,
4
+ mosaic_checkerboard,
5
+ )
miblab_plot/image_3d.py CHANGED
@@ -1,9 +1,6 @@
1
1
  from math import ceil
2
2
 
3
3
  from PIL import Image
4
- import numpy as np
5
-
6
-
7
4
  import numpy as np
8
5
  import matplotlib
9
6
  matplotlib.use('Agg')
@@ -12,31 +9,162 @@ from tqdm import tqdm
12
9
 
13
10
 
14
11
 
15
- def get_distinct_colors(rois, colormap='jet'):
12
+ def get_distinct_colors(rois, colormap='jet', opacity=0.6):
16
13
  if len(rois)==1:
17
- colors = [[1, 0, 0, 0.6]]
14
+ colors = [[1, 0, 0, opacity]]
18
15
  elif len(rois)==2:
19
- colors = [[1, 0, 0, 0.6], [0, 1, 0, 0.6]]
16
+ colors = [[1, 0, 0, opacity], [0, 1, 0, opacity]]
20
17
  elif len(rois)==3:
21
- colors = [[1, 0, 0, 0.6], [0, 1, 0, 0.6], [0, 0, 1, 0.6]]
18
+ colors = [[1, 0, 0, opacity], [0, 1, 0, opacity], [0, 0, 1, opacity]]
22
19
  else:
23
20
  n = len(rois)
24
21
  #cmap = cm.get_cmap(colormap, n)
25
22
  cmap = matplotlib.colormaps[colormap]
26
- colors = [cmap(i)[:3] + (0.6,) for i in np.linspace(0, 1, n)] # Set alpha to 0.6 for transparency
23
+ colors = [cmap(i)[:3] + (opacity,) for i in np.linspace(0, 1, n)]
27
24
 
28
25
  return colors
29
26
 
30
27
 
28
+ def mosaic_checkerboard(fixed, coreg, file, normalize=False, square_size=32, aspect_ratio=1.8, vmin=None, vmax=None):
29
+
30
+ fixed = np.array(fixed)
31
+ coreg = np.array(coreg)
32
+
33
+ if fixed.shape != coreg.shape:
34
+ raise ValueError("Input arrays must have the same shape")
35
+
36
+ if normalize:
37
+ fixed = _normalize(fixed)
38
+ coreg = _normalize(coreg)
39
+ # fixed = (fixed - fixed.min()) / (fixed.max() - fixed.min())
40
+ # coreg = (coreg - coreg.min()) / (coreg.max() - coreg.min())
41
+
42
+ # Set defaults color window
43
+ if vmin is None:
44
+ vmin=np.percentile(np.concatenate([fixed, coreg]), 5)
45
+ if vmax is None:
46
+ vmax=np.percentile(np.concatenate([fixed, coreg]), 95)
47
+
48
+ # Determine number of rows and columns
49
+ # c*r = n -> c=n/r
50
+ # c*w / r*h = a -> w*n/r = a*r*h -> (w*n) / (a*h) = r**2
51
+ width = fixed.shape[0]
52
+ height = fixed.shape[1]
53
+ n_mosaics = fixed.shape[2]
54
+ nrows = int(np.round(np.sqrt((width*n_mosaics)/(aspect_ratio*height))))
55
+ ncols = int(np.ceil(n_mosaics/nrows))
56
+
57
+ # Set up figure
58
+ fig, ax = plt.subplots(
59
+ nrows=nrows,
60
+ ncols=ncols,
61
+ gridspec_kw = {'wspace':0, 'hspace':0},
62
+ figsize=(ncols*width/max([width,height]), nrows*height/max([width,height])),
63
+ dpi=300,
64
+ )
65
+ plt.subplots_adjust(left=0, right=1, top=1, bottom=0)
66
+
67
+ # Build figure
68
+ i = 0
69
+ for row in tqdm(ax, desc='Building png'):
70
+ for col in row:
71
+
72
+ col.set_xticklabels([])
73
+ col.set_yticklabels([])
74
+ col.set_aspect('equal')
75
+ col.axis("off")
76
+
77
+ # Display the background image
78
+ if i < n_mosaics:
79
+ img = checkerboard(fixed[:,:,i], coreg[:,:,i], square_size)
80
+ col.imshow(
81
+ img.T,
82
+ cmap='gray',
83
+ interpolation='none',
84
+ vmin=vmin,
85
+ vmax=vmax,
86
+ )
87
+
88
+ i += 1
89
+
90
+ # fig.suptitle('Mask overlay', fontsize=14)
91
+ fig.savefig(file, bbox_inches='tight', pad_inches=0)
92
+ plt.close()
93
+
94
+
95
+
96
+ def _normalize(x: np.ndarray) -> np.ndarray:
97
+ """
98
+ Normalize an array so that median = 0 and IQR = 1.
99
+
100
+ Parameters
101
+ ----------
102
+ x : np.ndarray
103
+ Input array (any shape)
104
+
105
+ Returns
106
+ -------
107
+ np.ndarray
108
+ Normalized array
109
+ """
110
+ x = np.asarray(x, dtype=float)
111
+
112
+ q25, q50, q75 = np.percentile(x, [25, 50, 75])
113
+ iqr = q75 - q25
114
+
115
+ if iqr == 0:
116
+ raise ValueError("IQR is zero; cannot normalize")
117
+
118
+ return (x - q50) / iqr
119
+
120
+
121
+
122
+ def checkerboard(fixed: np.ndarray, coreg: np.ndarray, square_size: int) -> np.ndarray:
123
+ """
124
+ Creates a checkerboard pattern from two 2D arrays.
125
+
126
+ Parameters
127
+ ----------
128
+ fixed : np.ndarray
129
+ The first 2D array (e.g., fixed image)
130
+ coreg : np.ndarray
131
+ The second 2D array (e.g., coregistered image)
132
+ Must have the same shape as `fixed`.
133
+ square_size : int
134
+ The size of each checkerboard square in pixels.
135
+
136
+ Returns
137
+ -------
138
+ np.ndarray
139
+ Checkerboard array combining `fixed` and `coreg`.
140
+ """
141
+ if fixed.shape != coreg.shape:
142
+ raise ValueError("Input arrays must have the same shape")
143
+
144
+ rows, cols = fixed.shape
145
+
146
+ # Create a boolean checkerboard mask
147
+ row_blocks = np.arange(rows) // square_size
148
+ col_blocks = np.arange(cols) // square_size
149
+ mask = (row_blocks[:, None] + col_blocks[None, :]) % 2 == 0 # True = use fixed
150
+
151
+ # Build the checkerboard
152
+ checker = np.where(mask, fixed, coreg)
153
+
154
+ return checker
155
+
156
+
157
+
31
158
  def mosaic_overlay(
32
159
  img,
33
160
  rois,
34
161
  file,
35
162
  colormap='tab20',
36
- aspect_ratio=16/9,
163
+ aspect_ratio=1.8,
37
164
  margin=None,
38
165
  vmin=None,
39
166
  vmax=None,
167
+ opacity=0.6,
40
168
  ):
41
169
 
42
170
  # Set defaults color window
@@ -46,7 +174,7 @@ def mosaic_overlay(
46
174
  vmax=np.mean(img) + 2 * np.std(img)
47
175
 
48
176
  # Define RGBA colors (R, G, B, Alpha) — alpha controls transparency
49
- colors = get_distinct_colors(rois, colormap=colormap)
177
+ colors = get_distinct_colors(rois, colormap=colormap, opacity=opacity)
50
178
 
51
179
  # Get all masks as boolean arrays
52
180
  masks = [m.astype(bool) for m in rois.values()]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: miblab-plot
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: Plotting utilities for medical images
5
5
  Author-email: Steven Sourbron <s.sourbron@sheffield.ac.uk>
6
6
  License-Expression: Apache-2.0
@@ -0,0 +1,7 @@
1
+ miblab_plot/__init__.py,sha256=7k0ANRoyjRqb-6xxqpbR657tOMBf5UtLmtBif_PC93s,109
2
+ miblab_plot/image_3d.py,sha256=nwP_cF--H_rUSrL-Ozp6qzbO-4Y8159TkWOsg27s5b0,10779
3
+ miblab_plot-0.0.2.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
4
+ miblab_plot-0.0.2.dist-info/METADATA,sha256=vAnLpEphCq9CTiEX5JkUONETakTH2ElVgr88HAXBHyg,937
5
+ miblab_plot-0.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ miblab_plot-0.0.2.dist-info/top_level.txt,sha256=w6glSbOeiZvvL3Tywi8XTJxYAatD7VMkOifD5iKnTSU,12
7
+ miblab_plot-0.0.2.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- miblab_plot/__init__.py,sha256=rXyuHtEzmdjmiXdm-0ZURw1DxO_W70UDd0OxY9nKxO4,65
2
- miblab_plot/image_3d.py,sha256=Nah2J8QjWV9YsF9SSwaicSQJCPDaio0MS8aIEduCG1I,7035
3
- miblab_plot-0.0.1.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
4
- miblab_plot-0.0.1.dist-info/METADATA,sha256=xjCti8UCrj_gP7mPzIyTEPydXJWwuVTD5aVUmBjL68Q,937
5
- miblab_plot-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
- miblab_plot-0.0.1.dist-info/top_level.txt,sha256=w6glSbOeiZvvL3Tywi8XTJxYAatD7VMkOifD5iKnTSU,12
7
- miblab_plot-0.0.1.dist-info/RECORD,,