24. LBM Step 4: Collate Planes#

%gui qt
import sys
from pathlib import Path
import os
import numpy as np
import zarr
import napari

import logging
import lbm_caiman_python as lbm

# Importing this WONT WORK withotu adding to the path
sys.path.append('../..')  # TODO: Take this out when we upload to pypi

import pandas as pd
import mesmerize_core as mc
from mesmerize_viz import *
from mesmerize_core.caiman_extensions.cnmf import cnmf_cache

try:
    if __IPYTHON__:
        get_ipython().run_line_magic('load_ext', 'autoreload')
        get_ipython().run_line_magic('autoreload', '2')
except NameError:
    pass
try:
    import cv2
    cv2.setNumThreads(0)
except():
    pass

logging.basicConfig()

os.environ["CONDA_PREFIX_1"] = ""
if os.name == "nt":
    # disable the cache on windows, this will be automatic in a future version
    cnmf_cache.set_maxsize(0)
The autoreload extension is already loaded. To reload it, use:
  %reload_ext autoreload
chan_order = (
        np.array(
            [
                1,
                5,
                6,
                7,
                8,
                9,
                2,
                10,
                11,
                12,
                13,
                14,
                15,
                16,
                17,
                3,
                18,
                19,
                20,
                21,
                22,
                23,
                4,
                24,
                25,
                26,
                27,
                28,
            ]
        )
        - 1
)
parent_path = Path().home() / "caiman_data" / 'animal_01' / "session_01"

batch_path = parent_path / 'batch.pickle'
cnmf_batch_path = parent_path / 'results' / 'cnmf_batch.pickle'

# you could alos load the registration batch and
# save this patch in a new dataframe (saved to disk automatically)
cnmf_df = mc.load_batch(cnmf_batch_path)
cnmf_df=cnmf_df.caiman.reload_from_disk()
mc.set_parent_raw_data_path(str(parent_path))

# create a new batch
try:
    df = mc.load_batch(batch_path)
except (IsADirectoryError, FileNotFoundError):
    df = mc.create_batch(batch_path)

