flimkit 0.12.0__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.
Files changed (109) hide show
  1. flimkit-0.12.0/LICENSE.md +11 -0
  2. flimkit-0.12.0/PKG-INFO +356 -0
  3. flimkit-0.12.0/README.md +298 -0
  4. flimkit-0.12.0/flimkit/FLIM/__init__.py +0 -0
  5. flimkit-0.12.0/flimkit/FLIM/assemble.py +254 -0
  6. flimkit-0.12.0/flimkit/FLIM/batch.py +681 -0
  7. flimkit-0.12.0/flimkit/FLIM/bg_tools.py +51 -0
  8. flimkit-0.12.0/flimkit/FLIM/fit_tools.py +244 -0
  9. flimkit-0.12.0/flimkit/FLIM/fitters.py +1471 -0
  10. flimkit-0.12.0/flimkit/FLIM/irf_tools.py +617 -0
  11. flimkit-0.12.0/flimkit/FLIM/models.py +391 -0
  12. flimkit-0.12.0/flimkit/GPU/__init__.py +85 -0
  13. flimkit-0.12.0/flimkit/GPU/_base.py +391 -0
  14. flimkit-0.12.0/flimkit/GPU/cuda.py +10 -0
  15. flimkit-0.12.0/flimkit/GPU/mlx_backend.py +381 -0
  16. flimkit-0.12.0/flimkit/GPU/mps.py +10 -0
  17. flimkit-0.12.0/flimkit/GPU/rocm.py +10 -0
  18. flimkit-0.12.0/flimkit/GPU/torch_backend.py +385 -0
  19. flimkit-0.12.0/flimkit/UI/app_state.py +10 -0
  20. flimkit-0.12.0/flimkit/UI/controller.py +139 -0
  21. flimkit-0.12.0/flimkit/UI/expert_settings.py +248 -0
  22. flimkit-0.12.0/flimkit/UI/fit_help.py +206 -0
  23. flimkit-0.12.0/flimkit/UI/fov_preview.py +1085 -0
  24. flimkit-0.12.0/flimkit/UI/gui.py +3919 -0
  25. flimkit-0.12.0/flimkit/UI/icon.icns +0 -0
  26. flimkit-0.12.0/flimkit/UI/icon.ico +0 -0
  27. flimkit-0.12.0/flimkit/UI/icon.png +0 -0
  28. flimkit-0.12.0/flimkit/UI/irf_widget.py +103 -0
  29. flimkit-0.12.0/flimkit/UI/mode_controller.py +118 -0
  30. flimkit-0.12.0/flimkit/UI/modes/__init__.py +0 -0
  31. flimkit-0.12.0/flimkit/UI/modes/base.py +3 -0
  32. flimkit-0.12.0/flimkit/UI/modes/batch_mode.py +312 -0
  33. flimkit-0.12.0/flimkit/UI/modes/fov_mode.py +164 -0
  34. flimkit-0.12.0/flimkit/UI/modes/irf_mode.py +80 -0
  35. flimkit-0.12.0/flimkit/UI/modes/phasor_mode.py +131 -0
  36. flimkit-0.12.0/flimkit/UI/modes/stitch_mode.py +254 -0
  37. flimkit-0.12.0/flimkit/UI/phasor_panel.py +1087 -0
  38. flimkit-0.12.0/flimkit/UI/progress_window.py +113 -0
  39. flimkit-0.12.0/flimkit/UI/project_panel.py +262 -0
  40. flimkit-0.12.0/flimkit/UI/results_panel.py +332 -0
  41. flimkit-0.12.0/flimkit/UI/roi_tools.py +794 -0
  42. flimkit-0.12.0/flimkit/UI/utils.py +217 -0
  43. flimkit-0.12.0/flimkit/__init__.py +0 -0
  44. flimkit-0.12.0/flimkit/_version.py +41 -0
  45. flimkit-0.12.0/flimkit/cli.py +120 -0
  46. flimkit-0.12.0/flimkit/configs.py +148 -0
  47. flimkit-0.12.0/flimkit/dialogs.py +46 -0
  48. flimkit-0.12.0/flimkit/formats/BH/__init__.py +0 -0
  49. flimkit-0.12.0/flimkit/formats/BH/reader.py +296 -0
  50. flimkit-0.12.0/flimkit/formats/BH/writer.py +86 -0
  51. flimkit-0.12.0/flimkit/formats/ISS/__init__.py +0 -0
  52. flimkit-0.12.0/flimkit/formats/ISS/fdflim.py +86 -0
  53. flimkit-0.12.0/flimkit/formats/ISS/image.py +114 -0
  54. flimkit-0.12.0/flimkit/formats/ISS/reader.py +223 -0
  55. flimkit-0.12.0/flimkit/formats/PS/__init__.py +0 -0
  56. flimkit-0.12.0/flimkit/formats/PS/reader.py +202 -0
  57. flimkit-0.12.0/flimkit/formats/PTU/__init__.py +0 -0
  58. flimkit-0.12.0/flimkit/formats/PTU/decode.py +27 -0
  59. flimkit-0.12.0/flimkit/formats/PTU/phu.py +85 -0
  60. flimkit-0.12.0/flimkit/formats/PTU/reader.py +235 -0
  61. flimkit-0.12.0/flimkit/formats/PTU/series.py +258 -0
  62. flimkit-0.12.0/flimkit/formats/PTU/stitch.py +1182 -0
  63. flimkit-0.12.0/flimkit/formats/PTU/tools.py +94 -0
  64. flimkit-0.12.0/flimkit/formats/__init__.py +2 -0
  65. flimkit-0.12.0/flimkit/formats/flim_file.py +232 -0
  66. flimkit-0.12.0/flimkit/formats/phasor.py +132 -0
  67. flimkit-0.12.0/flimkit/formats/signal.py +170 -0
  68. flimkit-0.12.0/flimkit/image/tools.py +124 -0
  69. flimkit-0.12.0/flimkit/interactive.py +1857 -0
  70. flimkit-0.12.0/flimkit/mpl_backend.py +22 -0
  71. flimkit-0.12.0/flimkit/phasor/__init__.py +40 -0
  72. flimkit-0.12.0/flimkit/phasor/filters.py +127 -0
  73. flimkit-0.12.0/flimkit/phasor/fret.py +654 -0
  74. flimkit-0.12.0/flimkit/phasor/interactive.py +556 -0
  75. flimkit-0.12.0/flimkit/phasor/peaks.py +186 -0
  76. flimkit-0.12.0/flimkit/phasor/signal.py +90 -0
  77. flimkit-0.12.0/flimkit/phasor_launcher.py +314 -0
  78. flimkit-0.12.0/flimkit/plugins/__init__.py +137 -0
  79. flimkit-0.12.0/flimkit/plugins/bindings.py +116 -0
  80. flimkit-0.12.0/flimkit/plugins/builtin/__init__.py +3 -0
  81. flimkit-0.12.0/flimkit/plugins/builtin/core_tools.py +28 -0
  82. flimkit-0.12.0/flimkit/plugins/loader.py +371 -0
  83. flimkit-0.12.0/flimkit/plugins/registry.py +406 -0
  84. flimkit-0.12.0/flimkit/project.py +197 -0
  85. flimkit-0.12.0/flimkit/synth.py +145 -0
  86. flimkit-0.12.0/flimkit/utils/__init__.py +0 -0
  87. flimkit-0.12.0/flimkit/utils/batch_fit.py +301 -0
  88. flimkit-0.12.0/flimkit/utils/config_manager.py +119 -0
  89. flimkit-0.12.0/flimkit/utils/config_snapshot.py +30 -0
  90. flimkit-0.12.0/flimkit/utils/crash_handler.py +183 -0
  91. flimkit-0.12.0/flimkit/utils/display.py +197 -0
  92. flimkit-0.12.0/flimkit/utils/enhanced_outputs.py +345 -0
  93. flimkit-0.12.0/flimkit/utils/fancy.py +103 -0
  94. flimkit-0.12.0/flimkit/utils/lifetime_image.py +243 -0
  95. flimkit-0.12.0/flimkit/utils/misc.py +111 -0
  96. flimkit-0.12.0/flimkit/utils/plotting.py +190 -0
  97. flimkit-0.12.0/flimkit/utils/roi.py +370 -0
  98. flimkit-0.12.0/flimkit/utils/session.py +51 -0
  99. flimkit-0.12.0/flimkit/utils/update_check.py +198 -0
  100. flimkit-0.12.0/flimkit/utils/xlsx_tools.py +97 -0
  101. flimkit-0.12.0/flimkit/utils/xml_utils.py +219 -0
  102. flimkit-0.12.0/flimkit.egg-info/PKG-INFO +356 -0
  103. flimkit-0.12.0/flimkit.egg-info/SOURCES.txt +107 -0
  104. flimkit-0.12.0/flimkit.egg-info/dependency_links.txt +1 -0
  105. flimkit-0.12.0/flimkit.egg-info/entry_points.txt +2 -0
  106. flimkit-0.12.0/flimkit.egg-info/requires.txt +45 -0
  107. flimkit-0.12.0/flimkit.egg-info/top_level.txt +1 -0
  108. flimkit-0.12.0/pyproject.toml +95 -0
  109. flimkit-0.12.0/setup.cfg +4 -0
