celldetective 1.5.0b9__tar.gz → 1.5.0b10__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.
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/PKG-INFO +1 -1
- celldetective-1.5.0b10/celldetective/_version.py +1 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/_settings_measurements.py +10 -2
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/viewers/spot_detection_viewer.py +14 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/measure.py +11 -11
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/data_cleaning.py +7 -3
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective.egg-info/PKG-INFO +1 -1
- celldetective-1.5.0b10/tests/gui/test_spot_detection_viewer.py +394 -0
- celldetective-1.5.0b10/tests/test_measure.py +243 -0
- celldetective-1.5.0b9/celldetective/_version.py +0 -1
- celldetective-1.5.0b9/tests/gui/test_spot_detection_viewer.py +0 -187
- celldetective-1.5.0b9/tests/test_measure.py +0 -141
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/LICENSE +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/README.md +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/__main__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/datasets/segmentation_annotations/blank +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/datasets/signal_annotations/blank +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/event_detection_models.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/events.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/exceptions.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/extra_properties.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/filters.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/InitWindow.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/about.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/analyze_block.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/base/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/base/channel_norm_generator.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/base/components.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/base/feature_choice.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/base/figure_canvas.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/base/list_widget.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/base/styles.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/base/utils.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/base_annotator.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/classifier_widget.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/configure_new_exp.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/control_panel.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/dynamic_progress.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/event_annotator.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/generic_signal_plot.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/gui_utils.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/help/DL-segmentation-strategy.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/help/Threshold-vs-DL.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/help/cell-populations.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/help/exp-structure.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/help/feature-btrack.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/help/neighborhood.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/help/prefilter-for-segmentation.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/help/preprocessing.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/help/propagate-classification.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/help/track-postprocessing.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/help/tracking.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/interactions_block.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/interactive_timeseries_viewer.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/json_readers.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/layouts/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/layouts/background_model_free_layout.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/layouts/channel_offset_layout.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/layouts/local_correction_layout.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/layouts/model_fit_layout.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/layouts/operation_layout.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/layouts/protocol_designer_layout.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/measure_annotator.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/pair_event_annotator.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/plot_measurements.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/plot_signals_ui.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/preprocessing_block.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/process_block.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/seg_model_loader.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/_cellpose_model_params.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/_event_detection_model_params.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/_segmentation_model_params.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/_settings_base.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/_settings_event_model_training.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/_settings_neighborhood.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/_settings_segmentation.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/_settings_segmentation_model_training.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/_settings_signal_annotator.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/_settings_tracking.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/settings/_stardist_model_params.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/survival_ui.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/tableUI.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/table_ops/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/table_ops/_maths.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/table_ops/_merge_groups.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/table_ops/_merge_one_hot.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/table_ops/_query_table.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/table_ops/_rename_col.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/thresholds_gui.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/viewers/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/viewers/base_viewer.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/viewers/channel_offset_viewer.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/viewers/contour_viewer.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/viewers/size_viewer.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/viewers/threshold_viewer.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/workers.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/icons/logo-large.png +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/icons/logo.png +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/icons/signals_icon.png +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/icons/splash-test.png +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/icons/splash.png +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/icons/splash0.png +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/icons/survival2.png +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/icons/vignette_signals2.png +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/icons/vignette_signals2.svg +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/links/zenodo.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/log_manager.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/models/pair_signal_detection/blank +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/models/segmentation_effectors/blank +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/models/segmentation_generic/blank +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/models/segmentation_targets/blank +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/models/signal_detection/blank +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/models/tracking_configs/biased_motion.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/models/tracking_configs/mcf7.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/models/tracking_configs/no_z_motion.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/models/tracking_configs/ricm.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/models/tracking_configs/ricm2.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/napari/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/napari/utils.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/neighborhood.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/preprocessing.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/processes/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/processes/background_correction.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/processes/compute_neighborhood.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/processes/detect_events.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/processes/downloader.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/processes/load_table.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/processes/measure_cells.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/processes/segment_cells.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/processes/track_cells.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/processes/train_segmentation_model.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/processes/train_signal_model.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/processes/unified_process.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/regionprops/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/regionprops/_regionprops.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/regionprops/props.json +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/relative_measurements.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/scripts/analyze_signals.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/scripts/measure_cells.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/scripts/measure_relative.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/scripts/segment_cells.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/scripts/segment_cells_thresholds.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/scripts/track_cells.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/scripts/train_segmentation_model.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/scripts/train_signal_model.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/segmentation.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/signals.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/tracking.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/cellpose_utils/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/color_mappings.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/data_loaders.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/dataset_helpers.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/downloaders.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/event_detection/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/experiment.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/image_augmenters.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/image_cleaning.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/image_loaders.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/image_transforms.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/io.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/mask_cleaning.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/mask_transforms.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/masks.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/maths.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/model_getters.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/model_loaders.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/normalization.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/parsing.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/plots/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/plots/regression.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/resources.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/stardist_utils/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/stats.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/utils/types.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective.egg-info/SOURCES.txt +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective.egg-info/dependency_links.txt +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective.egg-info/entry_points.txt +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective.egg-info/not-zip-safe +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective.egg-info/requires.txt +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective.egg-info/top_level.txt +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/setup.cfg +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/setup.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/gui/__init__.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/gui/test_enhancements.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/gui/test_measure_annotator_bugfix.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/gui/test_new_project.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/gui/test_project.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/test_cellpose_fallback.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/test_events.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/test_filters.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/test_io.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/test_neighborhood.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/test_notebooks.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/test_partial_install.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/test_preprocessing.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/test_segmentation.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/test_signals.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/test_tracking.py +0 -0
- {celldetective-1.5.0b9 → celldetective-1.5.0b10}/tests/test_utils.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.5.0b10"
|
|
@@ -838,7 +838,6 @@ class SettingsMeasurements(CelldetectiveSettingsPanel):
|
|
|
838
838
|
self.imshow_digit_window.canvas.draw()
|
|
839
839
|
self.imshow_digit_window.show()
|
|
840
840
|
|
|
841
|
-
|
|
842
841
|
def control_haralick_intensity_histogram(self):
|
|
843
842
|
"""
|
|
844
843
|
Load an image for the first experiment movie found.
|
|
@@ -1123,7 +1122,9 @@ class SettingsMeasurements(CelldetectiveSettingsPanel):
|
|
|
1123
1122
|
# else:
|
|
1124
1123
|
# invert_value = None
|
|
1125
1124
|
|
|
1126
|
-
from celldetective.gui.viewers.spot_detection_viewer import
|
|
1125
|
+
from celldetective.gui.viewers.spot_detection_viewer import (
|
|
1126
|
+
SpotDetectionVisualizer,
|
|
1127
|
+
)
|
|
1127
1128
|
|
|
1128
1129
|
self.spot_visual = SpotDetectionVisualizer(
|
|
1129
1130
|
frame_slider=True,
|
|
@@ -1139,6 +1140,13 @@ class SettingsMeasurements(CelldetectiveSettingsPanel):
|
|
|
1139
1140
|
parent_diameter_le=self.diameter_value,
|
|
1140
1141
|
parent_threshold_le=self.threshold_value,
|
|
1141
1142
|
parent_preprocessing_list=self.spot_preprocessing.list,
|
|
1143
|
+
initial_diameter=self.diameter_value.text(),
|
|
1144
|
+
initial_threshold=self.threshold_value.text(),
|
|
1145
|
+
initial_preprocessing=(
|
|
1146
|
+
self.spot_preprocessing.list.items
|
|
1147
|
+
if self.spot_preprocessing.list.items
|
|
1148
|
+
else None
|
|
1149
|
+
),
|
|
1142
1150
|
# parent_invert_check=self.invert_check,
|
|
1143
1151
|
# invert = self.invert_check.isChecked(),
|
|
1144
1152
|
# invert_value = self.invert_value_le.text().replace(',','.'),
|
{celldetective-1.5.0b9 → celldetective-1.5.0b10}/celldetective/gui/viewers/spot_detection_viewer.py
RENAMED
|
@@ -43,6 +43,9 @@ class SpotDetectionVisualizer(StackVisualizer):
|
|
|
43
43
|
parent_preprocessing_list=None,
|
|
44
44
|
cell_type="targets",
|
|
45
45
|
labels=None,
|
|
46
|
+
initial_diameter=None,
|
|
47
|
+
initial_threshold=None,
|
|
48
|
+
initial_preprocessing=None,
|
|
46
49
|
*args,
|
|
47
50
|
**kwargs,
|
|
48
51
|
):
|
|
@@ -113,6 +116,17 @@ class SpotDetectionVisualizer(StackVisualizer):
|
|
|
113
116
|
min(self.target_channel, self.n_channels - 1)
|
|
114
117
|
)
|
|
115
118
|
|
|
119
|
+
# Initialize from provided values (sync with settings panel)
|
|
120
|
+
if initial_diameter is not None:
|
|
121
|
+
self.spot_diam_le.setText(str(initial_diameter))
|
|
122
|
+
if initial_threshold is not None:
|
|
123
|
+
self.spot_thresh_le.setText(str(initial_threshold))
|
|
124
|
+
if initial_preprocessing is not None and len(initial_preprocessing) > 0:
|
|
125
|
+
items_for_list = [a[0] for a in initial_preprocessing]
|
|
126
|
+
for it in items_for_list:
|
|
127
|
+
self.preprocessing.list.addItemToList(it)
|
|
128
|
+
self.preprocessing.list.items = list(initial_preprocessing)
|
|
129
|
+
|
|
116
130
|
def closeEvent(self, event):
|
|
117
131
|
"""Clean up resources on close."""
|
|
118
132
|
# Clear large arrays
|
|
@@ -23,6 +23,7 @@ from celldetective.utils.maths import step_function
|
|
|
23
23
|
from celldetective.utils.image_cleaning import interpolate_nan
|
|
24
24
|
from celldetective.preprocessing import field_correction
|
|
25
25
|
from celldetective.log_manager import get_logger
|
|
26
|
+
import pandas as pd
|
|
26
27
|
|
|
27
28
|
logger = get_logger(__name__)
|
|
28
29
|
|
|
@@ -279,8 +280,6 @@ def measure(
|
|
|
279
280
|
|
|
280
281
|
timestep_dataframes.append(measurements_at_t)
|
|
281
282
|
|
|
282
|
-
import pandas as pd
|
|
283
|
-
|
|
284
283
|
measurements = pd.concat(timestep_dataframes)
|
|
285
284
|
if trajectories is not None:
|
|
286
285
|
measurements = measurements.sort_values(
|
|
@@ -435,8 +434,11 @@ def measure_features(
|
|
|
435
434
|
|
|
436
435
|
if spot_detection is not None:
|
|
437
436
|
detection_channel = spot_detection.get("channel")
|
|
438
|
-
|
|
439
|
-
|
|
437
|
+
channels_list = (
|
|
438
|
+
list(channels) if not isinstance(channels, list) else channels
|
|
439
|
+
)
|
|
440
|
+
if detection_channel in channels_list:
|
|
441
|
+
ind = channels_list.index(detection_channel)
|
|
440
442
|
if "image_preprocessing" not in spot_detection:
|
|
441
443
|
spot_detection.update({"image_preprocessing": None})
|
|
442
444
|
|
|
@@ -458,8 +460,11 @@ def measure_features(
|
|
|
458
460
|
if normalisation_list:
|
|
459
461
|
for norm in normalisation_list:
|
|
460
462
|
target = norm.get("target_channel")
|
|
461
|
-
|
|
462
|
-
|
|
463
|
+
channels_list = (
|
|
464
|
+
list(channels) if not isinstance(channels, list) else channels
|
|
465
|
+
)
|
|
466
|
+
if target in channels_list:
|
|
467
|
+
ind = channels_list.index(target)
|
|
463
468
|
|
|
464
469
|
if norm["correction_type"] == "local":
|
|
465
470
|
normalised_image = normalise_by_cell(
|
|
@@ -529,7 +534,6 @@ def measure_features(
|
|
|
529
534
|
extra_properties=extra_props_list,
|
|
530
535
|
channel_names=channels,
|
|
531
536
|
)
|
|
532
|
-
import pandas as pd
|
|
533
537
|
|
|
534
538
|
df_props = pd.DataFrame(props)
|
|
535
539
|
|
|
@@ -598,7 +602,6 @@ def measure_features(
|
|
|
598
602
|
extra_properties=intensity_extra,
|
|
599
603
|
channel_names=channels,
|
|
600
604
|
)
|
|
601
|
-
import pandas as pd
|
|
602
605
|
|
|
603
606
|
df_props_border_d = pd.DataFrame(props_border)
|
|
604
607
|
|
|
@@ -821,8 +824,6 @@ def compute_haralick_features(
|
|
|
821
824
|
len(np.unique(labels)) - 1
|
|
822
825
|
), "Some cells have not been measured..."
|
|
823
826
|
|
|
824
|
-
import pandas as pd
|
|
825
|
-
|
|
826
827
|
return pd.DataFrame(haralick_properties)
|
|
827
828
|
|
|
828
829
|
|
|
@@ -1070,7 +1071,6 @@ def measure_at_position(pos, mode, return_measurements=False, threads=1):
|
|
|
1070
1071
|
|
|
1071
1072
|
table = pos + os.sep.join(["output", "tables", f"trajectories_{mode}.csv"])
|
|
1072
1073
|
if return_measurements:
|
|
1073
|
-
import pandas as pd
|
|
1074
1074
|
|
|
1075
1075
|
df = pd.read_csv(table)
|
|
1076
1076
|
return df
|
|
@@ -249,9 +249,13 @@ def rename_intensity_column(df, channels):
|
|
|
249
249
|
if np.any(test_digit):
|
|
250
250
|
index = int(sections[np.where(test_digit)[0]][-1])
|
|
251
251
|
else:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
)
|
|
252
|
+
# Check if the column already contains a channel name (e.g., spot detection columns)
|
|
253
|
+
# If so, skip silently as no renaming is needed
|
|
254
|
+
already_named = any(ch in intensity_cols[k] for ch in channel_names)
|
|
255
|
+
if not already_named:
|
|
256
|
+
print(
|
|
257
|
+
f"No valid channel index found for {intensity_cols[k]}... Skipping the renaming for {intensity_cols[k]}..."
|
|
258
|
+
)
|
|
255
259
|
continue
|
|
256
260
|
|
|
257
261
|
channel_name = channel_names[np.where(channel_indices == index)[0]][0]
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import numpy as np
|
|
3
|
+
import logging
|
|
4
|
+
from PyQt5.QtWidgets import QApplication
|
|
5
|
+
from celldetective.gui.viewers.spot_detection_viewer import SpotDetectionVisualizer
|
|
6
|
+
from celldetective.gui.gui_utils import PreprocessingLayout2
|
|
7
|
+
from unittest.mock import MagicMock, patch
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture(autouse=True)
|
|
11
|
+
def disable_logging():
|
|
12
|
+
"""Disable all logging to avoid Windows OSError with pytest capture."""
|
|
13
|
+
logger = logging.getLogger()
|
|
14
|
+
try:
|
|
15
|
+
logging.disable(logging.CRITICAL)
|
|
16
|
+
yield
|
|
17
|
+
finally:
|
|
18
|
+
logging.disable(logging.NOTSET)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture
|
|
22
|
+
def dummy_data():
|
|
23
|
+
"""
|
|
24
|
+
Create a dummy stack: 5 frames, 100x100 pixels, 2 channels.
|
|
25
|
+
Channel 0: Clean background (zeros)
|
|
26
|
+
Channel 1: Two Gaussian spots with high intensity
|
|
27
|
+
"""
|
|
28
|
+
frames = 5
|
|
29
|
+
y, x = 100, 100
|
|
30
|
+
channels = 2
|
|
31
|
+
|
|
32
|
+
stack = np.zeros((frames, y, x, channels), dtype=np.float32)
|
|
33
|
+
|
|
34
|
+
# Create coordinate grids
|
|
35
|
+
Y, X = np.ogrid[:y, :x]
|
|
36
|
+
|
|
37
|
+
# Gaussian spot 1 at (22, 22) - center of mask 1
|
|
38
|
+
center_y1, center_x1 = 22, 22
|
|
39
|
+
sigma = 2.0
|
|
40
|
+
gaussian1 = np.exp(-((Y - center_y1) ** 2 + (X - center_x1) ** 2) / (2 * sigma**2))
|
|
41
|
+
|
|
42
|
+
# Gaussian spot 2 at (62, 62) - center of mask 2
|
|
43
|
+
center_y2, center_x2 = 62, 62
|
|
44
|
+
gaussian2 = np.exp(-((Y - center_y2) ** 2 + (X - center_x2) ** 2) / (2 * sigma**2))
|
|
45
|
+
|
|
46
|
+
# Add to stack with high intensity (1000 for spot 1, 800 for spot 2)
|
|
47
|
+
spots_frame = (gaussian1 * 1000 + gaussian2 * 800).astype(np.float32)
|
|
48
|
+
for f in range(frames):
|
|
49
|
+
stack[f, :, :, 1] = spots_frame
|
|
50
|
+
|
|
51
|
+
# Channel 0 stays at zero (clean background)
|
|
52
|
+
|
|
53
|
+
# Create dummy masks (labels) - each spot is inside its own cell
|
|
54
|
+
masks = np.zeros((frames, y, x), dtype=np.uint16)
|
|
55
|
+
masks[:, 15:30, 15:30] = 1 # Mask around Spot 1 (22, 22)
|
|
56
|
+
masks[:, 55:70, 55:70] = 2 # Mask around Spot 2 (62, 62)
|
|
57
|
+
|
|
58
|
+
return stack, masks
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_spot_detection_visualizer_interactions(qtbot, dummy_data):
|
|
62
|
+
"""
|
|
63
|
+
Test interactions with SpotDetectionVisualizer.
|
|
64
|
+
"""
|
|
65
|
+
stack, labels = dummy_data
|
|
66
|
+
channel_names = ["Background", "Spots"]
|
|
67
|
+
|
|
68
|
+
# Mock parent widgets that might be updated by the visualizer
|
|
69
|
+
parent_channel_cb = MagicMock()
|
|
70
|
+
parent_diameter_le = MagicMock()
|
|
71
|
+
parent_threshold_le = MagicMock()
|
|
72
|
+
parent_preprocessing_list = MagicMock()
|
|
73
|
+
|
|
74
|
+
viewer = SpotDetectionVisualizer(
|
|
75
|
+
stack=stack,
|
|
76
|
+
labels=labels,
|
|
77
|
+
channel_names=channel_names,
|
|
78
|
+
n_channels=2,
|
|
79
|
+
parent_channel_cb=parent_channel_cb,
|
|
80
|
+
parent_diameter_le=parent_diameter_le,
|
|
81
|
+
parent_threshold_le=parent_threshold_le,
|
|
82
|
+
parent_preprocessing_list=parent_preprocessing_list,
|
|
83
|
+
window_title="Test Spot Detective",
|
|
84
|
+
channel_cb=True,
|
|
85
|
+
contrast_slider=False,
|
|
86
|
+
frame_slider=False,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
qtbot.addWidget(viewer)
|
|
90
|
+
viewer.show()
|
|
91
|
+
qtbot.waitForWindowShown(viewer)
|
|
92
|
+
|
|
93
|
+
# 1. Test Channel Selection
|
|
94
|
+
# Default is target_channel=0 (Background)
|
|
95
|
+
assert viewer.detection_channel == 0
|
|
96
|
+
|
|
97
|
+
# Switch to Spots channel (Index 1)
|
|
98
|
+
viewer.detection_channel_cb.setCurrentIndex(1)
|
|
99
|
+
assert viewer.detection_channel == 1
|
|
100
|
+
|
|
101
|
+
# Force frame update to ensure target_img is correct for channel 1
|
|
102
|
+
viewer.change_frame(0)
|
|
103
|
+
|
|
104
|
+
# Verify image updated to Channel 1, Frame 0
|
|
105
|
+
current_img = viewer.target_img
|
|
106
|
+
expected_img = stack[0, :, :, 1]
|
|
107
|
+
np.testing.assert_array_equal(current_img, expected_img)
|
|
108
|
+
|
|
109
|
+
# 2. Test Spot Detection Parameters
|
|
110
|
+
# Set Diameter (LoG works best with diameter ~ 2*sqrt(2)*sigma ~ 5.6 for sigma=2)
|
|
111
|
+
viewer.spot_diam_le.clear()
|
|
112
|
+
qtbot.keyClicks(viewer.spot_diam_le, "4")
|
|
113
|
+
assert viewer.spot_diam_le.text() == "4"
|
|
114
|
+
|
|
115
|
+
# Set Threshold (low threshold to ensure detection)
|
|
116
|
+
viewer.spot_thresh_le.clear()
|
|
117
|
+
qtbot.keyClicks(viewer.spot_thresh_le, "0.01")
|
|
118
|
+
assert viewer.spot_thresh_le.text() == "0.01"
|
|
119
|
+
|
|
120
|
+
# Manually trigger control_valid_parameters to update self.diameter and self.thresh
|
|
121
|
+
viewer.control_valid_parameters()
|
|
122
|
+
|
|
123
|
+
# Verify parameters were set
|
|
124
|
+
assert viewer.diameter == 4.0
|
|
125
|
+
assert viewer.thresh == 0.01
|
|
126
|
+
|
|
127
|
+
# Trigger detection by clicking apply button
|
|
128
|
+
qtbot.mouseClick(viewer.apply_diam_btn, 1) # Qt.LeftButton = 1
|
|
129
|
+
qtbot.wait(200) # Wait for detection to complete
|
|
130
|
+
|
|
131
|
+
# Check that we recovered 2 spots
|
|
132
|
+
# In dummy_data: Spot 1 at (22, 22), Spot 2 at (62, 62)
|
|
133
|
+
n_spots = (
|
|
134
|
+
len(viewer.spot_positions)
|
|
135
|
+
if hasattr(viewer, "spot_positions") and viewer.spot_positions is not None
|
|
136
|
+
else 0
|
|
137
|
+
)
|
|
138
|
+
assert (
|
|
139
|
+
n_spots == 2
|
|
140
|
+
), f"Expected 2 spots, found {n_spots}. Positions: {viewer.spot_positions if hasattr(viewer, 'spot_positions') else 'N/A'}"
|
|
141
|
+
|
|
142
|
+
# Verify positions roughly match (22, 22) and (62, 62)
|
|
143
|
+
# spot_positions are (x, y) pairs
|
|
144
|
+
pos = viewer.spot_positions
|
|
145
|
+
has_spot_1 = np.any(np.all(np.abs(pos - [22, 22]) < 5, axis=1))
|
|
146
|
+
has_spot_2 = np.any(np.all(np.abs(pos - [62, 62]) < 5, axis=1))
|
|
147
|
+
assert has_spot_1, f"Spot 1 not found near (22, 22). Positions: {pos}"
|
|
148
|
+
assert has_spot_2, f"Spot 2 not found near (62, 62). Positions: {pos}"
|
|
149
|
+
|
|
150
|
+
# 3. Test Preprocessing and Preview
|
|
151
|
+
# Ensure preview is unchecked initially
|
|
152
|
+
assert not viewer.preview_cb.isChecked()
|
|
153
|
+
|
|
154
|
+
# Check "Preview" - should show original image if filter list is empty
|
|
155
|
+
viewer.preview_cb.setChecked(True)
|
|
156
|
+
assert viewer.preview_cb.isChecked()
|
|
157
|
+
# Image should still match original target since no filters
|
|
158
|
+
np.testing.assert_array_equal(viewer.im.get_array(), expected_img)
|
|
159
|
+
|
|
160
|
+
# Add a filter: "gauss" with sigma=2
|
|
161
|
+
# Directly manipulate the list since dialog interaction is complex
|
|
162
|
+
viewer.preprocessing.list.items.append(["gauss", 2])
|
|
163
|
+
viewer.preprocessing.list.list_widget.addItems(["gauss_filter"])
|
|
164
|
+
|
|
165
|
+
# Force preview update
|
|
166
|
+
viewer.update_preview_if_active()
|
|
167
|
+
qtbot.wait(200)
|
|
168
|
+
|
|
169
|
+
preview_img = viewer.im.get_array()
|
|
170
|
+
assert not np.array_equal(
|
|
171
|
+
preview_img, expected_img
|
|
172
|
+
), "Preview image should differ after adding gaussian filter"
|
|
173
|
+
|
|
174
|
+
# 4. Remove Filter
|
|
175
|
+
# Select item 0
|
|
176
|
+
viewer.preprocessing.list.list_widget.setCurrentRow(0)
|
|
177
|
+
# Click remove button
|
|
178
|
+
viewer.preprocessing.delete_filter_btn.click()
|
|
179
|
+
|
|
180
|
+
qtbot.wait(200)
|
|
181
|
+
|
|
182
|
+
# Internal items should be empty
|
|
183
|
+
assert len(viewer.preprocessing.list.items) == 0
|
|
184
|
+
|
|
185
|
+
# Preview should revert to original
|
|
186
|
+
reverted_img = viewer.im.get_array()
|
|
187
|
+
np.testing.assert_array_equal(reverted_img, expected_img)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@pytest.fixture
|
|
191
|
+
def dark_spots_data():
|
|
192
|
+
"""
|
|
193
|
+
Create a dummy stack with DARK spots on a BRIGHT background.
|
|
194
|
+
This simulates scenarios like:
|
|
195
|
+
- Absorbing particles on a bright field
|
|
196
|
+
- Phase contrast imaging with dark nuclei
|
|
197
|
+
The invert preprocessing filter should be used to detect these spots.
|
|
198
|
+
"""
|
|
199
|
+
frames = 5
|
|
200
|
+
y, x = 100, 100
|
|
201
|
+
channels = 2
|
|
202
|
+
|
|
203
|
+
# Bright background (value 1000)
|
|
204
|
+
stack = np.ones((frames, y, x, channels), dtype=np.float32) * 1000
|
|
205
|
+
|
|
206
|
+
# Create coordinate grids
|
|
207
|
+
Y, X = np.ogrid[:y, :x]
|
|
208
|
+
|
|
209
|
+
# Dark Gaussian spot 1 at (22, 22)
|
|
210
|
+
center_y1, center_x1 = 22, 22
|
|
211
|
+
sigma = 2.0
|
|
212
|
+
gaussian1 = np.exp(-((Y - center_y1) ** 2 + (X - center_x1) ** 2) / (2 * sigma**2))
|
|
213
|
+
|
|
214
|
+
# Dark Gaussian spot 2 at (62, 62)
|
|
215
|
+
center_y2, center_x2 = 62, 62
|
|
216
|
+
gaussian2 = np.exp(-((Y - center_y2) ** 2 + (X - center_x2) ** 2) / (2 * sigma**2))
|
|
217
|
+
|
|
218
|
+
# Subtract from bright background to create dark spots
|
|
219
|
+
# Spot 1: drops to ~0 at center, Spot 2: drops to ~200 at center
|
|
220
|
+
dark_spots_frame = 1000 - (gaussian1 * 1000 + gaussian2 * 800)
|
|
221
|
+
dark_spots_frame = dark_spots_frame.astype(np.float32)
|
|
222
|
+
|
|
223
|
+
for f in range(frames):
|
|
224
|
+
stack[f, :, :, 1] = dark_spots_frame
|
|
225
|
+
|
|
226
|
+
# Channel 0 stays uniform bright (no spots)
|
|
227
|
+
|
|
228
|
+
# Create dummy masks
|
|
229
|
+
masks = np.zeros((frames, y, x), dtype=np.uint16)
|
|
230
|
+
masks[:, 15:30, 15:30] = 1 # Mask around Spot 1
|
|
231
|
+
masks[:, 55:70, 55:70] = 2 # Mask around Spot 2
|
|
232
|
+
|
|
233
|
+
return stack, masks
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def test_dark_spot_detection_with_invert(qtbot, dark_spots_data):
|
|
237
|
+
"""
|
|
238
|
+
Test detection of dark spots on a bright background using the invert filter.
|
|
239
|
+
|
|
240
|
+
Bug Prevention:
|
|
241
|
+
- Ensures the preprocessing pipeline (invert) correctly transforms images
|
|
242
|
+
before spot detection.
|
|
243
|
+
- Verifies that the detection uses the preprocessed image, not the raw image.
|
|
244
|
+
|
|
245
|
+
Steps:
|
|
246
|
+
1. Load stack with dark spots on bright background.
|
|
247
|
+
2. Switch to the dark spots channel.
|
|
248
|
+
3. Add an "invert" preprocessing filter with max value 1000.
|
|
249
|
+
4. Set detection parameters (diameter, threshold).
|
|
250
|
+
5. Trigger detection.
|
|
251
|
+
6. Verify that both dark spots are detected.
|
|
252
|
+
|
|
253
|
+
Expected Outcome:
|
|
254
|
+
- 2 spots detected at positions (22, 22) and (62, 62).
|
|
255
|
+
"""
|
|
256
|
+
stack, labels = dark_spots_data
|
|
257
|
+
channel_names = ["Uniform", "DarkSpots"]
|
|
258
|
+
|
|
259
|
+
parent_channel_cb = MagicMock()
|
|
260
|
+
parent_diameter_le = MagicMock()
|
|
261
|
+
parent_threshold_le = MagicMock()
|
|
262
|
+
parent_preprocessing_list = MagicMock()
|
|
263
|
+
|
|
264
|
+
viewer = SpotDetectionVisualizer(
|
|
265
|
+
stack=stack,
|
|
266
|
+
labels=labels,
|
|
267
|
+
channel_names=channel_names,
|
|
268
|
+
n_channels=2,
|
|
269
|
+
parent_channel_cb=parent_channel_cb,
|
|
270
|
+
parent_diameter_le=parent_diameter_le,
|
|
271
|
+
parent_threshold_le=parent_threshold_le,
|
|
272
|
+
parent_preprocessing_list=parent_preprocessing_list,
|
|
273
|
+
window_title="Test Dark Spot Detection",
|
|
274
|
+
channel_cb=True,
|
|
275
|
+
contrast_slider=False,
|
|
276
|
+
frame_slider=False,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
qtbot.addWidget(viewer)
|
|
280
|
+
viewer.show()
|
|
281
|
+
qtbot.waitForWindowShown(viewer)
|
|
282
|
+
|
|
283
|
+
# 1. Switch to DarkSpots channel (Index 1)
|
|
284
|
+
viewer.detection_channel_cb.setCurrentIndex(1)
|
|
285
|
+
assert viewer.detection_channel == 1
|
|
286
|
+
viewer.change_frame(0)
|
|
287
|
+
|
|
288
|
+
# Verify image is bright with dark spots
|
|
289
|
+
current_img = viewer.target_img
|
|
290
|
+
# The center of spot 1 (22, 22) should be dark (near 0)
|
|
291
|
+
assert (
|
|
292
|
+
current_img[22, 22] < 100
|
|
293
|
+
), f"Expected dark spot at (22,22), got {current_img[22, 22]}"
|
|
294
|
+
# The background should be bright (near 1000)
|
|
295
|
+
assert (
|
|
296
|
+
current_img[0, 0] > 900
|
|
297
|
+
), f"Expected bright background, got {current_img[0, 0]}"
|
|
298
|
+
|
|
299
|
+
# 2. Add invert preprocessing filter
|
|
300
|
+
# invert filter inverts the image: output = max_value - input
|
|
301
|
+
# With max_value=1000, dark spots become bright spots
|
|
302
|
+
viewer.preprocessing.list.items.append(["invert", 1000])
|
|
303
|
+
viewer.preprocessing.list.list_widget.addItems(["invert_1000"])
|
|
304
|
+
|
|
305
|
+
# 3. Set detection parameters
|
|
306
|
+
viewer.spot_diam_le.clear()
|
|
307
|
+
qtbot.keyClicks(viewer.spot_diam_le, "4")
|
|
308
|
+
viewer.spot_thresh_le.clear()
|
|
309
|
+
qtbot.keyClicks(viewer.spot_thresh_le, "0.01")
|
|
310
|
+
viewer.control_valid_parameters()
|
|
311
|
+
|
|
312
|
+
assert viewer.diameter == 4.0
|
|
313
|
+
assert viewer.thresh == 0.01
|
|
314
|
+
|
|
315
|
+
# Enable preview to verify inversion works
|
|
316
|
+
viewer.preview_cb.setChecked(True)
|
|
317
|
+
viewer.update_preview_if_active()
|
|
318
|
+
qtbot.wait(200)
|
|
319
|
+
|
|
320
|
+
# After inversion, the former dark spot at (22, 22) should now be bright
|
|
321
|
+
preview_img = viewer.im.get_array()
|
|
322
|
+
assert (
|
|
323
|
+
preview_img[22, 22] > 900
|
|
324
|
+
), f"After invert, spot should be bright. Got {preview_img[22, 22]}"
|
|
325
|
+
|
|
326
|
+
# 4. Trigger detection
|
|
327
|
+
qtbot.mouseClick(viewer.apply_diam_btn, 1)
|
|
328
|
+
qtbot.wait(200)
|
|
329
|
+
|
|
330
|
+
# 5. Verify spots detected
|
|
331
|
+
n_spots = (
|
|
332
|
+
len(viewer.spot_positions)
|
|
333
|
+
if hasattr(viewer, "spot_positions") and viewer.spot_positions is not None
|
|
334
|
+
else 0
|
|
335
|
+
)
|
|
336
|
+
assert (
|
|
337
|
+
n_spots == 2
|
|
338
|
+
), f"Expected 2 dark spots detected, found {n_spots}. Positions: {getattr(viewer, 'spot_positions', 'N/A')}"
|
|
339
|
+
|
|
340
|
+
# Verify positions
|
|
341
|
+
pos = viewer.spot_positions
|
|
342
|
+
has_spot_1 = np.any(np.all(np.abs(pos - [22, 22]) < 5, axis=1))
|
|
343
|
+
has_spot_2 = np.any(np.all(np.abs(pos - [62, 62]) < 5, axis=1))
|
|
344
|
+
assert has_spot_1, f"Dark Spot 1 not found near (22, 22). Positions: {pos}"
|
|
345
|
+
assert has_spot_2, f"Dark Spot 2 not found near (62, 62). Positions: {pos}"
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def test_viewer_initializes_from_parent_values(qtbot, dummy_data):
|
|
349
|
+
"""
|
|
350
|
+
Test that the viewer initializes its widgets from initial_* arguments.
|
|
351
|
+
"""
|
|
352
|
+
stack, labels = dummy_data
|
|
353
|
+
channel_names = ["Background", "Spots"]
|
|
354
|
+
|
|
355
|
+
# Mock parent widgets
|
|
356
|
+
parent_channel_cb = MagicMock()
|
|
357
|
+
parent_diameter_le = MagicMock()
|
|
358
|
+
parent_threshold_le = MagicMock()
|
|
359
|
+
parent_preprocessing_list = MagicMock()
|
|
360
|
+
|
|
361
|
+
initial_params = [["gauss", 1.5], ["invert", 500]]
|
|
362
|
+
|
|
363
|
+
viewer = SpotDetectionVisualizer(
|
|
364
|
+
stack=stack,
|
|
365
|
+
labels=labels,
|
|
366
|
+
channel_names=channel_names,
|
|
367
|
+
n_channels=2,
|
|
368
|
+
parent_channel_cb=parent_channel_cb,
|
|
369
|
+
parent_diameter_le=parent_diameter_le,
|
|
370
|
+
parent_threshold_le=parent_threshold_le,
|
|
371
|
+
parent_preprocessing_list=parent_preprocessing_list,
|
|
372
|
+
window_title="Test Init Values",
|
|
373
|
+
channel_cb=True,
|
|
374
|
+
contrast_slider=False,
|
|
375
|
+
frame_slider=False,
|
|
376
|
+
# Pass initial values
|
|
377
|
+
initial_diameter="8.5",
|
|
378
|
+
initial_threshold="1.2",
|
|
379
|
+
initial_preprocessing=initial_params,
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
qtbot.addWidget(viewer)
|
|
383
|
+
viewer.show()
|
|
384
|
+
qtbot.waitForWindowShown(viewer)
|
|
385
|
+
|
|
386
|
+
# Verify widgets were initialized with passed values
|
|
387
|
+
assert viewer.spot_diam_le.text() == "8.5"
|
|
388
|
+
assert viewer.spot_thresh_le.text() == "1.2"
|
|
389
|
+
|
|
390
|
+
# Verify preprocessing list was populated
|
|
391
|
+
current_filters = viewer.preprocessing.list.items
|
|
392
|
+
assert len(current_filters) == 2
|
|
393
|
+
assert current_filters[0] == ["gauss", 1.5]
|
|
394
|
+
assert current_filters[1] == ["invert", 500]
|