df=df.caiman.reload_from_disk()
cnmf_df
algo item_name input_movie_path params outputs added_time ran_time algo_duration comments uuid
0 cnmf cnmf_1 tiff\extracted_plane_1.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': faeb27e8-40b9-4e5a-ad... 2024-10-01T16:34:44 2024-10-01T16:54:16 946.94 sec None faeb27e8-40b9-4e5a-ad8c-b9cc73c03635
1 cnmf cnmf_1 tiff\extracted_plane_2.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 89b676c7-9480-47d7-b1... 2024-10-01T16:34:44 2024-10-01T17:10:06 949.6 sec None 89b676c7-9480-47d7-b190-e4bbc2a5decc
2 cnmf cnmf_1 tiff\extracted_plane_3.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': f03ead24-ab13-48e7-87... 2024-10-01T16:34:44 2024-10-01T17:28:57 1131.04 sec None f03ead24-ab13-48e7-87c0-27d5940a7c91
3 cnmf cnmf_1 tiff\extracted_plane_4.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 585a0fc2-40d4-45f2-ba... 2024-10-01T16:34:44 2024-10-01T17:51:18 1340.32 sec None 585a0fc2-40d4-45f2-badf-ba13d5cc77a6
4 cnmf cnmf_1 tiff\extracted_plane_5.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 544c2a52-0b90-4b74-ba... 2024-10-01T16:34:44 2024-10-01T18:08:22 1024.35 sec None 544c2a52-0b90-4b74-baae-b2ca913c7c2a
5 cnmf cnmf_1 tiff\extracted_plane_6.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': d6c3f33c-2b90-4c6d-a4... 2024-10-01T16:34:44 2024-10-01T18:24:00 937.73 sec None d6c3f33c-2b90-4c6d-a4ad-f237dae5f73b
6 cnmf cnmf_1 tiff\extracted_plane_7.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': ca269a68-06d0-4f96-b5... 2024-10-01T16:34:44 2024-10-01T18:39:39 939.58 sec None ca269a68-06d0-4f96-b508-87160fdf37ab
7 cnmf cnmf_1 tiff\extracted_plane_8.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': a9cb2077-073a-4552-96... 2024-10-01T16:34:44 2024-10-01T18:55:09 929.57 sec None a9cb2077-073a-4552-9669-e53ba0c96803
8 cnmf cnmf_1 tiff\extracted_plane_9.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': c0205f69-ccb6-4304-9c... 2024-10-01T16:34:44 2024-10-01T19:10:49 940.35 sec None c0205f69-ccb6-4304-9cee-18af09d11663
9 cnmf cnmf_1 tiff\extracted_plane_10.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 1fac642d-6cef-4a68-94... 2024-10-01T16:34:44 2024-10-01T19:28:15 1045.02 sec None 1fac642d-6cef-4a68-9415-737213b4462d
10 cnmf cnmf_1 tiff\extracted_plane_11.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 97569a0f-b5c4-41b5-92... 2024-10-01T16:34:44 2024-10-01T19:45:46 1051.62 sec None 97569a0f-b5c4-41b5-9268-b4ddeda9d740
11 cnmf cnmf_1 tiff\extracted_plane_12.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': c2d0864d-1313-4ded-8b... 2024-10-01T16:34:44 2024-10-01T20:01:40 953.4 sec None c2d0864d-1313-4ded-8b7f-4a7f02947b2d
12 cnmf cnmf_1 tiff\extracted_plane_13.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 8301c084-fe7c-4e84-b8... 2024-10-01T16:34:44 2024-10-01T20:17:40 960.7 sec None 8301c084-fe7c-4e84-b8d7-db6fcc8893ed
13 cnmf cnmf_1 tiff\extracted_plane_14.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 1eabd9f9-1a57-4c08-a8... 2024-10-01T16:34:44 2024-10-01T20:33:51 970.13 sec None 1eabd9f9-1a57-4c08-a830-9276371c00a9
14 cnmf cnmf_1 tiff\extracted_plane_15.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 25be7a5b-dd16-42cd-bd... 2024-10-01T16:34:44 2024-10-01T20:52:06 1095.19 sec None 25be7a5b-dd16-42cd-bd4a-1312b4c6e791
15 cnmf cnmf_1 tiff\extracted_plane_16.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': f87dc096-6bb2-40dd-8d... 2024-10-01T16:34:44 2024-10-01T21:08:23 977.02 sec None f87dc096-6bb2-40dd-8dff-20f1362423b0
16 cnmf cnmf_1 tiff\extracted_plane_17.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 0e6f8223-3002-4430-a9... 2024-10-01T16:34:44 2024-10-01T21:28:21 1197.8 sec None 0e6f8223-3002-4430-a987-d83a6d7c7bd8
17 cnmf cnmf_1 tiff\extracted_plane_18.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 52ab80e3-880f-4ae5-bd... 2024-10-01T16:34:44 2024-10-01T21:46:45 1104.12 sec None 52ab80e3-880f-4ae5-bd38-711c0138ebe2
18 cnmf cnmf_1 tiff\extracted_plane_19.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 65c8dfa4-2a65-4618-82... 2024-10-01T16:34:44 2024-10-01T22:05:32 1126.88 sec None 65c8dfa4-2a65-4618-8240-7f08fccce4ad
19 cnmf cnmf_1 tiff\extracted_plane_20.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 473b088b-1b8e-4ecb-97... 2024-10-01T16:34:44 2024-10-01T22:23:00 1048.28 sec None 473b088b-1b8e-4ecb-97e2-75eb299851b2
20 cnmf cnmf_1 tiff\extracted_plane_21.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 60d2fc66-20a9-4f00-b6... 2024-10-01T16:34:44 2024-10-01T22:41:10 1090.04 sec None 60d2fc66-20a9-4f00-b6f7-b9e80440efe7
21 cnmf cnmf_1 tiff\extracted_plane_22.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 8926526d-cc18-47e2-b5... 2024-10-01T16:34:44 2024-10-01T23:01:26 1215.23 sec None 8926526d-cc18-47e2-b599-1496b2c9f03e
22 cnmf cnmf_1 tiff\extracted_plane_23.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': f2b1c811-e02a-4058-a1... 2024-10-01T16:34:44 2024-10-01T23:20:00 1114.08 sec None f2b1c811-e02a-4058-a15b-861ee1128793
23 cnmf cnmf_1 tiff\extracted_plane_24.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 5c9bd42e-fb07-43f0-a1... 2024-10-01T16:34:44 2024-10-01T23:38:35 1114.86 sec None 5c9bd42e-fb07-43f0-a18d-0b9e516db4ec
24 cnmf cnmf_1 tiff\extracted_plane_25.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 84004c56-81c1-474b-ab... 2024-10-01T16:34:44 2024-10-01T23:57:28 1132.94 sec None 84004c56-81c1-474b-ab38-327fbf79bf8c
25 cnmf cnmf_1 tiff\extracted_plane_26.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 23521021-6791-4d41-85... 2024-10-01T16:34:44 2024-10-02T00:14:28 1019.75 sec None 23521021-6791-4d41-855d-779eefce6840
26 cnmf cnmf_1 tiff\extracted_plane_27.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 07907a0b-b138-41e3-99... 2024-10-01T16:34:44 2024-10-02T00:34:07 1179.51 sec None 07907a0b-b138-41e3-99a7-f7153738a640
27 cnmf cnmf_1 tiff\extracted_plane_28.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 8b7cbfb4-7c62-4780-a9... 2024-10-01T16:34:44 2024-10-02T00:49:55 947.96 sec None 8b7cbfb4-7c62-4780-a90c-5275cbe1f318

