flimkit 0.13.5__tar.gz → 0.13.6__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 (111) hide show
  1. {flimkit-0.13.5 → flimkit-0.13.6}/PKG-INFO +1 -1
  2. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/FLIM/batch.py +18 -2
  3. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/fov_preview.py +98 -10
  4. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/gui.py +340 -26
  5. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/irf_widget.py +15 -0
  6. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/modes/batch_mode.py +1 -34
  7. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/modes/fov_mode.py +2 -3
  8. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/modes/stitch_mode.py +2 -30
  9. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/phasor_panel.py +135 -148
  10. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/project_panel.py +7 -0
  11. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/roi_tools.py +34 -34
  12. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/_version.py +3 -2
  13. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/configs.py +21 -1
  14. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/PTU/reader.py +11 -10
  15. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/interactive.py +19 -3
  16. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/phasor_launcher.py +45 -9
  17. flimkit-0.13.6/flimkit/utils/apply_settings.py +89 -0
  18. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/display.py +27 -3
  19. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit.egg-info/PKG-INFO +1 -1
  20. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit.egg-info/SOURCES.txt +1 -0
  21. {flimkit-0.13.5 → flimkit-0.13.6}/LICENSE.md +0 -0
  22. {flimkit-0.13.5 → flimkit-0.13.6}/README.md +0 -0
  23. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/FLIM/__init__.py +0 -0
  24. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/FLIM/assemble.py +0 -0
  25. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/FLIM/bg_tools.py +0 -0
  26. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/FLIM/fit_tools.py +0 -0
  27. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/FLIM/fitters.py +0 -0
  28. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/FLIM/irf_tools.py +0 -0
  29. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/FLIM/models.py +0 -0
  30. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/GPU/__init__.py +0 -0
  31. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/GPU/_base.py +0 -0
  32. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/GPU/cuda.py +0 -0
  33. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/GPU/mlx_backend.py +0 -0
  34. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/GPU/mps.py +0 -0
  35. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/GPU/rocm.py +0 -0
  36. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/GPU/torch_backend.py +0 -0
  37. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/app_state.py +0 -0
  38. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/controller.py +0 -0
  39. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/expert_settings.py +0 -0
  40. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/fit_help.py +0 -0
  41. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/icon.icns +0 -0
  42. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/icon.ico +0 -0
  43. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/icon.png +0 -0
  44. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/mode_controller.py +0 -0
  45. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/modes/__init__.py +0 -0
  46. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/modes/base.py +0 -0
  47. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/modes/irf_mode.py +0 -0
  48. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/modes/phasor_mode.py +0 -0
  49. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/progress_window.py +0 -0
  50. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/results_panel.py +0 -0
  51. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/UI/utils.py +0 -0
  52. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/__init__.py +0 -0
  53. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/cli.py +0 -0
  54. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/dialogs.py +0 -0
  55. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/BH/__init__.py +0 -0
  56. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/BH/reader.py +0 -0
  57. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/BH/writer.py +0 -0
  58. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/ISS/__init__.py +0 -0
  59. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/ISS/fdflim.py +0 -0
  60. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/ISS/image.py +0 -0
  61. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/ISS/reader.py +0 -0
  62. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/PS/__init__.py +0 -0
  63. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/PS/reader.py +0 -0
  64. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/PTU/__init__.py +0 -0
  65. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/PTU/decode.py +0 -0
  66. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/PTU/phu.py +0 -0
  67. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/PTU/series.py +0 -0
  68. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/PTU/stitch.py +0 -0
  69. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/PTU/tools.py +0 -0
  70. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/__init__.py +0 -0
  71. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/flim_file.py +0 -0
  72. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/phasor.py +0 -0
  73. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/formats/signal.py +0 -0
  74. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/image/tools.py +0 -0
  75. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/mpl_backend.py +0 -0
  76. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/phasor/__init__.py +0 -0
  77. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/phasor/filters.py +0 -0
  78. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/phasor/fret.py +0 -0
  79. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/phasor/interactive.py +0 -0
  80. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/phasor/peaks.py +0 -0
  81. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/phasor/signal.py +0 -0
  82. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/plugins/__init__.py +0 -0
  83. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/plugins/bindings.py +0 -0
  84. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/plugins/builtin/__init__.py +0 -0
  85. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/plugins/builtin/core_tools.py +0 -0
  86. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/plugins/loader.py +0 -0
  87. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/plugins/registry.py +0 -0
  88. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/project.py +0 -0
  89. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/synth.py +0 -0
  90. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/__init__.py +0 -0
  91. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/batch_fit.py +0 -0
  92. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/config_manager.py +0 -0
  93. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/config_snapshot.py +0 -0
  94. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/crash_handler.py +0 -0
  95. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/enhanced_outputs.py +0 -0
  96. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/fancy.py +0 -0
  97. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/lifetime_image.py +0 -0
  98. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/misc.py +0 -0
  99. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/ome_zarr.py +0 -0
  100. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/plotting.py +0 -0
  101. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/roi.py +0 -0
  102. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/session.py +0 -0
  103. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/update_check.py +0 -0
  104. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/xlsx_tools.py +0 -0
  105. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit/utils/xml_utils.py +0 -0
  106. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit.egg-info/dependency_links.txt +0 -0
  107. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit.egg-info/entry_points.txt +0 -0
  108. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit.egg-info/requires.txt +0 -0
  109. {flimkit-0.13.5 → flimkit-0.13.6}/flimkit.egg-info/top_level.txt +0 -0
  110. {flimkit-0.13.5 → flimkit-0.13.6}/pyproject.toml +0 -0
  111. {flimkit-0.13.5 → flimkit-0.13.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flimkit
3
- Version: 0.13.5
3
+ Version: 0.13.6
4
4
  Summary: Fluorescence lifetime imaging analysis: reading, fitting, phasors and stitching
5
5
  Author-email: Alex Hunt <alexander.hunt@ed.ac.uk>
6
6
  License-Expression: MIT
@@ -20,6 +20,18 @@ from ..utils.batch_fit import (
20
20
  save_tile_lifetime_txt, save_map_stacks, plot_metric_summary,
21
21
  )
22
22
 
23
+ def _window_kwargs(args):
24
+ from ..interactive import parse_exclude_ns
25
+ return dict(
26
+ irf_shift_bins=getattr(args, 'irf_shift_bins', 2),
27
+ fit_start_ns=getattr(args, 'fit_start_ns', None),
28
+ fit_end_ns=getattr(args, 'fit_end_ns', None),
29
+ exclude_ns=parse_exclude_ns(getattr(args, 'exclude_ns', None)),
30
+ )
31
+
32
+ def _binning(args):
33
+ return max(1, int(getattr(args, 'binning', 1) or 1))
34
+
23
35
  def _save_4d_stacks(frame_positions, group_dir, group_label):
24
36
  sorted_t = sorted(frame_positions.keys())
25
37
  all_s = sorted({s for pos in frame_positions.values() for s in pos})
@@ -122,6 +134,7 @@ def fit_timelapse(ptu_dir, output_dir, args,
122
134
  polish=not getattr(args, 'no_polish', False),
123
135
  cost_function=getattr(args, 'cost_function', 'poisson'),
124
136
  sigma_max=sigma_max,
137
+ **_window_kwargs(args),
125
138
  )
126
139
  print(f' Reference fit: {time.time() - t0:.1f} s')
127
140
  taus_ns = global_summary.get('taus_ns', global_popt[:args.nexp] * 1e9)
@@ -163,7 +176,7 @@ def fit_timelapse(ptu_dir, output_dir, args,
163
176
  print(f'\n t={t} s={s}: {ptu_path.name}')
164
177
  t_start = time.time()
165
178
  ptu = FLIMFile(str(ptu_path), verbose=False)
166
- pixel_stack = ptu.raw_pixel_stack(channel=channel)
179
+ pixel_stack = ptu.raw_pixel_stack(channel=channel, binning=_binning(args))
167
180
  if pixel_stack.shape[2] != n_bins:
168
181
  nb = pixel_stack.shape[2]
169
182
  if nb > n_bins:
@@ -229,6 +242,7 @@ def fit_timelapse(ptu_dir, output_dir, args,
229
242
  polish=not getattr(args, 'no_polish', False),
230
243
  cost_function=getattr(args, 'cost_function', 'poisson'),
231
244
  sigma_max=sigma_max,
245
+ **_window_kwargs(args),
232
246
  )
233
247
  plot_summed(
234
248
  tile_decay, tile_summary, ptu, None,
@@ -430,6 +444,7 @@ def fit_zstack(ptu_dir, output_dir, args,
430
444
  polish=not getattr(args, 'no_polish', False),
431
445
  cost_function=getattr(args, 'cost_function', 'poisson'),
432
446
  sigma_max=sigma_max,
447
+ **_window_kwargs(args),
433
448
  )
434
449
  print(f' Reference fit: {time.time() - t0:.1f} s')
435
450
  taus_ns = global_summary.get('taus_ns', global_popt[:args.nexp] * 1e9)
@@ -489,7 +504,7 @@ def fit_zstack(ptu_dir, output_dir, args,
489
504
  slice_dir = group_dir / f'z{z:04d}'
490
505
  slice_dir.mkdir(exist_ok=True)
491
506
  ptu = FLIMFile(str(ptu_path), verbose=False)
492
- pixel_stack = ptu.raw_pixel_stack(channel=channel)
507
+ pixel_stack = ptu.raw_pixel_stack(channel=channel, binning=_binning(args))
493
508
  if pixel_stack.shape[2] != n_bins:
494
509
  nb = pixel_stack.shape[2]
495
510
  if nb > n_bins:
@@ -555,6 +570,7 @@ def fit_zstack(ptu_dir, output_dir, args,
555
570
  polish=not getattr(args, 'no_polish', False),
556
571
  cost_function=getattr(args, 'cost_function', 'poisson'),
557
572
  sigma_max=sigma_max,
573
+ **_window_kwargs(args),
558
574
  )
559
575
  plot_summed(
560
576
  tile_decay, tile_summary, ptu, None,
@@ -86,7 +86,7 @@ class FOVPreviewPanel:
86
86
  state='readonly', width=10)
87
87
  self._cmap_combo.grid(row=1, column=4, sticky='w', padx=2)
88
88
  self._cmap_combo['values'] = list(display.COLORMAPS.keys())
89
- ttk.Button(ctrl_frame, text='Update', width=8, command=self._update_flim_display).grid(row=1, column=5, sticky='w', padx=2)
89
+ ttk.Button(ctrl_frame, text='Update', width=8, command=self._on_update_display).grid(row=1, column=5, sticky='w', padx=2)
90
90
  self._bv_show_decay = tk.BooleanVar(value=True)
91
91
  ttk.Checkbutton(ctrl_frame, text='Show Decay Plot',
92
92
  variable=self._bv_show_decay,
@@ -108,6 +108,22 @@ class FOVPreviewPanel:
108
108
  value='amplitude', command=self._on_weighting_changed).pack(side='left')
109
109
  ttk.Radiobutton(wt_frame, text='Intensity', variable=self._sv_tau_weighting,
110
110
  value='intensity', command=self._on_weighting_changed).pack(side='left')
111
+ int_frame = ttk.Frame(ctrl_frame)
112
+ int_frame.grid(row=4, column=0, columnspan=6, sticky='w', pady=(4, 0))
113
+ ttk.Label(int_frame, text='Intensity:').pack(side='left', padx=(0, 4))
114
+ ttk.Label(int_frame, text='Min').pack(side='left')
115
+ self._sv_int_min = tk.StringVar()
116
+ ttk.Entry(int_frame, textvariable=self._sv_int_min, width=7).pack(side='left', padx=2)
117
+ ttk.Label(int_frame, text='Max').pack(side='left', padx=(6, 0))
118
+ self._sv_int_max = tk.StringVar()
119
+ ttk.Entry(int_frame, textvariable=self._sv_int_max, width=7).pack(side='left', padx=2)
120
+ ttk.Button(int_frame, text='Auto', width=6,
121
+ command=self._auto_intensity_scale).pack(side='left', padx=2)
122
+ self._sv_int_cmap = tk.StringVar(value='inferno')
123
+ int_combo = ttk.Combobox(int_frame, textvariable=self._sv_int_cmap, state='readonly',
124
+ width=9, values=display.INTENSITY_COLORMAPS)
125
+ int_combo.pack(side='left', padx=(6, 2))
126
+ int_combo.bind('<<ComboboxSelected>>', lambda _e: self._on_update_display())
111
127
  self._ptu_path = None
112
128
  self._lifetime_map = None
113
129
  self._pixel_maps = None
@@ -119,6 +135,7 @@ class FOVPreviewPanel:
119
135
  'gamma': 1.0,
120
136
  'cmap': 'viridis',
121
137
  }
138
+ self._int_display = {'vmin': None, 'vmax': None, 'cmap': 'inferno'}
122
139
  self._n_exp = 1
123
140
  self._irf_prompt = None
124
141
  self._roi_manager = RoiManager()
@@ -140,6 +157,7 @@ class FOVPreviewPanel:
140
157
  if not _keep_zstack:
141
158
  self._hide_zstack()
142
159
  self._lifetime_map = None
160
+ self._intensity_map = None
143
161
  self._pixel_maps = None
144
162
  self._cached_resid_data = None
145
163
  if not ptu_path or not Path(ptu_path).exists():
@@ -158,8 +176,8 @@ class FOVPreviewPanel:
158
176
  if is_image:
159
177
  stack = ptu.pixel_stack(channel=None, binning=1)
160
178
  intensity = stack.sum(axis=2)
161
- intensity_clipped = np.clip(intensity, 0, np.percentile(intensity, 99))
162
- self._ax_img.imshow(intensity_clipped, cmap='inferno', origin='upper')
179
+ self._intensity_map = intensity
180
+ self._draw_intensity(self._ax_img, intensity)
163
181
  else:
164
182
  intensity = None
165
183
  self._ax_img.text(0.5, 0.5, 'Point measurement\nno image to reconstruct',
@@ -187,6 +205,9 @@ class FOVPreviewPanel:
187
205
  self._ax_resid.grid(True, alpha=0.3)
188
206
  self._cached_resid_data = None
189
207
  self._redraw_region_overlays()
208
+ if is_image:
209
+ self._sync_intensity_controls()
210
+ self._ctrl_frame.grid()
190
211
  self._canvas_mpl.draw_idle()
191
212
  n_photons = int(decay.sum())
192
213
  if is_image:
@@ -275,8 +296,7 @@ class FOVPreviewPanel:
275
296
  taus_fit = global_summary.get('taus_ns', [])
276
297
  model = global_summary.get('model')
277
298
  self._ax_img.clear()
278
- intensity_clipped = np.clip(intensity, 0, np.percentile(intensity, 99))
279
- self._ax_img.imshow(intensity_clipped, cmap='inferno', origin='upper')
299
+ self._draw_intensity(self._ax_img, intensity)
280
300
  self._ax_img.set_title('Intensity', fontsize=9, fontweight='bold', color='white')
281
301
  self._strip_image_axes(self._ax_img)
282
302
  self._ax_flim.clear()
@@ -492,8 +512,7 @@ class FOVPreviewPanel:
492
512
  return
493
513
  intensity = tifffile.imread(str(intensity_files[0]))
494
514
  self._ax_img.clear()
495
- intensity_clipped = np.clip(intensity, 0, np.percentile(intensity, 99))
496
- self._ax_img.imshow(intensity_clipped, cmap='inferno', origin='upper')
515
+ self._draw_intensity(self._ax_img, intensity)
497
516
  self._ax_img.set_title('Stitched ROI', fontsize=9, fontweight='bold', color='white')
498
517
  self._strip_image_axes(self._ax_img)
499
518
  lifetime_data = None
@@ -595,6 +614,75 @@ class FOVPreviewPanel:
595
614
  pass
596
615
  self._lifetime_map = lifetime_map
597
616
  self._update_flim_display()
617
+ def _intensity_limits(self, intensity):
618
+ import numpy as np
619
+ lo = self._int_display.get('vmin')
620
+ hi = self._int_display.get('vmax')
621
+ if lo is None:
622
+ lo = 0.0
623
+ if hi is None:
624
+ finite = np.asarray(intensity)[np.isfinite(intensity)]
625
+ hi = float(np.percentile(finite, 99)) if finite.size > 0 else 1.0
626
+ if hi <= lo:
627
+ hi = lo + 1.0
628
+ return lo, hi
629
+
630
+ def _draw_intensity(self, ax, intensity):
631
+ lo, hi = self._intensity_limits(intensity)
632
+ cmap = display.get_colormap(self._int_display.get('cmap', 'inferno'))
633
+ return ax.imshow(intensity, cmap=cmap, origin='upper', vmin=lo, vmax=hi)
634
+
635
+ def _read_intensity_controls(self):
636
+ def num(var):
637
+ text = var.get().strip()
638
+ if text == '':
639
+ return None
640
+ try:
641
+ return float(text)
642
+ except ValueError:
643
+ return None
644
+ self._int_display = {
645
+ 'vmin': num(self._sv_int_min),
646
+ 'vmax': num(self._sv_int_max),
647
+ 'cmap': self._sv_int_cmap.get() or 'inferno',
648
+ }
649
+
650
+ def _sync_intensity_controls(self):
651
+ cs = self._int_display
652
+ self._sv_int_min.set('' if cs.get('vmin') is None else f"{cs['vmin']:g}")
653
+ self._sv_int_max.set('' if cs.get('vmax') is None else f"{cs['vmax']:g}")
654
+ self._sv_int_cmap.set(cs.get('cmap', 'inferno'))
655
+
656
+ def _sync_flim_controls(self):
657
+ cs = self._flim_color_scale
658
+ self._sv_tau_min.set('' if cs.get('vmin') is None else f"{cs['vmin']:.2f}")
659
+ self._sv_tau_max.set('' if cs.get('vmax') is None else f"{cs['vmax']:.2f}")
660
+ self._sv_gamma.set(str(cs.get('gamma', 1.0)))
661
+ self._sv_cmap.set(cs.get('cmap', 'viridis'))
662
+
663
+ def _update_intensity_display(self):
664
+ if self._intensity_map is None or not self._ax_img.get_visible():
665
+ return
666
+ title = self._ax_img.get_title() or 'Intensity'
667
+ self._ax_img.clear()
668
+ self._draw_intensity(self._ax_img, self._intensity_map)
669
+ self._ax_img.set_title(title, fontsize=9, fontweight='bold', color='white')
670
+ self._strip_image_axes(self._ax_img)
671
+ self._redraw_region_overlays()
672
+ self._canvas_mpl.draw_idle()
673
+
674
+ def _auto_intensity_scale(self):
675
+ self._sv_int_min.set('')
676
+ self._sv_int_max.set('')
677
+ self._on_update_display()
678
+
679
+ def _on_update_display(self):
680
+ self._read_intensity_controls()
681
+ self._update_intensity_display()
682
+ self._update_flim_display()
683
+ if self._lifetime_map is None:
684
+ self._save_color_scale_update()
685
+
598
686
  def _auto_detect_scale(self):
599
687
  import numpy as np
600
688
  if self._lifetime_map is None:
@@ -677,6 +765,7 @@ class FOVPreviewPanel:
677
765
  session_data = {key: existing_data[key].item() if existing_data[key].ndim == 0 else existing_data[key]
678
766
  for key in existing_data.files}
679
767
  session_data['fov_color_scale'] = json.dumps(self._flim_color_scale)
768
+ session_data['fov_intensity_scale'] = json.dumps(self._int_display)
680
769
  np.savez_compressed(session_file, **session_data)
681
770
  print(f"[Color Scale] ✓ Saved to {session_file.name}")
682
771
  except Exception as e:
@@ -706,6 +795,7 @@ class FOVPreviewPanel:
706
795
  if self._intensity_map is not None:
707
796
  session_data['fov_intensity_map'] = self._intensity_map
708
797
  session_data['fov_color_scale'] = json.dumps(self._flim_color_scale)
798
+ session_data['fov_intensity_scale'] = json.dumps(self._int_display)
709
799
  session_data['fov_n_exp'] = self._n_exp
710
800
  if self._ptu_path:
711
801
  session_data['fov_ptu_path'] = self._ptu_path
@@ -927,9 +1017,7 @@ class FOVPreviewPanel:
927
1017
  _ax.set_facecolor('black')
928
1018
  if self._ax_img.get_visible() and self._intensity_map is not None:
929
1019
  import numpy as np
930
- intensity_clipped = np.clip(self._intensity_map, 0,
931
- np.percentile(self._intensity_map, 99))
932
- self._ax_img.imshow(intensity_clipped, cmap='inferno', origin='upper')
1020
+ self._draw_intensity(self._ax_img, self._intensity_map)
933
1021
  self._ax_img.set_title(img_title, fontsize=9, fontweight='bold', color='white')
934
1022
  self._strip_image_axes(self._ax_img)
935
1023
  elif self._ax_img.get_visible():