Skip to content

Commit 6cf53ac

Browse files
adding cellpose2.0 options to GUI, and fixing some text warnings in GUI
1 parent babd78d commit 6cf53ac

File tree

5 files changed

+57
-45
lines changed

5 files changed

+57
-45
lines changed

suite2p/default_ops.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ def default_ops():
7474
'1Preg': False, # whether to perform high-pass filtering and tapering
7575
'spatial_hp': 42, # window for spatial high-pass filtering before registration
7676
'spatial_hp_reg': 42, # window for spatial high-pass filtering before registration
77-
'spatial_hp_detect': 25, # window for spatial high-pass filtering for neuropil subtraction before detection
7877
'pre_smooth': 0, # whether to smooth before high-pass filtering before registration
7978
'spatial_taper': 40, # how much to ignore on edges (important for vignetted windows, for FFT padding do not set BELOW 3*ops['smooth_sigma'])
8079

@@ -89,14 +88,16 @@ def default_ops():
8988
'threshold_scaling': 1.0, # adjust the automatically determined threshold by this scalar multiplier
9089
'max_overlap': 0.75, # cells with more overlap than this get removed during triage, before refinement
9190
'high_pass': 100, # running mean subtraction with window of size 'high_pass' (use low values for 1P)
91+
'spatial_hp_detect': 25, # window for spatial high-pass filtering for neuropil subtraction before detection
9292
'denoise': False, # denoise binned movie for cell detection in sparse_mode
9393

94-
# cell detection settings with cellpose
95-
'anatomical_only': 0, # use cellpose masks from mean image (no functional segmentation)
96-
'cellprob_threshold': 0.0, # cellprob_threshold for cellpose (if anatomical_only > 1)
97-
'flow_threshold': 1.5, # flow_threshold for cellpose (if anatomical_only > 1)
98-
'diameter': 0, # if anatomical_only, use diameter for cellpose, if 0 estimate diameter
99-
'pretrained_model': 'cyto', # path to pretrained model or model type string in Cellpose
94+
# cell detection settings with cellpose (used if anatomical_only > 0)
95+
'anatomical_only': 0, # run cellpose to get masks on 1: max_proj / mean_img; 2: mean_img; 3: mean_img enhanced, 4: max_proj
96+
'diameter': 0, # use diameter for cellpose, if 0 estimate diameter
97+
'cellprob_threshold': 0.0, # cellprob_threshold for cellpose
98+
'flow_threshold': 1.5, # flow_threshold for cellpose
99+
'spatial_hp_cp': 0, # high-pass image spatially by a multiple of the diameter
100+
'pretrained_model': 'cyto', # path to pretrained model or model type string in Cellpose (can be user model)
100101

101102
# classification parameters
102103
'soma_crop': True, # crop dendrites for cell classification stats like compactness

