@@ -48,9 +48,11 @@ def main():
4848 subject = snakemake .wildcards .subject
4949 max_rois = snakemake .params .max_rois
5050 n_cols = snakemake .params .n_cols
51+ use_n4_bg = snakemake .params .get ("use_n4_bg" , False )
5152
53+ spim_bg_path = snakemake .input .spim_n4 if use_n4_bg else snakemake .input .spim
5254 spim_img = ZarrNii .from_ome_zarr (
53- snakemake . input . spim ,
55+ spim_bg_path ,
5456 level = snakemake .params .level ,
5557 downsample_near_isotropic = True ,
5658 channel_labels = [snakemake .wildcards .stain ],
@@ -66,7 +68,7 @@ def main():
6668 aspect_axial = 1
6769
6870 spim_img_ds = ZarrNii .from_ome_zarr (
69- snakemake . input . spim ,
71+ spim_bg_path ,
7072 level = (int (snakemake .params .level ) + 5 ),
7173 downsample_near_isotropic = True ,
7274 channel_labels = [snakemake .wildcards .stain ],
@@ -103,7 +105,22 @@ def main():
103105 color = "gray" ,
104106 )
105107 ax .axis ("off" )
106- plt .savefig (snakemake .output .png , dpi = 120 , bbox_inches = "tight" )
108+ plt .savefig (snakemake .output .png , dpi = 150 , bbox_inches = "tight" )
109+ plt .close ()
110+ # Also save the no-mask version (same empty figure)
111+ fig , ax = plt .subplots (figsize = (18 , 12 ))
112+ ax .text (
113+ 0.5 ,
114+ 0.5 ,
115+ "No atlas ROIs found in subject" ,
116+ ha = "center" ,
117+ va = "center" ,
118+ transform = ax .transAxes ,
119+ fontsize = 12 ,
120+ color = "gray" ,
121+ )
122+ ax .axis ("off" )
123+ plt .savefig (snakemake .output .png_nomask , dpi = 150 , bbox_inches = "tight" )
107124 plt .close ()
108125 return
109126
@@ -126,6 +143,8 @@ def main():
126143 elif n_cols == 1 :
127144 axes = axes [:, np .newaxis ]
128145
146+ # Cache crops for both masked and no-mask figures
147+ cached_slices = []
129148 for i , row in enumerate (roi_rows ):
130149 ax_row = i // n_cols
131150 ax_col = i % n_cols
@@ -149,11 +168,12 @@ def main():
149168 spim_sl = spim_crop .data [0 , :, :].squeeze ().compute ()
150169 spim_sl = _apply_fixed_percentile_norm (spim_sl , glob_lo , glob_hi )
151170 mask_sl = mask_crop .data [0 , :, :].squeeze ().compute ()
171+ cached_slices .append ((label_name , spim_sl , mask_sl ))
152172
153173 ax .imshow (spim_sl , cmap = "gray" )
154174 mask_masked = np .ma .masked_where (mask_sl < 100 , mask_sl )
155175 ax .imshow (
156- mask_masked , cmap = "spring " , alpha = 0.6 , vmin = 0 , vmax = 100 , aspect = aspect_axial
176+ mask_masked , cmap = "Reds " , alpha = 0.6 , vmin = 0 , vmax = 100 , aspect = aspect_axial
157177 )
158178 ax .set_title (label_name , fontsize = 7 , pad = 2 )
159179 ax .set_xticks ([])
@@ -167,6 +187,41 @@ def main():
167187 plt .close ()
168188 print (f"Saved ROI zoom montage to { snakemake .output .png } " )
169189
190+ # --- No-mask version (reuses cached slices) ---
191+ n_rows_nm = int (np .ceil (n_rois / n_cols ))
192+ fig_nm , axes_nm = plt .subplots (
193+ n_rows_nm ,
194+ n_cols ,
195+ figsize = (n_cols * 3 , n_rows_nm * 3 ),
196+ constrained_layout = True ,
197+ )
198+ fig_nm .suptitle (
199+ f"ROI Zoom Montage QC (no mask overlay)\n "
200+ f"Subject: { subject } | Stain: { stain } | Method: { desc } " ,
201+ fontsize = 11 ,
202+ fontweight = "bold" ,
203+ )
204+ if n_rows_nm == 1 and n_cols == 1 :
205+ axes_nm = np .array ([[axes_nm ]])
206+ elif n_rows_nm == 1 :
207+ axes_nm = axes_nm [np .newaxis , :]
208+ elif n_cols == 1 :
209+ axes_nm = axes_nm [:, np .newaxis ]
210+
211+ for i , (label_name , spim_sl , _ ) in enumerate (cached_slices ):
212+ ax_nm = axes_nm [i // n_cols , i % n_cols ]
213+ ax_nm .imshow (spim_sl , cmap = "gray" )
214+ ax_nm .set_title (label_name , fontsize = 7 , pad = 2 )
215+ ax_nm .set_xticks ([])
216+ ax_nm .set_yticks ([])
217+
218+ for i in range (n_rois , n_rows_nm * n_cols ):
219+ axes_nm [i // n_cols , i % n_cols ].axis ("off" )
220+
221+ plt .savefig (snakemake .output .png_nomask , dpi = 150 , bbox_inches = "tight" )
222+ plt .close ()
223+ print (f"Saved no-mask ROI zoom montage to { snakemake .output .png_nomask } " )
224+
170225
171226if __name__ == "__main__" :
172227 main ()
0 commit comments