Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
a7d7fc9
VERSION (dev) to 1.11.5 after release
pgunn Jan 15, 2025
38dc06a
Fix typing errors in sbx_utils
ethanbb Jan 17, 2025
e63a98a
Add test for bidirectional SBX movie loading
ethanbb Jan 17, 2025
56bcb79
Don't modify or use recordsPerBuffer in sbx_shape
ethanbb Jan 17, 2025
7926044
Remove check for return value of tifffile.imwrite
ethanbb Jan 18, 2025
86ed5ab
Merge pull request #1452 from proektlab/sbx-framerate-fix
pgunn Jan 18, 2025
8090e27
Addresses #1454 using pyside6
pgunn Jan 22, 2025
db6ddac
volpy_gui: change import errors based on a stackoverflow suggestion
pgunn Jan 22, 2025
96d8a06
Add pyside6 to pyproject - so many places to update things!
pgunn Jan 24, 2025
d1236ec
Add workaround to fix pyqtgraph issue 2380
pgunn Jan 27, 2025
66120c7
volpy_gui: use limits keyword, which makes the menu actually work
pgunn Jan 27, 2025
6ef1051
If we're going qt6, let's go qt6. Fix plugin path
pgunn Jan 27, 2025
a663909
Change all np.Inf to np.inf
Jan 29, 2025
0f17607
Merge pull request #1457 from fdeguire03/dev
pgunn Jan 31, 2025
a41dc59
caiman.base.movies: Remove a number of methods that seem to be unused
pgunn Jan 31, 2025
244b081
Merge pull request #1458 from flatironinstitute/dev-cleanup_movie
pgunn Jan 31, 2025
8d69792
Forgot to add Kushal to the authors in pyproject
pgunn Feb 4, 2025
f89104a
Merge pull request #1455 from flatironinstitute/dev-volpy_qt6
pgunn Feb 5, 2025
98475bb
Forgot to update environment-minimal.yml for pyside6
pgunn Feb 5, 2025
f7d21de
demos: Don't use the useless return value to CNMF.fit()
pgunn Feb 7, 2025
54d4acb
Adjust the tests to not use return values from methods that should no…
pgunn Feb 7, 2025
383dcb8
cnmf.py:CNMF:fit() -- remove return value
pgunn Feb 11, 2025
6d40c41
CNNF:deconvolve() -- remove return value (and remove some old py2 code)
pgunn Feb 11, 2025
99810c7
CNMF: remove hals methods (unused in the codebase, broken code)
pgunn Feb 11, 2025
1dad866
CNMF: remove return value of update_temporal, update_spatial, initialize
pgunn Feb 11, 2025
35ff29a
Fix more stuff that used return value of cnmf.fit()
pgunn Feb 11, 2025
34d28c2
github tests: avoid defaults channel
pgunn Feb 11, 2025
b8bd582
demo_seeded_CNMF: remove useless assignment for estimates visualisati…
pgunn Feb 12, 2025
93d9abf
Add notes of cleanup need to online_cnmf, whitespace fix in movies
pgunn Feb 12, 2025
9bd0496
cnmf.estimates: Remove a lot of unnecessary "return self" from method…
pgunn Feb 12, 2025
8b1e380
Darned editor messed up my last diff
pgunn Feb 12, 2025
2314d2d
And another typo
pgunn Feb 12, 2025
ae2c50a
Add dependency versions to pyproject.toml
ethanbb Feb 26, 2025
c62c326
Add dev dependencies (for testing) plus some that were missing
ethanbb Feb 26, 2025
2c7e309
Move dev dependencies into main list
ethanbb Feb 26, 2025
45bf9e1
Merge pull request #1474 from proektlab/versions-in-pyproject
pgunn Feb 26, 2025
91aaf4b
Small improvements to function documentation in cnmf/temporal
pgunn Feb 27, 2025
b48c1a3
cnmf: Abort run if we get empty spatial components. Also small loggin…
pgunn Feb 27, 2025
4089c3e
Missed a bit of code in refit that relied on the return value of cnm.…
pgunn Feb 27, 2025
3d69f99
Fix more return value dependencies
pgunn Feb 27, 2025
c7d63df
CNMF and Estimates: add __str__, __repr__, __getitem__
pgunn Mar 4, 2025
86e63a0
CNMF.initialization.greedyROI() - Add a limited mechanism to specify …
pgunn Mar 4, 2025
963c260
Fix last diff; forgot to fix different var names after refactor
pgunn Mar 4, 2025
e1b5943
Plumb init args up to initialize_components()
pgunn Mar 14, 2025
b93f4c8
greedyROI param plumbing: update docs, get everything in a better shape
pgunn Mar 20, 2025
f4713d8
Merge pull request #1478 from flatironinstitute/more_params_for_nmf
pgunn Mar 25, 2025
f43dda9
Merge pull request #1464 from flatironinstitute/dev-fix_calling_conve…
pgunn Mar 26, 2025
0bc0aed
Handle cases where parameters end up with bad values for OnACID given…
pgunn Mar 26, 2025
66c73e7
Fix missing datafile concern in #1467
pgunn Apr 4, 2025
c38e761
Forgot to add avg_mask_fixed to list in caimanmanager
pgunn Apr 4, 2025
db988c1
seeded_CNMF notebook: Forgot a newline
pgunn Apr 4, 2025
aec3b0b
Merge pull request #1486 from flatironinstitute/fix_demo_seeded_notebook
pgunn Apr 8, 2025
a05c96b
Merge pull request #1485 from flatironinstitute/spot_bad_params_for_h…
pgunn Apr 9, 2025
8f0defc
Fix refactoring mistake I made in commit 38436e1
pgunn Apr 15, 2025
64dfbad
Adjust background check so if there are no components we don't prod a…
pgunn Apr 17, 2025
a3924fa
demo_pipeline_voltage_imaging.py: Save the demo data with other demo …
pgunn Apr 25, 2025
d524fbc
volpy: also have notebook put sample data same place the rest of caim…
pgunn Apr 25, 2025
d987e5e
Readme: more on env vars, install routes
pgunn May 8, 2025
7ac6fad
Readme: Fix formatting goof
pgunn May 8, 2025
28f7b4c
VERSION to 1.12.1 in dev
pgunn May 13, 2025
881e627
setup.py: Fix mistake in packaging new datafiles
pgunn May 13, 2025
d31e606
feat(fit_file): add output_dir and return_mc kwargs with CAIMAN_TEMP …
MilagrosMarin May 29, 2026
e39cea7
fix: use caiman.paths.fn_relocated in apply_shifts_movie
MilagrosMarin May 29, 2026
94d3bf2
VERSION: bump to 1.12.1+dj1
MilagrosMarin May 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ jobs:
activate-environment: caiman
conda-solver: libmamba
miniforge-version: latest
channels: conda-forge
conda-remove-defaults: "true"

