fucciphase 0.0.2__py3-none-any.whl → 0.0.3__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.
- fucciphase/__init__.py +7 -1
- fucciphase/__main__.py +12 -0
- fucciphase/fucci_phase.py +97 -51
- fucciphase/io.py +16 -12
- fucciphase/main_cli.py +72 -9
- fucciphase/napari/tracks_to_napari.py +14 -16
- fucciphase/phase.py +77 -74
- fucciphase/plot.py +236 -87
- fucciphase/sensor.py +31 -31
- fucciphase/tracking_utilities.py +63 -8
- fucciphase/utils/__init__.py +14 -1
- fucciphase/utils/checks.py +2 -5
- fucciphase/utils/dtw.py +2 -4
- fucciphase/utils/normalize.py +6 -8
- fucciphase/utils/simulator.py +1 -1
- fucciphase/utils/track_postprocessing.py +6 -8
- fucciphase/utils/trackmate.py +12 -12
- fucciphase-0.0.3.dist-info/METADATA +238 -0
- fucciphase-0.0.3.dist-info/RECORD +25 -0
- {fucciphase-0.0.2.dist-info → fucciphase-0.0.3.dist-info}/WHEEL +1 -1
- fucciphase-0.0.2.dist-info/METADATA +0 -137
- fucciphase-0.0.2.dist-info/RECORD +0 -24
- {fucciphase-0.0.2.dist-info → fucciphase-0.0.3.dist-info}/entry_points.txt +0 -0
- {fucciphase-0.0.2.dist-info → fucciphase-0.0.3.dist-info}/licenses/LICENSE +0 -0
fucciphase/__init__.py
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
FUCCIphase: Analysis tools for cell-cycle estimation from FUCCI imaging.
|
|
3
|
+
|
|
4
|
+
This module exposes the main public API of the package, including the
|
|
5
|
+
core processing functions and the package version.
|
|
6
|
+
|
|
7
|
+
"""
|
|
2
8
|
|
|
3
9
|
from importlib.metadata import PackageNotFoundError, version
|
|
4
10
|
|
fucciphase/__main__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module entry point for ``python -m fucciphase``.
|
|
3
|
+
|
|
4
|
+
This thin wrapper forwards execution to :func:`fucciphase.main_cli.main_cli`,
|
|
5
|
+
which implements the command-line interface used by the ``fucciphase``
|
|
6
|
+
console script.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .main_cli import main_cli
|
|
10
|
+
|
|
11
|
+
if __name__ == "__main__":
|
|
12
|
+
main_cli()
|
fucciphase/fucci_phase.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import List, Optional, Union
|
|
3
2
|
|
|
4
3
|
import pandas as pd
|
|
5
4
|
|
|
@@ -11,20 +10,28 @@ from .utils import normalize_channels, split_trackmate_tracks
|
|
|
11
10
|
|
|
12
11
|
def process_dataframe(
|
|
13
12
|
df: pd.DataFrame,
|
|
14
|
-
channels:
|
|
13
|
+
channels: list[str],
|
|
15
14
|
sensor: FUCCISensor,
|
|
16
|
-
thresholds:
|
|
15
|
+
thresholds: list[float],
|
|
17
16
|
use_moving_average: bool = True,
|
|
18
17
|
window_size: int = 7,
|
|
19
|
-
manual_min:
|
|
20
|
-
manual_max:
|
|
18
|
+
manual_min: list[float] | None = None,
|
|
19
|
+
manual_max: list[float] | None = None,
|
|
21
20
|
generate_unique_tracks: bool = False,
|
|
22
21
|
track_id_name: str = "TRACK_ID",
|
|
23
22
|
label_id_name: str = "name",
|
|
24
23
|
estimate_percentage: bool = True,
|
|
25
24
|
) -> None:
|
|
26
|
-
"""
|
|
27
|
-
|
|
25
|
+
"""Apply the FUCCIphase analysis pipeline to an existing dataframe.
|
|
26
|
+
|
|
27
|
+
This function assumes that tracking and fluorescence information are
|
|
28
|
+
already available in a pandas DataFrame with the expected column
|
|
29
|
+
structure. It performs the same core steps as ``process_trackmate``,
|
|
30
|
+
but skips the TrackMate file I/O and starts directly from tabular data.
|
|
31
|
+
|
|
32
|
+
Use this when your tracking pipeline already provides a dataframe, or
|
|
33
|
+
when you have manually assembled the input table and still want to use
|
|
34
|
+
FUCCIphase for cell-cycle analysis and visualization.
|
|
28
35
|
|
|
29
36
|
The dataframe must contain ID and TRACK_ID features.
|
|
30
37
|
|
|
@@ -38,34 +45,53 @@ def process_dataframe(
|
|
|
38
45
|
Parameters
|
|
39
46
|
----------
|
|
40
47
|
df : pandas.DataFrame
|
|
41
|
-
|
|
48
|
+
Input dataframe containing tracking and intensity features.
|
|
49
|
+
|
|
42
50
|
channels: List[str]
|
|
43
|
-
|
|
51
|
+
Names of columns holding FUCCI fluorescence information.
|
|
52
|
+
|
|
44
53
|
sensor : FUCCISensor
|
|
45
|
-
FUCCI sensor with phase
|
|
54
|
+
FUCCI sensor with phase-specific parameters.
|
|
55
|
+
|
|
46
56
|
thresholds: List[float]
|
|
47
|
-
Thresholds to separate phases
|
|
57
|
+
Thresholds used to separate cell-cycle phases.
|
|
58
|
+
|
|
48
59
|
use_moving_average : bool, optional
|
|
49
|
-
|
|
60
|
+
If True, apply a moving average before normalization. Default is True.
|
|
61
|
+
|
|
50
62
|
window_size : int, optional
|
|
51
|
-
Window size of the moving average
|
|
63
|
+
Window size of the moving average. Default is 7.
|
|
64
|
+
|
|
52
65
|
manual_min : Optional[List[float]], optional
|
|
53
66
|
Manually determined minimum for each channel, by default None
|
|
67
|
+
|
|
54
68
|
manual_max : Optional[List[float]], optional
|
|
55
69
|
Manually determined maximum for each channel, by default None
|
|
56
|
-
|
|
57
|
-
Estimate cell cycle percentage
|
|
58
|
-
label_id_name: str
|
|
59
|
-
Give an indentifier for the spot name (needed for unique track ID generation)
|
|
70
|
+
|
|
60
71
|
generate_unique_tracks: bool
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
If True, assign unique track IDs to split tracks. This requires
|
|
73
|
+
using the appropriate action in TrackMate. Default is False.
|
|
74
|
+
|
|
63
75
|
track_id_name: str
|
|
64
|
-
Name of column
|
|
76
|
+
Name of the column containing track IDs. Default is ``"TRACK_ID"``.
|
|
77
|
+
|
|
78
|
+
label_id_name: str
|
|
79
|
+
Column name identifying the spot label / name (used for unique
|
|
80
|
+
track ID generation). Default is ``"name"``.
|
|
81
|
+
|
|
82
|
+
estimate_percentage: bool, optional
|
|
83
|
+
If True, estimate cell-cycle percentage along each track. Default is True.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
None
|
|
88
|
+
The input dataframe is modified in-place. No value is returned.
|
|
65
89
|
"""
|
|
90
|
+
# ensure that the number of provided channels matches the sensor definition
|
|
66
91
|
if len(channels) != sensor.fluorophores:
|
|
67
92
|
raise ValueError(f"Need to provide {sensor.fluorophores} channel names.")
|
|
68
93
|
|
|
94
|
+
# optionally split TrackMate subtracks and re-label them as unique tracks
|
|
69
95
|
if generate_unique_tracks:
|
|
70
96
|
if "TRACK_ID" in df.columns:
|
|
71
97
|
split_trackmate_tracks(df, label_id_name=label_id_name)
|
|
@@ -86,7 +112,7 @@ def process_dataframe(
|
|
|
86
112
|
track_id_name=track_id_name,
|
|
87
113
|
)
|
|
88
114
|
|
|
89
|
-
# compute the phases
|
|
115
|
+
# compute the phases (and, optionally, the cycle percentage)
|
|
90
116
|
generate_cycle_phases(
|
|
91
117
|
df,
|
|
92
118
|
sensor=sensor,
|
|
@@ -97,20 +123,29 @@ def process_dataframe(
|
|
|
97
123
|
|
|
98
124
|
|
|
99
125
|
def process_trackmate(
|
|
100
|
-
xml_path:
|
|
101
|
-
channels:
|
|
126
|
+
xml_path: str | Path,
|
|
127
|
+
channels: list[str],
|
|
102
128
|
sensor: FUCCISensor,
|
|
103
|
-
thresholds:
|
|
129
|
+
thresholds: list[float],
|
|
104
130
|
use_moving_average: bool = True,
|
|
105
131
|
window_size: int = 7,
|
|
106
|
-
manual_min:
|
|
107
|
-
manual_max:
|
|
132
|
+
manual_min: list[float] | None = None,
|
|
133
|
+
manual_max: list[float] | None = None,
|
|
108
134
|
generate_unique_tracks: bool = False,
|
|
109
135
|
estimate_percentage: bool = True,
|
|
136
|
+
output_dir: str | Path | None = None,
|
|
110
137
|
) -> pd.DataFrame:
|
|
111
|
-
"""
|
|
112
|
-
|
|
113
|
-
|
|
138
|
+
"""Run the full FUCCIphase pipeline on a TrackMate export.
|
|
139
|
+
|
|
140
|
+
This high-level helper takes tracking data exported from Fiji/TrackMate
|
|
141
|
+
(typically XML or CSV), converts it into a pandas DataFrame with the
|
|
142
|
+
expected fucciphase columns, applies basic quality checks and
|
|
143
|
+
preprocessing, and estimates cell-cycle phase information that can be
|
|
144
|
+
used for downstream analysis and plotting.
|
|
145
|
+
|
|
146
|
+
The returned table is intended to be the main entry point for
|
|
147
|
+
fucciphase workflows, and is compatible with the plotting and
|
|
148
|
+
visualization functions provided in this package.
|
|
114
149
|
|
|
115
150
|
This function applies the following steps:
|
|
116
151
|
- load the XML file and generate a dataframe from the spots and tracks
|
|
@@ -124,36 +159,40 @@ def process_trackmate(
|
|
|
124
159
|
Parameters
|
|
125
160
|
----------
|
|
126
161
|
xml_path : Union[str, Path]
|
|
127
|
-
Path to the XML file
|
|
128
|
-
channels: List[str]
|
|
129
|
-
Names of
|
|
130
|
-
generate_unique_tracks: bool
|
|
131
|
-
Assign unique track IDs to splitted tracks.
|
|
132
|
-
Requires usage of action in TrackMate.
|
|
162
|
+
Path to the TrackMate XML file.
|
|
163
|
+
channels : List[str]
|
|
164
|
+
Names of columns holding FUCCI fluorescence information.
|
|
133
165
|
sensor : FUCCISensor
|
|
134
|
-
FUCCI sensor with phase
|
|
135
|
-
thresholds: List[float]
|
|
136
|
-
Thresholds to separate phases
|
|
166
|
+
FUCCI sensor with phase-specific parameters.
|
|
167
|
+
thresholds : List[float]
|
|
168
|
+
Thresholds used to separate cell-cycle phases.
|
|
137
169
|
use_moving_average : bool, optional
|
|
138
|
-
|
|
170
|
+
If True, apply a moving average before normalization. Default is True.
|
|
139
171
|
window_size : int, optional
|
|
140
|
-
Window size of the moving average
|
|
172
|
+
Window size of the moving average. Default is 7.
|
|
141
173
|
manual_min : Optional[List[float]], optional
|
|
142
|
-
Manually determined minimum for each channel, by default None
|
|
174
|
+
Manually determined minimum for each channel, by default None.
|
|
143
175
|
manual_max : Optional[List[float]], optional
|
|
144
|
-
Manually determined maximum for each channel, by default None
|
|
145
|
-
|
|
146
|
-
|
|
176
|
+
Manually determined maximum for each channel, by default None.
|
|
177
|
+
generate_unique_tracks : bool, optional
|
|
178
|
+
If True, assign unique track IDs to split tracks. This requires
|
|
179
|
+
using the appropriate action in TrackMate. Default is False.
|
|
180
|
+
estimate_percentage : bool, optional
|
|
181
|
+
If True, estimate cell-cycle percentage along each track. Default is True.
|
|
182
|
+
output_dir : Optional[Union[str, Path]], optional
|
|
183
|
+
Optional directory where the updated XML should be written. If None,
|
|
184
|
+
the file is saved next to the input XML.
|
|
147
185
|
|
|
148
186
|
Returns
|
|
149
187
|
-------
|
|
150
|
-
|
|
151
|
-
Dataframe with the cell
|
|
188
|
+
pandas.DataFrame
|
|
189
|
+
Dataframe with the cell-cycle percentage and the corresponding phases.
|
|
190
|
+
|
|
152
191
|
"""
|
|
153
|
-
# read the XML
|
|
192
|
+
# read the XML and extract the dataframe and XML wrapper
|
|
154
193
|
df, tmxml = read_trackmate_xml(xml_path)
|
|
155
194
|
|
|
156
|
-
# process the dataframe
|
|
195
|
+
# process the dataframe in-place (and also get a reference to it)
|
|
157
196
|
process_dataframe(
|
|
158
197
|
df,
|
|
159
198
|
channels,
|
|
@@ -167,12 +206,19 @@ def process_trackmate(
|
|
|
167
206
|
estimate_percentage=estimate_percentage,
|
|
168
207
|
)
|
|
169
208
|
|
|
170
|
-
# update the XML
|
|
209
|
+
# update the XML with the new features
|
|
171
210
|
tmxml.update_features(df)
|
|
172
211
|
|
|
173
|
-
# export the
|
|
212
|
+
# export the updated XML next to the original file
|
|
174
213
|
new_name = Path(xml_path).stem + "_processed.xml"
|
|
175
|
-
|
|
214
|
+
|
|
215
|
+
if output_dir is not None:
|
|
216
|
+
output_dir = Path(output_dir)
|
|
217
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
218
|
+
new_path = output_dir / new_name
|
|
219
|
+
else:
|
|
220
|
+
new_path = Path(xml_path).parent / new_name
|
|
221
|
+
|
|
176
222
|
tmxml.save_xml(new_path)
|
|
177
223
|
|
|
178
224
|
return df
|
fucciphase/io.py
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import Tuple, Union
|
|
3
2
|
|
|
4
3
|
import pandas as pd
|
|
5
4
|
|
|
6
5
|
from .utils import TrackMateXML
|
|
7
6
|
|
|
8
7
|
|
|
9
|
-
def read_trackmate_xml(xml_path:
|
|
10
|
-
"""Read a
|
|
8
|
+
def read_trackmate_xml(xml_path: Path | str) -> tuple[pd.DataFrame, TrackMateXML]:
|
|
9
|
+
"""Read a TrackMate-exported XML file and return data and XML wrapper.
|
|
11
10
|
|
|
12
11
|
Parameters
|
|
13
12
|
----------
|
|
14
13
|
xml_path : Union[Path, str]
|
|
15
|
-
Path to the
|
|
14
|
+
Path to the XML file.
|
|
16
15
|
|
|
17
16
|
Returns
|
|
18
17
|
-------
|
|
19
18
|
df : pandas.DataFrame
|
|
20
|
-
Dataframe containing the
|
|
19
|
+
Dataframe containing the spot and track data, sorted by FRAME.
|
|
21
20
|
trackmate : TrackMateXML
|
|
22
|
-
TrackMateXML object
|
|
21
|
+
TrackMateXML object wrapping the original XML and allowing
|
|
22
|
+
feature updates / re-export.
|
|
23
23
|
"""
|
|
24
24
|
# read in the xml file
|
|
25
25
|
trackmate = TrackMateXML(xml_path)
|
|
@@ -32,27 +32,31 @@ def read_trackmate_xml(xml_path: Union[Path, str]) -> Tuple[pd.DataFrame, TrackM
|
|
|
32
32
|
return df, trackmate
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
def read_trackmate_csv(csv_path:
|
|
36
|
-
"""Read a
|
|
35
|
+
def read_trackmate_csv(csv_path: Path | str) -> pd.DataFrame:
|
|
36
|
+
"""Read a TrackMate-exported CSV file.
|
|
37
37
|
|
|
38
38
|
The first three rows (excluding header) of the csv file are skipped as
|
|
39
39
|
they contain duplicate titles of columns and units (Trackmate specific).
|
|
40
40
|
|
|
41
|
+
The first three rows (excluding the header) of the CSV file are
|
|
42
|
+
skipped as they contain duplicate column titles and units
|
|
43
|
+
(TrackMate-specific).
|
|
41
44
|
|
|
42
45
|
Parameters
|
|
43
46
|
----------
|
|
44
|
-
csv_path : str
|
|
45
|
-
Path to the
|
|
47
|
+
csv_path : Union[Path, str]
|
|
48
|
+
Path to the CSV file.
|
|
46
49
|
|
|
47
50
|
Returns
|
|
48
51
|
-------
|
|
49
52
|
df : pandas.DataFrame
|
|
50
|
-
Dataframe containing the
|
|
53
|
+
Dataframe containing the CSV data with converted dtypes.
|
|
51
54
|
|
|
52
55
|
Raises
|
|
53
56
|
------
|
|
54
57
|
ValueError
|
|
55
|
-
If the
|
|
58
|
+
If the CSV file does not contain both MEAN_INTENSITY_CH1 and
|
|
59
|
+
MEAN_INTENSITY_CH2 columns.
|
|
56
60
|
"""
|
|
57
61
|
df = pd.read_csv(csv_path, encoding="unicode_escape", skiprows=[1, 2, 3])
|
|
58
62
|
|
fucciphase/main_cli.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import json
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
import pandas as pd
|
|
5
6
|
|
|
@@ -15,12 +16,37 @@ except ImportError as err:
|
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def main_cli() -> None:
|
|
18
|
-
"""Fucciphase CLI.
|
|
19
|
+
"""Fucciphase CLI: Command-line entry point for FUCCIphase.
|
|
20
|
+
|
|
21
|
+
This function is invoked by the ``fucciphase`` console script and
|
|
22
|
+
implements the standard command-line workflow:
|
|
23
|
+
|
|
24
|
+
1. Parse command-line arguments describing:
|
|
25
|
+
- a TrackMate tracking file (XML or CSV),
|
|
26
|
+
- a reference cell-cycle trace in CSV format,
|
|
27
|
+
- an optional FUCCI sensor JSON file,
|
|
28
|
+
- the acquisition timestep and channel names.
|
|
29
|
+
2. Load the reference data and rename its fluorescence columns to
|
|
30
|
+
match the user-specified channel names.
|
|
31
|
+
3. Load and preprocess the tracking data using either
|
|
32
|
+
:func:`process_trackmate` (for XML) or
|
|
33
|
+
:func:`process_dataframe` (for CSV).
|
|
34
|
+
4. Estimate cell-cycle percentages for each track by subsequence
|
|
35
|
+
alignment against the reference trace.
|
|
36
|
+
5. Write the processed table to ``<tracking_file>_processed.csv`` in
|
|
37
|
+
the same directory as the input file.
|
|
38
|
+
|
|
39
|
+
The function is designed to be used from the command line and does
|
|
40
|
+
not return a value. It will raise a ``ValueError`` if the tracking
|
|
41
|
+
file does not have an XML or CSV extension.
|
|
42
|
+
"""
|
|
19
43
|
parser = argparse.ArgumentParser(
|
|
20
44
|
prog="fucciphase",
|
|
21
45
|
description="FUCCIphase tool to estimate cell cycle phases and percentages.",
|
|
22
46
|
epilog="Please report bugs and errors on GitHub.",
|
|
23
47
|
)
|
|
48
|
+
|
|
49
|
+
# -------------- 1. Parse command-line arguments --------------
|
|
24
50
|
parser.add_argument("tracking_file", type=str, help="TrackMate XML or CSV file")
|
|
25
51
|
parser.add_argument(
|
|
26
52
|
"-ref",
|
|
@@ -61,13 +87,19 @@ def main_cli() -> None:
|
|
|
61
87
|
)
|
|
62
88
|
|
|
63
89
|
args = parser.parse_args()
|
|
90
|
+
# Decide where to store outputs (CSV and, for XML input, processed XML)
|
|
91
|
+
output_dir = Path("outputs")
|
|
92
|
+
output_dir.mkdir(exist_ok=True)
|
|
64
93
|
|
|
94
|
+
# ---------------- 2. Load and adapt the reference cell-cycle trace ----------------
|
|
65
95
|
reference_df = pd.read_csv(args.reference_file)
|
|
96
|
+
# The reference file is expected to contain 'cyan' and 'magenta' columns;
|
|
97
|
+
# they are renamed here to match the actual channel names in the data.
|
|
66
98
|
reference_df.rename(
|
|
67
99
|
columns={"cyan": args.cyan_channel, "magenta": args.magenta_channel},
|
|
68
100
|
inplace=True,
|
|
69
101
|
)
|
|
70
|
-
|
|
102
|
+
# ---------------- 3. Build the sensor model ----------------
|
|
71
103
|
if args.sensor_file is not None:
|
|
72
104
|
with open(args.sensor_file) as fp:
|
|
73
105
|
sensor_properties = json.load(fp)
|
|
@@ -75,15 +107,19 @@ def main_cli() -> None:
|
|
|
75
107
|
else:
|
|
76
108
|
sensor = get_fuccisa_default_sensor()
|
|
77
109
|
|
|
110
|
+
# ---------------- 4. Load and preprocess the tracking data ----------------
|
|
78
111
|
if args.tracking_file.endswith(".xml"):
|
|
112
|
+
# XML: let process_trackmate handle I/O and preprocessing
|
|
79
113
|
df = process_trackmate(
|
|
80
114
|
args.tracking_file,
|
|
81
115
|
channels=[args.cyan_channel, args.magenta_channel],
|
|
82
116
|
sensor=sensor,
|
|
83
117
|
thresholds=[0.1, 0.1],
|
|
84
118
|
generate_unique_tracks=args.generate_unique_tracks,
|
|
119
|
+
output_dir=output_dir,
|
|
85
120
|
)
|
|
86
121
|
elif args.tracking_file.endswith(".csv"):
|
|
122
|
+
# CSV: read the table and then run the processing pipeline on it
|
|
87
123
|
df = pd.read_csv(args.tracking_file)
|
|
88
124
|
process_dataframe(
|
|
89
125
|
df,
|
|
@@ -95,6 +131,7 @@ def main_cli() -> None:
|
|
|
95
131
|
else:
|
|
96
132
|
raise ValueError("Tracking file must be an XML or CSV file.")
|
|
97
133
|
|
|
134
|
+
# ---------------- 5. Estimate cell-cycle percentages ----------------
|
|
98
135
|
track_id_name = "UNIQUE_TRACK_ID"
|
|
99
136
|
if not args.generate_unique_tracks:
|
|
100
137
|
track_id_name = "TRACK_ID"
|
|
@@ -104,13 +141,31 @@ def main_cli() -> None:
|
|
|
104
141
|
dt=args.timestep,
|
|
105
142
|
channels=[args.cyan_channel, args.magenta_channel],
|
|
106
143
|
reference_data=reference_df,
|
|
107
|
-
track_id_name=track_id_name
|
|
144
|
+
track_id_name=track_id_name,
|
|
108
145
|
)
|
|
109
|
-
|
|
146
|
+
# ---------------- 6. Save results ----------------
|
|
147
|
+
tracking_path = Path(args.tracking_file)
|
|
148
|
+
output_csv = output_dir / (tracking_path.stem + "_processed.csv")
|
|
149
|
+
df.to_csv(output_csv, index=False)
|
|
110
150
|
|
|
111
151
|
|
|
112
152
|
def main_visualization() -> None:
|
|
113
|
-
"""Fucciphase visualization.
|
|
153
|
+
"""Fucciphase visualization.
|
|
154
|
+
|
|
155
|
+
Launch a napari-based visualization of FUCCIphase results.
|
|
156
|
+
|
|
157
|
+
This command-line entry point loads a processed FUCCIphase CSV file
|
|
158
|
+
together with the corresponding OME-TIFF time-lapse movie and
|
|
159
|
+
segmentation masks, then opens an interactive napari viewer showing:
|
|
160
|
+
|
|
161
|
+
- cyan and magenta fluorescence channels,
|
|
162
|
+
- segmentation masks as a labels layer,
|
|
163
|
+
- tracks and cell-cycle information overlaid on the image.
|
|
164
|
+
|
|
165
|
+
The function is intended to be invoked via the ``fucciphase-napari``
|
|
166
|
+
console script and does not return a value.
|
|
167
|
+
|
|
168
|
+
"""
|
|
114
169
|
parser = argparse.ArgumentParser(
|
|
115
170
|
prog="fucciphase-napari",
|
|
116
171
|
description="FUCCIphase napari script to launch visualization.",
|
|
@@ -142,13 +197,19 @@ def main_visualization() -> None:
|
|
|
142
197
|
required=True,
|
|
143
198
|
)
|
|
144
199
|
parser.add_argument(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
200
|
+
"--pixel_size",
|
|
201
|
+
type=float,
|
|
202
|
+
help="Pixel size, only used if not in metadata",
|
|
203
|
+
default=None,
|
|
204
|
+
)
|
|
149
205
|
|
|
150
206
|
args = parser.parse_args()
|
|
151
207
|
|
|
208
|
+
# Decide where to store outputs (CSV and, for XML input, processed XML)
|
|
209
|
+
output_dir = Path("outputs")
|
|
210
|
+
output_dir.mkdir(exist_ok=True)
|
|
211
|
+
|
|
212
|
+
# Try to read the video using AICSImage; fall back to bioio if needed
|
|
152
213
|
AICSIMAGE = False
|
|
153
214
|
BIOIMAGE = False
|
|
154
215
|
try:
|
|
@@ -169,6 +230,8 @@ def main_visualization() -> None:
|
|
|
169
230
|
image = AICSImage(args.video)
|
|
170
231
|
elif BIOIMAGE:
|
|
171
232
|
image = BioImage(args.video, reader=bioio_ome_tiff.Reader)
|
|
233
|
+
|
|
234
|
+
# Determine spatial scale; fall back to unit scale or user-provided pixel size
|
|
172
235
|
scale = (image.physical_pixel_sizes.Y, image.physical_pixel_sizes.X)
|
|
173
236
|
if None in scale:
|
|
174
237
|
if args.pixel_size is not None:
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import List, Optional
|
|
2
|
-
|
|
3
1
|
import numpy as np
|
|
4
2
|
import pandas as pd
|
|
5
3
|
|
|
@@ -15,18 +13,18 @@ def add_trackmate_data_to_viewer(
|
|
|
15
13
|
df: pd.DataFrame,
|
|
16
14
|
viewer: napari.Viewer,
|
|
17
15
|
scale: tuple,
|
|
18
|
-
image_data:
|
|
19
|
-
colormaps:
|
|
20
|
-
labels:
|
|
21
|
-
cycle_percentage_id:
|
|
16
|
+
image_data: list[np.ndarray],
|
|
17
|
+
colormaps: list[str],
|
|
18
|
+
labels: np.ndarray | None,
|
|
19
|
+
cycle_percentage_id: str | None = "CELL_CYCLE_PERC_POST",
|
|
22
20
|
dim: int = 2,
|
|
23
|
-
textkwargs:
|
|
24
|
-
label_id_name:
|
|
25
|
-
track_id_name:
|
|
26
|
-
time_id_name:
|
|
27
|
-
pos_x_id_name:
|
|
28
|
-
pos_y_id_name:
|
|
29
|
-
crop_fov:
|
|
21
|
+
textkwargs: dict | None = None,
|
|
22
|
+
label_id_name: str | None = "MAX_INTENSITY_CH3",
|
|
23
|
+
track_id_name: str | None = "TRACK_ID",
|
|
24
|
+
time_id_name: str | None = "POSITION_T",
|
|
25
|
+
pos_x_id_name: str | None = "POSITION_X",
|
|
26
|
+
pos_y_id_name: str | None = "POSITION_Y",
|
|
27
|
+
crop_fov: list[float] | None = None,
|
|
30
28
|
) -> None:
|
|
31
29
|
"""Overlay tracking result and video.
|
|
32
30
|
|
|
@@ -73,7 +71,7 @@ def add_trackmate_data_to_viewer(
|
|
|
73
71
|
labels_layer = viewer.add_labels(new_labels, scale=scale)
|
|
74
72
|
labels_layer.contour = 10
|
|
75
73
|
|
|
76
|
-
for image, colormap in zip(image_data, colormaps):
|
|
74
|
+
for image, colormap in zip(image_data, colormaps, strict=True):
|
|
77
75
|
viewer.add_image(image, blending="additive", colormap=colormap, scale=scale)
|
|
78
76
|
# TODO implement cropping, filter points in / outside range
|
|
79
77
|
# crop_fov =
|
|
@@ -95,8 +93,8 @@ def pandas_df_to_napari_tracks(
|
|
|
95
93
|
frame_id_name: str,
|
|
96
94
|
position_x_name: str,
|
|
97
95
|
position_y_name: str,
|
|
98
|
-
feature_name:
|
|
99
|
-
colormaps_dict:
|
|
96
|
+
feature_name: str | None = None,
|
|
97
|
+
colormaps_dict: dict | None = None,
|
|
100
98
|
) -> None:
|
|
101
99
|
"""Add tracks to Napari track layer.
|
|
102
100
|
Splitting and merging are not yet implemented.
|