small-fish-gui 2.0.1__py3-none-any.whl → 2.0.3__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 (51) hide show
  1. small_fish_gui/__init__.py +9 -3
  2. small_fish_gui/batch/integrity.py +2 -2
  3. small_fish_gui/batch/pipeline.py +46 -11
  4. small_fish_gui/batch/prompt.py +102 -41
  5. small_fish_gui/batch/update.py +26 -13
  6. small_fish_gui/batch/utils.py +1 -1
  7. small_fish_gui/gui/__init__.py +1 -0
  8. small_fish_gui/gui/_napari_widgets.py +418 -6
  9. small_fish_gui/gui/layout.py +332 -112
  10. small_fish_gui/gui/napari_visualiser.py +107 -22
  11. small_fish_gui/gui/prompts.py +161 -48
  12. small_fish_gui/gui/testing.ipynb +231 -24
  13. small_fish_gui/gui/tooltips.py +7 -1
  14. small_fish_gui/hints.py +23 -7
  15. small_fish_gui/interface/__init__.py +7 -1
  16. small_fish_gui/interface/default_settings.py +118 -0
  17. small_fish_gui/interface/image.py +43 -11
  18. small_fish_gui/interface/settings.json +50 -0
  19. small_fish_gui/interface/testing.ipynb +4354 -0
  20. small_fish_gui/interface/user_settings.py +96 -0
  21. small_fish_gui/main_menu.py +13 -1
  22. small_fish_gui/pipeline/{_signaltonoise.py → _bigfish_wrapers.py} +59 -7
  23. small_fish_gui/pipeline/_colocalisation.py +23 -24
  24. small_fish_gui/pipeline/_preprocess.py +46 -32
  25. small_fish_gui/pipeline/actions.py +48 -5
  26. small_fish_gui/pipeline/detection.py +71 -141
  27. small_fish_gui/pipeline/segmentation.py +360 -268
  28. small_fish_gui/pipeline/spots.py +3 -3
  29. small_fish_gui/pipeline/utils.py +5 -1
  30. small_fish_gui/README.md → small_fish_gui-2.0.3.dist-info/METADATA +50 -2
  31. small_fish_gui-2.0.3.dist-info/RECORD +46 -0
  32. {small_fish_gui-2.0.1.dist-info → small_fish_gui-2.0.3.dist-info}/WHEEL +1 -1
  33. small_fish_gui/.github/workflows/python-publish.yml +0 -39
  34. small_fish_gui/LICENSE +0 -24
  35. small_fish_gui/batch/values.txt +0 -65
  36. small_fish_gui/default_values.py +0 -51
  37. small_fish_gui/gui/screenshot/general_help_screenshot.png +0 -0
  38. small_fish_gui/gui/screenshot/mapping_help_screenshot.png +0 -0
  39. small_fish_gui/gui/screenshot/segmentation_help_screenshot.png +0 -0
  40. small_fish_gui/illustrations/DetectionVitrine_filtre.png +0 -0
  41. small_fish_gui/illustrations/DetectionVitrine_signal.png +0 -0
  42. small_fish_gui/illustrations/FocciVitrine.png +0 -0
  43. small_fish_gui/illustrations/FocciVitrine_no_spots.png +0 -0
  44. small_fish_gui/illustrations/Segmentation2D.png +0 -0
  45. small_fish_gui/illustrations/Segmentation2D_with_labels.png +0 -0
  46. small_fish_gui/logo.png +0 -0
  47. small_fish_gui/pipeline/testing.ipynb +0 -3636
  48. small_fish_gui/requirements.txt +0 -19
  49. small_fish_gui-2.0.1.dist-info/METADATA +0 -75
  50. small_fish_gui-2.0.1.dist-info/RECORD +0 -59
  51. {small_fish_gui-2.0.1.dist-info → small_fish_gui-2.0.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,18 +1,18 @@
1
1
  """
2
- Contains code to handle detection as well as bigfish wrappers related to spot detection.
2
+ Submodule to handle detection as well as bigfish wrappers related to spot detection.
3
3
  """
4
4
  from ..hints import pipeline_parameters
5
5
 
6
6
  from ._preprocess import ParameterInputError
7
7
  from ._preprocess import check_integrity, convert_parameters_types
8
8
 
9
- from ..gui.napari_visualiser import correct_spots, threshold_selection
9
+ from ..gui.napari_visualiser import correct_spots, interactive_detection
10
10
  from ..gui import add_default_loading
11
11
  from ..gui import detection_parameters_promt
12
12
 
13
13
  from ..interface import get_voxel_size
14
14
  from ..utils import compute_anisotropy_coef
15
- from ._signaltonoise import compute_snr_spots
15
+ from ._bigfish_wrapers import compute_snr_spots, _apply_log_filter, _local_maxima_mask
16
16
 
17
17
  from magicgui import magicgui
18
18
 
@@ -20,7 +20,7 @@ from types import GeneratorType
20
20
  from napari.types import LayerDataTuple
21
21
 
22
22
  import numpy as np
23
- from numpy import NaN
23
+ from numpy import nan
24
24
  import pandas as pd
25
25
  import FreeSimpleGUI as sg
26
26
  import bigfish.detection as detection
@@ -31,8 +31,6 @@ from bigfish.detection.spot_detection import get_object_radius_pixel
31
31
  from skimage.measure import regionprops
32
32
  from scipy.ndimage import binary_dilation
33
33
 
34
-
35
-
36
34
  def compute_auto_threshold(images, voxel_size=None, spot_radius=None, log_kernel_size=None, minimum_distance=None, im_number= 15, crop_zstack= None) :
37
35
  """
38
36
  Compute bigfish auto threshold efficiently for list of images. In case on large set of images user can set im_number to only consider a random subset of image for threshold computation.
@@ -214,8 +212,13 @@ def initiate_detection(user_parameters : pipeline_parameters, map_, shape) :
214
212
 
215
213
  #Attempt to read voxel size from metadata
216
214
  voxel_size = get_voxel_size(user_parameters['image_path'])
217
- if voxel_size is None or not user_parameters.get('voxel_size') is None:
218
- pass
215
+ if voxel_size is None :
216
+ if not user_parameters.get('voxel_size') is None:
217
+ pass
218
+ else :
219
+ detection_parameters['voxel_size_z'] = None
220
+ detection_parameters['voxel_size_y'] = None
221
+ detection_parameters['voxel_size_x'] = None
219
222
  else :
220
223
  detection_parameters['voxel_size'] = [round(v) if isinstance(v, (float,int)) else None for v in voxel_size]
221
224
  detection_parameters['voxel_size_z'] = detection_parameters['voxel_size'][0] if isinstance(detection_parameters['voxel_size'][0], (float,int)) else None
@@ -223,8 +226,8 @@ def initiate_detection(user_parameters : pipeline_parameters, map_, shape) :
223
226
  detection_parameters['voxel_size_x'] = detection_parameters['voxel_size'][2] if isinstance(detection_parameters['voxel_size'][2], (float,int)) else None
224
227
 
225
228
  #Setting default spot size to 1.5 voxel
226
- if detection_parameters.get('spot_size') is None :
227
- detection_parameters['spot_size_z'] = round(detection_parameters['voxel_size_z']*1.5) if isinstance(detection_parameters['voxel_size_z'], (float,int)) else None
229
+ if detection_parameters.get('spot_size') is None and not detection_parameters.get('voxel_size') is None:
230
+ detection_parameters['spot_size_z'] = round(detection_parameters['voxel_size_z']*1.5) if isinstance(detection_parameters.get('voxel_size_z'), (float,int)) else None
228
231
  detection_parameters['spot_size_y'] = round(detection_parameters['voxel_size_y']*1.5) if isinstance(detection_parameters['voxel_size_y'],(float,int)) else None
229
232
  detection_parameters['spot_size_x'] = round(detection_parameters['voxel_size_x']*1.5) if isinstance(detection_parameters['voxel_size_x'],(float,int)) else None
230
233
 
@@ -258,7 +261,7 @@ def initiate_detection(user_parameters : pipeline_parameters, map_, shape) :
258
261
  return user_parameters
259
262
 
260
263
  @add_default_loading
261
- def _launch_detection(image, image_input_values: dict) :
264
+ def detect_spots(image, image_input_values: dict) :
262
265
 
263
266
  """
264
267
  Performs spots detection
@@ -267,7 +270,7 @@ def _launch_detection(image, image_input_values: dict) :
267
270
  #Extract parameters
268
271
  voxel_size = image_input_values['voxel_size']
269
272
  threshold = image_input_values.get('threshold')
270
- threshold_penalty = image_input_values.setdefault('threshold penalty', 1)
273
+ threshold_penalty = image_input_values.setdefault('threshold_penalty', 1)
271
274
  spot_size = image_input_values.get('spot_size')
272
275
  log_kernel_size = image_input_values.get('log_kernel_size')
273
276
  minimum_distance = image_input_values.get('minimum_distance')
@@ -275,7 +278,7 @@ def _launch_detection(image, image_input_values: dict) :
275
278
 
276
279
  if type(threshold) == type(None) :
277
280
  threshold = threshold_penalty * compute_auto_threshold(image, voxel_size=voxel_size, spot_radius=spot_size, log_kernel_size=log_kernel_size, minimum_distance=minimum_distance)
278
- threshold = max(threshold,15) # Force threshold to be at least 15 to match napari widget and to not have too many spots for weak configs
281
+ threshold = max(threshold,1)
279
282
 
280
283
  filtered_image = _apply_log_filter(
281
284
  image=image,
@@ -291,29 +294,11 @@ def _launch_detection(image, image_input_values: dict) :
291
294
  minimum_distance=minimum_distance
292
295
  )
293
296
 
294
- if threshold_user_selection :
295
-
296
- threshold_slider = _create_threshold_slider(
297
- logfiltered_image=filtered_image,
298
- local_maxima=local_maxima,
299
- default=threshold,
300
- min_value=filtered_image[local_maxima].min(),
301
- max_value=filtered_image[local_maxima].max(),
302
- voxel_size=voxel_size
303
- )
304
-
305
- spots, threshold = threshold_selection(
306
- image=image,
307
- filtered_image=filtered_image,
308
- threshold_slider=threshold_slider,
309
- voxel_size=voxel_size
310
- )
311
- else :
312
- spots = detection.spots_thresholding(
313
- image=filtered_image,
314
- mask_local_max=local_maxima,
315
- threshold=threshold
316
- )[0]
297
+ spots = detection.spots_thresholding(
298
+ image=filtered_image,
299
+ mask_local_max=local_maxima,
300
+ threshold=threshold
301
+ )[0]
317
302
 
318
303
  return spots, threshold
319
304
 
@@ -340,7 +325,16 @@ def launch_dense_region_deconvolution(image, spots, image_input_values: dict,) :
340
325
  deconvolution_kernel = image_input_values.get('deconvolution_kernel')
341
326
  dim = image_input_values['dim']
342
327
 
343
- spots, dense_regions, ref_spot = detection.decompose_dense(image=image, spots=spots, voxel_size=voxel_size, spot_radius=spot_size, kernel_size=deconvolution_kernel, alpha=alpha, beta=beta, gamma=gamma)
328
+ spots, dense_regions, ref_spot = detection.decompose_dense(
329
+ image=image,
330
+ spots=spots,
331
+ voxel_size=voxel_size,
332
+ spot_radius=spot_size,
333
+ kernel_size=deconvolution_kernel,
334
+ alpha=alpha,
335
+ beta=beta,
336
+ gamma=gamma
337
+ )
344
338
  del dense_regions, ref_spot
345
339
 
346
340
  return spots
@@ -356,7 +350,7 @@ def launch_post_detection(image, spots, image_input_values: dict,) :
356
350
  fov_res['spot_number'] = len(spots)
357
351
  snr_res = compute_snr_spots(image, spots, voxel_size, spot_size)
358
352
  if len(spots) == 0 :
359
- fov_res['spotsSignal_median'], fov_res['spotsSignal_mean'], fov_res['spotsSignal_std'] = np.NaN, np.NaN, np.NaN
353
+ fov_res['spotsSignal_median'], fov_res['spotsSignal_mean'], fov_res['spotsSignal_std'] = np.nan, np.nan, np.nan
360
354
  else :
361
355
  if dim == 3 :
362
356
  Z,Y,X = list(zip(*spots))
@@ -381,9 +375,9 @@ def _compute_cell_snr(image: np.ndarray, bbox, spots, voxel_size, spot_size) :
381
375
 
382
376
  if len(spots) == 0 :
383
377
  res = {
384
- 'snr_mean' : np.NaN,
385
- 'snr_median' : np.NaN,
386
- 'snr_std' : np.NaN,
378
+ 'snr_mean' : np.nan,
379
+ 'snr_median' : np.nan,
380
+ 'snr_std' : np.nan,
387
381
  }
388
382
 
389
383
  return res
@@ -541,8 +535,8 @@ def launch_cell_extraction(
541
535
  else : raise AssertionError("Impossible number of dim for foci : ", len(foci_index))
542
536
  foci_in_nuc_number = nuc_mask[tuple(foci_index)].astype(bool).sum()
543
537
  else :
544
- foci_number = np.NaN
545
- foci_in_nuc_number = np.NaN
538
+ foci_number = np.nan
539
+ foci_in_nuc_number = np.nan
546
540
 
547
541
  #Signal to noise
548
542
  snr_dict = _compute_cell_snr(
@@ -628,11 +622,34 @@ def launch_detection(
628
622
  do_dense_region_deconvolution = user_parameters['do_dense_regions_deconvolution']
629
623
  do_clustering = user_parameters['do_cluster_computation']
630
624
 
631
- spots, threshold = _launch_detection(image, user_parameters, hide_loading = hide_loading)
632
-
633
- if do_dense_region_deconvolution :
634
- spots = launch_dense_region_deconvolution(image, spots, user_parameters, hide_loading = hide_loading)
635
-
625
+ if user_parameters['show_interactive_threshold_selector'] :
626
+ spots, image, updated_parameters = interactive_detection(
627
+ image=image,
628
+ voxel_size=user_parameters['voxel_size'],
629
+ interactive_threshold=True,
630
+ dense_region_deconvolution=user_parameters['do_dense_regions_deconvolution'],
631
+ do_background_removal= False,
632
+ default_threshold = user_parameters['threshold'],
633
+ default_kernel_size = user_parameters['log_kernel_size'],
634
+ default_min_distance = user_parameters['minimum_distance'],
635
+ default_spot_radius = user_parameters['spot_size'],
636
+ deconvolution_spot_radius = user_parameters['spot_size'],
637
+ deconvolution_kernel_size = user_parameters['log_kernel_size'],
638
+ alpha = user_parameters['alpha'],
639
+ beta = user_parameters['beta'],
640
+ gamma = user_parameters['gamma'],
641
+ other_image = other_image if user_parameters['is_multichannel'] else None,
642
+ )
643
+
644
+ user_parameters.update(updated_parameters)
645
+
646
+ else :
647
+ spots, threshold = detect_spots(image, user_parameters, hide_loading = hide_loading)
648
+ user_parameters['threshold'] = threshold
649
+
650
+ if do_dense_region_deconvolution :
651
+ spots = launch_dense_region_deconvolution(image, spots, user_parameters, hide_loading = hide_loading)
652
+
636
653
  if do_clustering :
637
654
  clusters, clustered_spots = launch_clustering(spots, user_parameters, hide_loading = hide_loading) #012 are coordinates #3 is number of spots per cluster, #4 is cluster index
638
655
  spots, spots_cluster_id = clustered_spots[:,:-1], clustered_spots[:,-1]
@@ -641,8 +658,6 @@ def launch_detection(
641
658
  clusters = None
642
659
  spots_cluster_id = None
643
660
 
644
- user_parameters['threshold'] = threshold
645
-
646
661
  if user_parameters['show_napari_corrector'] :
647
662
 
648
663
  spots, clusters, new_cluster_radius, new_min_spot_number = correct_spots(
@@ -671,7 +686,7 @@ def launch_detection(
671
686
  post_detection_dict = launch_post_detection(image, spots, user_parameters, hide_loading = hide_loading)
672
687
  fov_result.update(post_detection_dict)
673
688
 
674
- return user_parameters, fov_result, spots, clusters, spots_cluster_id
689
+ return user_parameters, fov_result, spots, clusters, spots_cluster_id, image
675
690
 
676
691
 
677
692
  def launch_features_computation(
@@ -720,7 +735,7 @@ def launch_features_computation(
720
735
  if type(cell_label) != type(None) and type(nucleus_label) != type(None):
721
736
  frame_results['cell_number'] = len(cell_result_dframe)
722
737
  else :
723
- frame_results['cell_number'] = NaN
738
+ frame_results['cell_number'] = nan
724
739
  frame_results['spots'] = spots
725
740
  frame_results['clusters'] = clusters
726
741
  frame_results['spots_cluster_id'] = spots_cluster_id
@@ -742,9 +757,9 @@ def launch_features_computation(
742
757
  cell_result_dframe['total_rna_number'] = cell_result_dframe['nb_rna_in_nuc'] + cell_result_dframe['nb_rna_out_nuc']
743
758
  else : # This can happen when segmentation is performed and detects cells but they are on fov edges and thus removed by big-fish.
744
759
  print("\033[1;31m All segmented cells where skipped because they are found on fov edges (incomplete cells), if you want to analyse this image check segmentation.\033[00m")
745
- cell_result_dframe['nb_rna_in_nuc'] = np.NaN
746
- cell_result_dframe['nb_rna_out_nuc'] = np.NaN
747
- cell_result_dframe['total_rna_number'] = np.NaN
760
+ cell_result_dframe['nb_rna_in_nuc'] = np.nan
761
+ cell_result_dframe['nb_rna_out_nuc'] = np.nan
762
+ cell_result_dframe['total_rna_number'] = np.nan
748
763
 
749
764
 
750
765
  return frame_results, cell_result_dframe
@@ -763,7 +778,7 @@ def _compute_clustered_spots_dataframe(clustered_spots) :
763
778
  })
764
779
 
765
780
  null_idx = df[df['cluster_id'] == -1].index
766
- df.loc[null_idx, 'cluster_id'] = np.NaN
781
+ df.loc[null_idx, 'cluster_id'] = np.nan
767
782
 
768
783
  return df
769
784
 
@@ -802,91 +817,6 @@ def get_nucleus_signal(image, other_images, user_parameters) :
802
817
  else :
803
818
  return image
804
819
 
805
- def _create_threshold_slider(
806
- logfiltered_image : np.ndarray,
807
- local_maxima : np.ndarray,
808
- default : int,
809
- min_value : int,
810
- max_value : int,
811
- voxel_size
812
- ) :
813
-
814
- if isinstance(default, float) : default = round(default)
815
- min_value = max(min_value,15) #Security to avoid user put too low threshold and crashes Napari if out of memory.
816
-
817
- @magicgui(
818
- threshold={'widget_type' : 'Slider', 'value' : default, 'min' : min_value, 'max' : max_value, 'tracking' : True,},
819
- auto_call=False,
820
- call_button= "Apply"
821
- )
822
- def threshold_slider(threshold: int) -> LayerDataTuple:
823
- spots = detection.spots_thresholding(
824
- image=logfiltered_image,
825
- mask_local_max=local_maxima,
826
- threshold=threshold
827
- )[0]
828
-
829
- scale = compute_anisotropy_coef(voxel_size)
830
-
831
- layer_args = {
832
- 'size': 5,
833
- 'scale' : scale,
834
- 'face_color' : 'transparent',
835
- 'border_color' : 'red',
836
- 'symbol' : 'disc',
837
- 'opacity' : 0.7,
838
- 'blending' : 'translucent',
839
- 'name': 'single spots',
840
- 'features' : {'threshold' : threshold},
841
- 'visible' : True,
842
- }
843
- return (spots, layer_args , 'points')
844
- return threshold_slider
845
-
846
- def _apply_log_filter(
847
- image: np.ndarray,
848
- voxel_size : tuple,
849
- spot_radius : tuple,
850
- log_kernel_size,
851
-
852
- ) :
853
- """
854
- Apply spot detection steps until local maxima step (just before final threshold).
855
- Return filtered image.
856
- """
857
-
858
- ndim = image.ndim
859
-
860
- if type(log_kernel_size) == type(None) :
861
- log_kernel_size = get_object_radius_pixel(
862
- voxel_size_nm=voxel_size,
863
- object_radius_nm=spot_radius,
864
- ndim=ndim)
865
-
866
-
867
- image_filtered = stack.log_filter(image, log_kernel_size)
868
-
869
- return image_filtered
870
-
871
- def _local_maxima_mask(
872
- image_filtered: np.ndarray,
873
- voxel_size : tuple,
874
- spot_radius : tuple,
875
- minimum_distance
876
-
877
- ) :
878
-
879
- ndim = image_filtered.ndim
880
-
881
- if type(minimum_distance) == type(None) :
882
- minimum_distance = get_object_radius_pixel(
883
- voxel_size_nm=voxel_size,
884
- object_radius_nm=spot_radius,
885
- ndim=ndim)
886
- mask_local_max = detection.local_maximum_detection(image_filtered, minimum_distance)
887
-
888
- return mask_local_max.astype(bool)
889
-
890
820
  def output_spot_tiffvisual(channel,spots_list, path_output, dot_size = 3, rescale = True):
891
821
 
892
822
  """