24.1. Get outputs for each z-plane#

results = {}
for i, row in cnmf_df.iterrows():
    if row["outputs"] is not None and row['algo']=='cnmf': # item has already been run
        results[i] = cnmf_df.iloc[i].cnmf.get_output()
results
{0: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18ed447b1f0>,
 1: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18ed447b730>,
 2: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18ed447b340>,
 3: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18ed4479e10>,
 4: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18ed447b0a0>,
 5: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3ba3e0>,
 6: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3ba980>,
 7: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3baa40>,
 8: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3b8a60>,
 9: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3b83d0>,
 10: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3baa70>,
 11: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3bb790>,
 12: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18ed447b4c0>,
 13: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3bada0>,
 14: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3b81f0>,
 15: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3a9c90>,
 16: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3aa5f0>,
 17: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3a8be0>,
 18: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3aaf50>,
 19: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3aa020>,
 20: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3aa170>,
 21: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3bb1f0>,
 22: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18ed447b2b0>,
 23: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3a9a80>,
 24: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3a09d0>,
 25: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3a0820>,
 26: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18e6b3a3010>,
 27: <caiman.source_extraction.cnmf.cnmf.CNMF at 0x18ed447be20>}
e1 = results[0].estimates
e2 = results[1].estimates
import numpy as np
from scipy.sparse import csr_matrix, vstack
from caiman.source_extraction.cnmf import estimates
from scipy.sparse import lil_matrix
%load_ext autoreload
%autoreload 2
import lbm_caiman_python as lbm

res = lbm.collation.combine_z_planes(results)
The autoreload extension is already loaded. To reload it, use:
  %reload_ext autoreload

24.2. Total number of Neurons#

comps = {}
for i, row in cnmf_df.iterrows():
    if row["outputs"] is not None and row['algo']=='cnmf': # item has already been run
        comps[i] = cnmf_df.iloc[i].cnmf.get_good_components()
comps
{0: array([   1,    2,    4, ..., 8919, 8920, 8921]),
 1: array([   0,    1,    2, ..., 9015, 9017, 9018]),
 2: array([   0,    1,    2, ..., 8788, 8789, 8791]),
 3: array([    1,     3,     4, ..., 10061, 10063, 10065]),
 4: array([   0,    1,    4, ..., 8774, 8775, 8776]),
 5: array([   0,    1,    3, ..., 8845, 8846, 8848]),
 6: array([   0,    1,    2, ..., 8814, 8816, 8818]),
 7: array([   0,    1,    2, ..., 8846, 8847, 8848]),
 8: array([   0,    1,    3, ..., 8880, 8881, 8882]),
 9: array([   0,    2,    3, ..., 9042, 9043, 9044]),
 10: array([   3,    5,    6, ..., 9312, 9313, 9314]),
 11: array([   1,    7,    9, ..., 9460, 9461, 9462]),
 12: array([   4,    5,    9, ..., 9752, 9754, 9755]),
 13: array([   1,    3,    4, ..., 9915, 9917, 9918]),
 14: array([    0,     1,     3, ..., 10016, 10048, 10051]),
 15: array([    0,     1,     2, ..., 10127, 10128, 10129]),
 16: array([    0,     1,     2, ..., 10024, 10025, 10026]),
 17: array([   0,    1,    2, ..., 9151, 9152, 9154]),
 18: array([   0,    1,    2, ..., 9234, 9235, 9236]),
 19: array([   0,    1,    2, ..., 9453, 9456, 9457]),
 20: array([   0,    5,    8, ..., 9821, 9822, 9823]),
 21: array([   0,    4,    7, ..., 9972, 9973, 9974]),
 22: array([    0,     6,    22, ..., 10088, 10090, 10091]),
 23: array([    0,     2,     5, ..., 10213, 10214, 10215]),
 24: array([    1,    31,    48, ..., 10341, 10344, 10345]),
 25: array([    6,     9,    36, ..., 10460, 10472, 10475]),
 26: array([   16,    32,    45, ..., 10406, 10408, 10412]),
 27: array([    2,    18,    27, ..., 10316, 10317, 10324])}
