fucciphase 0.0.1__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 +12 -0
- fucciphase/fucci_phase.py +178 -0
- fucciphase/io.py +67 -0
- fucciphase/napari/__init__.py +5 -0
- fucciphase/napari/tracks_to_napari.py +117 -0
- fucciphase/phase.py +501 -0
- fucciphase/plot.py +548 -0
- fucciphase/py.typed +5 -0
- fucciphase/sensor.py +454 -0
- fucciphase/tracking_utilities.py +81 -0
- fucciphase/utils/__init__.py +35 -0
- fucciphase/utils/checks.py +16 -0
- fucciphase/utils/dtw.py +59 -0
- fucciphase/utils/normalize.py +202 -0
- fucciphase/utils/phase_fit.py +47 -0
- fucciphase/utils/simulator.py +85 -0
- fucciphase/utils/track_postprocessing.py +454 -0
- fucciphase/utils/trackmate.py +295 -0
- fucciphase-0.0.1.dist-info/METADATA +137 -0
- fucciphase-0.0.1.dist-info/RECORD +22 -0
- fucciphase-0.0.1.dist-info/WHEEL +4 -0
- fucciphase-0.0.1.dist-info/licenses/LICENSE +29 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import xml.etree.ElementTree as et
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
# TrackMate XML tags
|
|
10
|
+
MODEL = "Model"
|
|
11
|
+
FEATURES = "FeatureDeclarations"
|
|
12
|
+
SPOT_FEATURES = "SpotFeatures"
|
|
13
|
+
ALL_SPOTS = "AllSpots"
|
|
14
|
+
N_SPOTS = "nspots"
|
|
15
|
+
ID = "ID"
|
|
16
|
+
SUBTRACK_REGEX = r"Track_[0-9]+\.[a-z]+"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# TODO test on very large trackmate files, since this is potentially a bottleneck here
|
|
20
|
+
class TrackMateXML:
|
|
21
|
+
"""Class to handle TrackMate xml files.
|
|
22
|
+
|
|
23
|
+
TrackMate xml files are structured as follows:
|
|
24
|
+
root
|
|
25
|
+
├── Log
|
|
26
|
+
├── Model
|
|
27
|
+
│ ├── FeatureDeclarations
|
|
28
|
+
│ ├── AllSpots
|
|
29
|
+
│ │ └── SpotsInFrame
|
|
30
|
+
│ │ └── Spot
|
|
31
|
+
│ ├── AllTracks
|
|
32
|
+
│ └── FilteredTracks
|
|
33
|
+
├── Settings
|
|
34
|
+
├── GUIState
|
|
35
|
+
└── DisplaySettings
|
|
36
|
+
|
|
37
|
+
This class allows reading in the tree and converting the spots to a
|
|
38
|
+
pandas dataframe. The features (columns) can also be updated and the
|
|
39
|
+
xml file be saved.
|
|
40
|
+
|
|
41
|
+
Attributes
|
|
42
|
+
----------
|
|
43
|
+
nspots : int
|
|
44
|
+
Number of spots in the xml file.
|
|
45
|
+
features : Dict[str, type]
|
|
46
|
+
List of all features in the xml file, and whether they are integer features.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, xml_path: Union[str, Path]) -> None:
|
|
50
|
+
"""Initialize the TrackMateXML object.
|
|
51
|
+
|
|
52
|
+
The xml file is parsed and the model and all spots are imported.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
xml_path : Union[str, Path]
|
|
57
|
+
Path to the xml file.
|
|
58
|
+
"""
|
|
59
|
+
# parse tree
|
|
60
|
+
self._tree: et.ElementTree[et.Element[str]] = et.parse(xml_path)
|
|
61
|
+
self._root: et.Element | Any = self._tree.getroot()
|
|
62
|
+
|
|
63
|
+
# placeholders
|
|
64
|
+
self._model: Optional[et.Element] = None
|
|
65
|
+
self._allspots: Optional[et.Element] = None
|
|
66
|
+
|
|
67
|
+
self.nspots: int = 0 # number of spots
|
|
68
|
+
self.features: Dict[str, type] = {} # features and their types
|
|
69
|
+
self.spot_features: List[str] = [] # list of spot features
|
|
70
|
+
|
|
71
|
+
# import model and all spots
|
|
72
|
+
self._import_data()
|
|
73
|
+
|
|
74
|
+
def _get_spot_features(self) -> None:
|
|
75
|
+
"""Get the spot features from the tree."""
|
|
76
|
+
if self._allspots is not None:
|
|
77
|
+
spot_features: List[str] = []
|
|
78
|
+
for frame in self._allspots:
|
|
79
|
+
for spot in frame:
|
|
80
|
+
spot_features.extend(spot.attrib.keys())
|
|
81
|
+
break
|
|
82
|
+
break
|
|
83
|
+
|
|
84
|
+
self.spot_features = spot_features
|
|
85
|
+
|
|
86
|
+
def _get_features(self) -> None:
|
|
87
|
+
"""Compare spot features and features declaration, keep the intersection and
|
|
88
|
+
register the dtypes in a public member.
|
|
89
|
+
"""
|
|
90
|
+
if self._model is not None:
|
|
91
|
+
features = {}
|
|
92
|
+
for element in self._model:
|
|
93
|
+
if element.tag == FEATURES:
|
|
94
|
+
for feature in element:
|
|
95
|
+
if feature.tag == SPOT_FEATURES:
|
|
96
|
+
for spot_feature in feature:
|
|
97
|
+
# get feature name
|
|
98
|
+
feature_name = spot_feature.attrib["feature"]
|
|
99
|
+
|
|
100
|
+
# check if feature is integer
|
|
101
|
+
is_integer = spot_feature.attrib["isint"] == "true"
|
|
102
|
+
|
|
103
|
+
# add feature to dictionary
|
|
104
|
+
features[feature_name] = int if is_integer else float
|
|
105
|
+
|
|
106
|
+
# get spot features
|
|
107
|
+
self._get_spot_features()
|
|
108
|
+
|
|
109
|
+
# keep only features that are in both spot features and features
|
|
110
|
+
features_key = set(features.keys())
|
|
111
|
+
features_to_keep = features_key - (features_key - set(self.spot_features))
|
|
112
|
+
self.features = {feature: features[feature] for feature in features_to_keep}
|
|
113
|
+
|
|
114
|
+
def _import_data(self) -> None:
|
|
115
|
+
"""Import the model and all spots from the xml file.
|
|
116
|
+
|
|
117
|
+
Raises
|
|
118
|
+
------
|
|
119
|
+
ValueError
|
|
120
|
+
If the xml file does not contain a "Model" tag.
|
|
121
|
+
ValueError
|
|
122
|
+
If the "Model" tag does not contain an "AllSpots" tag.
|
|
123
|
+
"""
|
|
124
|
+
# get model
|
|
125
|
+
for element in self._root:
|
|
126
|
+
if element.tag == MODEL:
|
|
127
|
+
self._model = element
|
|
128
|
+
|
|
129
|
+
if self._model is None:
|
|
130
|
+
raise ValueError('"Model" tag not found in xml file.')
|
|
131
|
+
|
|
132
|
+
# get allspots
|
|
133
|
+
for element in self._model:
|
|
134
|
+
if element.tag == ALL_SPOTS:
|
|
135
|
+
self._allspots = element
|
|
136
|
+
self.nspots = int(element.attrib[N_SPOTS])
|
|
137
|
+
|
|
138
|
+
if self._allspots is None:
|
|
139
|
+
raise ValueError('"AllSpots" tag not found in xml file.')
|
|
140
|
+
|
|
141
|
+
# get feature declarations
|
|
142
|
+
self._get_features()
|
|
143
|
+
|
|
144
|
+
def _add_track_ids(self, df: pd.DataFrame) -> None:
|
|
145
|
+
# extract track IDs (if there are spots)
|
|
146
|
+
if len(df) > 0:
|
|
147
|
+
# some spots do not have tracks associated
|
|
148
|
+
track_ids = np.full_like(df[ID].values, -1)
|
|
149
|
+
if self._model is not None:
|
|
150
|
+
for element in self._model:
|
|
151
|
+
if element.tag == "AllTracks":
|
|
152
|
+
for track in element:
|
|
153
|
+
track_id = int(track.attrib["TRACK_ID"])
|
|
154
|
+
|
|
155
|
+
for edge in track:
|
|
156
|
+
spot_source = edge.attrib["SPOT_SOURCE_ID"]
|
|
157
|
+
spot_target = edge.attrib["SPOT_TARGET_ID"]
|
|
158
|
+
|
|
159
|
+
# get row index of the source and target spots
|
|
160
|
+
source_index = df[df[ID] == spot_source].index[0]
|
|
161
|
+
target_index = df[df[ID] == spot_target].index[0]
|
|
162
|
+
|
|
163
|
+
# update track IDs
|
|
164
|
+
track_ids[source_index] = track_id
|
|
165
|
+
track_ids[target_index] = track_id
|
|
166
|
+
|
|
167
|
+
# update dataframe
|
|
168
|
+
df["TRACK_ID"] = track_ids
|
|
169
|
+
else:
|
|
170
|
+
df["TRACK_ID"] = []
|
|
171
|
+
|
|
172
|
+
# add track id to the spot features (this avoids exporting it to the xml later)
|
|
173
|
+
self.spot_features.append("TRACK_ID")
|
|
174
|
+
|
|
175
|
+
def to_pandas(self) -> pd.DataFrame:
|
|
176
|
+
"""Export the spots as a pandas dataframe.
|
|
177
|
+
|
|
178
|
+
Returns
|
|
179
|
+
-------
|
|
180
|
+
pd.DataFrame
|
|
181
|
+
Dataframe containing the spots.
|
|
182
|
+
"""
|
|
183
|
+
df = pd.DataFrame()
|
|
184
|
+
|
|
185
|
+
# loop over all frames and add spots to a dataframe
|
|
186
|
+
if self._allspots is not None:
|
|
187
|
+
spot_count = 0
|
|
188
|
+
for frame in self._allspots:
|
|
189
|
+
# only run on frames with spots
|
|
190
|
+
if len(frame) > 0:
|
|
191
|
+
# for each spot in the frame
|
|
192
|
+
for spot in frame:
|
|
193
|
+
# if this is the first spot, initialize dataframe
|
|
194
|
+
if spot_count == 0:
|
|
195
|
+
df = pd.DataFrame(columns=spot.attrib.keys())
|
|
196
|
+
|
|
197
|
+
# add the spot to the dataframe
|
|
198
|
+
df.loc[spot_count] = spot.attrib
|
|
199
|
+
spot_count += 1
|
|
200
|
+
|
|
201
|
+
# add tracks IDs
|
|
202
|
+
self._add_track_ids(df)
|
|
203
|
+
|
|
204
|
+
# fix type of TRACK_ID
|
|
205
|
+
self.features["TRACK_ID"] = int
|
|
206
|
+
# convert features to their declared types
|
|
207
|
+
return df.astype(self.features)
|
|
208
|
+
|
|
209
|
+
def update_features(self, df: pd.DataFrame) -> None:
|
|
210
|
+
"""Update the xml tree with new features, where features are columns of the
|
|
211
|
+
dataset.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
df : pd.DataFrame
|
|
216
|
+
Dataframe containing the new features.
|
|
217
|
+
"""
|
|
218
|
+
# compare number of spots
|
|
219
|
+
if len(df) != self.nspots:
|
|
220
|
+
raise ValueError(
|
|
221
|
+
f"Number of spots in the dataframe ({len(df)}) does not match number "
|
|
222
|
+
f"of spots in xml file ({self.nspots})."
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# check if ID column is in the dataframe
|
|
226
|
+
if ID not in df.columns:
|
|
227
|
+
raise ValueError(f"Column {ID} not found in dataframe.")
|
|
228
|
+
|
|
229
|
+
# new features
|
|
230
|
+
new_features = set(df.columns) - set(self.spot_features)
|
|
231
|
+
|
|
232
|
+
if self._allspots is not None:
|
|
233
|
+
# update features
|
|
234
|
+
for feature in new_features:
|
|
235
|
+
self.features[feature] = df[feature].dtype
|
|
236
|
+
|
|
237
|
+
# if there are spots and features
|
|
238
|
+
if len(df) > 0 and len(new_features) > 0:
|
|
239
|
+
# loop over the frames and spots
|
|
240
|
+
for frame in self._allspots:
|
|
241
|
+
for spot in frame:
|
|
242
|
+
# get ID
|
|
243
|
+
spot_id = spot.attrib[ID]
|
|
244
|
+
|
|
245
|
+
# get spot
|
|
246
|
+
spot_df = df[df[ID] == spot_id]
|
|
247
|
+
|
|
248
|
+
# add the new feature
|
|
249
|
+
for feature in new_features:
|
|
250
|
+
spot.attrib[feature] = str(spot_df[feature].values[0])
|
|
251
|
+
|
|
252
|
+
def save_xml(self, xml_path: Union[str, Path]) -> None:
|
|
253
|
+
"""Save the xml file.
|
|
254
|
+
|
|
255
|
+
Parameters
|
|
256
|
+
----------
|
|
257
|
+
xml_path : Union[str, Path]
|
|
258
|
+
Path to the xml file.
|
|
259
|
+
"""
|
|
260
|
+
with open(xml_path, "wb") as f:
|
|
261
|
+
self._tree.write(f)
|
|
262
|
+
|
|
263
|
+
def get_full_tracks(
|
|
264
|
+
df: pd.DataFrame,
|
|
265
|
+
channels: List[str],
|
|
266
|
+
track_id_name: str = "UNIQUE_TRACK_ID",
|
|
267
|
+
spot_name: str = "name",
|
|
268
|
+
frame_name: str = "FRAME",
|
|
269
|
+
min_length: int = 40,
|
|
270
|
+
) -> Tuple[List[pd.DataFrame], List[pd.DataFrame]]:
|
|
271
|
+
"""Locate all tracks that may describe a full cycle.
|
|
272
|
+
Tracks need to be auto-named by TrackMate.
|
|
273
|
+
For example, Track_1a, Track_1aa, Track_1b etc.
|
|
274
|
+
This can be done in TrackMate by executing an action after the tracking.
|
|
275
|
+
In addition, tracks longer than a certain minimum length can be selected.
|
|
276
|
+
"""
|
|
277
|
+
regex = "Track_[0-9]+.[a-z]+"
|
|
278
|
+
candidate_tracks: List[pd.DataFrame] = []
|
|
279
|
+
save_tracks: List[pd.DataFrame] = []
|
|
280
|
+
track_ids = df[track_id_name].unique()
|
|
281
|
+
for track_id in track_ids:
|
|
282
|
+
track = df[df[track_id_name] == track_id]
|
|
283
|
+
name = track[spot_name].iloc[0]
|
|
284
|
+
last_frame = track[frame_name].max()
|
|
285
|
+
# is the track a subtrack
|
|
286
|
+
match = re.match(regex, name)
|
|
287
|
+
# is there a subtrack
|
|
288
|
+
next_match = any(df[spot_name].str.match(name + "[a-z]+").unique())
|
|
289
|
+
if match is not None and last_frame < df[frame_name].max():
|
|
290
|
+
if next_match:
|
|
291
|
+
save_tracks.append(track[[frame_name, *channels]])
|
|
292
|
+
else:
|
|
293
|
+
if len(track) > min_length:
|
|
294
|
+
candidate_tracks.append(track[[frame_name, *channels]])
|
|
295
|
+
return save_tracks, candidate_tracks
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fucciphase
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Cell cycle analysis plugin.
|
|
5
|
+
Project-URL: homepage, https://github.com/nobias-ht/fucciphase
|
|
6
|
+
Project-URL: repository, https://github.com/nobias-ht/fucciphase
|
|
7
|
+
Author-email: Joran Deschamps <joran.deschamps@fht.org>
|
|
8
|
+
License: BSD-3-Clause
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Requires-Python: >=3.8
|
|
18
|
+
Requires-Dist: dtaidistance
|
|
19
|
+
Requires-Dist: lineagetree<1.5.0
|
|
20
|
+
Requires-Dist: matplotlib
|
|
21
|
+
Requires-Dist: monotonic-derivative
|
|
22
|
+
Requires-Dist: numpy
|
|
23
|
+
Requires-Dist: openpyxl
|
|
24
|
+
Requires-Dist: pandas
|
|
25
|
+
Requires-Dist: scipy
|
|
26
|
+
Requires-Dist: svgwrite
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: ipython; extra == 'dev'
|
|
29
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
30
|
+
Requires-Dist: pdbpp; extra == 'dev'
|
|
31
|
+
Requires-Dist: pre-commit; extra == 'dev'
|
|
32
|
+
Requires-Dist: rich; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
34
|
+
Provides-Extra: doc
|
|
35
|
+
Requires-Dist: sphinx; extra == 'doc'
|
|
36
|
+
Provides-Extra: jupyter
|
|
37
|
+
Requires-Dist: jupyter; extra == 'jupyter'
|
|
38
|
+
Provides-Extra: test
|
|
39
|
+
Requires-Dist: pytest; extra == 'test'
|
|
40
|
+
Requires-Dist: pytest-cov; extra == 'test'
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
|
|
43
|
+
# fucciphase
|
|
44
|
+
|
|
45
|
+
[](https://github.com/Synthetic-Physiology-Lab/fucciphase/raw/main/LICENSE)
|
|
46
|
+
[](https://pypi.org/project/fucciphase)
|
|
47
|
+
[](https://python.org)
|
|
48
|
+
[](https://github.com/Synthetic-Physiology-Lab/fucciphase/actions/workflows/ci.yml)
|
|
49
|
+
[](https://codecov.io/gh/Synthetic-Physiology-Lab/fucciphase)
|
|
50
|
+
[](https://results.pre-commit.ci/latest/github/Synthetic-Physiology-Lab/fucciphase/main)
|
|
51
|
+
|
|
52
|
+
FUCCI cell cycle analysis plugin.
|
|
53
|
+
Obtain cell cycle information from FUCCI fluorescence intensities.
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
The best way to run fucciphase is to install it in a virtual conda environment.
|
|
58
|
+
Make sure that git is installed and can be called from the command line.
|
|
59
|
+
|
|
60
|
+
(SOON) To install from pip:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install fucciphase
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If you wish to install it from source:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/Synthetic-Physiology-Lab/fucciphase
|
|
70
|
+
cd fucciphase
|
|
71
|
+
pip install -e .
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The installation should not take longer than a few seconds (depending on your internet connection).
|
|
75
|
+
|
|
76
|
+
To use the notebooks, also install jupyter:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install jupyter
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
Fucci phase currently supports loading a
|
|
85
|
+
[TrackMate](https://imagej.net/plugins/trackmate/) XML file:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from fucciphase import process_trackmate
|
|
89
|
+
from fucciphase.sensor import get_fuccisa_default_sensor
|
|
90
|
+
|
|
91
|
+
trackmate_xml = "path/to/trackmate.xml"
|
|
92
|
+
channel1 = "MEAN_INTENSITY_CH3"
|
|
93
|
+
channel2 = "MEAN_INTENSITY_CH4"
|
|
94
|
+
|
|
95
|
+
sensor = get_fuccisa_default_sensor()
|
|
96
|
+
|
|
97
|
+
df = process_trackmate(trackmate_xml,
|
|
98
|
+
channels=[channel1, channel2],
|
|
99
|
+
sensor=sensor,
|
|
100
|
+
thresholds=[0.1, 0.1])
|
|
101
|
+
print(df)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The TrackMate XML is converted to a [Pandas](https://pandas.pydata.org/) DataFrame.
|
|
105
|
+
Thus, in general data (e.g., stored in a CSV or XLSX file) that can be parsed into
|
|
106
|
+
a DataFrame is supported.
|
|
107
|
+
|
|
108
|
+
Have a look at the examples in the `example` folder to get more information!
|
|
109
|
+
|
|
110
|
+
The runtime of the scripts depends on your datasize. 2D samples with a few hundred to a few thousand cells
|
|
111
|
+
can be processed in a few minutes. Visualization in Napari can take a bit longer.
|
|
112
|
+
Standard processing does not require a powerful computer.
|
|
113
|
+
Make sure that you have sufficient RAM to load videos for visualization in Napari.
|
|
114
|
+
|
|
115
|
+
## Development
|
|
116
|
+
|
|
117
|
+
To develop fucciphase, clone the repository, install fucciphase in your environment
|
|
118
|
+
and install the pre-commit hooks:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
git clone https://github.com/Synthetic-Physiology-Lab/fucciphase
|
|
122
|
+
cd fucciphase
|
|
123
|
+
pip install -e ".[test, dev]"
|
|
124
|
+
pre-commit install
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
If you want to build the documentation, replace the abovementioned pip install by:
|
|
128
|
+
```bash
|
|
129
|
+
pip install -e ".[test, dev, doc]"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Cite us
|
|
133
|
+
|
|
134
|
+
Di Sante, M., Pezzotti, M., Zimmermann, J., Enrico, A., Deschamps, J., Balmas, E.,
|
|
135
|
+
Becca, S., Solito, S., Reali, A., Bertero, A., Jug, F. and Pasqualini, F.S., 2025.
|
|
136
|
+
CALIPERS: Cell cycle-aware live imaging for phenotyping experiments and regeneration studies.
|
|
137
|
+
bioRxiv, https://doi.org/10.1101/2024.12.19.629259
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
fucciphase/__init__.py,sha256=8PA6UNaYjycfPpOg7e5ArP3k9Lj9H15Lmw-Mnd14azY,375
|
|
2
|
+
fucciphase/fucci_phase.py,sha256=VgydHVN0nWzC6Eeh0LRaPHdScL2w7C5X7IhGSvyp4qw,6134
|
|
3
|
+
fucciphase/io.py,sha256=ELcoxsPHzcg9tf48MaMNnuSMxe0Fd3Nc9LCWbQ6p4I8,1715
|
|
4
|
+
fucciphase/phase.py,sha256=jy2l9LD81FTk6TIlyWyYPbVnwClGP9N4RrYyqtiMStg,17130
|
|
5
|
+
fucciphase/plot.py,sha256=Obfv2VOHdDN4Az_JUErJ22ESAgHhdsBaavdsS0f6xZ4,18593
|
|
6
|
+
fucciphase/py.typed,sha256=esB4cHc6c07uVkGtqf8at7ttEnprwRxwk8obY8Qumq4,187
|
|
7
|
+
fucciphase/sensor.py,sha256=6-WEI8viI5fSVyKHnECmKYKaROW4tQaPaNW4hHYVAcA,14788
|
|
8
|
+
fucciphase/tracking_utilities.py,sha256=IfKH2fyPo7fkW3PBvQrCz-UcfrdkOj7D-iLJabynvfw,2776
|
|
9
|
+
fucciphase/napari/__init__.py,sha256=At9Shk6HfDf6obtQaM0yKG4NOZVO6YxD2-J1M2ZGm7w,198
|
|
10
|
+
fucciphase/napari/tracks_to_napari.py,sha256=US9uAzGVJi5wAJ4CbUi9XztRFbrMRK7t0oJrQ40aDEg,4094
|
|
11
|
+
fucciphase/utils/__init__.py,sha256=E4wk7ygjHQQ-8vj4VNHseMLE-PQ1bZK2gvDFaO5Z1eU,1011
|
|
12
|
+
fucciphase/utils/checks.py,sha256=ZTe6cq11Y2e3suM3vECqCvvFxQTMOJqXMPS8gdCP7qc,651
|
|
13
|
+
fucciphase/utils/dtw.py,sha256=MDJEJUT9oSz8Iu6SeWFSBhVgZmLnE3CXE_2cbEl6TE8,1682
|
|
14
|
+
fucciphase/utils/normalize.py,sha256=eGytBDjmWcr1OY88McCJNcmy4zioM2DbtQConDoIVRw,6037
|
|
15
|
+
fucciphase/utils/phase_fit.py,sha256=Ht_dEyuLYonv6is9qQ-Xd95pQR7IR-8C8mv0ckDcp4E,1743
|
|
16
|
+
fucciphase/utils/simulator.py,sha256=7bmrO0IWUqsk4CyM-PlVpG2QTJxjTsY1h6buxmTs1iM,2322
|
|
17
|
+
fucciphase/utils/track_postprocessing.py,sha256=cTe3OOCR4dxFBZwN7XdbzDbnsgouoJql8rv4WwvmbM8,14438
|
|
18
|
+
fucciphase/utils/trackmate.py,sha256=dir4ayS1Fl-gtK8NTbP7t7CBLnrumC8LgPiCwxsBf-c,10666
|
|
19
|
+
fucciphase-0.0.1.dist-info/METADATA,sha256=TL-dyIUGTeLdahG5YiQiWhq3i8o3esSpvujY47rLP14,4900
|
|
20
|
+
fucciphase-0.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
21
|
+
fucciphase-0.0.1.dist-info/licenses/LICENSE,sha256=pQGrOGpOTwikEzkZ8Zc9XLQwbaZ85TMJP-GaWCNZciw,1554
|
|
22
|
+
fucciphase-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023, Joran Deschamps
|
|
4
|
+
Copyright (c) 2023, Julius Zimmermann (contributor)
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|