flimkit 0.12.0__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.
Files changed (104) hide show
  1. flimkit/FLIM/__init__.py +0 -0
  2. flimkit/FLIM/assemble.py +254 -0
  3. flimkit/FLIM/batch.py +681 -0
  4. flimkit/FLIM/bg_tools.py +51 -0
  5. flimkit/FLIM/fit_tools.py +244 -0
  6. flimkit/FLIM/fitters.py +1471 -0
  7. flimkit/FLIM/irf_tools.py +617 -0
  8. flimkit/FLIM/models.py +391 -0
  9. flimkit/GPU/__init__.py +85 -0
  10. flimkit/GPU/_base.py +391 -0
  11. flimkit/GPU/cuda.py +10 -0
  12. flimkit/GPU/mlx_backend.py +381 -0
  13. flimkit/GPU/mps.py +10 -0
  14. flimkit/GPU/rocm.py +10 -0
  15. flimkit/GPU/torch_backend.py +385 -0
  16. flimkit/UI/app_state.py +10 -0
  17. flimkit/UI/controller.py +139 -0
  18. flimkit/UI/expert_settings.py +248 -0
  19. flimkit/UI/fit_help.py +206 -0
  20. flimkit/UI/fov_preview.py +1085 -0
  21. flimkit/UI/gui.py +3919 -0
  22. flimkit/UI/icon.icns +0 -0
  23. flimkit/UI/icon.ico +0 -0
  24. flimkit/UI/icon.png +0 -0
  25. flimkit/UI/irf_widget.py +103 -0
  26. flimkit/UI/mode_controller.py +118 -0
  27. flimkit/UI/modes/__init__.py +0 -0
  28. flimkit/UI/modes/base.py +3 -0
  29. flimkit/UI/modes/batch_mode.py +312 -0
  30. flimkit/UI/modes/fov_mode.py +164 -0
  31. flimkit/UI/modes/irf_mode.py +80 -0
  32. flimkit/UI/modes/phasor_mode.py +131 -0
  33. flimkit/UI/modes/stitch_mode.py +254 -0
  34. flimkit/UI/phasor_panel.py +1087 -0
  35. flimkit/UI/progress_window.py +113 -0
  36. flimkit/UI/project_panel.py +262 -0
  37. flimkit/UI/results_panel.py +332 -0
  38. flimkit/UI/roi_tools.py +794 -0
  39. flimkit/UI/utils.py +217 -0
  40. flimkit/__init__.py +0 -0
  41. flimkit/_version.py +41 -0
  42. flimkit/cli.py +120 -0
  43. flimkit/configs.py +148 -0
  44. flimkit/dialogs.py +46 -0
  45. flimkit/formats/BH/__init__.py +0 -0
  46. flimkit/formats/BH/reader.py +296 -0
  47. flimkit/formats/BH/writer.py +86 -0
  48. flimkit/formats/ISS/__init__.py +0 -0
  49. flimkit/formats/ISS/fdflim.py +86 -0
  50. flimkit/formats/ISS/image.py +114 -0
  51. flimkit/formats/ISS/reader.py +223 -0
  52. flimkit/formats/PS/__init__.py +0 -0
  53. flimkit/formats/PS/reader.py +202 -0
  54. flimkit/formats/PTU/__init__.py +0 -0
  55. flimkit/formats/PTU/decode.py +27 -0
  56. flimkit/formats/PTU/phu.py +85 -0
  57. flimkit/formats/PTU/reader.py +235 -0
  58. flimkit/formats/PTU/series.py +258 -0
  59. flimkit/formats/PTU/stitch.py +1182 -0
  60. flimkit/formats/PTU/tools.py +94 -0
  61. flimkit/formats/__init__.py +2 -0
  62. flimkit/formats/flim_file.py +232 -0
  63. flimkit/formats/phasor.py +132 -0
  64. flimkit/formats/signal.py +170 -0
  65. flimkit/image/tools.py +124 -0
  66. flimkit/interactive.py +1857 -0
  67. flimkit/mpl_backend.py +22 -0
  68. flimkit/phasor/__init__.py +40 -0
  69. flimkit/phasor/filters.py +127 -0
  70. flimkit/phasor/fret.py +654 -0
  71. flimkit/phasor/interactive.py +556 -0
  72. flimkit/phasor/peaks.py +186 -0
  73. flimkit/phasor/signal.py +90 -0
  74. flimkit/phasor_launcher.py +314 -0
  75. flimkit/plugins/__init__.py +137 -0
  76. flimkit/plugins/bindings.py +116 -0
  77. flimkit/plugins/builtin/__init__.py +3 -0
  78. flimkit/plugins/builtin/core_tools.py +28 -0
  79. flimkit/plugins/loader.py +371 -0
  80. flimkit/plugins/registry.py +406 -0
  81. flimkit/project.py +197 -0
  82. flimkit/synth.py +145 -0
  83. flimkit/utils/__init__.py +0 -0
  84. flimkit/utils/batch_fit.py +301 -0
  85. flimkit/utils/config_manager.py +119 -0
  86. flimkit/utils/config_snapshot.py +30 -0
  87. flimkit/utils/crash_handler.py +183 -0
  88. flimkit/utils/display.py +197 -0
  89. flimkit/utils/enhanced_outputs.py +345 -0
  90. flimkit/utils/fancy.py +103 -0
  91. flimkit/utils/lifetime_image.py +243 -0
  92. flimkit/utils/misc.py +111 -0
  93. flimkit/utils/plotting.py +190 -0
  94. flimkit/utils/roi.py +370 -0
  95. flimkit/utils/session.py +51 -0
  96. flimkit/utils/update_check.py +198 -0
  97. flimkit/utils/xlsx_tools.py +97 -0
  98. flimkit/utils/xml_utils.py +219 -0
  99. flimkit-0.12.0.dist-info/METADATA +356 -0
  100. flimkit-0.12.0.dist-info/RECORD +104 -0
  101. flimkit-0.12.0.dist-info/WHEEL +5 -0
  102. flimkit-0.12.0.dist-info/entry_points.txt +2 -0
  103. flimkit-0.12.0.dist-info/licenses/LICENSE.md +11 -0
  104. flimkit-0.12.0.dist-info/top_level.txt +1 -0
