labdata 0.1.2__tar.gz → 0.1.3__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.3}/PKG-INFO +1 -1
- {labdata-0.1.2 → labdata-0.1.3}/labdata/__init__.py +1 -1
- {labdata-0.1.2 → labdata-0.1.3}/labdata/compute/caiman.py +7 -5
- {labdata-0.1.2 → labdata-0.1.3}/labdata/compute/suite2p.py +2 -1
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/segmentation.py +97 -50
- {labdata-0.1.2 → labdata-0.1.3}/labdata.egg-info/PKG-INFO +1 -1
- {labdata-0.1.2 → labdata-0.1.3}/LICENSE +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/README.md +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/cli.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/compute/__init__.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/compute/ec2.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/compute/ephys.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/compute/pose.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/compute/schedulers.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/compute/singularity.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/compute/utils.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/copy.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/__init__.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/compute.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/console.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/decisiontask.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/explorer/__init__.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/explorer/cellsegmentation.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/explorer/spikesorting.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/index.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/notes.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/procedures.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/sorting.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/dashboard/video.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/export.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/rules/__init__.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/rules/ephys.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/rules/imaging.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/rules/utils.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/s3.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/schema/__init__.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/schema/ephys.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/schema/general.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/schema/histology.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/schema/onephoton.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/schema/procedures.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/schema/tasks.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/schema/twophoton.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/schema/utils.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/schema/video.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/stacks.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/utils.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata/widgets.py +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata.egg-info/SOURCES.txt +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata.egg-info/dependency_links.txt +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata.egg-info/entry_points.txt +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata.egg-info/requires.txt +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/labdata.egg-info/top_level.txt +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/pyproject.toml +0 -0
- {labdata-0.1.2 → labdata-0.1.3}/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)
|
|
@@ -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
|
-
selected_only=(
|
|
529
|
+
selected_only=(sel_label_filter != 'All'))
|
|
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): '
|
|
@@ -744,10 +766,20 @@ def segmentation_tab(schema=None):
|
|
|
744
766
|
total_s = float(traces_df['t_sec'].max())
|
|
745
767
|
if total_s > window_s:
|
|
746
768
|
max_start = round(total_s - window_s, 2)
|
|
747
|
-
_lc, _sc = st.columns([1,
|
|
769
|
+
_lc, _tb, _sc, _tf = st.columns([1, 0.2, 10, 0.2])
|
|
748
770
|
_lc.markdown('**Time (s)**')
|
|
749
|
-
|
|
750
|
-
|
|
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)
|
|
774
|
+
st.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)
|
|
778
|
+
st.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')
|
|
751
783
|
else:
|
|
752
784
|
t_start = 0.0
|
|
753
785
|
t_end = t_start + window_s
|
|
@@ -773,7 +805,7 @@ def segmentation_tab(schema=None):
|
|
|
773
805
|
)
|
|
774
806
|
st.altair_chart(trace_chart, width='stretch')
|
|
775
807
|
|
|
776
|
-
# classification editor — shows currently picked cells
|
|
808
|
+
# classification editor — shows currently picked cells and cells whose
|
|
777
809
|
# classification was changed from the original value
|
|
778
810
|
st.write('**ROI classification**')
|
|
779
811
|
editor_rows = list(st.session_state['seg_editor_rows'])
|
|
@@ -788,7 +820,7 @@ def segmentation_tab(schema=None):
|
|
|
788
820
|
|
|
789
821
|
# currently picked cells OR cells whose classification changed from the original
|
|
790
822
|
editor_rows = [r for r in editor_rows
|
|
791
|
-
if (r['plane'], r['roi']) in picked_keys or r['
|
|
823
|
+
if (r['plane'], r['roi']) in picked_keys or r['selection'] != r['_orig']]
|
|
792
824
|
|
|
793
825
|
# Add picked cells not yet in the list
|
|
794
826
|
existing = {(r['plane'], r['roi']) for r in editor_rows}
|
|
@@ -798,9 +830,9 @@ def segmentation_tab(schema=None):
|
|
|
798
830
|
entry = (schema.CellSegmentation.Selection
|
|
799
831
|
& dict(roi_key, selection_method=sel_method)).fetch(
|
|
800
832
|
'selection', as_dict=True)
|
|
801
|
-
orig =
|
|
833
|
+
orig = int(entry[0]['selection']) if entry else None
|
|
802
834
|
editor_rows.append({'plane': plane_num, 'roi': roi_num,
|
|
803
|
-
'method': sel_method, '
|
|
835
|
+
'method': sel_method, 'selection': orig, '_orig': orig})
|
|
804
836
|
existing.add((plane_num, roi_num))
|
|
805
837
|
|
|
806
838
|
if editor_rows != st.session_state['seg_editor_rows']:
|
|
@@ -830,48 +862,52 @@ def segmentation_tab(schema=None):
|
|
|
830
862
|
editor_display = pd.DataFrame([
|
|
831
863
|
{'●': _color_swatch(roi_color_map.get(f'plane {r["plane"]}, roi {r["roi"]}', '#888888')),
|
|
832
864
|
**({'□': crop_map.get((r['plane'], r['roi']))} if crop_map else {}),
|
|
833
|
-
'plane': r['plane'], 'roi': r['roi'], 'method': r['method'],
|
|
865
|
+
'plane': r['plane'], 'roi': r['roi'], 'method': r['method'],
|
|
866
|
+
'selection': r['selection'],
|
|
834
867
|
**({
|
|
835
|
-
'
|
|
836
|
-
'
|
|
837
|
-
'
|
|
838
|
-
'
|
|
868
|
+
'snr': _fmt(snr_map.get((r['plane'], r['roi']), {}).get('snr')),
|
|
869
|
+
'diam': _fmt(snr_map.get((r['plane'], r['roi']), {}).get('diameter')),
|
|
870
|
+
'aspect':_fmt(snr_map.get((r['plane'], r['roi']), {}).get('aspect_ratio')),
|
|
871
|
+
'skew': _fmt(snr_map.get((r['plane'], r['roi']), {}).get('trace_skewness')),
|
|
872
|
+
'noise': _fmt(snr_map.get((r['plane'], r['roi']), {}).get('noise_level')),
|
|
839
873
|
} if snr_map else {})}
|
|
840
874
|
for r in editor_rows
|
|
841
875
|
]) if editor_rows else pd.DataFrame(
|
|
842
|
-
columns=['●'] + (['□'] if crop_map else []) + ['plane', 'roi', 'method', '
|
|
843
|
-
+ (['diam', '
|
|
876
|
+
columns=['●'] + (['□'] if crop_map else []) + ['plane', 'roi', 'method', 'selection']
|
|
877
|
+
+ (['snr', 'diam', 'aspect', 'skew', 'noise'] if snr_map else []))
|
|
844
878
|
|
|
845
879
|
_crop_col_cfg = {'□': st.column_config.ImageColumn('ROI', width='small')} if crop_map else {}
|
|
846
880
|
_metrics_col_cfg = {
|
|
881
|
+
'snr': st.column_config.NumberColumn('SNR', format='%.1f', width='small'),
|
|
847
882
|
'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',
|
|
883
|
+
'aspect':st.column_config.NumberColumn('aspect', format='%.2f', width='small'),
|
|
884
|
+
'skew': st.column_config.NumberColumn('skew', format='%.2f', width='small'),
|
|
885
|
+
'noise': st.column_config.NumberColumn('noise', format='%.3f', width='small'),
|
|
851
886
|
} if snr_map else {}
|
|
852
887
|
edited = st.data_editor(
|
|
853
888
|
editor_display, hide_index=True, width='stretch',
|
|
854
889
|
column_config={
|
|
855
890
|
'●': st.column_config.ImageColumn(' ', width='small'),
|
|
856
891
|
**_crop_col_cfg,
|
|
857
|
-
'
|
|
892
|
+
'selection': st.column_config.NumberColumn('selection', step=1, width='small'),
|
|
858
893
|
**_metrics_col_cfg,
|
|
859
894
|
},
|
|
860
|
-
disabled=['●', '□', 'plane', 'roi', 'method', 'diam', '
|
|
895
|
+
disabled=['●', '□', 'plane', 'roi', 'method', 'snr', 'diam', 'aspect', 'skew', 'noise'],
|
|
861
896
|
key='seg_selection_editor',
|
|
862
897
|
)
|
|
863
898
|
|
|
864
|
-
|
|
899
|
+
def _do_save(df):
|
|
865
900
|
changed = 0
|
|
866
|
-
for _, erow in
|
|
867
|
-
|
|
901
|
+
for _, erow in df.iterrows():
|
|
902
|
+
raw = erow.get('selection')
|
|
903
|
+
if raw is None:
|
|
868
904
|
continue
|
|
869
905
|
roi_key = dict(seg_key, plane_num=int(erow['plane']),
|
|
870
906
|
roi_num=int(erow['roi']),
|
|
871
907
|
selection_method=sel_method)
|
|
872
908
|
try:
|
|
873
909
|
schema.CellSegmentation.Selection.update1(
|
|
874
|
-
{**roi_key, 'selection':
|
|
910
|
+
{**roi_key, 'selection': int(raw)})
|
|
875
911
|
changed += 1
|
|
876
912
|
except Exception as exc:
|
|
877
913
|
st.error(f'Error updating plane {erow["plane"]} roi {erow["roi"]}: {exc}')
|
|
@@ -883,3 +919,14 @@ def segmentation_tab(schema=None):
|
|
|
883
919
|
st.session_state.pop('seg_selection_editor', None)
|
|
884
920
|
st.rerun()
|
|
885
921
|
|
|
922
|
+
_bA, _bB = st.columns([1, 1])
|
|
923
|
+
if _bA.button('Save classification', key='seg_save_selection'):
|
|
924
|
+
_do_save(edited)
|
|
925
|
+
if _bB.button('Propagate first', key='seg_propagate_first',
|
|
926
|
+
disabled=edited.empty or edited['selection'].isna().all()):
|
|
927
|
+
first_val = int(edited['selection'].dropna().iloc[0])
|
|
928
|
+
updated = [dict(r, selection=first_val) for r in editor_rows]
|
|
929
|
+
st.session_state['seg_editor_rows'] = updated
|
|
930
|
+
st.session_state.pop('seg_selection_editor', None)
|
|
931
|
+
st.rerun()
|
|
932
|
+
|
|
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
|