labdata 0.1.2__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {labdata-0.1.2 → labdata-0.1.4}/PKG-INFO +1 -1
- {labdata-0.1.2 → labdata-0.1.4}/labdata/__init__.py +1 -1
- {labdata-0.1.2 → labdata-0.1.4}/labdata/compute/caiman.py +7 -5
- {labdata-0.1.2 → labdata-0.1.4}/labdata/compute/suite2p.py +2 -1
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/segmentation.py +144 -82
- {labdata-0.1.2 → labdata-0.1.4}/labdata.egg-info/PKG-INFO +1 -1
- {labdata-0.1.2 → labdata-0.1.4}/LICENSE +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/README.md +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/cli.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/compute/__init__.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/compute/ec2.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/compute/ephys.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/compute/pose.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/compute/schedulers.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/compute/singularity.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/compute/utils.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/copy.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/__init__.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/compute.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/console.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/decisiontask.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/explorer/__init__.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/explorer/cellsegmentation.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/explorer/spikesorting.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/index.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/notes.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/procedures.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/sorting.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/dashboard/video.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/export.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/rules/__init__.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/rules/ephys.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/rules/imaging.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/rules/utils.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/s3.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/schema/__init__.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/schema/ephys.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/schema/general.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/schema/histology.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/schema/onephoton.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/schema/procedures.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/schema/tasks.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/schema/twophoton.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/schema/utils.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/schema/video.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/stacks.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/utils.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata/widgets.py +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata.egg-info/SOURCES.txt +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata.egg-info/dependency_links.txt +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata.egg-info/entry_points.txt +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata.egg-info/requires.txt +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/labdata.egg-info/top_level.txt +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/pyproject.toml +0 -0
- {labdata-0.1.2 → labdata-0.1.4}/setup.cfg +0 -0
|
@@ -22,7 +22,7 @@ class CaimanCompute(Suite2pCompute):
|
|
|
22
22
|
# default parameters
|
|
23
23
|
self.parameters = dict(algorithm_name = 'caiman')
|
|
24
24
|
# will only store these in CellSegmentationParams
|
|
25
|
-
self.parameter_keys = ['pw_rigid','p','gSig','gSig_filt','
|
|
25
|
+
self.parameter_keys = ['pw_rigid','p','gSig','gSig_filt','max_shifts','gSiz','merge_thr','rf','stride','tsub',
|
|
26
26
|
'ssub','nb','min_corr','min_pnr','ssub_B','ring_size_factor',
|
|
27
27
|
'min_SNR', 'rval_thr', 'use_cnn', 'detrendWin','quantileMin','denoise_dff']
|
|
28
28
|
|
|
@@ -50,7 +50,7 @@ class CaimanCompute(Suite2pCompute):
|
|
|
50
50
|
parser.add_argument('-m','--pwrigid',
|
|
51
51
|
action='store_true', default=False,
|
|
52
52
|
help = "Piecewise-rigid registration")
|
|
53
|
-
parser.add_argument('--max-allowed-
|
|
53
|
+
parser.add_argument('--max-allowed-shifts',
|
|
54
54
|
action='store', default=[20,20], type = int, nargs = 2,
|
|
55
55
|
help = "maximum allowed shift for motion correction")
|
|
56
56
|
parser.add_argument('-p',
|
|
@@ -122,7 +122,6 @@ class CaimanCompute(Suite2pCompute):
|
|
|
122
122
|
max_shifts = args.max_allowed_shifts,
|
|
123
123
|
p = int(args.p),
|
|
124
124
|
gSig = [int(a) for a in args.gsig],
|
|
125
|
-
gSig_filt = [int(a) for a in args.gsig_filt],
|
|
126
125
|
gSiz = [int(a) for a in 2*np.array(args.gsig) + 1],
|
|
127
126
|
merge_thr = float(args.merge_thr),
|
|
128
127
|
rf = int(args.rf),
|
|
@@ -140,7 +139,10 @@ class CaimanCompute(Suite2pCompute):
|
|
|
140
139
|
detrendWin = int(args.detrend_win),
|
|
141
140
|
denoise_dff = bool(args.denoise_dff),
|
|
142
141
|
quantileMin = float(args.quantile_min),
|
|
143
|
-
roi = args.roi)
|
|
142
|
+
roi = args.roi)
|
|
143
|
+
if not args.gsig_filt is None:
|
|
144
|
+
params['gSig_filt'] = [int(a) for a in args.gsig_filt],
|
|
145
|
+
|
|
144
146
|
self.parameters = params
|
|
145
147
|
|
|
146
148
|
def _compute(self):
|
|
@@ -338,7 +340,7 @@ class CaimanCompute(Suite2pCompute):
|
|
|
338
340
|
os.unlink(f)
|
|
339
341
|
else:
|
|
340
342
|
print(f'[{self.name} job] Kept the temporary folder {temporary_folder}.')
|
|
341
|
-
CellSegmentationMetrics().populate(
|
|
343
|
+
self.schema.CellSegmentationMetrics().populate(dkey, display_progress = True)
|
|
342
344
|
|
|
343
345
|
def setup_cluster(n_cpus,cluster = None):
|
|
344
346
|
from caiman import stop_server, cluster
|
|
@@ -384,7 +384,8 @@ class Suite2pCompute(BaseCompute):
|
|
|
384
384
|
os.unlink(f)
|
|
385
385
|
else:
|
|
386
386
|
print(f'[{self.name} job] Kept the temporary folder {savedir}.')
|
|
387
|
-
|
|
387
|
+
for key in seskeys:
|
|
388
|
+
self.schema.CellSegmentationMetrics.populate(key,display_progress = True, processes = DEFAULT_N_JOBS)
|
|
388
389
|
|
|
389
390
|
def extract_dff_fissa(stats,binary,dims,fs, batch_size,tau):
|
|
390
391
|
'''
|
|
@@ -4,8 +4,7 @@ import pandas as pd
|
|
|
4
4
|
import streamlit as st
|
|
5
5
|
import numpy as np
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
MAX_PLOT = 5
|
|
9
8
|
|
|
10
9
|
def _normalize_image(img):
|
|
11
10
|
img = np.asarray(img, dtype=float)
|
|
@@ -84,7 +83,7 @@ def segmentation_tab(schema=None):
|
|
|
84
83
|
|
|
85
84
|
@st.cache_data
|
|
86
85
|
def get_roi_color_proj(subject_name, session_name, dataset_name, parameter_set_num,
|
|
87
|
-
sel_method,
|
|
86
|
+
sel_method, sel_label):
|
|
88
87
|
from labdata.stacks import roi_color_projection
|
|
89
88
|
key = dict(subject_name=subject_name, session_name=session_name,
|
|
90
89
|
dataset_name=dataset_name, parameter_set_num=parameter_set_num)
|
|
@@ -100,10 +99,10 @@ def segmentation_tab(schema=None):
|
|
|
100
99
|
roi_nums = list(roi_table.fetch('roi_num'))
|
|
101
100
|
if not roi_nums:
|
|
102
101
|
continue
|
|
103
|
-
if
|
|
102
|
+
if sel_label is not None:
|
|
104
103
|
sel_rois = set(
|
|
105
104
|
(schema.CellSegmentation.Selection
|
|
106
|
-
& dict(plane_key, selection_method=sel_method, selection=
|
|
105
|
+
& dict(plane_key, selection_method=sel_method, selection=sel_label)
|
|
107
106
|
).fetch('roi_num'))
|
|
108
107
|
cell_indices = [i for i, rn in enumerate(roi_nums) if rn in sel_rois]
|
|
109
108
|
else:
|
|
@@ -126,6 +125,16 @@ def segmentation_tab(schema=None):
|
|
|
126
125
|
(schema.CellSegmentation.Selection & key).fetch('selection_method')))
|
|
127
126
|
return methods or ['auto']
|
|
128
127
|
|
|
128
|
+
@st.cache_data
|
|
129
|
+
def get_selection_labels(subject_name, session_name, dataset_name, parameter_set_num,
|
|
130
|
+
sel_method):
|
|
131
|
+
key = dict(subject_name=subject_name, session_name=session_name,
|
|
132
|
+
dataset_name=dataset_name, parameter_set_num=parameter_set_num,
|
|
133
|
+
selection_method=sel_method)
|
|
134
|
+
vals = sorted(set(int(v) for v in
|
|
135
|
+
(schema.CellSegmentation.Selection & key).fetch('selection')))
|
|
136
|
+
return vals
|
|
137
|
+
|
|
129
138
|
@st.cache_data
|
|
130
139
|
def get_roi_data(subject_name, session_name, dataset_name, parameter_set_num, sel_method):
|
|
131
140
|
key = dict(subject_name=subject_name, session_name=session_name,
|
|
@@ -154,7 +163,8 @@ def segmentation_tab(schema=None):
|
|
|
154
163
|
centers.append(dict(roi_num=r['roi_num'], plane_num=pnum,
|
|
155
164
|
cy=float(np.median(rows_idx)),
|
|
156
165
|
cx=float(np.median(cols_idx)),
|
|
157
|
-
db_label=db_label
|
|
166
|
+
db_label=db_label,
|
|
167
|
+
sel_value=is_cell))
|
|
158
168
|
return centers
|
|
159
169
|
|
|
160
170
|
@st.cache_data
|
|
@@ -441,17 +451,22 @@ def segmentation_tab(schema=None):
|
|
|
441
451
|
if 'seg_sel_method_pending' in st.session_state:
|
|
442
452
|
st.session_state['seg_sel_method'] = st.session_state.pop('seg_sel_method_pending')
|
|
443
453
|
|
|
444
|
-
cA, cB, cC
|
|
454
|
+
cA, cB, cC = st.columns([1, 1, 2])
|
|
445
455
|
show_rois = cA.toggle('Show ROI centers', value=True, key='seg_show_rois')
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
show_traces = cC.toggle('Show traces', value=True, key='seg_show_traces')
|
|
449
|
-
sel_method = cD.selectbox('Selection method', methods, key='seg_sel_method',
|
|
456
|
+
show_traces = cB.toggle('Show traces', value=True, key='seg_show_traces')
|
|
457
|
+
sel_method = cC.selectbox('Selection method', methods, key='seg_sel_method',
|
|
450
458
|
label_visibility='collapsed',
|
|
451
459
|
disabled=not show_rois)
|
|
452
460
|
|
|
453
|
-
# Duplicate / delete selection methods
|
|
454
|
-
|
|
461
|
+
# Duplicate / delete selection methods + label filter
|
|
462
|
+
sel_labels = get_selection_labels(
|
|
463
|
+
subject, row['session_name'], row['dataset_name'], param_set, sel_method)
|
|
464
|
+
lsT, lsS, _, dC = st.columns([0.4, 0.6, 1, 2])
|
|
465
|
+
lsT.markdown('<div style="padding-top:0.4em"><b>Selection:</b></div>',
|
|
466
|
+
unsafe_allow_html=True)
|
|
467
|
+
sel_label_filter = lsS.selectbox(
|
|
468
|
+
'Filter by label', ['All'] + sel_labels,
|
|
469
|
+
key='seg_label_filter', label_visibility='collapsed')
|
|
455
470
|
with dC:
|
|
456
471
|
dname, dbtn, del_btn = st.columns([3, 1, 1])
|
|
457
472
|
new_name = dname.text_input('New method name', key='seg_new_method_name',
|
|
@@ -511,7 +526,7 @@ def segmentation_tab(schema=None):
|
|
|
511
526
|
with st.spinner('Loading ROI color projection…'):
|
|
512
527
|
roi_color_projs = get_roi_color_proj(
|
|
513
528
|
subject, row['session_name'], row['dataset_name'], param_set, sel_method,
|
|
514
|
-
|
|
529
|
+
sel_label=None if sel_label_filter == 'All' else int(sel_label_filter))
|
|
515
530
|
|
|
516
531
|
all_projections = roi_color_projs + list(projections)
|
|
517
532
|
|
|
@@ -522,18 +537,22 @@ def segmentation_tab(schema=None):
|
|
|
522
537
|
size_domain = ['cell', 'not cell', 'unclassified', 'picked']
|
|
523
538
|
size_range = [80, 80, 60, 160]
|
|
524
539
|
|
|
525
|
-
# Cell navigation
|
|
540
|
+
# Cell navigation: only iterate through displayed ROIs, sorted by SNR if possible
|
|
526
541
|
snr_map = get_snr_map(subject, row['session_name'], row['dataset_name'], param_set)
|
|
542
|
+
displayed_centers = centers
|
|
543
|
+
if sel_label_filter != 'All':
|
|
544
|
+
displayed_centers = [c for c in displayed_centers
|
|
545
|
+
if c['sel_value'] == int(sel_label_filter)]
|
|
527
546
|
if snr_map:
|
|
528
547
|
cell_list = sorted(
|
|
529
|
-
[(c['plane_num'], c['roi_num']) for c in
|
|
548
|
+
[(c['plane_num'], c['roi_num']) for c in displayed_centers],
|
|
530
549
|
key=lambda x: -(snr_map.get(x, {}).get('snr') or 0.0)
|
|
531
|
-
) if
|
|
550
|
+
) if displayed_centers else []
|
|
532
551
|
else:
|
|
533
552
|
cell_list = sorted(
|
|
534
|
-
[(c['plane_num'], c['roi_num']) for c in
|
|
553
|
+
[(c['plane_num'], c['roi_num']) for c in displayed_centers],
|
|
535
554
|
key=lambda x: (x[0], x[1])
|
|
536
|
-
) if
|
|
555
|
+
) if displayed_centers else []
|
|
537
556
|
|
|
538
557
|
def _nav_cursor():
|
|
539
558
|
"""Return index of the last picked cell in cell_list, or -1."""
|
|
@@ -559,7 +578,7 @@ def segmentation_tab(schema=None):
|
|
|
559
578
|
get_traces.clear()
|
|
560
579
|
st.rerun()
|
|
561
580
|
|
|
562
|
-
# Keyboard
|
|
581
|
+
# Keyboard: w or p/s or n = prev/next cell, d/a = time forward/back
|
|
563
582
|
_kb_html = """
|
|
564
583
|
<script>
|
|
565
584
|
(function() {
|
|
@@ -569,8 +588,10 @@ def segmentation_tab(schema=None):
|
|
|
569
588
|
parent.addEventListener('keydown', function(e) {
|
|
570
589
|
if (e.target && (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA')) return;
|
|
571
590
|
var label = null;
|
|
572
|
-
if (e.key === '
|
|
573
|
-
if (e.key === '
|
|
591
|
+
if ((e.key === 'w') || (e.key === 'p')) label = 'prev cell';
|
|
592
|
+
if ((e.key === 's') || (e.key === 'n')) label = 'next cell';
|
|
593
|
+
if (e.key === 'd') label = '+';
|
|
594
|
+
if (e.key === 'a') label = '-';
|
|
574
595
|
if (!label) return;
|
|
575
596
|
var btns = parent.querySelectorAll('button');
|
|
576
597
|
for (var i = 0; i < btns.length; i++) {
|
|
@@ -586,7 +607,7 @@ def segmentation_tab(schema=None):
|
|
|
586
607
|
import streamlit.components.v1 as _stc
|
|
587
608
|
_stc.html(_kb_html, height=0)
|
|
588
609
|
|
|
589
|
-
# projection tabs
|
|
610
|
+
# projection tabs
|
|
590
611
|
_other_names = sorted(set(r['proj_name'] for r in projections))
|
|
591
612
|
proj_names = (['roi color'] if roi_color_projs else []) + _other_names
|
|
592
613
|
if not proj_names:
|
|
@@ -619,12 +640,13 @@ def segmentation_tab(schema=None):
|
|
|
619
640
|
scatter = None
|
|
620
641
|
if show_rois and plane_centers:
|
|
621
642
|
picked_set = {(p['plane_num'], p['roi_num']) for p in picked}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
643
|
+
if sel_label_filter == 'All':
|
|
644
|
+
visible = plane_centers
|
|
645
|
+
else:
|
|
646
|
+
lf = int(sel_label_filter)
|
|
647
|
+
visible = [c for c in plane_centers
|
|
648
|
+
if c['sel_value'] == lf
|
|
649
|
+
or (c['plane_num'], c['roi_num']) in picked_set]
|
|
628
650
|
if visible:
|
|
629
651
|
roi_df = pd.DataFrame(visible)
|
|
630
652
|
roi_df['status'] = roi_df.apply(
|
|
@@ -694,7 +716,7 @@ def segmentation_tab(schema=None):
|
|
|
694
716
|
|
|
695
717
|
st.divider()
|
|
696
718
|
|
|
697
|
-
plot_picked = picked[-
|
|
719
|
+
plot_picked = picked[-MAX_PLOT:]
|
|
698
720
|
|
|
699
721
|
hA, hB = st.columns([3, 1])
|
|
700
722
|
hA.write(f'**ROIs** (plotting last {len(plot_picked)} of {len(picked)} selected): '
|
|
@@ -736,50 +758,77 @@ def segmentation_tab(schema=None):
|
|
|
736
758
|
frame_rate, _ = get_frame_rate(subject, row['session_name'], row['dataset_name'])
|
|
737
759
|
traces_df = traces_df.copy()
|
|
738
760
|
traces_df['t_sec'] = traces_df['t'] / frame_rate
|
|
739
|
-
|
|
740
761
|
y_min = min(0.0, float(traces_df['value'].min()))
|
|
741
762
|
y_max = float(traces_df['value'].max())
|
|
742
|
-
|
|
743
763
|
window_s = 300.0
|
|
744
764
|
total_s = float(traces_df['t_sec'].max())
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
765
|
+
|
|
766
|
+
def _draw_traces():
|
|
767
|
+
if total_s > window_s:
|
|
768
|
+
max_start = round(total_s - window_s, 2)
|
|
769
|
+
_lc, _tb, _sc, _tf = st.columns([1, 0.2, 10, 0.2])
|
|
770
|
+
_lc.markdown('**Time (s)**')
|
|
771
|
+
if _tb.button('-', key='seg_time_back', use_container_width=True):
|
|
772
|
+
cur = st.session_state.get('seg_trace_window', 0.0)
|
|
773
|
+
st.session_state['seg_trace_window'] = max(0.0, cur - window_s/2)
|
|
774
|
+
_rerun()
|
|
775
|
+
if _tf.button('+', key='seg_time_fwd', use_container_width=True):
|
|
776
|
+
cur = st.session_state.get('seg_trace_window', 0.0)
|
|
777
|
+
st.session_state['seg_trace_window'] = min(max_start, cur + window_s/2)
|
|
778
|
+
_rerun()
|
|
779
|
+
t_start = _sc.slider('Time (s)', 0.0, max_start,
|
|
780
|
+
st.session_state.get('seg_trace_window', 0.0),
|
|
781
|
+
step=30.0, key='seg_trace_window',
|
|
782
|
+
label_visibility='collapsed')
|
|
783
|
+
else:
|
|
784
|
+
t_start = 0.0
|
|
785
|
+
t_end = t_start + window_s
|
|
786
|
+
view_df = traces_df[
|
|
787
|
+
(traces_df['t_sec'] >= t_start) & (traces_df['t_sec'] <= t_end)]
|
|
788
|
+
trace_chart = (
|
|
789
|
+
alt.Chart(view_df)
|
|
790
|
+
.mark_line(strokeWidth=1.2)
|
|
791
|
+
.encode(
|
|
792
|
+
x=alt.X('t_sec:Q', title='Time (s)',
|
|
793
|
+
scale=alt.Scale(domain=[t_start, t_end]),
|
|
794
|
+
axis=alt.Axis(tickCount=5, tickSize=5, grid=False)),
|
|
795
|
+
y=alt.Y('value:Q', title=trace_type,
|
|
796
|
+
scale=alt.Scale(domain=[y_min-0.1, y_max+0.1]),
|
|
797
|
+
axis=alt.Axis(tickCount=5, tickSize=5, grid=False)),
|
|
798
|
+
color=alt.Color('roi:N', title='ROI',
|
|
799
|
+
scale=alt.Scale(domain=roi_labels,
|
|
800
|
+
range=roi_color_range)),
|
|
801
|
+
tooltip=['roi:N',
|
|
802
|
+
alt.Tooltip('t_sec:Q', title='Time (s)', format='.2f'),
|
|
803
|
+
alt.Tooltip('value:Q', format='.4f')],
|
|
804
|
+
)
|
|
805
|
+
.properties(height=220).interactive()
|
|
771
806
|
)
|
|
772
|
-
.
|
|
773
|
-
|
|
774
|
-
st
|
|
807
|
+
st.altair_chart(trace_chart, width='stretch')
|
|
808
|
+
|
|
809
|
+
if hasattr(st, 'fragment'):
|
|
810
|
+
def _rerun(): st.rerun(scope='fragment')
|
|
811
|
+
st.fragment(_draw_traces)()
|
|
812
|
+
else:
|
|
813
|
+
def _rerun(): st.rerun()
|
|
814
|
+
_draw_traces()
|
|
775
815
|
|
|
776
|
-
# classification editor — shows currently picked cells
|
|
816
|
+
# classification editor — shows currently picked cells and cells whose
|
|
777
817
|
# classification was changed from the original value
|
|
778
818
|
st.write('**ROI classification**')
|
|
819
|
+
|
|
820
|
+
# avoid second re-run
|
|
821
|
+
if st.session_state.pop('_seg_propagate_pending', False):
|
|
822
|
+
pv = st.session_state.pop('_seg_propagate_val', None)
|
|
823
|
+
if pv is not None:
|
|
824
|
+
st.session_state['seg_editor_rows'] = [
|
|
825
|
+
dict(r, selection=pv) for r in st.session_state['seg_editor_rows']]
|
|
826
|
+
st.session_state.pop('seg_selection_editor', None)
|
|
827
|
+
|
|
779
828
|
editor_rows = list(st.session_state['seg_editor_rows'])
|
|
780
829
|
picked_keys = {(p['plane_num'], p['roi_num']) for p in picked}
|
|
781
830
|
|
|
782
|
-
# pending
|
|
831
|
+
# pending editor_rows so filtering sees the current values
|
|
783
832
|
widget_edits = (st.session_state.get('seg_selection_editor') or {}).get('edited_rows', {})
|
|
784
833
|
for i in range(len(editor_rows)):
|
|
785
834
|
edits = widget_edits.get(i, widget_edits.get(str(i), {}))
|
|
@@ -788,7 +837,7 @@ def segmentation_tab(schema=None):
|
|
|
788
837
|
|
|
789
838
|
# currently picked cells OR cells whose classification changed from the original
|
|
790
839
|
editor_rows = [r for r in editor_rows
|
|
791
|
-
if (r['plane'], r['roi']) in picked_keys or r['
|
|
840
|
+
if (r['plane'], r['roi']) in picked_keys or r['selection'] != r['_orig']]
|
|
792
841
|
|
|
793
842
|
# Add picked cells not yet in the list
|
|
794
843
|
existing = {(r['plane'], r['roi']) for r in editor_rows}
|
|
@@ -798,9 +847,9 @@ def segmentation_tab(schema=None):
|
|
|
798
847
|
entry = (schema.CellSegmentation.Selection
|
|
799
848
|
& dict(roi_key, selection_method=sel_method)).fetch(
|
|
800
849
|
'selection', as_dict=True)
|
|
801
|
-
orig =
|
|
850
|
+
orig = int(entry[0]['selection']) if entry else None
|
|
802
851
|
editor_rows.append({'plane': plane_num, 'roi': roi_num,
|
|
803
|
-
'method': sel_method, '
|
|
852
|
+
'method': sel_method, 'selection': orig, '_orig': orig})
|
|
804
853
|
existing.add((plane_num, roi_num))
|
|
805
854
|
|
|
806
855
|
if editor_rows != st.session_state['seg_editor_rows']:
|
|
@@ -830,48 +879,52 @@ def segmentation_tab(schema=None):
|
|
|
830
879
|
editor_display = pd.DataFrame([
|
|
831
880
|
{'●': _color_swatch(roi_color_map.get(f'plane {r["plane"]}, roi {r["roi"]}', '#888888')),
|
|
832
881
|
**({'□': crop_map.get((r['plane'], r['roi']))} if crop_map else {}),
|
|
833
|
-
'plane': r['plane'], 'roi': r['roi'], 'method': r['method'],
|
|
882
|
+
'plane': r['plane'], 'roi': r['roi'], 'method': r['method'],
|
|
883
|
+
'selection': r['selection'],
|
|
834
884
|
**({
|
|
835
|
-
'
|
|
836
|
-
'
|
|
837
|
-
'
|
|
838
|
-
'
|
|
885
|
+
'snr': _fmt(snr_map.get((r['plane'], r['roi']), {}).get('snr')),
|
|
886
|
+
'diam': _fmt(snr_map.get((r['plane'], r['roi']), {}).get('diameter')),
|
|
887
|
+
'aspect':_fmt(snr_map.get((r['plane'], r['roi']), {}).get('aspect_ratio')),
|
|
888
|
+
'skew': _fmt(snr_map.get((r['plane'], r['roi']), {}).get('trace_skewness')),
|
|
889
|
+
'noise': _fmt(snr_map.get((r['plane'], r['roi']), {}).get('noise_level')),
|
|
839
890
|
} if snr_map else {})}
|
|
840
891
|
for r in editor_rows
|
|
841
892
|
]) if editor_rows else pd.DataFrame(
|
|
842
|
-
columns=['●'] + (['□'] if crop_map else []) + ['plane', 'roi', 'method', '
|
|
843
|
-
+ (['diam', '
|
|
893
|
+
columns=['●'] + (['□'] if crop_map else []) + ['plane', 'roi', 'method', 'selection']
|
|
894
|
+
+ (['snr', 'diam', 'aspect', 'skew', 'noise'] if snr_map else []))
|
|
844
895
|
|
|
845
896
|
_crop_col_cfg = {'□': st.column_config.ImageColumn('ROI', width='small')} if crop_map else {}
|
|
846
897
|
_metrics_col_cfg = {
|
|
898
|
+
'snr': st.column_config.NumberColumn('SNR', format='%.1f', width='small'),
|
|
847
899
|
'diam': st.column_config.NumberColumn('diam (px)', format='%.1f', width='small'),
|
|
848
|
-
'
|
|
849
|
-
'skew': st.column_config.NumberColumn('skew',
|
|
850
|
-
'noise': st.column_config.NumberColumn('noise',
|
|
900
|
+
'aspect':st.column_config.NumberColumn('aspect', format='%.2f', width='small'),
|
|
901
|
+
'skew': st.column_config.NumberColumn('skew', format='%.2f', width='small'),
|
|
902
|
+
'noise': st.column_config.NumberColumn('noise', format='%.3f', width='small'),
|
|
851
903
|
} if snr_map else {}
|
|
852
904
|
edited = st.data_editor(
|
|
853
905
|
editor_display, hide_index=True, width='stretch',
|
|
854
906
|
column_config={
|
|
855
907
|
'●': st.column_config.ImageColumn(' ', width='small'),
|
|
856
908
|
**_crop_col_cfg,
|
|
857
|
-
'
|
|
909
|
+
'selection': st.column_config.NumberColumn('selection', step=1, width='small'),
|
|
858
910
|
**_metrics_col_cfg,
|
|
859
911
|
},
|
|
860
|
-
disabled=['●', '□', 'plane', 'roi', 'method', 'diam', '
|
|
912
|
+
disabled=['●', '□', 'plane', 'roi', 'method', 'snr', 'diam', 'aspect', 'skew', 'noise'],
|
|
861
913
|
key='seg_selection_editor',
|
|
862
914
|
)
|
|
863
915
|
|
|
864
|
-
|
|
916
|
+
def _do_save(df):
|
|
865
917
|
changed = 0
|
|
866
|
-
for _, erow in
|
|
867
|
-
|
|
918
|
+
for _, erow in df.iterrows():
|
|
919
|
+
raw = erow.get('selection')
|
|
920
|
+
if raw is None:
|
|
868
921
|
continue
|
|
869
922
|
roi_key = dict(seg_key, plane_num=int(erow['plane']),
|
|
870
923
|
roi_num=int(erow['roi']),
|
|
871
924
|
selection_method=sel_method)
|
|
872
925
|
try:
|
|
873
926
|
schema.CellSegmentation.Selection.update1(
|
|
874
|
-
{**roi_key, 'selection':
|
|
927
|
+
{**roi_key, 'selection': int(raw)})
|
|
875
928
|
changed += 1
|
|
876
929
|
except Exception as exc:
|
|
877
930
|
st.error(f'Error updating plane {erow["plane"]} roi {erow["roi"]}: {exc}')
|
|
@@ -883,3 +936,12 @@ def segmentation_tab(schema=None):
|
|
|
883
936
|
st.session_state.pop('seg_selection_editor', None)
|
|
884
937
|
st.rerun()
|
|
885
938
|
|
|
939
|
+
_bA, _bB = st.columns([1, 1])
|
|
940
|
+
if _bA.button('Save classification', key='seg_save_selection'):
|
|
941
|
+
_do_save(edited)
|
|
942
|
+
if _bB.button('Propagate first', key='seg_propagate_first',
|
|
943
|
+
disabled=edited.empty or bool(edited['selection'].isna().all())):
|
|
944
|
+
first_val = int(edited['selection'].dropna().iloc[0])
|
|
945
|
+
st.session_state['_seg_propagate_pending'] = True
|
|
946
|
+
st.session_state['_seg_propagate_val'] = first_val
|
|
947
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|