@@ -0,0 +1,11 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2026 Alex Hunt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ **If you use FLIMKit in academic work, a citation or acknowledgment is appreciated.** There is no formal publication yet but it is planned — for now, please reference the GitHub repository and the author's name.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,356 @@
1
+ Metadata-Version: 2.4
2
+ Name: flimkit
3
+ Version: 0.12.0
4
+ Summary: Fluorescence lifetime imaging analysis: reading, fitting, phasors and stitching
5
+ Author-email: Alex Hunt <alexander.hunt@ed.ac.uk>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/FLIMKit/FLIMKit
8
+ Project-URL: Documentation, https://github.com/FLIMKit/FLIMKit/wiki
9
+ Project-URL: Issues, https://github.com/FLIMKit/FLIMKit/issues
10
+ Keywords: FLIM,fluorescence,lifetime,microscopy,TCSPC,phasor
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
15
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE.md
19
+ Requires-Dist: colorama==0.4.6
20
+ Requires-Dist: inquirer==3.4.1
21
+ Requires-Dist: lfdfiles
22
+ Requires-Dist: lz4>=4.0
23
+ Requires-Dist: matplotlib==3.10.8
24
+ Requires-Dist: numba
25
+ Requires-Dist: numpy==2.4.2
26
+ Requires-Dist: opencv-python-headless==4.13.0.90
27
+ Requires-Dist: openpyxl==3.1.5
28
+ Requires-Dist: pandas==3.0.1
29
+ Requires-Dist: Pillow>=10.0.0
30
+ Requires-Dist: psutil
31
+ Requires-Dist: phasorpy==0.12
32
+ Requires-Dist: photonsfile
33
+ Requires-Dist: ptufile
34
+ Requires-Dist: PyWavelets>=1.4.0
35
+ Requires-Dist: scipy==1.17.1
36
+ Requires-Dist: sdtfile
37
+ Requires-Dist: shapely==2.1.2
38
+ Requires-Dist: tifffile
39
+ Requires-Dist: tqdm==4.67.3
40
+ Requires-Dist: xarray==2026.1.0
41
+ Provides-Extra: gui
42
+ Requires-Dist: tkinterdnd2==0.4.3; extra == "gui"
43
+ Requires-Dist: TKinterModernThemes; extra == "gui"
44
+ Provides-Extra: notebook
45
+ Requires-Dist: ipython==9.10.0; extra == "notebook"
46
+ Requires-Dist: ipywidgets==8.1.8; extra == "notebook"
47
+ Provides-Extra: segmentation
48
+ Requires-Dist: cellpose>=3.0; extra == "segmentation"
49
+ Provides-Extra: mlx
50
+ Requires-Dist: mlx; extra == "mlx"
51
+ Provides-Extra: torch
52
+ Requires-Dist: torch; extra == "torch"
53
+ Provides-Extra: test
54
+ Requires-Dist: pytest==9.0.2; extra == "test"
55
+ Provides-Extra: all
56
+ Requires-Dist: flimkit[gui,notebook,segmentation]; extra == "all"
57
+ Dynamic: license-file
58
+
59
+ # <img src="flimkit/UI/icon.png" alt="" width="32" height="32"> FLIMKit
60
+
61
+ [![tests](https://github.com/FLIMKit/FLIMKit/actions/workflows/test.yml/badge.svg)](https://github.com/FLIMKit/FLIMKit/actions/workflows/test.yml)
62
+ [![Python versions](https://img.shields.io/badge/python-3.12%20%7C%203.13%20%7C%203.14-blue.svg)](https://github.com/FLIMKit/FLIMKit#installation)
63
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE.md)
64
+ [![DOI](https://zenodo.org/badge/1168642847.svg)](https://doi.org/10.5281/zenodo.21931131)
65
+
66
+ > **Warning:** Active development. Cross-validate results with other software before drawing conclusions. API and file formats may change without deprecation.
67
+
68
+ FLIMKit is a Python toolkit for FLIM data from FLIM microscope systems and common TCSPC / time-tag formats (PicoQuant `.ptu`, Becker & Hickl `.sdt`, ISS time-tag, Photonscore `.photons`). Built as a drop-in for FLIM microscope software, with two workflows:
69
+
70
+ - **Reconvolution fitting**: mono/bi/tri-exponential lifetime fitting with full IRF deconvolution, per-pixel and summed modes, multi-tile ROI stitching, batch processing, timelapse and z-stack analysis (a stack fitted as one FOV with a shared reference lifetime), a settable fit window with exclusion bands (drop reflection peaks mid-decay), and optional time-varying background correction (FLIMfit-style, from a measured fluorophore-free reference)
71
+ - **Lifetime distribution fitting**: Gaussian and Lorentzian continuous α(τ) distributions (Lakowicz §4.11.2), per-ROI and per-pixel maps with GPU acceleration
72
+ - **Phasor analysis**: calibrated phasor plots, interactive elliptical cursors, spatial filtering (gaussian/median/wavelet), two-component decomposition, automatic peak detection, session save/load
73
+
74
+ Four entry points: desktop GUI, guided terminal UI, CLI scripts, Python API.
75
+
76
+ [Examples repo](https://github.com/FLIMKit/FLIMKit-Examples.git) | [Full documentation](https://github.com/FLIMKit/FLIMKit/wiki)
77
+
78
+ ## GUI
79
+
80
+ ![FLIMKit GUI](Docs/ui.png)
81
+
82
+ Desktop GUI showing reconvolution fitting with per-pixel lifetime maps, summed decay curve, and full IRF deconvolution. The residual graph now displays beneath the tail fit for detailed fit quality assessment.
83
+
84
+ ## Installation
85
+
86
+ Python ≥ 3.12 required (3.14 recommended, official builds use 3.14).
87
+
88
+ From PyPI, for analysis in scripts, notebooks and the terminal:
89
+
90
+ ```bash
91
+ pip install flimkit # readers, fitting, phasors, stitching, the CLI
92
+ pip install "flimkit[gui]" # adds the desktop window
93
+ ```
94
+
95
+ That install fits on the CPU. `pip install "flimkit[mlx]"` adds the GPU on
96
+ Apple Silicon, and `pip install "flimkit[torch]"` adds CUDA on Linux and MPS on
97
+ a Mac. Windows CUDA and AMD ROCm need a wheel from PyTorch's own index, which
98
+ pip metadata cannot ask for, so clone it for those, and for the terminal CLIs
99
+ at the repository root:
100
+
101
+ ```bash
102
+ git clone https://github.com/FLIMKit/FLIMKit.git
103
+ cd FLIMKit
104
+ python install.py # auto-detects GPU and installs the right backend
105
+ python validate_installation.py # 10 checks - all should pass
106
+ ```
107
+
108
+ For development work (PyInstaller + test dependencies):
109
+
110
+ ```bash
111
+ python install.py --dev
112
+ ```
113
+
114
+ Or download the compiled app from the Releases tab (no Python needed).
115
+
116
+ ## Docker / TrueNAS SCALE
117
+
118
+ A pre-built image is available on Docker Hub. It runs the full desktop GUI in a browser via xpra (no installation needed on the client).
119
+
120
+ **Pull and run:**
121
+
122
+ ```bash
123
+ docker run -d \
124
+ -p 14500:14500 \
125
+ -v /path/to/your/data:/data \
126
+ --name flimkit \
127
+ alex1075/flimkit:latest
128
+ ```
129
+
130
+ Then open **http://localhost:14500** in your browser. PTU files and data should be placed in the folder you mount to `/data` - use the file dialog inside the app to navigate there.
131
+
132
+ **TrueNAS SCALE (Custom App):**
133
+
134
+ 1. Apps → Discover Apps → Custom App
135
+ 2. Paste the contents of `docker/docker-compose.yaml` from this repo
136
+ 3. Edit the volume paths to match your pool (e.g. `/mnt/tank/microscopy:/data`)
137
+ 4. Deploy - TrueNAS will pull the image automatically
138
+
139
+ **Build from source** (required if you want to push your own changes):
140
+
141
+ ```bash
142
+ python docker/build_docker.py # builds linux/amd64, pushes to Docker Hub
143
+ ```
144
+
145
+ Requires Docker Desktop with buildx. On Apple Silicon, buildx cross-compiles for `linux/amd64` automatically.
146
+
147
+ ## Usage
148
+
149
+ ### Desktop GUI
150
+
151
+ ```bash
152
+ python main.py
153
+ ```
154
+
155
+ Five tabs: **Single FOV Fit**, **Tile Stitch / Fit**, **Batch ROI Fit**, **Machine IRF Builder**, **Phasor Analysis**. The right panel shows an FOV preview (intensity image + summed decay) and switches to the interactive phasor view when that tab is active. The Single FOV Fit tab has an Analysis toggle next to the input file: switch it to **Z-stack** to pick a folder of `region_zX.ptu` slices (one PTU per z-slice, e.g. a Leica `.sptw` workspace) and fit the whole stack as one FOV with a shared reference lifetime. The FOV Preview shows the intensity when you pick the folder and the fitted FLIM after fitting, with a z-slider (shown only for a z-stack) to scroll through depth. In a project folder, a z-stack appears as one collapsed entry (a `Z` tag) in the browser and reloads its fitted stack when selected.
156
+
157
+ ### Terminal UI
158
+
159
+ ```bash
160
+ python main.py --cli
161
+ ```
162
+
163
+ ### CLI
164
+
165
+ ```bash
166
+ python fit_cli.py --ptu data.ptu --machine-irf machine_irf_default.npy --nexp 2
167
+ python phasor_cli.py --ptu data.ptu --irf irf.xlsx
168
+ ```
169
+
170
+ ### Synthetic data
171
+
172
+ Generates FLIM data with a known ground truth (sample PTU + matching IRF PTU + a JSON of the parameters used) for cross-software validation. Optional reflection peaks, pile-up and background let you reproduce specific artefacts; `--sdt` also writes Becker & Hickl versions.
173
+
174
+ ```bash
175
+ python synth_cli.py --out ./validation --tau 3.0,0.8 --amps 0.7,0.3 --photons 1e5
176
+ ```
177
+
178
+ ### Python API
179
+
180
+ ```python
181
+ from flimkit.phasor_launcher import launch_phasor
182
+ state = launch_phasor('data.ptu', irf_path='irf.xlsx')
183
+
184
+ # With spatial phasor filtering
185
+ state = launch_phasor('data.ptu', irf_path='irf.xlsx',
186
+ phasor_filter='gaussian', filter_kwargs={'sigma': 1.5})
187
+ ```
188
+
189
+ ## Supported formats
190
+
191
+ FLIMKit auto-detects the file type and routes every format through one loader (`FLIMFile` in `flimkit.formats`), so the fitting, phasor, stitching, and ROI workflows are identical whatever the instrument wrote.
192
+
193
+ | Format | Extension | Loads as | Reader | Validated against real files |
194
+ |---|---|---|---|---|
195
+ | PicoQuant PTU | `.ptu` | Fitting + phasor | [`ptufile`](https://github.com/cgohlke/ptufile) | Yes (32 files) |
196
+ | Becker & Hickl SDT | `.sdt` | Fitting + phasor | [`sdtfile`](https://github.com/cgohlke/sdtfile) | Yes (bit-identical) |
197
+ | Photonscore LINCam | `.photons` | Fitting + phasor | [`photonsfile`](https://github.com/alex1075/photonsfile) | Yes (bit-exact vs SDK) |
198
+ | PicoQuant BIN | `.bin` | Fitting + phasor | [`ptufile`](https://github.com/cgohlke/ptufile) | Upstream |
199
+ | PicoQuant PHU | `.phu` | Fitting (no image) | [`ptufile`](https://github.com/cgohlke/ptufile) | Upstream |
200
+ | SimFCS B&H | `.b&h` | Fitting + phasor | [`lfdfiles`](https://github.com/cgohlke/lfdfiles) | Upstream (no time axis in file) |
201
+ | SimFCS BHZ | `.bhz` | Fitting + phasor | [`lfdfiles`](https://github.com/cgohlke/lfdfiles) | Upstream (no time axis in file) |
202
+ | ImSpector FLIM TIFF | `.tif`, `.tiff` (sniffed) | Fitting + phasor | [`tifffile`](https://github.com/cgohlke/tifffile) | Upstream |
203
+ | ISS Vista TDFLIM | `.iss-tdflim`, `.tdflim` | Fitting + phasor | [`lfdfiles`](https://github.com/cgohlke/lfdfiles) | Upstream |
204
+ | FLIM LABS imaging | `.json` (sniffed) | Fitting + phasor | [`phasorpy`](https://github.com/phasorpy/phasorpy) | Upstream |
205
+ | ISS time-tag | `.tagtime`, `.tagchannel`, `.tagdecay` | Fitting + phasor | FLIMKit (from ISS spec) | **No** |
206
+ | ISS FD-FLIM | `.ifli` | Phasor only | [`lfdfiles`](https://github.com/cgohlke/lfdfiles) | Upstream |
207
+ | SimFCS referenced | `.ref`, `.r64` | Phasor only | [`lfdfiles`](https://github.com/cgohlke/lfdfiles) | Upstream (no frequency in file) |
208
+ | PhasorPy OME-TIFF | `.ome.tif` (sniffed) | Phasor only | [`tifffile`](https://github.com/cgohlke/tifffile) | Upstream |
209
+ | FLIM LABS phasor | `.json` (sniffed) | Phasor only | [`phasorpy`](https://github.com/phasorpy/phasorpy) | Upstream |
210
+ | ISS intensity image | `.ifi` | Intensity only | FLIMKit (from ISS spec) | **No** |
211
+
212
+ "Upstream" means decoding is delegated to a maintained third-party reader that is tested against real files by its own author; FLIMKit has not independently re-validated it. "Sniffed" means the extension is ambiguous, so the file is identified by content, not by its name.
213
+
214
+ Formats whose files carry no time axis (`.b&h`, `.bhz`) or no modulation frequency (`.ref`, `.r64`) will prompt for the missing value, since fits and the universal circle cannot be computed without it.
215
+
216
+ **PicoQuant `.ptu` (T3 mode):**
217
+
218
+ - PicoHarp T3 (Leica FALCON / STELLARIS)
219
+ - HydraHarp v1/v2 T3
220
+ - TimeHarp 260 N / P T3
221
+ - MultiHarp / generic T3
222
+
223
+ Decoding and image reconstruction use Christoph Gohlke's [`ptufile`](https://github.com/cgohlke/ptufile). FLIMKit's original T3 decoder, checked against `ptufile` on 32 real files (Leica, PicoQuant, Chroma, Zeiss; image and point-mode), is kept as a reference in the separate `flim-native-decoders` repository (see `flimkit/formats/PTU/NOTICE.md`).
224
+
225
+ **Becker & Hickl `.sdt` (SPC TCSPC):** the histogram / image `.sdt` files SPCM saves (per-pixel decays already binned). Read with Christoph Gohlke's [`sdtfile`](https://github.com/cgohlke/sdtfile); FLIMKit's own decoder, written from Becker & Hickl's official SPCM documentation and checked bit-for-bit against `sdtfile` on real files, is kept as a reference in `flim-native-decoders`. Thanks to Becker & Hickl for supporting this (see `flimkit/formats/BH/NOTICE.md`).
226
+
227
+ **Photonscore `.photons` (LINCam, D7 container):** the position-sensitive photon-counting files from Photonscore's LINCam systems. Read with [`photonsfile`](https://github.com/alex1075/photonsfile), a pure-Python reader for the D7 container (paged protobuf, seed plus delta-coded photon streams) spun out of FLIMKit, with no native dependency and validated bit-exact against the Photonscore SDK. Each photon carries an (x, y) position and a TCSPC micro-time, so the image is formed by binning the positions into a pixel grid and the decay by histogramming the micro-time; `dt` calibration comes from the file's `TacChannel` attribute. Thanks to Photonscore for supporting this and for open-sourcing the D7 format (see `flimkit/formats/PS/NOTICE.md`).
228
+
229
+ **ISS `.TAGTIME` + `.TAGCHANNEL` + `.TAGDECAY` (FastFLIM / Vista, time-domain):** the three time-tag files are read together (point the loader at any one of them or their shared basename). ISS records explicit frame, line, and pixel markers, so the per-pixel reconstruction is exact.
230
+
231
+ **ISS `.ifi` (intensity image):** ISS's plain intensity-image export (`VISTAIMAGE` header, float pixels per channel and frame). No lifetime data, so it loads as an intensity image only (no fitting or phasor).
232
+
233
+ **ISS `.ifli` (frequency-domain / FD-FLIM):** ISS's FD-FLIM lifetime-image export (`VistaFLImage` header). This is already phasor data (per-pixel phase and modulation at each modulation frequency), so it loads straight into phasor analysis with fitting disabled - there is no decay to fit. Decoding is delegated to Christoph Gohlke's [`lfdfiles`](https://github.com/cgohlke/lfdfiles) (`VistaIfli`), which handles the version-dependent header and the multi-position / spectral axes; FLIMKit applies the file's reference-sample calibration on top. ISS's time-domain `.iss-tdflim` / `.tdflim` archives are read through the same library.
234
+
235
+ > **The ISS time-tag and `.ifi` readers are experimental and need testing.** The `.TAGTIME`/`.TAGCHANNEL`/`.TAGDECAY` triplet and `.ifi` readers were written from ISS's format specifications and have **not been validated against real ISS acquisitions** (byte order and the marker conventions are assumptions). Treat their results as unverified and cross-check them. If you have ISS data, trying it and reporting back is very welcome. The `.ifli` and `.tdflim` paths are delegated to `lfdfiles` and so inherit that library's own testing. PicoQuant `.ptu`, Becker & Hickl `.sdt` and Photonscore `.photons` are validated against real files.
236
+
237
+ Imaging files are reconstructed into a per-pixel decay cube `(Y, X, H)` from their markers (PTU scan-line markers, B&H image blocks, ISS frame/line/pixel markers) or, for Photonscore, from each photon's (x, y) position; the intensity image is that cube summed over the time axis.
238
+
239
+ **Read as a decay only (no image):** image reconstruction needs scan / line markers, so FLIMKit fits the decay but cannot rebuild a FLIM image from a file that has none. This covers single-spot, point, and FCS acquisitions (for example PicoQuant TimeHarp 260P point measurements), and any file exported without imaging markers.
240
+
241
+ **IRF sources:**
242
+
243
+ - Measured / scatter IRF from a `.ptu`
244
+ - Leica IRF from an exported `.xlsx` or `.csv` file (interpolated or analytical model)
245
+ - PicoQuant SymPhoTime Check file (`.pck`) histogram
246
+ - A built machine IRF, or an analytical Gaussian IRF
247
+
248
+ A `.pck` IRF must come from the same instrument and TCSPC resolution as the data being fit.
249
+
250
+ **Not supported yet:**
251
+
252
+ - **T2-mode PTUs.** FLIMKit decodes T3 mode only (one TCSPC histogram per sync period). T2 records are raw global timestamps with no per-period decay, so a T2 `.ptu` will not produce a meaningful decay.
253
+ - Older PicoQuant formats (`.pt3`, `.ht3`, `.pt2`) and Becker & Hickl raw `.spc` photon streams.
254
+ - Leica `.lif` and proprietary LMSCOMPRESSED blocks. Export `.ptu` from LAS X instead.
255
+
256
+ ## Machine IRF (do this first)
257
+
258
+ Before fitting, build a machine IRF for your system once and reuse it across sessions. You need matched `.ptu` + `.xlsx` pairs, 10-20 is a good number.
259
+
260
+ In the GUI, go to **Machine IRF Builder**, point it at your pairs folder, and save as `machine_irf_default`. From source this goes to `flimkit/machine_irf/`; compiled app saves to `~/.flimkit/machine_irf/`.
261
+
262
+ ```python
263
+ from flimkit.FLIM.irf_tools import build_machine_irf_from_folder
264
+
265
+ build_machine_irf_from_folder(
266
+ folder="/path/to/pairs",
267
+ align_anchor="peak",
268
+ reducer="median",
269
+ save=True,
270
+ output_name="machine_irf_default",
271
+ )
272
+ ```
273
+
274
+ ## GPU Acceleration
275
+
276
+ Per-pixel fitting uses a batched matrix solver that runs on GPU when a supported backend is detected. This applies to both single-FOV and tile-ROI pipelines. The same `fit_per_pixel()` function is used in both.
277
+
278
+ | Backend | Hardware | Notes |
279
+ |---|---|---|
280
+ | MLX | Apple Silicon (M1/M2/M3/M4) | Detected automatically |
281
+ | PyTorch MPS | Apple Silicon | Fallback if MLX not installed |
282
+ | PyTorch CUDA | NVIDIA | `pip install torch --index-url https://download.pytorch.org/whl/cu126` |
283
+ | PyTorch ROCm | AMD | `pip install torch --index-url https://download.pytorch.org/whl/rocm6.2` |
284
+
285
+ `python install.py` detects your hardware and installs the right backend automatically. No extra flags needed at runtime.
286
+
287
+ **Limitations:** `--free-tau-perpixel` mode uses batched Adam on GPU when a backend is available (n_exp ≥ 2). `fit_summed` (single global fit) is always CPU - it's fast enough not to matter.
288
+
289
+ **Compiled app and GPU:** The compiled app bundles whatever GPU libraries are installed on the *build* machine. A binary built on Apple Silicon will have MLX/MPS; one built on a CUDA machine will have CUDA. If you need GPU in the compiled app, build it yourself on the target hardware. See [Compiled App](#compiled-app-macos--windows--linux).
290
+
291
+ ## Tests
292
+
293
+ Not strictly necessary, but useful after making code changes.
294
+
295
+ ```bash
296
+ python install.py --dev # install test requirements first
297
+
298
+ cd flimkit_tests
299
+ python run_tests.py # all tests
300
+ python run_tests.py -c # with coverage report
301
+ python run_tests.py integration # integration tests only
302
+ ```
303
+
304
+ ## Outputs
305
+
306
+ | Format | Description |
307
+ |---|---|
308
+ | PNG | Intensity and lifetime map images |
309
+ | OME-TIFF | Lossless export with metadata - opens in Fiji/ImageJ |
310
+ | GeoJSON | ROI geometries and stats - imports directly into QuPath |
311
+ | CSV | Fit summaries and per-ROI statistics |
312
+ | NPZ | Session files for restoring analysis state |
313
+
314
+ ## Roadmap
315
+
316
+ Done: single FOV fitting, tile stitching, batch ROI processing, timelapse and z-stack analysis, phasor analysis, GUI, session restoration, compiled app, ROI analysis with QuPath export.
317
+
318
+ Up next: config persistence, stat histograms, auto-region detection, batch n-exp in GUI. Chemical validation and publication pending.
319
+
320
+ See [ROADMAP.md](Docs/ROADMAP.md) for details.
321
+
322
+ ## Notes on FLIM microscope software comparison
323
+
324
+ Fitted lifetimes from FLIMKit will typically read slightly higher than FLIM microscope software for the same data. This is a consequence of IRF placement: FLIMKit anchors the IRF at the steepest-rise point of the leading edge, which differs from how FLIM microscope software places the IRF. The difference is systematic and reproducible across acquisitions.
325
+
326
+ ## References
327
+
328
+ **Lifetime distribution fitting** - Gaussian and Lorentzian α(τ) models:
329
+ > Lakowicz, J.R. (2006). *Principles of Fluorescence Spectroscopy* (3rd ed.). Springer. §4.11.2, pp. 141-144.
330
+
331
+ **Time-varying background correction** - measured background decay `B = V·b(t) + Z`:
332
+ > Görlitz, F. et al. (2017). Open Source High Content Analysis Utilizing Automated Fluorescence Lifetime Imaging Microscopy. *J. Vis. Exp.* (119), e55119. https://doi.org/10.3791/55119
333
+
334
+ **PhasorPy**: Gohlke, C. et al. Zenodo. https://doi.org/10.5281/zenodo.13862586
335
+
336
+ **ptufile / sdtfile / lfdfiles / tifffile** (PicoQuant `.ptu`, Becker & Hickl `.sdt`, SimFCS and ISS formats, and TIFF): Gohlke, C. https://github.com/cgohlke/ptufile, https://github.com/cgohlke/sdtfile, https://github.com/cgohlke/lfdfiles, https://github.com/cgohlke/tifffile
337
+
338
+ **photonsfile** (Photonscore LINCam `.photons` / D7 reader): Hunt, A. and A. Akram. Zenodo. https://doi.org/10.5281/zenodo.21360199
339
+
340
+ **Tile stitching**: Preibisch et al. (2009). *Bioinformatics* 25(11). https://doi.org/10.1093/bioinformatics/btp184
341
+
342
+ **Cellpose-SAM**: Pachitariu & Stringer (2025). *bioRxiv*. https://doi.org/10.1101/2025.04.28.651001
343
+
344
+ ## Acknowledgements
345
+
346
+ FLIMKit is designed, developed, and maintained by Alex Hunt. Anthropic's Claude AI was used as an assistant for parts of the GUI implementation, compiled app builds, code debugging, and Docker packaging; all scientific design, fitting/phasor methods, validation, and the overall architecture are the author's own work.
347
+
348
+ Contributions by Zhen Yuan were developed with assistance from OpenAI's GPT-5.6 Sol, operated through Hermes Agent by Nous Research. This assistance supported implementation, testing, and documentation. Zhen Yuan directed and reviewed the work and remains responsible for the submitted implementations.
349
+
350
+ FLIMKit reads several instrument formats. PicoQuant `.ptu` and Becker & Hickl `.sdt` reading is delegated to Christoph Gohlke's `ptufile` and `sdtfile` libraries, the SimFCS (`.b&h`, `.bhz`, `.ref`, `.r64`) and ISS (`.ifli`, `.iss-tdflim`) formats to his `lfdfiles`, and all TIFF reading, including ImSpector FLIM TIFF and PhasorPy OME-TIFF, to his `tifffile`; thank you to Christoph Gohlke for maintaining them, and for PhasorPy, which FLIMKit uses as its phasor backbone. Photonscore `.photons` reading is delegated to `photonsfile`, which was written for FLIMKit and spun out as a standalone library so it can be used without FLIMKit. Becker & Hickl GmbH, ISS, Inc. and Photonscore GmbH supported the original readers directly: thank you to Becker & Hickl (in particular Dr. Jens Balke and Enzo Marscheck) for the SPCM file-structure documentation and sample `.sdt` files, to ISS (in particular Dr. Shih-Chu Liao, and to Anand Yethiraj at the University of Guelph for the introduction) for the FastFLIM / Vista format specifications, and to Photonscore GmbH for the LINCam SDK, a sample `.photons` file, and for open-sourcing the D7 format. FLIMKit's own `.ptu` and `.sdt` decoders (the `.ptu` one written from PicoQuant's published documentation, without direct input from PicoQuant) are kept as cross-checked references in the `flim-native-decoders` repository. Per-format provenance is in each reader's `NOTICE.md` (`flimkit/formats/<FORMAT>/NOTICE.md`).
351
+
352
+ Thank you to the users who have contributed features and reported problems: to Lin Yangchen (@linyangchen) for the request to accept LAS X calculated-IRF exports as CSV as well as XLSX, and to zhen yuan (@zhenyuan992) for implementing that support, including the delimiter and decimal-comma handling needed for locale-specific exports.
353
+
354
+ ## Contact
355
+
356
+ Alex Hunt: alexander.hunt@ed.ac.uk