Skip to content

Commit b48c1a3

Browse files
committed
cnmf: Abort run if we get empty spatial components. Also small logging and code legibility improvements and remove old py27 code
1 parent 91aaf4b commit b48c1a3

File tree

1 file changed

+25
-23
lines changed
  • caiman/source_extraction/cnmf

1 file changed

+25
-23
lines changed

caiman/source_extraction/cnmf/cnmf.py

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -364,12 +364,9 @@ def fit_file(self, motion_correct=False, indices=None, include_eval=False):
364364
Cn = caiman.summary_images.local_correlations(images[::max(T//1000, 1)], swap_dim=False)
365365
Cn[np.isnan(Cn)] = 0
366366
fit_cnm.save(fname_new[:-5] + '_init.hdf5')
367-
#fit_cnm.params.change_params({'p': self.params.get('preprocess', 'p')})
368-
# RE-RUN seeded CNMF on accepted patches to refine and perform deconvolution
367+
# Rerun seeded CNMF on accepted patches to refine and perform deconvolution
369368
cnm2 = fit_cnm.refit(images, dview=self.dview)
370369
cnm2.estimates.evaluate_components(images, cnm2.params, dview=self.dview)
371-
# update object with selected components
372-
#cnm2.estimates.select_components(use_object=True)
373370
# Extract DF/F values
374371
cnm2.estimates.detrend_df_f(quantileMin=8, frames_window=250)
375372
cnm2.estimates.Cn = Cn
@@ -415,21 +412,21 @@ def fit(self, images, indices=(slice(None), slice(None))) -> None:
415412
416413
indices: list of slice objects along dimensions (x,y[,z]) for processing only part of the FOV
417414
418-
#Returns:
419-
# self: updated using the cnmf algorithm with C,A,S,b,f computed according to the given initial values
420-
421415
http://www.cell.com/neuron/fulltext/S0896-6273(15)01084-3
422416
423417
"""
424418
logger = logging.getLogger("caiman")
425419
# Todo : to compartment
426420
if isinstance(indices, slice):
427421
indices = [indices]
422+
428423
if isinstance(indices, tuple):
429424
indices = list(indices)
425+
430426
indices = [slice(None)] + indices
431427
if len(indices) < len(images.shape):
432428
indices = indices + [slice(None)]*(len(images.shape) - len(indices))
429+
433430
dims_orig = images.shape[1:]
434431
dims_sliced = images[tuple(indices)].shape[1:]
435432
is_sliced = (dims_orig != dims_sliced)
@@ -441,27 +438,25 @@ def fit(self, images, indices=(slice(None), slice(None))) -> None:
441438
T = images.shape[0]
442439
self.params.set('online', {'init_batch': T})
443440
self.dims = images.shape[1:]
444-
#self.params.data['dims'] = images.shape[1:]
445441
Y = np.transpose(images, list(range(1, len(self.dims) + 1)) + [0])
446442
Yr = np.transpose(np.reshape(images, (T, -1), order='F'))
447443
if np.isfortran(Yr):
448444
raise Exception('The file is in F order, it should be in C order (see save_memmap function)')
449445

450446
logger.info((T,) + self.dims)
451447

452-
# Make sure filename is pointed correctly (numpy sets it to None sometimes)
448+
# Make sure filename is correctly set (numpy sets it to None sometimes)
453449
try:
454450
Y.filename = images.filename
455451
Yr.filename = images.filename
456452
self.mmap_file = images.filename
457-
except AttributeError: # if no memmapping cause working with small data
453+
except AttributeError: # if no memmapping because we're working with small data
458454
pass
459455

460456
logger.info(f"Using {self.params.get('patch', 'n_processes')} processes")
461457
# FIXME The code below is really ugly and it's hard to tell if it's doing the right thing
462458
if self.params.get('preprocess', 'n_pixels_per_process') is None:
463-
avail_memory_per_process = psutil.virtual_memory()[
464-
1] / 2.**30 / self.params.get('patch', 'n_processes')
459+
avail_memory_per_process = psutil.virtual_memory()[1] / 2.**30 / self.params.get('patch', 'n_processes')
465460
mem_per_pix = 3.6977678498329843e-09
466461
npx_per_proc = int(avail_memory_per_process / 8. / mem_per_pix / T)
467462
npx_per_proc = int(np.minimum(npx_per_proc, np.prod(self.dims) // self.params.get('patch', 'n_processes')))
@@ -489,22 +484,21 @@ def fit(self, images, indices=(slice(None), slice(None))) -> None:
489484

490485

491486
if self.remove_very_bad_comps:
492-
logger.info('removing bad components : ')
487+
logger.info('Removing bad components')
493488
final_frate = 10
494489
r_values_min = 0.5 # threshold on space consistency
495490
fitness_min = -15 # threshold on time variability
496491
fitness_delta_min = -15
497492
Npeaks = 10
498493
traces = np.array(self.C)
499-
logger.info('estimating the quality...')
494+
logger.info('Estimating component qualities...')
500495
idx_components, idx_components_bad, fitness_raw,\
501496
fitness_delta, r_values = estimate_components_quality(
502497
traces, Y, self.estimates.A, self.estimates.C, self.estimates.b, self.estimates.f,
503498
final_frate=final_frate, Npeaks=Npeaks, r_values_min=r_values_min,
504499
fitness_min=fitness_min, fitness_delta_min=fitness_delta_min, return_all=True, N=5)
505500

506-
logger.info(('Keeping ' + str(len(idx_components)) +
507-
' and discarding ' + str(len(idx_components_bad))))
501+
logger.info(f"Keeping {len(idx_components)} components and discarding {len(idx_components_bad)} components")
508502
self.estimates.C = self.estimates.C[idx_components]
509503
self.estimates.A = self.estimates.A[:, idx_components] # type: ignore # not provable that self.initialise provides a value
510504
self.estimates.YrA = self.estimates.YrA[idx_components]
@@ -566,6 +560,12 @@ def fit(self, images, indices=(slice(None), slice(None))) -> None:
566560
raise Exception(
567561
'You need to provide a memory mapped file as input if you use patches!!')
568562

563+
# We sometimes need to investigate what changes between before run_CNMF_patches and after that/before we update
564+
# components. This code block here is ready to uncomment for such debugging.
565+
#print("D: About to run run_CNMF_patches(), entering a shell. Will open a shell again afterwards")
566+
#import code
567+
#code.interact(local=dict(globals(), **locals()) )
568+
569569
self.estimates.A, self.estimates.C, self.estimates.YrA, self.estimates.b, self.estimates.f, \
570570
self.estimates.sn, self.estimates.optional_outputs = run_CNMF_patches(
571571
images.filename, self.dims + (T,), self.params,
@@ -575,8 +575,14 @@ def fit(self, images, indices=(slice(None), slice(None))) -> None:
575575
del_duplicates=self.params.get('patch', 'del_duplicates'),
576576
indices=indices)
577577

578+
#print("D: Finished with run_CNMF_patches(), self.estimates.* are populated. Next step would be update_temporal() but first: Entering a shell.")
579+
#code.interact(local=dict(globals(), **locals()) )
580+
581+
if len(list(np.where(~self.estimates.b.any(axis=0))[0])) > 0: # If any of the background ended up completely empty
582+
raise Exception("After run_CNMF_patches(), one or more of the background components is empty. Please restart analysis with init/nb set to a lower value")
583+
578584
self.estimates.bl, self.estimates.c1, self.estimates.g, self.estimates.neurons_sn = None, None, None, None
579-
logger.info("merging")
585+
logger.info("Merging")
580586
self.estimates.merged_ROIs = [0]
581587

582588

@@ -615,7 +621,7 @@ def fit(self, images, indices=(slice(None), slice(None))) -> None:
615621
while len(self.estimates.merged_ROIs) > 0:
616622
self.merge_comps(Yr, mx=np.inf)
617623

618-
logger.info("update temporal")
624+
logger.info("Updating temporal components")
619625
self.update_temporal(Yr, use_init=False)
620626

621627
self.estimates.normalize_components()
@@ -750,11 +756,7 @@ def update_temporal(self, Y, use_init=True, **kwargs) -> None:
750756
pr = inspect.signature(self.update_temporal)
751757
params = [k for k, v in pr.parameters.items() if '=' in str(v)]
752758
kw2 = {k: lc[k] for k in params}
753-
try:
754-
kwargs_new = {**kw2, **kwargs}
755-
except(): # python 2.7
756-
kwargs_new = kw2.copy()
757-
kwargs_new.update(kwargs)
759+
kwargs_new = {**kw2, **kwargs}
758760
self.params.set('temporal', kwargs_new)
759761

760762
self.estimates.C, self.estimates.A, self.estimates.b, self.estimates.f, self.estimates.S, \

0 commit comments

Comments
 (0)