suite2p/detection/anatomical.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import numpy as np
22
from typing import Any, Dict
3-
from scipy.ndimage import find_objects
3+
from scipy.ndimage import find_objects, gaussian_filter
44
from cellpose.models import CellposeModel, Cellpose
55
from cellpose import transforms, dynamics
66
from cellpose.utils import fill_holes_and_remove_small_masks
7+
from cellpose.transforms import normalize99
78
import time
89
import cv2
910
import os
@@ -150,25 +151,29 @@ def select_rois(ops: Dict[str, Any], mov: np.ndarray,
150151
max_proj = mov.max(axis=0)
151152
#max_proj = np.percentile(mov, 90, axis=0) #.mean(axis=0)
152153
if ops['anatomical_only'] == 1:
153-
mproj = np.log(np.maximum(1e-3, max_proj / np.maximum(1e-3, mean_img)))
154+
img = np.log(np.maximum(1e-3, max_proj / np.maximum(1e-3, mean_img)))
154155
weights = max_proj
155156
elif ops['anatomical_only']==2:
156-
mproj = mean_img
157+
img = mean_img
157158
weights = 0.1 + np.clip((mean_img - np.percentile(mean_img,1)) /
158159
(np.percentile(mean_img,99) - np.percentile(mean_img,1)), 0, 1)
159-
else:
160+
elif ops['anatomical_only']==3:
160161
if 'meanImgE' in ops:
161-
mproj = ops['meanImgE'][ops['yrange'][0]:ops['yrange'][1], ops['xrange'][0]:ops['xrange'][1]]
162+
img = ops['meanImgE'][ops['yrange'][0]:ops['yrange'][1], ops['xrange'][0]:ops['xrange'][1]]
162163
else:
163-
mproj = mean_img
164+
img = mean_img
164165
print('no enhanced mean image, using mean image instead')
165166
weights = 0.1 + np.clip((mean_img - np.percentile(mean_img,1)) /
166167
(np.percentile(mean_img,99) - np.percentile(mean_img,1)), 0, 1)
168+
else:
169+
img = max_proj.copy()
170+
weights = max_proj
171+
167172
t0 = time.time()
168173
if diameter is not None:
169174
if isinstance(diameter, (list, np.ndarray)) and len(ops['diameter'])>1:
170175
rescale = diameter[1] / diameter[0]
171-
mproj = cv2.resize(mproj, (Lxc, int(Lyc*rescale)))
176+
img = cv2.resize(img, (Lxc, int(Lyc*rescale)))
172177
else:
173178
rescale = 1.0
174179
diameter = [diameter, diameter]
@@ -178,13 +183,18 @@ def select_rois(ops: Dict[str, Any], mov: np.ndarray,
178183
print("!NOTE! diameter set to 0 or None, diameter will be estimated by cellpose")
179184
else:
180185
print("!NOTE! diameter set to 0 or None, diameter will be estimated by cellpose")
181-
masks, centers, median_diam, mask_diams = roi_detect(mproj, diameter=diameter[1],
186+
187+
if ops.get('spatial_hp_cp', 0):
188+
img = np.clip(normalize99(img), 0, 1)
189+
img -= gaussian_filter(img, diameter[1]*ops['spatial_hp_cp'])
190+
191+
masks, centers, median_diam, mask_diams = roi_detect(img, diameter=diameter[1],
182192
flow_threshold=ops['flow_threshold'],
183193
cellprob_threshold=ops['cellprob_threshold'],
184194
pretrained_model=ops['pretrained_model'])
185195
if rescale != 1.0:
186196
masks = cv2.resize(masks, (Lxc, Lyc), interpolation=cv2.INTER_NEAREST)
187-
mproj = cv2.resize(mproj, (Lxc, Lyc))
197+
img = cv2.resize(img, (Lxc, Lyc))
188198
stats = masks_to_stats(masks, weights)
189199
print('Detected %d ROIs, %0.2f sec' % (len(stats), time.time() - t0))
190200

@@ -194,7 +204,7 @@ def select_rois(ops: Dict[str, Any], mov: np.ndarray,
194204
'Vmax': 0,
195205
'ihop': 0,
196206
'Vsplit': 0,
197-
'Vcorr': mproj,
207+
'Vcorr': img,
198208
'Vmap': 0,
199209
'spatscale_pix': 0
200210
}

suite2p/detection/detect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def detection_wrapper(f_reg, mov=None, yrange=None, xrange=None,
139139
print('~~~ tried to import cellpose to run anatomical but failed, install with: ~~~')
140140
print('$ pip install cellpose')
141141
else:
142-
print('>>>> CELLPOSE finding masks in ' + ['max_proj / mean_img', 'mean_img', 'enhanced_mean_img'][int(ops['anatomical_only'])-1])
142+
print('>>>> CELLPOSE finding masks in ' + ['max_proj / mean_img', 'mean_img', 'enhanced_mean_img', 'max_proj'][int(ops['anatomical_only'])-1])
143143
stat = anatomical.select_rois(
144144
ops=ops,
145145
mov=mov,

suite2p/gui/masks.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -230,17 +230,9 @@ def init_masks(parent):
230230
parent.rois['Lam'] = np.zeros((2,3,Ly,Lx), np.float32)
231231
parent.rois['iROI'] = -1 * np.ones((2,3,Ly,Lx), np.int32)
232232

233-
for n in range(len(parent.roi_text_labels)):
233+
if parent.checkBoxN.isChecked():
234234
parent.checkBoxN.setChecked(False)
235-
try:
236-
parent.p1.removeItem(parent.roi_text_labels[n])
237-
except:
238-
pass
239-
try:
240-
parent.p2.removeItem(parent.roi_text_labels[n])
241-
except:
242-
pass
243-
235+
244236
# ignore merged cells
245237
iignore = np.zeros(ncells, 'bool')
246238
parent.roi_text_labels = []
@@ -536,14 +528,13 @@ def flip_roi(parent):
536528
n = parent.ichosen
537529
i = int(1-parent.iscell[n])
538530
i0 = 1-i
539-
if parent.roitext:
540-
if i0==1:
531+
if parent.checkBoxN.isChecked():
532+
if i0==0:
541533
parent.p1.removeItem(parent.roi_text_labels[n])
542534
parent.p2.addItem(parent.roi_text_labels[n])
543535
else:
544536
parent.p2.removeItem(parent.roi_text_labels[n])
545537
parent.p1.addItem(parent.roi_text_labels[n])
546-
547538

548539
# remove ROI
549540
remove_roi(parent, n, i0)

suite2p/gui/rungui.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,21 @@ def reset_ops(self):
8181
def create_buttons(self):
8282
self.intkeys = ['nplanes', 'nchannels', 'functional_chan', 'align_by_chan', 'nimg_init',
8383
'batch_size', 'max_iterations', 'nbinned','inner_neuropil_radius',
84-
'min_neuropil_pixels', 'spatial_scale', 'do_registration']
84+
'min_neuropil_pixels', 'spatial_scale', 'do_registration', 'anatomical_only']
8585
self.boolkeys = ['delete_bin', 'move_bin','do_bidiphase', 'reg_tif', 'reg_tif_chan2',
8686
'save_mat', 'save_NWB' 'combined', '1Preg', 'nonrigid',
8787
'connected', 'roidetect', 'neuropil_extract',
8888
'spikedetect', 'keep_movie_raw', 'allow_overlap', 'sparse_mode']
89+
self.stringkeys = ['pretrained_model']
8990
tifkeys = ['nplanes','nchannels','functional_chan','tau','fs','do_bidiphase','bidiphase', 'multiplane_parallel', 'ignore_flyback']
9091
outkeys = ['preclassify','save_mat','save_NWB','combined','reg_tif','reg_tif_chan2','aspect','delete_bin','move_bin']
9192
regkeys = ['do_registration','align_by_chan','nimg_init','batch_size','smooth_sigma', 'smooth_sigma_time','maxregshift','th_badframes','keep_movie_raw','two_step_registration']
9293
nrkeys = [['nonrigid','block_size','snr_thresh','maxregshiftNR'], ['1Preg','spatial_hp_reg','pre_smooth','spatial_taper']]
93-
cellkeys = ['roidetect', 'denoise', 'anatomical_only', 'diameter', 'spatial_scale', 'threshold_scaling', 'max_overlap','max_iterations','high_pass']
94+
cellkeys = ['roidetect', 'denoise', 'spatial_scale', 'threshold_scaling', 'max_overlap','max_iterations','high_pass','spatial_hp_detect']
95+
anatkeys = ['anatomical_only', 'diameter', 'cellprob_threshold', 'flow_threshold', 'pretrained_model', 'spatial_hp_cp']
9496
neudeconvkeys = [['neuropil_extract', 'allow_overlap','inner_neuropil_radius','min_neuropil_pixels'], ['soma_crop','spikedetect','win_baseline','sig_baseline','neucoeff']]
95-
keys = [tifkeys, outkeys, regkeys, nrkeys, cellkeys, neudeconvkeys]
96-
labels = ['Main settings','Output settings','Registration',['Nonrigid','1P'],'ROI detection',['Extraction/Neuropil','Classification/Deconvolution']]
97+
keys = [tifkeys, outkeys, regkeys, nrkeys, cellkeys, anatkeys, neudeconvkeys]
98+
labels = ['Main settings','Output settings','Registration',['Nonrigid','1P'],'Functional detect', 'Anat detect', ['Extraction/Neuropil','Classify/Deconv']]
9799
tooltips = ['each tiff has this many planes in sequence',
98100
'each tiff has this many channels per plane',
99101
'this channel is used to extract functional ROIs (1-based)',
@@ -130,15 +132,20 @@ def create_buttons(self):
130132
'window for spatial high-pass filtering before registration',
131133
'whether to smooth before high-pass filtering before registration',
132134
"how much to ignore on edges (important for vignetted windows, for FFT padding do not set BELOW 3*smooth_sigma)",
133-
'if 1, run cell (ROI) detection',
135+
'if 1, run cell (ROI) detection (either functional or anatomical if anatomical_only > 0)',
134136
'if 1, run PCA denoising on binned movie to improve cell detection',
135-
'run cellpose to get masks on 1: max_proj / mean_img; 2: mean_img; or 3: mean_img enhanced',
136-
'if anatomical_only>0, input average diameter of ROIs in recording (can give a list e.g. 6,9)',
137-
'if anatomical_only=0, choose size of ROIs: 0 = multi-scale; 1 = 6 pixels, 2 = 12, 3 = 24, 4 = 48',
137+
'choose size of ROIs: 0 = multi-scale; 1 = 6 pixels, 2 = 12, 3 = 24, 4 = 48',
138138
'adjust the automatically determined threshold for finding ROIs by this scalar multiplier',
139139
'ROIs with greater than this overlap as a fraction of total pixels will be discarded',
140140
'maximum number of iterations for ROI detection',
141-
'running mean subtraction with window of size "high_pass" (use low values for 1P)',
141+
'temporal running mean subtraction with window of size "high_pass" (use low values for 1P)',
142+
'spatial high-pass filter size (used to remove spatially-correlated neuropil)',
143+
'run cellpose to get masks on 1: max_proj / mean_img; 2: mean_img; 3: mean_img enhanced, 4: max_proj',
144+
'input average diameter of ROIs in recording (can give a list e.g. 6,9 if aspect not equal), if set to 0 auto-determination run by Cellpose',
145+
'cellprob_threshold for cellpose',
146+
'flow_threshold for cellpose (throws out masks, if getting too few masks, set to 0)',
147+
'high-pass image spatially by a multiple of the diameter (if field is non-uniform, a value of ~2 is recommended',
148+
'model type string from Cellpose (can be a built-in model or a user model that is added to the Cellpose GUI)',
142149
'whether or not to extract neuropil; if 0, Fneu is set to 0',
143150
'allow shared pixels to be used for fluorescence extraction from overlapping ROIs (otherwise excluded from both ROIs)',
144151
'number of pixels between ROI and neuropil donut',
@@ -147,7 +154,8 @@ def create_buttons(self):
147154
'if 1, run spike detection (deconvolution)',
148155
'window for maximin',
149156
'smoothing constant for gaussian filter',
150-
'neuropil coefficient']
157+
'neuropil coefficient',
158+
]
151159

152160
bigfont = QtGui.QFont("Arial", 10, QtGui.QFont.Bold)
153161
qlabel = QLabel('File paths')
@@ -263,7 +271,7 @@ def create_buttons(self):
263271
self.layout.addWidget(self.binlabel,19,0,1,cw)
264272
self.runButton = QPushButton('RUN SUITE2P')
265273
self.runButton.clicked.connect(self.run_S2P)
266-
n0 = 21
274+
n0 = 22
267275
self.layout.addWidget(self.runButton,n0,0,1,1)
268276
self.runButton.setEnabled(False)
269277
self.textEdit = QTextEdit()
@@ -289,7 +297,7 @@ def create_buttons(self):
289297
self.cleanButton.clicked.connect(self.clean_script)
290298
self.cleanLabel = QLabel('')
291299
self.layout.addWidget(self.cleanLabel,n0,4,1,12)
292-
n0+=1
300+
#n0+=1
293301
self.listOps = QPushButton('save settings and\n add more (batch)')
294302
self.listOps.clicked.connect(self.add_batch)
295303
self.layout.addWidget(self.listOps,n0,12,1,2)
@@ -357,7 +365,7 @@ def add_ops(self):
357365

358366
def compile_ops_db(self):
359367
for k,key in enumerate(self.keylist):
360-
self.ops[key] = self.editlist[k].get_text(self.intkeys, self.boolkeys)
368+
self.ops[key] = self.editlist[k].get_text(self.intkeys, self.boolkeys, self.stringkeys)
361369
self.db = {}
362370
self.db['data_path'] = self.data_path
363371
self.db['subfolders'] = []
@@ -606,7 +614,7 @@ def __init__(self,k,key,parent=None):
606614
self.key = key
607615
#self.textEdited.connect(lambda: self.edit_changed(parent.ops, k))
608616

609-
def get_text(self,intkeys,boolkeys):
617+
def get_text(self,intkeys,boolkeys,stringkeys):
610618
key = self.key
611619
if key=='diameter' or key=='block_size':
612620
diams = self.text().replace(' ','').split(',')
@@ -625,6 +633,8 @@ def get_text(self,intkeys,boolkeys):
625633
okey = int(float(self.text()))
626634
elif key in boolkeys:
627635
okey = bool(int(self.text()))
636+
elif key in stringkeys:
637+
okey = self.text()
628638
else:
629639
okey = float(self.text())
630640
return okey

0 commit comments

Comments
 (0)