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.
- flimkit/FLIM/__init__.py +0 -0
- flimkit/FLIM/assemble.py +254 -0
- flimkit/FLIM/batch.py +681 -0
- flimkit/FLIM/bg_tools.py +51 -0
- flimkit/FLIM/fit_tools.py +244 -0
- flimkit/FLIM/fitters.py +1471 -0
- flimkit/FLIM/irf_tools.py +617 -0
- flimkit/FLIM/models.py +391 -0
- flimkit/GPU/__init__.py +85 -0
- flimkit/GPU/_base.py +391 -0
- flimkit/GPU/cuda.py +10 -0
- flimkit/GPU/mlx_backend.py +381 -0
- flimkit/GPU/mps.py +10 -0
- flimkit/GPU/rocm.py +10 -0
- flimkit/GPU/torch_backend.py +385 -0
- flimkit/UI/app_state.py +10 -0
- flimkit/UI/controller.py +139 -0
- flimkit/UI/expert_settings.py +248 -0
- flimkit/UI/fit_help.py +206 -0
- flimkit/UI/fov_preview.py +1085 -0
- flimkit/UI/gui.py +3919 -0
- flimkit/UI/icon.icns +0 -0
- flimkit/UI/icon.ico +0 -0
- flimkit/UI/icon.png +0 -0
- flimkit/UI/irf_widget.py +103 -0
- flimkit/UI/mode_controller.py +118 -0
- flimkit/UI/modes/__init__.py +0 -0
- flimkit/UI/modes/base.py +3 -0
- flimkit/UI/modes/batch_mode.py +312 -0
- flimkit/UI/modes/fov_mode.py +164 -0
- flimkit/UI/modes/irf_mode.py +80 -0
- flimkit/UI/modes/phasor_mode.py +131 -0
- flimkit/UI/modes/stitch_mode.py +254 -0
- flimkit/UI/phasor_panel.py +1087 -0
- flimkit/UI/progress_window.py +113 -0
- flimkit/UI/project_panel.py +262 -0
- flimkit/UI/results_panel.py +332 -0
- flimkit/UI/roi_tools.py +794 -0
- flimkit/UI/utils.py +217 -0
- flimkit/__init__.py +0 -0
- flimkit/_version.py +41 -0
- flimkit/cli.py +120 -0
- flimkit/configs.py +148 -0
- flimkit/dialogs.py +46 -0
- flimkit/formats/BH/__init__.py +0 -0
- flimkit/formats/BH/reader.py +296 -0
- flimkit/formats/BH/writer.py +86 -0
- flimkit/formats/ISS/__init__.py +0 -0
- flimkit/formats/ISS/fdflim.py +86 -0
- flimkit/formats/ISS/image.py +114 -0
- flimkit/formats/ISS/reader.py +223 -0
- flimkit/formats/PS/__init__.py +0 -0
- flimkit/formats/PS/reader.py +202 -0
- flimkit/formats/PTU/__init__.py +0 -0
- flimkit/formats/PTU/decode.py +27 -0
- flimkit/formats/PTU/phu.py +85 -0
- flimkit/formats/PTU/reader.py +235 -0
- flimkit/formats/PTU/series.py +258 -0
- flimkit/formats/PTU/stitch.py +1182 -0
- flimkit/formats/PTU/tools.py +94 -0
- flimkit/formats/__init__.py +2 -0
- flimkit/formats/flim_file.py +232 -0
- flimkit/formats/phasor.py +132 -0
- flimkit/formats/signal.py +170 -0
- flimkit/image/tools.py +124 -0
- flimkit/interactive.py +1857 -0
- flimkit/mpl_backend.py +22 -0
- flimkit/phasor/__init__.py +40 -0
- flimkit/phasor/filters.py +127 -0
- flimkit/phasor/fret.py +654 -0
- flimkit/phasor/interactive.py +556 -0
- flimkit/phasor/peaks.py +186 -0
- flimkit/phasor/signal.py +90 -0
- flimkit/phasor_launcher.py +314 -0
- flimkit/plugins/__init__.py +137 -0
- flimkit/plugins/bindings.py +116 -0
- flimkit/plugins/builtin/__init__.py +3 -0
- flimkit/plugins/builtin/core_tools.py +28 -0
- flimkit/plugins/loader.py +371 -0
- flimkit/plugins/registry.py +406 -0
- flimkit/project.py +197 -0
- flimkit/synth.py +145 -0
- flimkit/utils/__init__.py +0 -0
- flimkit/utils/batch_fit.py +301 -0
- flimkit/utils/config_manager.py +119 -0
- flimkit/utils/config_snapshot.py +30 -0
- flimkit/utils/crash_handler.py +183 -0
- flimkit/utils/display.py +197 -0
- flimkit/utils/enhanced_outputs.py +345 -0
- flimkit/utils/fancy.py +103 -0
- flimkit/utils/lifetime_image.py +243 -0
- flimkit/utils/misc.py +111 -0
- flimkit/utils/plotting.py +190 -0
- flimkit/utils/roi.py +370 -0
- flimkit/utils/session.py +51 -0
- flimkit/utils/update_check.py +198 -0
- flimkit/utils/xlsx_tools.py +97 -0
- flimkit/utils/xml_utils.py +219 -0
- flimkit-0.12.0.dist-info/METADATA +356 -0
- flimkit-0.12.0.dist-info/RECORD +104 -0
- flimkit-0.12.0.dist-info/WHEEL +5 -0
- flimkit-0.12.0.dist-info/entry_points.txt +2 -0
- flimkit-0.12.0.dist-info/licenses/LICENSE.md +11 -0
- 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
|