num_components = [len(c) for c in comps.values()]
sum(num_components)
73973
from ipywidgets import Tab, interact_manual
@interact_manual(parent_path=str(parent_path), batch_path=str(cnmf_batch_path))
def start_widget(parent_path, batch_path):
    mc.set_parent_raw_data_path(parent_path)

    df = mc.load_batch(batch_path)

    tab = Tab()

    # mcorr_container = df.mcorr.viz()
    cnmf_container = df.cnmf.viz(start_index=1)

    tab.children = [cnmf_container.show()]
    tab.titles = ["cnmf"]
    display(tab)

24.3. Combine results of different z_planes#

df_rows = cnmf_df.iloc[cnmf_df[cnmf_df['algo']=='cnmf'].index].copy()
df_rows
algo item_name input_movie_path params outputs added_time ran_time algo_duration comments uuid
0 cnmf cnmf_1 tiff\extracted_plane_1.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': faeb27e8-40b9-4e5a-ad... 2024-10-01T16:34:44 2024-10-01T16:54:16 946.94 sec None faeb27e8-40b9-4e5a-ad8c-b9cc73c03635
1 cnmf cnmf_1 tiff\extracted_plane_2.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 89b676c7-9480-47d7-b1... 2024-10-01T16:34:44 2024-10-01T17:10:06 949.6 sec None 89b676c7-9480-47d7-b190-e4bbc2a5decc
2 cnmf cnmf_1 tiff\extracted_plane_3.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': f03ead24-ab13-48e7-87... 2024-10-01T16:34:44 2024-10-01T17:28:57 1131.04 sec None f03ead24-ab13-48e7-87c0-27d5940a7c91
3 cnmf cnmf_1 tiff\extracted_plane_4.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 585a0fc2-40d4-45f2-ba... 2024-10-01T16:34:44 2024-10-01T17:51:18 1340.32 sec None 585a0fc2-40d4-45f2-badf-ba13d5cc77a6
4 cnmf cnmf_1 tiff\extracted_plane_5.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 544c2a52-0b90-4b74-ba... 2024-10-01T16:34:44 2024-10-01T18:08:22 1024.35 sec None 544c2a52-0b90-4b74-baae-b2ca913c7c2a
5 cnmf cnmf_1 tiff\extracted_plane_6.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': d6c3f33c-2b90-4c6d-a4... 2024-10-01T16:34:44 2024-10-01T18:24:00 937.73 sec None d6c3f33c-2b90-4c6d-a4ad-f237dae5f73b
6 cnmf cnmf_1 tiff\extracted_plane_7.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': ca269a68-06d0-4f96-b5... 2024-10-01T16:34:44 2024-10-01T18:39:39 939.58 sec None ca269a68-06d0-4f96-b508-87160fdf37ab
7 cnmf cnmf_1 tiff\extracted_plane_8.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': a9cb2077-073a-4552-96... 2024-10-01T16:34:44 2024-10-01T18:55:09 929.57 sec None a9cb2077-073a-4552-9669-e53ba0c96803
8 cnmf cnmf_1 tiff\extracted_plane_9.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': c0205f69-ccb6-4304-9c... 2024-10-01T16:34:44 2024-10-01T19:10:49 940.35 sec None c0205f69-ccb6-4304-9cee-18af09d11663
9 cnmf cnmf_1 tiff\extracted_plane_10.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 1fac642d-6cef-4a68-94... 2024-10-01T16:34:44 2024-10-01T19:28:15 1045.02 sec None 1fac642d-6cef-4a68-9415-737213b4462d
10 cnmf cnmf_1 tiff\extracted_plane_11.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 97569a0f-b5c4-41b5-92... 2024-10-01T16:34:44 2024-10-01T19:45:46 1051.62 sec None 97569a0f-b5c4-41b5-9268-b4ddeda9d740
11 cnmf cnmf_1 tiff\extracted_plane_12.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': c2d0864d-1313-4ded-8b... 2024-10-01T16:34:44 2024-10-01T20:01:40 953.4 sec None c2d0864d-1313-4ded-8b7f-4a7f02947b2d
12 cnmf cnmf_1 tiff\extracted_plane_13.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 8301c084-fe7c-4e84-b8... 2024-10-01T16:34:44 2024-10-01T20:17:40 960.7 sec None 8301c084-fe7c-4e84-b8d7-db6fcc8893ed
13 cnmf cnmf_1 tiff\extracted_plane_14.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 1eabd9f9-1a57-4c08-a8... 2024-10-01T16:34:44 2024-10-01T20:33:51 970.13 sec None 1eabd9f9-1a57-4c08-a830-9276371c00a9
14 cnmf cnmf_1 tiff\extracted_plane_15.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 25be7a5b-dd16-42cd-bd... 2024-10-01T16:34:44 2024-10-01T20:52:06 1095.19 sec None 25be7a5b-dd16-42cd-bd4a-1312b4c6e791
15 cnmf cnmf_1 tiff\extracted_plane_16.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': f87dc096-6bb2-40dd-8d... 2024-10-01T16:34:44 2024-10-01T21:08:23 977.02 sec None f87dc096-6bb2-40dd-8dff-20f1362423b0
16 cnmf cnmf_1 tiff\extracted_plane_17.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 0e6f8223-3002-4430-a9... 2024-10-01T16:34:44 2024-10-01T21:28:21 1197.8 sec None 0e6f8223-3002-4430-a987-d83a6d7c7bd8
17 cnmf cnmf_1 tiff\extracted_plane_18.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 52ab80e3-880f-4ae5-bd... 2024-10-01T16:34:44 2024-10-01T21:46:45 1104.12 sec None 52ab80e3-880f-4ae5-bd38-711c0138ebe2
18 cnmf cnmf_1 tiff\extracted_plane_19.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 65c8dfa4-2a65-4618-82... 2024-10-01T16:34:44 2024-10-01T22:05:32 1126.88 sec None 65c8dfa4-2a65-4618-8240-7f08fccce4ad
19 cnmf cnmf_1 tiff\extracted_plane_20.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 473b088b-1b8e-4ecb-97... 2024-10-01T16:34:44 2024-10-01T22:23:00 1048.28 sec None 473b088b-1b8e-4ecb-97e2-75eb299851b2
20 cnmf cnmf_1 tiff\extracted_plane_21.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 60d2fc66-20a9-4f00-b6... 2024-10-01T16:34:44 2024-10-01T22:41:10 1090.04 sec None 60d2fc66-20a9-4f00-b6f7-b9e80440efe7
21 cnmf cnmf_1 tiff\extracted_plane_22.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 8926526d-cc18-47e2-b5... 2024-10-01T16:34:44 2024-10-01T23:01:26 1215.23 sec None 8926526d-cc18-47e2-b599-1496b2c9f03e
22 cnmf cnmf_1 tiff\extracted_plane_23.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': f2b1c811-e02a-4058-a1... 2024-10-01T16:34:44 2024-10-01T23:20:00 1114.08 sec None f2b1c811-e02a-4058-a15b-861ee1128793
23 cnmf cnmf_1 tiff\extracted_plane_24.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 5c9bd42e-fb07-43f0-a1... 2024-10-01T16:34:44 2024-10-01T23:38:35 1114.86 sec None 5c9bd42e-fb07-43f0-a18d-0b9e516db4ec
24 cnmf cnmf_1 tiff\extracted_plane_25.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 84004c56-81c1-474b-ab... 2024-10-01T16:34:44 2024-10-01T23:57:28 1132.94 sec None 84004c56-81c1-474b-ab38-327fbf79bf8c
25 cnmf cnmf_1 tiff\extracted_plane_26.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 23521021-6791-4d41-85... 2024-10-01T16:34:44 2024-10-02T00:14:28 1019.75 sec None 23521021-6791-4d41-855d-779eefce6840
26 cnmf cnmf_1 tiff\extracted_plane_27.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 07907a0b-b138-41e3-99... 2024-10-01T16:34:44 2024-10-02T00:34:07 1179.51 sec None 07907a0b-b138-41e3-99a7-f7153738a640
27 cnmf cnmf_1 tiff\extracted_plane_28.tif {'main': {'fr': 9.62, 'dxy': (1.0, 1.0), 'deca... {'mean-projection-path': 8b7cbfb4-7c62-4780-a9... 2024-10-01T16:34:44 2024-10-02T00:49:55 947.96 sec None 8b7cbfb4-7c62-4780-a90c-5275cbe1f318
import dask.array as da
projs = da.stack([da.from_array(row.caiman.get_projection('mean')) for i, row in df_rows.iterrows() if row["outputs"] is not None and row['algo']=='cnmf'])
projs.shape
(28, 583, 536)
# sort first index according to chan_order
projs = projs[chan_order]
import fastplotlib as fpl

widget = fpl.ImageWidget(projs)
widget.show()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[61], line 3
      1 import fastplotlib as fpl
----> 3 widget = fpl.ImageWidget(projs)
      4 widget.show()

File ~\anaconda3\envs\mescore\lib\site-packages\fastplotlib\widgets\image.py:577, in ImageWidget.__init__(self, data, dims_order, slider_dims, window_funcs, frame_apply, grid_shape, names, grid_plot_kwargs, histogram_widget, **kwargs)
    574 subplot.set_title(name)
    576 if histogram_widget:
--> 577     hlut = HistogramLUT(
    578         data=d,
    579         image_graphic=ig,
    580         name="histogram_lut"
    581     )
    583     subplot.docks["right"].add_graphic(hlut)
    584     subplot.docks["right"].size = 80

File ~\anaconda3\envs\mescore\lib\site-packages\fastplotlib\widgets\histogram_lut.py:44, in HistogramLUT.__init__(self, data, image_graphic, nbins, flank_divisor, **kwargs)
     40 self._data = weakref.proxy(data)
     42 self._scale_factor: float = 1.0
---> 44 hist, edges, hist_scaled, edges_flanked = self._calculate_histogram(data)
     46 line_data = np.column_stack([hist_scaled, edges_flanked])
     48 self.line = LineGraphic(line_data)

File ~\anaconda3\envs\mescore\lib\site-packages\fastplotlib\widgets\histogram_lut.py:146, in HistogramLUT._calculate_histogram(self, data)
    142     ss2 = max(1, int(data.shape[2] / 100))
    144     data_ss = data[::ss0, ::ss1, ::ss2]
--> 146     hist, edges = np.histogram(data_ss, bins=self._nbins)
    148 else:
    149     # allow max of 1000 x 1000
    150     # this takes ~4ms on a 8 core Ryzen laptop
    151     ss0 = max(1, int(data.shape[0] / 1_000))

File ~\anaconda3\envs\mescore\lib\site-packages\dask\array\core.py:1756, in Array.__array_function__(self, func, types, args, kwargs)
   1753 if has_keyword(da_func, "like"):
   1754     kwargs["like"] = self
-> 1756 return da_func(*args, **kwargs)

File ~\anaconda3\envs\mescore\lib\site-packages\dask\array\routines.py:962, in histogram(a, bins, range, normed, weights, density)
    959     scalar_bins = np.ndim(bins) == 0
    961 if bins is None or (scalar_bins and range is None):
--> 962     raise ValueError(
    963         "dask.array.histogram requires either specifying "
    964         "bins as an iterable or specifying both a range and "
    965         "the number of bins"
    966     )
    968 if weights is not None and weights.chunks != a.chunks:
    969     raise ValueError("Input array and weights must have the same chunked structure")

ValueError: dask.array.histogram requires either specifying bins as an iterable or specifying both a range and the number of bins
widget.close()
import napari
napari.view_image(projs, name = 'z-stack')
Viewer(camera=Camera(center=(0.0, 291.0, 267.5), zoom=1.0244402985074625, angles=(0.0, 0.0, 90.0), perspective=0.0, mouse_pan=True, mouse_zoom=True), cursor=Cursor(position=(13.0, 1.0, 0.0), scaled=True, style=<CursorStyle.STANDARD: 'standard'>, size=1.0), dims=Dims(ndim=3, ndisplay=2, order=(0, 1, 2), axis_labels=('0', '1', '2'), rollable=(True, True, True), range=(RangeTuple(start=0.0, stop=27.0, step=1.0), RangeTuple(start=0.0, stop=582.0, step=1.0), RangeTuple(start=0.0, stop=535.0, step=1.0)), margin_left=(0.0, 0.0, 0.0), margin_right=(0.0, 0.0, 0.0), point=(13.0, 291.0, 267.0), last_used=0), grid=GridCanvas(stride=1, shape=(-1, -1), enabled=False), layers=[<Image layer 'z-stack' at 0x196004b0910>], help='use <2> for transform', status='Ready', tooltip=Tooltip(visible=False, text=''), theme='dark', title='napari', mouse_over_canvas=False, mouse_move_callbacks=[], mouse_drag_callbacks=[], mouse_double_click_callbacks=[], mouse_wheel_callbacks=[<function dims_scroll at 0x00000195FCF8DBD0>], _persisted_mouse_event={}, _mouse_drag_gen={}, _mouse_wheel_gen={}, _keymap={})

24.4. z-stack alignment#

24.5. 1. Align each plane to the reference plane#

reference_image = projs.mean(axis=0).compute()

24.6. 2. Align each plane to the reference plane#

from skimage.registration import phase_cross_correlation
from scipy.ndimage import shift
import dask
from dask import delayed
import napari

def compute_shift_and_align(image, template):
    shift_estimate, error, diffphase = phase_cross_correlation(template, image)
    print(f"Shift estimate: {shift_estimate}")
    return shift(image, shift=(-shift_estimate[0], -shift_estimate[1]), mode='constant', cval=0)

compute_shift_and_align_delayed = delayed(compute_shift_and_align)

aligned_images = [
    compute_shift_and_align_delayed(projs[i].compute(), reference_image)
    for i in range(projs.shape[0])
]

aligned_images = dask.compute(*aligned_images)
aligned_stack = np.stack(aligned_images, axis=0)
aligned_stack = da.from_array(aligned_stack)
def compute_shift_and_align_pair(image1, image2):
    shift_estimate, error, diffphase = phase_cross_correlation(image1, image2)
    print(f"Shift estimate between planes: {shift_estimate}")
    # Align image2 to image1
    aligned_image = shift(image2, shift=(-shift_estimate[0], -shift_estimate[1]), mode='constant', cval=0)
    return aligned_image, shift_estimate

aligned_images = []
shift_estimates = []

# Use the first image as the starting point
aligned_images.append(projs[0].compute())

for i in range(1, projs.shape[0]):
    image1 = aligned_images[-1]
    image2 = projs[i].compute()
    aligned_image, shift_estimate = compute_shift_and_align_pair(image1, image2)
    aligned_images.append(aligned_image)
    shift_estimates.append(shift_estimate)

aligned_stack = np.stack(aligned_images, axis=0)
Shift estimate between planes: [ 0. 18.]
Shift estimate between planes: [0. 0.]
Shift estimate between planes: [ 0. 16.]
Shift estimate between planes: [ 0. -1.]
Shift estimate between planes: [ 0. 16.]
Shift estimate between planes: [0. 9.]
Shift estimate between planes: [  0. -22.]
Shift estimate between planes: [ 0. 48.]
Shift estimate between planes: [  0. -36.]
Shift estimate between planes: [ 0. 48.]
Shift estimate between planes: [  0. -36.]
Shift estimate between planes: [ 0. 48.]
Shift estimate between planes: [  0. -36.]
Shift estimate between planes: [ 0. 48.]
Shift estimate between planes: [   5. -266.]
Shift estimate between planes: [  -5. -250.]
Shift estimate between planes: [  5. 250.]
Shift estimate between planes: [  -5. -234.]
Shift estimate between planes: [  5. 249.]
Shift estimate between planes: [230. -35.]
Shift estimate between planes: [-230.   48.]
Shift estimate between planes: [230. -35.]
Shift estimate between planes: [-230.   48.]
Shift estimate between planes: [230. -35.]
Shift estimate between planes: [-230.   47.]
Shift estimate between planes: [230. -35.]
Shift estimate between planes: [-230.   47.]
def visualize_transform(image_1, image_2):
    from dipy.align.transforms import AffineTransform2D
    from dipy.align.imaffine import AffineRegistration
    import matplotlib.pyplot as plt
    
    affreg = AffineRegistration()
    transform = AffineTransform2D()
    affine = affreg.optimize(image_1, image_2, transform, params0=None)
    
    image_2_xform = affine.transform(image_2)

    fig, axes = plt.subplots(1, 3, figsize=(8, 4))
    ax = axes.ravel()

    ax[0].imshow(image_1)
    ax[1].imshow(image_2)

    stereo = np.zeros((image_1.shape[0], image_1.shape[1], 3), dtype=np.uint8)
    stereo[..., 0] = image_1
    stereo[..., 1] = image_2_xform
    ax[2].imshow(stereo)
    fig.tight_layout()
    return image_2_xform

tranformed = visualize_transform(projs[0].compute(), projs[1].compute())
data = np.stack([projs[0].compute(), tranformed], axis=0)
viewer = napari.view_image(data, name='Aligned Z-Planes')
Optimizing level 2 [max iter: 10000]
Optimizing level 1 [max iter: 1000]
Optimizing level 0 [max iter: 100]
../_images/a1cc60bbe9c45869a95380518679b1039fe9db0935998540d0c591a67dc9bb2c.png
shift_estimates = np.array(shift_estimates)
plt.figure()
plt.plot(shift_estimates[:, 1], label='Shift X')
plt.plot(shift_estimates[:, 0], label='Shift Y')
plt.legend()
plt.title('Shift Estimates Between Consecutive Planes')
plt.xlabel('Z-plane Index')
plt.ylabel('Shift (pixels)')
plt.show()
../_images/13c64d0eb37fd51b4a8e95a1d7be791eab255042f12ad071d7c07f498d44feff.png
viewer = napari.view_image(projs, name = 'z-stack')
viewer.add_image(aligned_stack, name = 'aligned z-stack')
<Image layer 'aligned z-stack' at 0x196bec56d10>
import numpy as np
from skimage.registration import phase_cross_correlation
from scipy.ndimage import shift
import napari
import matplotlib.pyplot as plt

def compute_horizontal_shift_and_align_pair(image1, image2):
    """
    Compute the horizontal shift between two images and align image2 to image1.
    
    Parameters:
        image1 (ndarray): The reference image (previous z-plane).
        image2 (ndarray): The image to align (current z-plane).
        
    Returns:
        aligned_image (ndarray): The aligned image.
        x_shift (float): The estimated horizontal shift.
    """
    # Compute the shift using phase cross-correlation
    shift_estimate, error, diffphase = phase_cross_correlation(image1, image2)

    # Only take the shift along x-axis (horizontal shift)
    x_shift = shift_estimate[1]
    print(f"Horizontal shift estimate between planes: {x_shift:.2f} pixels")

    aligned_image = shift(image2, shift=(0, -x_shift), mode='constant', cval=0)
    return aligned_image, x_shift

# Compute the first image and add it to the aligned_images list
first_image = projs[0].compute()
second_image = projs[1].compute()

# Align the second image to the first image using only horizontal shifts
aligned_image, x_shift = compute_horizontal_shift_and_align_pair(first_image, second_image)

data = np.stack([first_image, aligned_image], axis=0)
viewer = napari.view_image(data, name='Aligned Z-Planes')
Horizontal shift estimate between planes: 18.00 pixels
# Initialize lists to store aligned images and shift estimates

# Iterate through each pair of z-planes
for i in range(1, projs.shape[0]):
    # Get the previous aligned image
    image1 = aligned_images[-1]

    # Compute the current image
    image2 = projs[i].compute()

    # Align image2 to image1 using only horizontal shifts
    aligned_image, x_shift = compute_horizontal_shift_and_align_pair(image1, image2)

    # Append the aligned image and shift estimate to the lists
    aligned_images.append(aligned_image)
    shift_estimates.append(x_shift)

# Stack the aligned images into a 3D array
aligned_stack = np.stack(aligned_images, axis=0)

# Visualize the horizontal shift estimates
plt.figure(figsize=(10, 5))
plt.plot(range(1, len(shift_estimates) + 1), shift_estimates, marker='o')
plt.title('Horizontal Shift Estimates Between Consecutive Z-Planes')
plt.xlabel('Z-plane Index')
plt.ylabel('Horizontal Shift (pixels)')
plt.grid(True)
plt.show()

# Visualize the original and aligned stacks using napari
viewer = napari.view_image(projs.compute(), name='Original Z-Stack')
viewer.add_image(aligned_stack, name='Aligned Z-Stack')