celldetective 1.5.0b8__py3-none-any.whl → 1.5.0b9__py3-none-any.whl
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/_version.py +1 -1
- celldetective/gui/base/list_widget.py +1 -1
- celldetective/gui/generic_signal_plot.py +14 -14
- celldetective/gui/gui_utils.py +27 -6
- celldetective/gui/plot_signals_ui.py +32 -15
- celldetective/gui/survival_ui.py +39 -11
- celldetective/gui/tableUI.py +69 -148
- celldetective/gui/thresholds_gui.py +45 -5
- celldetective/gui/viewers/spot_detection_viewer.py +136 -27
- {celldetective-1.5.0b8.dist-info → celldetective-1.5.0b9.dist-info}/METADATA +1 -1
- {celldetective-1.5.0b8.dist-info → celldetective-1.5.0b9.dist-info}/RECORD +17 -16
- tests/gui/test_spot_detection_viewer.py +187 -0
- tests/test_signals.py +131 -112
- {celldetective-1.5.0b8.dist-info → celldetective-1.5.0b9.dist-info}/WHEEL +0 -0
- {celldetective-1.5.0b8.dist-info → celldetective-1.5.0b9.dist-info}/entry_points.txt +0 -0
- {celldetective-1.5.0b8.dist-info → celldetective-1.5.0b9.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.5.0b8.dist-info → celldetective-1.5.0b9.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
celldetective/__init__.py,sha256=LfnOyfUnYPGDc8xcsF_PfYEL7-CqAb7BMBPBIWGv84o,666
|
|
2
2
|
celldetective/__main__.py,sha256=Rzzu9ArxZSgfBVjV-lyn-3oanQB2MumQR6itK5ZaRpA,2597
|
|
3
|
-
celldetective/_version.py,sha256=
|
|
3
|
+
celldetective/_version.py,sha256=C-mBr40Do8xTMcYdWQX-DapmWmTsr2JSmIPIRtYIXUU,24
|
|
4
4
|
celldetective/event_detection_models.py,sha256=A7ZJFhJwfdhzfxJ-YZIj6IoI9Gc1hyAodFkKt8kNxDk,93549
|
|
5
5
|
celldetective/events.py,sha256=n15R53c7QZ2wT8gjb0oeNikQbuRBrVVbyNsRCqXjzXA,8166
|
|
6
6
|
celldetective/exceptions.py,sha256=f3VmIYOthWTiqMEV5xQCox2rw5c5e7yog88h-CcV4oI,356
|
|
@@ -26,28 +26,28 @@ celldetective/gui/configure_new_exp.py,sha256=vgT6ozRIGDvT3R0qENlqvDn17BpKnwyJRh
|
|
|
26
26
|
celldetective/gui/control_panel.py,sha256=dMNzgivt6GdYROPlYpEY5TTNcANenm9ifUI3W3OcpOo,24337
|
|
27
27
|
celldetective/gui/dynamic_progress.py,sha256=wjTmDPy62qssY0zdteP3T9umIGGQk0UvebFIdn54CIc,16676
|
|
28
28
|
celldetective/gui/event_annotator.py,sha256=JVd64Gch_qzXv29VJGAa2Ytk-WhxifZVgMK-hAxnGu4,41262
|
|
29
|
-
celldetective/gui/generic_signal_plot.py,sha256=
|
|
30
|
-
celldetective/gui/gui_utils.py,sha256=
|
|
29
|
+
celldetective/gui/generic_signal_plot.py,sha256=47kXIuMcnQXjeNEdzM_G1WbW9TL5eMSjHC9XgWXMly4,49492
|
|
30
|
+
celldetective/gui/gui_utils.py,sha256=l7P6emKVEciCRdmnbfYvJAhl0MnbT3QkL2zpSPuHRoY,34120
|
|
31
31
|
celldetective/gui/interactions_block.py,sha256=34VaHFrdKsq1hYuXrosmpP15JU26dSfbyx4lyt1jxNg,28440
|
|
32
32
|
celldetective/gui/interactive_timeseries_viewer.py,sha256=u_amAhLdEHRpYSRwPDtVm5v-JZIz0ANTcG4YGksX1Vo,16079
|
|
33
33
|
celldetective/gui/json_readers.py,sha256=t5rhtIxACj0pdwLrnPs-QjyhQo3P25UGWGgOCIBhQxs,4572
|
|
34
34
|
celldetective/gui/measure_annotator.py,sha256=ljNbsKmFXQk0R9zEfBZ6XfBHzFmlL7Gt6QyPHyqh08g,38357
|
|
35
35
|
celldetective/gui/pair_event_annotator.py,sha256=9PT67-8FJxcL7lSDIAZcZmrW75G_R-fpRellMOsgets,128788
|
|
36
36
|
celldetective/gui/plot_measurements.py,sha256=a_Mks-5XUTn2QEYih0PlXGp2lX3C34zuhK9ozzE1guM,56593
|
|
37
|
-
celldetective/gui/plot_signals_ui.py,sha256=
|
|
37
|
+
celldetective/gui/plot_signals_ui.py,sha256=fxgkUZ_m7jFTXOwdzfJdl8wt3n8RECny3n4Tpk-GE6w,29016
|
|
38
38
|
celldetective/gui/preprocessing_block.py,sha256=cgUBv-eQBwfidK48-cTWJi0PS62wlXhsNLnsKhy6MQY,14967
|
|
39
39
|
celldetective/gui/process_block.py,sha256=KVfXnC55EYGlgKVDXDSMGu_MZbxjbQaQz4Ulyl5iqSE,76881
|
|
40
40
|
celldetective/gui/seg_model_loader.py,sha256=CqcyT1ZeQ27R54RmOHjAt1pfRwjtvS-Psw29PpFsT5A,23549
|
|
41
|
-
celldetective/gui/survival_ui.py,sha256=
|
|
42
|
-
celldetective/gui/tableUI.py,sha256=
|
|
43
|
-
celldetective/gui/thresholds_gui.py,sha256=
|
|
41
|
+
celldetective/gui/survival_ui.py,sha256=YBtbkjXgJW4KLpRaFysJc_po4OjVcLyCmGSWKtMbjdk,17353
|
|
42
|
+
celldetective/gui/tableUI.py,sha256=8cHVKMgTHMWLf_2yxYo003FQ0TaN7Cf13rBKBZQtefQ,58989
|
|
43
|
+
celldetective/gui/thresholds_gui.py,sha256=KxPmCmoMGGwwHFwPWHCOr1wPTyOPR2d-zucuAvZfSqk,34783
|
|
44
44
|
celldetective/gui/workers.py,sha256=WAtVxFEvHApBSAZMVyYsya_DHL_bYo8b57dbgUJQHc4,14890
|
|
45
45
|
celldetective/gui/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
46
|
celldetective/gui/base/channel_norm_generator.py,sha256=HJ57wBMdBIvca92V477T-aPuxvvC2aC5BDMssKSQKCQ,14592
|
|
47
47
|
celldetective/gui/base/components.py,sha256=jNUsCU_QE7QUFR0_xEvEPFEBYolMJt7YXGUKMjF7uOE,8155
|
|
48
48
|
celldetective/gui/base/feature_choice.py,sha256=n1T2fPoNLiTDS_6fa_GuGReDhBW11HdUrKy2RywotF8,2800
|
|
49
49
|
celldetective/gui/base/figure_canvas.py,sha256=mSiIYvEfz7MYMgdPDf6RKSMpKN8FkeZL7lugDNnDpnM,2245
|
|
50
|
-
celldetective/gui/base/list_widget.py,sha256=
|
|
50
|
+
celldetective/gui/base/list_widget.py,sha256=APt7rCfMRRlFnHUwvwrwZGMTYM6M10zgu6uoHHsmmuA,4694
|
|
51
51
|
celldetective/gui/base/styles.py,sha256=3Kz1eXw6OLr90wtErhK0KPJyJbyhAlqkniqm0JNGwFc,7407
|
|
52
52
|
celldetective/gui/base/utils.py,sha256=KojauRKGM9XKNhaWn211p6mJNZWIHLH75yeLpDd7pvA,1103
|
|
53
53
|
celldetective/gui/help/DL-segmentation-strategy.json,sha256=PZD9xXjrwbX3TiudHJPuvcyZD28o4k-fVgeTd7dBKzI,1583
|
|
@@ -92,7 +92,7 @@ celldetective/gui/viewers/base_viewer.py,sha256=Wn4na79xRL1R6PSXIoE_UabxJNtNQ-Y5
|
|
|
92
92
|
celldetective/gui/viewers/channel_offset_viewer.py,sha256=cywBkxyMPyKIuwZTGA03DBSS4a-H1SAohMJYOPISLEE,16289
|
|
93
93
|
celldetective/gui/viewers/contour_viewer.py,sha256=riHj03LKXLoa-Ys2o2EhCE5nULfcHMohx9LFoXbI6zU,14720
|
|
94
94
|
celldetective/gui/viewers/size_viewer.py,sha256=uXITjaxg5dhQ09Q6TNUxwLxi-ZglyGFcxEH1RtGIZWw,6020
|
|
95
|
-
celldetective/gui/viewers/spot_detection_viewer.py,sha256=
|
|
95
|
+
celldetective/gui/viewers/spot_detection_viewer.py,sha256=IsSqbdJ7Fuzc7CSIt5U8LoeHuq-GLgg-igqlw5NRn7g,16502
|
|
96
96
|
celldetective/gui/viewers/threshold_viewer.py,sha256=F-13JF2wFhyvvKfUvgRcSjWL3leAliOXy5yUergndnE,12000
|
|
97
97
|
celldetective/icons/logo-large.png,sha256=FXSwV3u6zEKcfpuSn4unnqB0oUnN9cHqQ9BCKWytrpg,36631
|
|
98
98
|
celldetective/icons/logo.png,sha256=wV2OS8_dU5Td5cgdPbCOU3JpMpTwNuYLnfVcnQX0tJA,2437
|
|
@@ -167,7 +167,7 @@ celldetective/utils/event_detection/__init__.py,sha256=GvsdyQLMTXJj1S_FfRXjrpOxE
|
|
|
167
167
|
celldetective/utils/plots/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
168
168
|
celldetective/utils/plots/regression.py,sha256=oUCn29-hp7PxMqC-R0yoL60KMw5ZWpZAIoCDh2ErlcY,1764
|
|
169
169
|
celldetective/utils/stardist_utils/__init__.py,sha256=SY2kxFNXSRjXN4ncs3heDdXT3UNk8M3dELJQySysAf4,4231
|
|
170
|
-
celldetective-1.5.
|
|
170
|
+
celldetective-1.5.0b9.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
171
171
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
172
172
|
tests/test_cellpose_fallback.py,sha256=BJZTDFF8sFR1x7rDbvZQ2RQOB1OP6wuFBRfc8zbl5zw,3513
|
|
173
173
|
tests/test_events.py,sha256=eLFwwEEJfQAdwhews3-fn1HSvzozcNNFN_Qn0gOvQkE,685
|
|
@@ -179,7 +179,7 @@ tests/test_notebooks.py,sha256=7HVmYiytsz0QIJ11iRkGGs4_hzNjofXAUs_OZou3Gm0,301
|
|
|
179
179
|
tests/test_partial_install.py,sha256=G69-GNcJ9YNgs6K2bVTEZO0Jpb14xMRQWTm8A6VuIco,2841
|
|
180
180
|
tests/test_preprocessing.py,sha256=c0rKS9d5h37uDcV7fVOTnn5GMVbEB84b8ZTCTdRmvFs,1422
|
|
181
181
|
tests/test_segmentation.py,sha256=k1b_zIZdlytEdJcHjAUQEO3gTBAHtv5WvrwQN2xD4kc,3470
|
|
182
|
-
tests/test_signals.py,sha256=
|
|
182
|
+
tests/test_signals.py,sha256=upfhDyQMquOOKuYf7a34TglimHK74oYWQ9RyuK7Mg7c,4285
|
|
183
183
|
tests/test_tracking.py,sha256=_YLjwQ3EChQssoHDfEnUJ7fI1yC5KEcJsFnAVR64L70,18909
|
|
184
184
|
tests/test_utils.py,sha256=aSB_eMw9fzTsnxxdYoNmdQQRrXkWqBXB7Uv4TGU6kYE,4778
|
|
185
185
|
tests/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -187,8 +187,9 @@ tests/gui/test_enhancements.py,sha256=3x9au_rkQtMZ94DRj3OaEHKPr511RrWqBAUAcNQn1y
|
|
|
187
187
|
tests/gui/test_measure_annotator_bugfix.py,sha256=tPfgWNKC0UkvrVssSrUcVDC1qgpzx6l2yCqvKtKYkM4,4544
|
|
188
188
|
tests/gui/test_new_project.py,sha256=wRjW2vEaZb0LWT-f8G8-Ptk8CW9z8-FDPLpV5uqj6ck,8778
|
|
189
189
|
tests/gui/test_project.py,sha256=KzAnodIc0Ovta0ARL5Kr5PkOR5euA6qczT_GhEZpyE4,4710
|
|
190
|
-
|
|
191
|
-
celldetective-1.5.
|
|
192
|
-
celldetective-1.5.
|
|
193
|
-
celldetective-1.5.
|
|
194
|
-
celldetective-1.5.
|
|
190
|
+
tests/gui/test_spot_detection_viewer.py,sha256=g-Lyi1sJNWCDy3gPkRU0RlP1BJaOJ6Gg3Ouy0cX_F3Y,6415
|
|
191
|
+
celldetective-1.5.0b9.dist-info/METADATA,sha256=-Vdw3UnjUJDidGKCCb8Z_5EcQ9Z4TUj35w5KYx6z1x4,11523
|
|
192
|
+
celldetective-1.5.0b9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
193
|
+
celldetective-1.5.0b9.dist-info/entry_points.txt,sha256=2NU6_EOByvPxqBbCvjwxlVlvnQreqZ3BKRCVIKEv3dg,62
|
|
194
|
+
celldetective-1.5.0b9.dist-info/top_level.txt,sha256=6rsIKKfGMKgud7HPuATcpq6EhdXwcg_yknBVWn9x4C4,20
|
|
195
|
+
celldetective-1.5.0b9.dist-info/RECORD,,
|
|
@@ -0,0 +1,187 @@
|
|
|
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)
|
tests/test_signals.py
CHANGED
|
@@ -1,135 +1,154 @@
|
|
|
1
1
|
import unittest
|
|
2
|
-
import matplotlib.pyplot as plt
|
|
3
2
|
import numpy as np
|
|
4
3
|
import os
|
|
5
4
|
import random
|
|
6
5
|
import pandas as pd
|
|
7
6
|
import shutil
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
|
|
9
|
+
def sigmoid(t, t0, dt, A, offset):
|
|
10
|
+
return A / (1 + np.exp(-(t - t0) / dt)) + offset
|
|
11
|
+
|
|
11
12
|
|
|
12
13
|
def generate_fake_signal_data(n_signals):
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
14
|
+
|
|
15
|
+
timeline = np.linspace(0, 100, 100)
|
|
16
|
+
amplitudes = list(np.linspace(2000, 3000, 100))
|
|
17
|
+
slopes = list(np.linspace(0.5, 5, 100))
|
|
18
|
+
means = list(np.linspace(-100, 200, 100))
|
|
19
|
+
random_cut = list(np.linspace(25, 200, 176, dtype=int))
|
|
20
|
+
noise_levels = list(np.linspace(1, 100, 100, dtype=int))
|
|
21
|
+
|
|
22
|
+
trajectories = []
|
|
23
|
+
for i in range(n_signals):
|
|
24
|
+
|
|
25
|
+
a = random.sample(amplitudes, k=1)[0]
|
|
26
|
+
dt = random.sample(slopes, k=1)[0]
|
|
27
|
+
mu = random.sample(means, k=1)[0]
|
|
28
|
+
cut = random.sample(random_cut, k=1)[0]
|
|
29
|
+
n = random.sample(noise_levels, k=1)[0]
|
|
30
|
+
|
|
31
|
+
if mu <= 0.0:
|
|
32
|
+
cclass = 2
|
|
33
|
+
t0 = -1
|
|
34
|
+
elif (mu > 0) * (mu <= 100):
|
|
35
|
+
cclass = 0
|
|
36
|
+
t0 = mu
|
|
37
|
+
else:
|
|
38
|
+
cclass = 1
|
|
39
|
+
t0 = -1
|
|
40
|
+
|
|
41
|
+
noise = [random.random() * n for i in range(len(timeline))]
|
|
42
|
+
signal = sigmoid(timeline, mu, dt, a, 0) + noise
|
|
43
|
+
signal = signal[:cut]
|
|
44
|
+
if mu >= cut:
|
|
45
|
+
cclass = 1
|
|
46
|
+
t0 = -1
|
|
47
|
+
|
|
48
|
+
for j in range(len(signal)):
|
|
49
|
+
trajectories.append(
|
|
50
|
+
{
|
|
51
|
+
"TRACK_ID": i,
|
|
52
|
+
"POSITION_X": 0.0,
|
|
53
|
+
"POSITION_Y": 0.0,
|
|
54
|
+
"FRAME": j,
|
|
55
|
+
"signal": signal[j],
|
|
56
|
+
"t0": t0,
|
|
57
|
+
"cclass": cclass,
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
trajectories = pd.DataFrame(trajectories)
|
|
62
|
+
|
|
63
|
+
return trajectories
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def export_set(trajectories, name="set.npy", output_folder="."):
|
|
67
|
+
|
|
68
|
+
training_set = []
|
|
69
|
+
cols = trajectories.columns
|
|
70
|
+
tracks = np.unique(trajectories["TRACK_ID"].to_numpy())
|
|
71
|
+
|
|
72
|
+
for track in tracks:
|
|
73
|
+
signals = {}
|
|
74
|
+
for c in cols:
|
|
75
|
+
signals.update(
|
|
76
|
+
{c: trajectories.loc[trajectories["TRACK_ID"] == track, c].to_numpy()}
|
|
77
|
+
)
|
|
78
|
+
time_of_interest = trajectories.loc[
|
|
79
|
+
trajectories["TRACK_ID"] == track, "t0"
|
|
80
|
+
].to_numpy()[0]
|
|
81
|
+
cclass = trajectories.loc[
|
|
82
|
+
trajectories["TRACK_ID"] == track, "cclass"
|
|
83
|
+
].to_numpy()[0]
|
|
84
|
+
signals.update({"time_of_interest": time_of_interest, "class": cclass})
|
|
85
|
+
training_set.append(signals)
|
|
86
|
+
|
|
87
|
+
np.save(os.sep.join([output_folder, name]), training_set)
|
|
70
88
|
|
|
71
89
|
|
|
72
90
|
class TestCreateSignalModel(unittest.TestCase):
|
|
73
91
|
|
|
74
|
-
|
|
92
|
+
def test_create_model(self):
|
|
75
93
|
|
|
76
|
-
|
|
94
|
+
from celldetective.event_detection_models import SignalDetectionModel
|
|
77
95
|
|
|
78
96
|
model = SignalDetectionModel(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
channel_option=["signal"],
|
|
98
|
+
model_signal_length=128,
|
|
99
|
+
n_channels=1,
|
|
100
|
+
n_conv=2,
|
|
101
|
+
n_classes=3,
|
|
102
|
+
dense_collection=512,
|
|
103
|
+
dropout_rate=0.1,
|
|
104
|
+
label="test",
|
|
105
|
+
)
|
|
88
106
|
|
|
89
107
|
|
|
90
108
|
class TestTrainSignalModel(unittest.TestCase):
|
|
91
109
|
|
|
92
|
-
|
|
93
|
-
|
|
110
|
+
@classmethod
|
|
111
|
+
def setUpClass(self):
|
|
94
112
|
|
|
95
|
-
|
|
113
|
+
from celldetective.event_detection_models import SignalDetectionModel
|
|
96
114
|
|
|
97
115
|
self.trajectories = generate_fake_signal_data(300)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
116
|
+
if not os.path.exists("temp"):
|
|
117
|
+
os.mkdir("temp")
|
|
118
|
+
export_set(self.trajectories, name="set.npy", output_folder="temp")
|
|
119
|
+
self.model = SignalDetectionModel(
|
|
120
|
+
channel_option=["signal"],
|
|
121
|
+
model_signal_length=128,
|
|
122
|
+
n_channels=1,
|
|
123
|
+
n_conv=2,
|
|
124
|
+
n_classes=3,
|
|
125
|
+
dense_collection=512,
|
|
126
|
+
dropout_rate=0.1,
|
|
127
|
+
label="test",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def test_train_signal_model(self):
|
|
131
|
+
|
|
132
|
+
self.model.fit_from_directory(
|
|
133
|
+
["temp"],
|
|
134
|
+
normalize=True,
|
|
135
|
+
normalization_percentile=None,
|
|
136
|
+
normalization_values=None,
|
|
137
|
+
normalization_clip=None,
|
|
138
|
+
channel_option=["signal"],
|
|
139
|
+
target_directory="temp",
|
|
140
|
+
augment=False,
|
|
141
|
+
model_name="None",
|
|
142
|
+
validation_split=0.2,
|
|
143
|
+
test_split=0.1,
|
|
144
|
+
batch_size=16,
|
|
145
|
+
epochs=1,
|
|
146
|
+
recompile_pretrained=False,
|
|
147
|
+
learning_rate=0.01,
|
|
148
|
+
show_plots=False,
|
|
149
|
+
)
|
|
150
|
+
shutil.rmtree("temp")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if __name__ == "__main__":
|
|
154
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|