- name: Install OS Dependencies
shell: bash -l {0}
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ There are two primary ways to install Caiman.
The easiest route is to install the miniforge distribution of Anaconda, and use that to install the rest using prebuilt packages. Most users should take this path.

## Route B
The alternative route is to make sure you have a working compiler, create a python virtualenv, grab the caiman sources, and use pip to populate the virtualenv and build Caiman. This route is not as tested and is not presently documented; it is a standard pip-based install.
The alternative route is to make sure you have a working compiler, create a python virtualenv, grab the caiman sources, and use pip to populate the virtualenv and build Caiman. This route is not as tested and is not presently documented; it is a standard pip-based install (although it will invoke your C++ compiler to build some components).

# Quick start (Route A)
Follow these three steps to get started quickly, from installation to working through a demo notebook. If you do not already have conda installed, [you can find it here](https://github.com/conda-forge/miniforge). The miniforge distribution of conda is preferred; it will require fewer steps and likely encounter fewer issues. If you are using a different distro of conda, you will likely need to add `-c conda-forge` to the commands you use to make your environment.
Expand Down Expand Up @@ -62,6 +62,11 @@ Jupyter will open. Navigate to demos/notebooks/ and click on `demo_pipeline.ipyn

> `<your home>` in the first line is your home directory, its location depdnding on your OS/computer. On Linux/Mac it is `~` while on Windows it will be something like `C:\Users\your_user_name\`

# Quick Start (Route B)
This differs from the quick start above in two ways:
* For the first step only, go to [this doc](https://github.com/flatironinstitute/CaImAn/blob/main/docs/source/Installation.rst) and run through the parts of section 1B relevant to your operating system. After that, steps 2 and onward are the same
* You will probably want to manually set some environment variables before any use of caiman; see [here](https://github.com/conda-forge/caiman-feedstock/blob/main/recipe/activate.sh) for a Linux/OSX example, or [here](https://github.com/conda-forge/caiman-feedstock/blob/main/recipe/activate.bat) for a Windows example. Either make a note of this or modify your dotfiles/configuration to do it for you.

## For installation help
Caiman should install easily on Linux, Mac, and Windows. If you run into problems, we have a dedicated [installation page](./docs/source/Installation.rst). If you don't find what you need there, [create an issue](https://github.com/flatironinstitute/Caiman/issues) on GitHub.

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.11.4
1.12.1+dj1
238 changes: 1 addition & 237 deletions caiman/base/movies.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,90 +172,6 @@ def motion_correct(self,

return self, shifts, xcorrs, template

def motion_correct_3d(self,
max_shift_z=5,
max_shift_w=5,
max_shift_h=5,
num_frames_template=None,
template=None,
method='opencv',
remove_blanks=False,
interpolation='cubic'):
"""
Extract shifts and motion corrected movie automatically,

for more control consider the functions extract_shifts and apply_shifts
Disclaimer, it might change the object itself.

Args:
max_shift,z,max_shift_w,max_shift_h: maximum pixel shifts allowed when
correcting in the axial, width, and height directions

template: if a good template for frame by frame correlation exists
it can be passed. If None it is automatically computed

method: depends on what is installed 'opencv' or 'skimage'. 'skimage'
is an order of magnitude slower

num_frames_template: if only a subset of the movies needs to be loaded
for efficiency/speed reasons


Returns:
self: motion corrected movie, it might change the object itself

shifts : tuple, contains x, y, and z shifts and correlation with template

xcorrs: cross correlation of the movies with the template

template: the computed template
"""

if template is None: # if template is not provided it is created
if num_frames_template is None:
num_frames_template = 10e7 / (self.shape[1] * self.shape[2])

frames_to_skip = int(np.maximum(1, self.shape[0] / num_frames_template))

# sometimes it is convenient to only consider a subset of the
# movie when computing the median
submov = self[::frames_to_skip, :].copy()
templ = submov.bin_median_3d() # create template with portion of movie
shifts, xcorrs = submov.extract_shifts_3d(
max_shift_z=max_shift_z, # NOTE: extract_shifts_3d has not been implemented yet - use skimage
max_shift_w=max_shift_w,
max_shift_h=max_shift_h,
template=templ,
method=method)
submov.apply_shifts_3d( # NOTE: apply_shifts_3d has not been implemented yet
shifts, interpolation=interpolation, method=method)
template = submov.bin_median_3d()
del submov
m = self.copy()
shifts, xcorrs = m.extract_shifts_3d(max_shift_z=max_shift_z,
max_shift_w=max_shift_w,
max_shift_h=max_shift_h,
template=template,
method=method)
m = m.apply_shifts_3d(shifts, interpolation=interpolation, method=method)
template = (m.bin_median_3d())
del m
else:
template = template - np.percentile(template, 8)

# now use the good template to correct
shifts, xcorrs = self.extract_shifts_3d(max_shift_z=max_shift_z,
max_shift_w=max_shift_w,
max_shift_h=max_shift_h,
template=template,
method=method)
self = self.apply_shifts_3d(shifts, interpolation=interpolation, method=method)

if remove_blanks:
raise Exception("motion_correct_3d(): The remove_blanks parameter was never functional and should not be used")

return self, shifts, xcorrs, template

def bin_median(self, window: int = 10) -> np.ndarray:
""" compute median of 3D array in along axis o by binning values

Expand Down Expand Up @@ -465,7 +381,6 @@ def apply_shifts(self, shifts, interpolation: str = 'linear', method: str = 'ope
min_, max_)

elif method == 'skimage':

tform = skimage.transform.AffineTransform(translation=(-sh_y_n, -sh_x_n))
self[i] = skimage.transform.warp(frame, tform, preserve_range=True, order=interpolation)

Expand All @@ -477,54 +392,6 @@ def apply_shifts(self, shifts, interpolation: str = 'linear', method: str = 'ope

return self

def debleach(self):
""" Debleach by fiting a model to the median intensity.
"""
#todo: todocument
if not isinstance(self[0, 0, 0], np.float32):
warnings.warn('Casting the array to float32')
self = np.asanyarray(self, dtype=np.float32)

t, _, _ = self.shape
x = np.arange(t)
y = np.median(self.reshape(t, -1), axis=1)

def expf(x, a, b, c):
return a * np.exp(-b * x) + c

def linf(x, a, b):
return a * x + b

try:
p0:tuple = (y[0] - y[-1], 1e-6, y[-1])
popt, _ = scipy.optimize.curve_fit(expf, x, y, p0=p0)
y_fit = expf(x, *popt)
except:
p0 = (float(y[-1] - y[0]) / float(x[-1] - x[0]), y[0])
popt, _ = scipy.optimize.curve_fit(linf, x, y, p0=p0)
y_fit = linf(x, *popt)

norm = y_fit - np.median(y[:])
for frame in range(t):
self[frame, :, :] = self[frame, :, :] - norm[frame]

return self

def return_cropped(self, crop_top=0, crop_bottom=0, crop_left=0, crop_right=0, crop_begin=0, crop_end=0) -> np.ndarray:
"""
Return a cropped version of the movie
The returned version is independent of the original, which is less memory-efficient but also less likely to be surprising.

Args:
crop_top/crop_bottom/crop_left,crop_right: how much to trim from each side

crop_begin/crop_end: (undocumented)
"""
t, h, w = self.shape
ret = np.zeros(( t - crop_end - crop_begin, h - crop_bottom - crop_top, w - crop_right - crop_left))
ret[:,:,:] = self[crop_begin:t - crop_end, crop_top:h - crop_bottom, crop_left:w - crop_right]
return ret

def removeBL(self, windowSize:int=100, quantilMin:int=8, in_place:bool=False, returnBL:bool=False):
"""
Remove baseline from movie using percentiles over a window
Expand Down Expand Up @@ -655,30 +522,6 @@ def computeDFF(self, secsWindow: int = 5, quantilMin: int = 8, method: str = 'on
meta_data=self.meta_data,
file_name=self.file_name)

def NonnegativeMatrixFactorization(self,
n_components: int = 30,
init: str = 'nndsvd',
beta: int = 1,
tol=5e-7,
sparseness: str = 'components',
**kwargs) -> tuple[np.ndarray, np.ndarray]:
"""
See documentation for scikit-learn NMF
"""
if np.min(self) < 0:
raise ValueError("All values must be positive")

T, h, w = self.shape
Y = np.reshape(self, (T, h * w))
Y = Y - np.percentile(Y, 1)
Y = np.clip(Y, 0, np.Inf)
estimator = sklearn.decomposition.NMF(n_components=n_components, init=init, tol=tol, **kwargs)
time_components = estimator.fit_transform(Y)
components_ = estimator.components_
space_components = np.reshape(components_, (n_components, h, w))

return space_components, time_components

def IPCA(self, components: int = 50, batch: int = 1000) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""
Iterative Principal Component analysis, see sklearn.decomposition.incremental_pca
Expand Down Expand Up @@ -855,46 +698,6 @@ def local_correlations(self,

return Cn

def partition_FOV_KMeans(self,
tradeoff_weight: float = .5,
fx: float = .25,
fy: float = .25,
n_clusters: int = 4,
max_iter: int = 500) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""
Partition the FOV in clusters that are grouping pixels close in space and in mutual correlation

Args:
tradeoff_weight:between 0 and 1 will weight the contributions of distance and correlation in the overall metric

fx,fy: downsampling factor to apply to the movie

n_clusters,max_iter: KMeans algorithm parameters

Returns:
fovs:array 2D encoding the partitions of the FOV

mcoef: matrix of pairwise correlation coefficients

distanceMatrix: matrix of picel distances
"""
_, h1, w1 = self.shape
self.resize(fx, fy)
T, h, w = self.shape
Y = np.reshape(self, (T, h * w))
mcoef = np.corrcoef(Y.T)

idxA, idxB = np.meshgrid(list(range(w)), list(range(h)))
coordmat = np.vstack((idxA.flatten(), idxB.flatten()))
distanceMatrix = sklearn.metrics.pairwise.euclidean_distances(coordmat.T)
distanceMatrix = distanceMatrix / np.max(distanceMatrix)
estim = sklearn.cluster.KMeans(n_clusters=n_clusters, max_iter=max_iter)
kk = estim.fit(tradeoff_weight * mcoef - (1 - tradeoff_weight) * distanceMatrix)
labs = kk.labels_
fovs = np.reshape(labs, (h, w))
fovs = cv2.resize(np.uint8(fovs), (w1, h1), 1. / fx, 1. / fy, interpolation=cv2.INTER_NEAREST)
return np.uint8(fovs), mcoef, distanceMatrix

def extract_traces_from_masks(self, masks: np.ndarray) -> caiman.base.traces.trace:
"""
Args:
Expand Down Expand Up @@ -978,19 +781,6 @@ def resize(self, fx=1, fy=1, fz=1, interpolation=cv2.INTER_AREA):

return self

def guided_filter_blur_2D(self, guide_filter, radius: int = 5, eps=0):
"""
performs guided filtering on each frame. See opencv documentation of cv2.ximgproc.guidedFilter
"""
logger = logging.getLogger("caiman")

for idx, fr in enumerate(self):
if idx % 1000 == 0:
logger.debug(f"At index: {idx}")
self[idx] = cv2.ximgproc.guidedFilter(guide_filter, fr, radius=radius, eps=eps)

return self

def bilateral_blur_2D(self, diameter: int = 5, sigmaColor: int = 10000, sigmaSpace=0):
"""
performs bilateral filtering on each frame. See opencv documentation of cv2.bilateralFilter
Expand Down Expand Up @@ -1071,33 +861,6 @@ def to_2D(self, order='F') -> np.ndarray:
T = self.shape[0]
return np.reshape(self, (T, -1), order=order)

def zproject(self, method: str = 'mean', cmap=matplotlib.cm.gray, aspect='auto', **kwargs) -> np.ndarray:
"""
Compute and plot projection across time:

Args:
method: String
'mean','median','std'

**kwargs: dict
arguments to imagesc

Raises:
Exception 'Method not implemented'
"""
# TODO: make the imshow optional
# TODO: todocument
if method == 'mean':
zp = np.mean(self, axis=0)
elif method == 'median':
zp = np.median(self, axis=0)
elif method == 'std':
zp = np.std(self, axis=0)
else:
raise Exception('Method not implemented')
plt.imshow(zp, cmap=cmap, aspect=aspect, **kwargs)
return zp

def play(self,
gain: float = 1,
fr=None,
Expand Down Expand Up @@ -2340,3 +2103,4 @@ def view(button):
def rgb2gray(rgb):
# Standard mathematical conversion
return np.dot(rgb[..., :3], [0.299, 0.587, 0.114])

3 changes: 2 additions & 1 deletion caiman/caimanmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
# standard_movies: These are needed by the demo
standard_movies = [
os.path.join('example_movies', 'data_endoscope.tif'),
os.path.join('example_movies', 'demoMovie.tif')
os.path.join('example_movies', 'demoMovie.tif'),
os.path.join('example_movies', 'avg_mask_fixed.png')
]

###############
Expand Down
1 change: 1 addition & 0 deletions caiman/motion_correction.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ def apply_shifts_movie(self, fname, rigid_shifts: Optional[bool] = None, save_me
if save_memmap:
dims = m_reg.shape
fname_tot = caiman.paths.memmap_frames_filename(save_base_name, dims[1:], dims[0], order)
fname_tot = caiman.paths.fn_relocated(fname_tot)
big_mov = np.memmap(fname_tot, mode='w+', dtype=np.float32,
shape=caiman.mmapping.prepare_shape((np.prod(dims[1:]), dims[0])), order=order)
big_mov[:] = np.reshape(m_reg.transpose(1, 2, 0), (np.prod(dims[1:]), dims[0]), order='F')
Expand Down
Loading