flimkit/image/tools.py ADDED
@@ -0,0 +1,124 @@
1
+ import cv2
2
+ import numpy as np
3
+ from pathlib import Path
4
+ from flimkit.formats import FLIMFile
5
+
6
+ def make_intensity_image(ptu_path, rotate_90_cw=True, save_image=False):
7
+ ptu = FLIMFile(ptu_path, verbose=False)
8
+ stack = ptu.raw_pixel_stack(channel=ptu.photon_channel)
9
+ intensity = stack.sum(axis=-1)
10
+ if rotate_90_cw:
11
+ intensity = np.rot90(intensity, k=-1)
12
+ print(' Rotated 90° clockwise.')
13
+ if save_image:
14
+ out_path = Path(ptu_path).stem + '_intensity.png'
15
+ normed = cv2.normalize(intensity.astype(np.float32), None, 0, 255,
16
+ cv2.NORM_MINMAX).astype(np.uint8)
17
+ cv2.imwrite(out_path, normed)
18
+ print(f" Saved intensity image: {out_path}")
19
+ return intensity
20
+
21
+ def make_cell_mask(intensity_image, save_mask=False, path=None, name=None,
22
+ flow_threshold=0.5, cellprob_threshold=0.0, resize_to=224,
23
+ gpu=True):
24
+ try:
25
+ from cellpose import models as cp_models
26
+ except ImportError:
27
+ raise ImportError("cellpose is not installed - pip install 'cellpose>=3.0'")
28
+ if isinstance(intensity_image, (str, Path)):
29
+ img = cv2.imread(str(intensity_image), cv2.IMREAD_GRAYSCALE)
30
+ if img is None:
31
+ raise FileNotFoundError(f"Could not read image: {intensity_image}")
32
+ elif isinstance(intensity_image, np.ndarray):
33
+ img = intensity_image
34
+ else:
35
+ raise TypeError(f"Expected file path or ndarray, got {type(intensity_image)}")
36
+ if img.ndim != 2:
37
+ raise ValueError(f"Expected 2-D array, got shape {img.shape}")
38
+ orig_h, orig_w = img.shape
39
+ lo, hi = np.percentile(img, [1, 99])
40
+ span = float(hi - lo) if hi != lo else 1.0
41
+ img_u8 = (np.clip((img.astype(np.float64) - lo) / span, 0.0, 1.0) * 255).astype(np.uint8)
42
+ if resize_to is not None and (orig_h != resize_to or orig_w != resize_to):
43
+ img_input = cv2.resize(img_u8, (resize_to, resize_to), interpolation=cv2.INTER_LINEAR)
44
+ else:
45
+ img_input = img_u8
46
+ model = cp_models.CellposeModel(gpu=gpu)
47
+ labels, _, _ = model.eval(
48
+ img_input,
49
+ flow_threshold=flow_threshold,
50
+ cellprob_threshold=cellprob_threshold,
51
+ normalize={'tile_norm_blocksize': 0},
52
+ )
53
+ labels = labels.astype(np.int32)
54
+ if resize_to is not None and (orig_h != resize_to or orig_w != resize_to):
55
+ labels = cv2.resize(labels, (orig_w, orig_h), interpolation=cv2.INTER_NEAREST)
56
+ mask_bool = labels > 0
57
+ if save_mask and path is not None:
58
+ out_path = str(Path(path).with_suffix('')) + '_cell_mask.png'
59
+ cv2.imwrite(out_path, mask_bool.astype(np.uint8) * 255)
60
+ print(f" Saved cell mask: {out_path}")
61
+ return mask_bool
62
+
63
+ def apply_intensity_threshold(intensity_image, threshold):
64
+ return intensity_image >= threshold
65
+
66
+ def pick_intensity_threshold(intensity_image, initial=None):
67
+ from flimkit.mpl_backend import select_backend
68
+ select_backend()
69
+ import matplotlib.pyplot as plt
70
+ from matplotlib.widgets import Slider, Button
71
+ img = intensity_image.astype(float)
72
+ vmax = img.max()
73
+ if initial is None:
74
+ initial = max(1, int(vmax * 0.05))
75
+ state = {'threshold': int(initial)}
76
+ fig, ax = plt.subplots(1, 1, figsize=(8, 8))
77
+ plt.subplots_adjust(bottom=0.22)
78
+ ax.imshow(img, cmap='gray', interpolation='nearest')
79
+ ax.set_title(f"Intensity image - threshold = {state['threshold']} photons")
80
+ ax.set_axis_off()
81
+ overlay_rgba = np.zeros((*img.shape, 4), dtype=float)
82
+ mask_below = img < state['threshold']
83
+ overlay_rgba[mask_below] = [1, 0, 0, 0.35]
84
+ overlay_im = ax.imshow(overlay_rgba, interpolation='nearest')
85
+ n_above = int((~mask_below).sum())
86
+ n_total = img.size
87
+ count_text = ax.text(
88
+ 0.01, 0.01,
89
+ f"{n_above:,}/{n_total:,} pixels kept ({100 * n_above / n_total:.1f} %)",
90
+ transform=ax.transAxes, fontsize=9,
91
+ color='white', backgroundcolor=(0, 0, 0, 0.5),
92
+ verticalalignment='bottom',
93
+ )
94
+ ax_slider = plt.axes([0.15, 0.08, 0.65, 0.03])
95
+ slider = Slider(ax_slider, 'Min photons', 0, max(int(vmax), 1),
96
+ valinit=initial, valstep=1, valfmt='%d')
97
+
98
+ def _update(val):
99
+ thr = int(val)
100
+ state['threshold'] = thr
101
+ mask_below = img < thr
102
+ rgba = np.zeros((*img.shape, 4), dtype=float)
103
+ rgba[mask_below] = [1, 0, 0, 0.35]
104
+ overlay_im.set_data(rgba)
105
+ n_above = int((~mask_below).sum())
106
+ ax.set_title(f"Intensity image - threshold = {thr} photons")
107
+ count_text.set_text(
108
+ f"{n_above:,}/{n_total:,} pixels kept ({100 * n_above / n_total:.1f} %)"
109
+ )
110
+ fig.canvas.draw_idle()
111
+ slider.on_changed(_update)
112
+ ax_btn = plt.axes([0.40, 0.02, 0.20, 0.04])
113
+ btn = Button(ax_btn, 'Accept', hovercolor='lightgreen')
114
+ def _accept(event):
115
+ plt.close(fig)
116
+ btn.on_clicked(_accept)
117
+ def _on_key(event):
118
+ if event.key in ('enter', 'return'):
119
+ plt.close(fig)
120
+ fig.canvas.mpl_connect('key_press_event', _on_key)
121
+ plt.show()
122
+ chosen = state['threshold']
123
+ print(f" Intensity threshold selected: {chosen} photons")
124
+ return chosen