miblab-plot 0.0.1__tar.gz → 0.0.2__tar.gz
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-0.0.1/src/miblab_plot.egg-info → miblab_plot-0.0.2}/PKG-INFO +1 -1
- {miblab_plot-0.0.1 → miblab_plot-0.0.2}/pyproject.toml +1 -1
- miblab_plot-0.0.2/src/miblab_plot/__init__.py +5 -0
- {miblab_plot-0.0.1 → miblab_plot-0.0.2}/src/miblab_plot/image_3d.py +138 -10
- {miblab_plot-0.0.1 → miblab_plot-0.0.2/src/miblab_plot.egg-info}/PKG-INFO +1 -1
- miblab_plot-0.0.1/src/miblab_plot/__init__.py +0 -1
- {miblab_plot-0.0.1 → miblab_plot-0.0.2}/LICENSE +0 -0
- {miblab_plot-0.0.1 → miblab_plot-0.0.2}/README.md +0 -0
- {miblab_plot-0.0.1 → miblab_plot-0.0.2}/setup.cfg +0 -0
- {miblab_plot-0.0.1 → miblab_plot-0.0.2}/src/miblab_plot.egg-info/SOURCES.txt +0 -0
- {miblab_plot-0.0.1 → miblab_plot-0.0.2}/src/miblab_plot.egg-info/dependency_links.txt +0 -0
- {miblab_plot-0.0.1 → miblab_plot-0.0.2}/src/miblab_plot.egg-info/requires.txt +0 -0
- {miblab_plot-0.0.1 → miblab_plot-0.0.2}/src/miblab_plot.egg-info/top_level.txt +0 -0
|
@@ -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,
|
|
14
|
+
colors = [[1, 0, 0, opacity]]
|
|
18
15
|
elif len(rois)==2:
|
|
19
|
-
colors = [[1, 0, 0,
|
|
16
|
+
colors = [[1, 0, 0, opacity], [0, 1, 0, opacity]]
|
|
20
17
|
elif len(rois)==3:
|
|
21
|
-
colors = [[1, 0, 0,
|
|
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] + (
|
|
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=
|
|
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 +0,0 @@
|
|
|
1
|
-
from miblab_plot.image_3d import volume_to_mosaic, mosaic_overlay
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|