diff --git a/livecellx/core/sct_operator.py b/livecellx/core/sct_operator.py index a8ccce8b..79334d88 100644 --- a/livecellx/core/sct_operator.py +++ b/livecellx/core/sct_operator.py @@ -40,6 +40,7 @@ def __init__( img_dataset=None, time_span=(0, None), meta=None, + uns=None, ): self.select_info = [] # [cur_sct, cur_sc, selected_shape_index] self.operator = operator @@ -60,6 +61,11 @@ def __init__( else: self.meta = meta + if uns is None: + self.uns = {} + else: + self.uns = uns + def remove_sc_operator(self, sc_operator): self.sc_operators.remove(sc_operator) @@ -348,9 +354,19 @@ def setup_from_sctc(self, sctc: SingleCellTrajectoryCollection, contour_sample_n self.traj_collection = sctc return shape_layer + def setup_by_timespan(self, span: tuple, contour_sample_num=20): + tmp_sctc = filter_sctc_by_time_span(self.traj_collection, span) + if self.shape_layer is not None: + self.remove_shape_layer() + shape_layer = NapariVisualizer.gen_trajectories_shapes( + tmp_sctc, self.viewer, contour_sample_num=contour_sample_num + ) + self.setup_shape_layer(shape_layer) + return shape_layer + def store_shape_layer_info(self, update_slice=None): """ - Stores the original face colors, properties, single cells, and shape data of the shape layer. + Stores the original face colors, properties, single cells, and shape data of the shape layer. Note that the single cell objects are copied as reference. Args: update_slice: A slice object representing the range of indices to update. If None, all indices are updated. @@ -864,6 +880,9 @@ def create_scs_edit_viewer_by_interval( clear_prev_batch=True, contour_sample_num=30, ): + """ + Creates a viewer and an sct_operator for editing SingleCellStatic objects. + """ # TODO: a potential bug is that the slice index is not the same concept as the time. A solution is to add time frame to shape properties # Here for now we assume indices represents timeframes sct_operator = create_scs_edit_viewer( @@ -920,8 +939,8 @@ def _move_span(viewer, offset): if clear_prev_batch: # TODO: shapes may be invisible, though select is sc/sct based and should be fine sct_operator.clear_selection() - temp_sc_trajs = create_sctc_from_scs(single_cells) - temp_sc_trajs = filter_sctc_by_time_span(temp_sc_trajs, cur_span) + all_sc_trajs = create_sctc_from_scs(single_cells) + temp_sc_trajs = filter_sctc_by_time_span(all_sc_trajs, cur_span) if len(temp_sc_trajs) != 0: sct_operator.setup_from_sctc(temp_sc_trajs, contour_sample_num=contour_sample_num) else: @@ -950,3 +969,78 @@ def load_from_cur_step(viewer): _move_span(viewer, cur_step - cur_idx) return viewer + + +def create_sctc_edit_viewer_by_interval( + sctc: SingleCellTrajectoryCollection, + img_dataset: LiveCellImageDataset, + span_interval=10, + viewer=None, + clear_prev_batch=True, + contour_sample_num=30, + uns_cur_idx_key="_lcx_sctc_cur_idx", +): + """ + Creates a viewer and an sct_operator for editing SingleCellStatic objects. + """ + # TODO: a potential bug is that the slice index is not the same concept as the time. A solution is to add time frame to shape properties + # Here for now we assume indices represents timeframes + sct_operator = create_scts_operator_viewer( + sctc, + img_dataset=img_dataset, + viewer=viewer, + contour_sample_num=contour_sample_num, + ) + + viewer = sct_operator.viewer + max_time = max(img_dataset.times) + cur_index = 0 + + sct_operator.uns[uns_cur_idx_key] = cur_index + + def _get_cur_idx(sct_operator): + cur_idx = sct_operator.uns[uns_cur_idx_key] + return cur_idx + + def _move_span(viewer, offset): + try: + cur_idx = _get_cur_idx(sct_operator) + cur_idx += offset + cur_idx = min(cur_idx, max_time) # (max_time - span_interval) is acceptable as well here + cur_idx = max(cur_idx, 0) + cur_span = (cur_idx, cur_idx + span_interval) + sct_operator.uns[uns_cur_idx_key] = cur_idx + # if clear_prev_batch: + # sct_operator.close() + # sct_operator = create_scs_edit_viewer(single_cells, img_dataset = dic_dataset, viewer = viewer, time_span=cur_span) + if clear_prev_batch: + # TODO: shapes may be invisible, though select is sc/sct based and should be fine + sct_operator.clear_selection() + sct_operator.setup_by_timespan(cur_span, contour_sample_num=contour_sample_num) + viewer.dims.set_point(0, cur_idx) + except Exception as e: + print("Error:", e) + print("Failed to load next span. Please try again.") + import traceback + + traceback.print_exc() + + @viewer.bind_key("n") + def next_span(viewer): + print(">>> debug: next_span") + _move_span(viewer, span_interval + 1) + + @viewer.bind_key("b") + def prev_span(viewer): + print(">>> debug: prev_span") + _move_span(viewer, -span_interval - 1) + + @viewer.bind_key("m") + def load_from_cur_step(viewer): + print(">>> debug: cur_idx span") + cur_step = viewer.dims.point[0] + cur_idx = _get_cur_idx(sct_operator) + _move_span(viewer, cur_step - cur_idx) + + load_from_cur_step(viewer) + return sct_operator diff --git a/notebooks/tutorial_create_sct_operator_interval.ipynb b/notebooks/tutorial_create_sct_operator_interval.ipynb new file mode 100644 index 00000000..3d48185c --- /dev/null +++ b/notebooks/tutorial_create_sct_operator_interval.ipynb @@ -0,0 +1,70 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from livecellx.sample_data import tutorial_three_image_sys\n", + "from livecellx.core.io_sc import prep_scs_from_mask_dataset\n", + "\n", + "dic_dataset, mask_dataset = tutorial_three_image_sys()\n", + "single_cells = prep_scs_from_mask_dataset(mask_dataset, dic_dataset)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List\n", + "from livecellx.track.sort_tracker_utils import (\n", + " gen_SORT_detections_input_from_contours,\n", + " update_traj_collection_by_SORT_tracker_detection,\n", + " track_SORT_bbox_from_contours,\n", + " track_SORT_bbox_from_scs\n", + ")\n", + "\n", + "\n", + "traj_collection = track_SORT_bbox_from_scs(single_cells, dic_dataset, mask_dataset=mask_dataset, max_age=0, min_hits=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from livecellx.core.sct_operator import create_scs_edit_viewer, SctOperator, create_scs_edit_viewer_by_interval, _get_viewer_sct_operator, create_sctc_edit_viewer_by_interval\n", + "import livecellx\n", + "import importlib\n", + "importlib.reload(livecellx.core.sct_operator)\n", + "\n", + "sct_opeartor = livecellx.core.sct_operator.create_sctc_edit_viewer_by_interval(traj_collection, img_dataset=dic_dataset, span_interval=0)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "livecell-work", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}