spacr 0.1.11__tar.gz → 0.1.16__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 (57) hide show
  1. {spacr-0.1.11/spacr.egg-info → spacr-0.1.16}/PKG-INFO +12 -26
  2. {spacr-0.1.11 → spacr-0.1.16}/README.rst +10 -19
  3. {spacr-0.1.11 → spacr-0.1.16}/setup.py +39 -24
  4. {spacr-0.1.11 → spacr-0.1.16}/spacr/__init__.py +12 -14
  5. spacr-0.1.11/spacr/annotate_app.py → spacr-0.1.16/spacr/app_annotate.py +25 -157
  6. spacr-0.1.11/spacr/classify_app.py → spacr-0.1.16/spacr/app_classify.py +25 -20
  7. spacr-0.1.11/spacr/make_masks_app.py → spacr-0.1.16/spacr/app_make_masks.py +5 -4
  8. spacr-0.1.11/spacr/make_masks_app_v2.py → spacr-0.1.16/spacr/app_make_masks_v2.py +2 -4
  9. spacr-0.1.11/spacr/mask_app.py → spacr-0.1.16/spacr/app_mask.py +27 -23
  10. spacr-0.1.11/spacr/measure_app.py → spacr-0.1.16/spacr/app_measure.py +22 -15
  11. {spacr-0.1.11 → spacr-0.1.16}/spacr/gui.py +15 -17
  12. {spacr-0.1.11 → spacr-0.1.16}/spacr/gui_utils.py +152 -106
  13. {spacr-0.1.11 → spacr-0.1.16/spacr.egg-info}/PKG-INFO +12 -26
  14. {spacr-0.1.11 → spacr-0.1.16}/spacr.egg-info/SOURCES.txt +6 -6
  15. spacr-0.1.16/spacr.egg-info/entry_points.txt +8 -0
  16. {spacr-0.1.11 → spacr-0.1.16}/spacr.egg-info/requires.txt +1 -6
  17. {spacr-0.1.11 → spacr-0.1.16}/tests/test_annotate_app.py +1 -1
  18. {spacr-0.1.11 → spacr-0.1.16}/tests/test_gui_classify_app.py +1 -1
  19. {spacr-0.1.11 → spacr-0.1.16}/tests/test_gui_mask_app.py +1 -1
  20. {spacr-0.1.11 → spacr-0.1.16}/tests/test_gui_measure_app.py +1 -1
  21. {spacr-0.1.11 → spacr-0.1.16}/tests/test_gui_sim_app.py +1 -1
  22. {spacr-0.1.11 → spacr-0.1.16}/tests/test_mask_app.py +1 -1
  23. spacr-0.1.11/spacr.egg-info/entry_points.txt +0 -9
  24. {spacr-0.1.11 → spacr-0.1.16}/LICENSE +0 -0
  25. {spacr-0.1.11 → spacr-0.1.16}/MANIFEST.in +0 -0
  26. {spacr-0.1.11 → spacr-0.1.16}/setup.cfg +0 -0
  27. {spacr-0.1.11 → spacr-0.1.16}/spacr/__main__.py +0 -0
  28. {spacr-0.1.11 → spacr-0.1.16}/spacr/chris.py +0 -0
  29. {spacr-0.1.11 → spacr-0.1.16}/spacr/core.py +0 -0
  30. {spacr-0.1.11 → spacr-0.1.16}/spacr/deep_spacr.py +0 -0
  31. {spacr-0.1.11 → spacr-0.1.16}/spacr/graph_learning.py +0 -0
  32. {spacr-0.1.11 → spacr-0.1.16}/spacr/io.py +0 -0
  33. {spacr-0.1.11 → spacr-0.1.16}/spacr/logger.py +0 -0
  34. {spacr-0.1.11 → spacr-0.1.16}/spacr/measure.py +0 -0
  35. {spacr-0.1.11 → spacr-0.1.16}/spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model +0 -0
  36. {spacr-0.1.11 → spacr-0.1.16}/spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv +0 -0
  37. {spacr-0.1.11 → spacr-0.1.16}/spacr/models/cp/toxo_pv_lumen.CP_model +0 -0
  38. {spacr-0.1.11 → spacr-0.1.16}/spacr/plot.py +0 -0
  39. {spacr-0.1.11 → spacr-0.1.16}/spacr/sequencing.py +0 -0
  40. {spacr-0.1.11 → spacr-0.1.16}/spacr/settings.py +0 -0
  41. {spacr-0.1.11 → spacr-0.1.16}/spacr/sim.py +0 -0
  42. {spacr-0.1.11 → spacr-0.1.16}/spacr/sim_app.py +0 -0
  43. {spacr-0.1.11 → spacr-0.1.16}/spacr/timelapse.py +0 -0
  44. {spacr-0.1.11 → spacr-0.1.16}/spacr/utils.py +0 -0
  45. {spacr-0.1.11 → spacr-0.1.16}/spacr/version.py +0 -0
  46. {spacr-0.1.11 → spacr-0.1.16}/spacr.egg-info/dependency_links.txt +0 -0
  47. {spacr-0.1.11 → spacr-0.1.16}/spacr.egg-info/top_level.txt +0 -0
  48. {spacr-0.1.11 → spacr-0.1.16}/tests/test_core.py +0 -0
  49. {spacr-0.1.11 → spacr-0.1.16}/tests/test_gui_utils.py +0 -0
  50. {spacr-0.1.11 → spacr-0.1.16}/tests/test_io.py +0 -0
  51. {spacr-0.1.11 → spacr-0.1.16}/tests/test_measure.py +0 -0
  52. {spacr-0.1.11 → spacr-0.1.16}/tests/test_plot.py +0 -0
  53. {spacr-0.1.11 → spacr-0.1.16}/tests/test_sim.py +0 -0
  54. {spacr-0.1.11 → spacr-0.1.16}/tests/test_timelapse.py +0 -0
  55. {spacr-0.1.11 → spacr-0.1.16}/tests/test_train.py +0 -0
  56. {spacr-0.1.11 → spacr-0.1.16}/tests/test_umap.py +0 -0
  57. {spacr-0.1.11 → spacr-0.1.16}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.1.11
3
+ Version: 0.1.16
4
4
  Summary: Spatial phenotype analysis of crisp screens (SpaCr)
5
5
  Home-page: https://github.com/EinarOlafsson/spacr
6
6
  Author: Einar Birnir Olafsson
@@ -9,7 +9,6 @@ Classifier: Programming Language :: Python :: 3
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
11
  License-File: LICENSE
12
- Requires-Dist: dgl==0.9.1
13
12
  Requires-Dist: torch<3.0,>=2.2.1
14
13
  Requires-Dist: torchvision<1.0,>=0.17.1
15
14
  Requires-Dist: torch-geometric<3.0,>=2.5.1
@@ -40,12 +39,8 @@ Requires-Dist: ttf_opensans>=2020.10.30
40
39
  Requires-Dist: customtkinter<6.0,>=5.2.2
41
40
  Requires-Dist: biopython<2.0,>=1.80
42
41
  Requires-Dist: lxml<6.0,>=5.1.0
43
- Requires-Dist: qtpy<2.5,>=2.4.1
44
- Requires-Dist: superqt<0.7,>=0.6.7
45
- Requires-Dist: pyqt6<6.8,>=6.7.1
46
- Requires-Dist: pyqtgraph<0.14,>=0.13.7
47
42
  Provides-Extra: dev
48
- Requires-Dist: pytest>=3.9; extra == "dev"
43
+ Requires-Dist: pytest<3.11,>=3.9; extra == "dev"
49
44
  Provides-Extra: headless
50
45
  Requires-Dist: opencv-python-headless; extra == "headless"
51
46
  Provides-Extra: full
@@ -67,7 +62,7 @@ Requires-Dist: opencv-python; extra == "full"
67
62
  SpaCr
68
63
  =====
69
64
 
70
- Spatial phenotype analysis of CRISPR-Cas9 screens (SpaCr). The spatial organization of organelles and proteins within cells constitutes a key level of functional regulation. In the context of infectious disease, the spatial relationships between host cell structures and intracellular pathogens are critical to understand host clearance mechanisms and how pathogens evade them. SpaCr is a Python-based software package for generating single-cell image data for deep-learning sub-cellular/cellular phenotypic classification from pooled genetic CRISPR-Cas9 screens. SpaCr provides a flexible toolset to extract single-cell images and measurements from high-content cell painting experiments, train deep-learning models to classify cellular/subcellular phenotypes, simulate, and analyze pooled CRISPR-Cas9 imaging screens.
65
+ Spatial phenotype analysis of CRISPR-Cas9 screens (SpaCr). The spatial organization of organelles and proteins within cells constitutes a key level of functional regulation. In the context of infectious disease, the spatial relationships between host cell structures and intracellular pathogens are critical to understanding host clearance mechanisms and how pathogens evade them. SpaCr is a Python-based software package for generating single-cell image data for deep-learning sub-cellular/cellular phenotypic classification from pooled genetic CRISPR-Cas9 screens. SpaCr provides a flexible toolset to extract single-cell images and measurements from high-content cell painting experiments, train deep-learning models to classify cellular/subcellular phenotypes, simulate, and analyze pooled CRISPR-Cas9 imaging screens.
71
66
 
72
67
  Features
73
68
  --------
@@ -76,9 +71,9 @@ Features
76
71
 
77
72
  - **Object Measurements:** Measurements for each object including scikit-image-regionprops, intensity percentiles, shannon-entropy, pearsons and manders correlations, homogeneity, and radial distribution. Measurements are saved to a SQL database in object-level tables.
78
73
 
79
- - **Crop Images:** Objects (e.g., cells) can be saved as PNGs from the object area or bounding box area of each object. Object paths are saved in a SQL database that can be annotated and used to train CNNs/Transformer models for classification tasks.
74
+ - **Crop Images:** Save objects (cells, nuclei, pathogen, cytoplasm) as images. Object image paths are saved in a SQL database.
80
75
 
81
- - **Train CNNs or Transformers:** Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images. Train Torch models with IRM/ERM, checkpointing.
76
+ - **Train CNNs or Transformers:** Train Torch models to classify single object images.
82
77
 
83
78
  - **Manual Annotation:** Supports manual annotation of single-cell images and segmentation to refine training datasets for training CNNs/Transformers or cellpose, respectively.
84
79
 
@@ -95,29 +90,20 @@ Features
95
90
  Installation
96
91
  ------------
97
92
 
98
- Requires Tkinter for graphical user interface features.
93
+ If using Windows, switch to Linux—it's free, open-source, and better.
99
94
 
100
- Ubuntu
101
- ~~~~~~
95
+ Before installing SpaCr on OSX ensure OpenMP is installed::
102
96
 
103
- Before installing SpaCr, ensure Tkinter is installed:
97
+ brew install libomp
104
98
 
105
- (Tkinter is included with the standard Python installation on macOS, and Windows)
106
-
107
- On Linux:
108
-
109
- ::
99
+ SpaCr GUI requires Tkinter. On Linux, ensure Tkinter is installed. (Tkinter is included with the standard Python installation on macOS and Windows)::
110
100
 
111
101
  sudo apt-get install python3-tk
112
102
 
113
- Install spacr with pip
114
-
115
- ::
103
+ Install SpaCr with pip::
116
104
 
117
105
  pip install spacr
118
106
 
119
- Run spacr GUI:
120
-
121
- ::
107
+ Run SpaCr GUI::
122
108
 
123
- gui
109
+ spacr
@@ -14,7 +14,7 @@
14
14
  SpaCr
15
15
  =====
16
16
 
17
- Spatial phenotype analysis of CRISPR-Cas9 screens (SpaCr). The spatial organization of organelles and proteins within cells constitutes a key level of functional regulation. In the context of infectious disease, the spatial relationships between host cell structures and intracellular pathogens are critical to understand host clearance mechanisms and how pathogens evade them. SpaCr is a Python-based software package for generating single-cell image data for deep-learning sub-cellular/cellular phenotypic classification from pooled genetic CRISPR-Cas9 screens. SpaCr provides a flexible toolset to extract single-cell images and measurements from high-content cell painting experiments, train deep-learning models to classify cellular/subcellular phenotypes, simulate, and analyze pooled CRISPR-Cas9 imaging screens.
17
+ Spatial phenotype analysis of CRISPR-Cas9 screens (SpaCr). The spatial organization of organelles and proteins within cells constitutes a key level of functional regulation. In the context of infectious disease, the spatial relationships between host cell structures and intracellular pathogens are critical to understanding host clearance mechanisms and how pathogens evade them. SpaCr is a Python-based software package for generating single-cell image data for deep-learning sub-cellular/cellular phenotypic classification from pooled genetic CRISPR-Cas9 screens. SpaCr provides a flexible toolset to extract single-cell images and measurements from high-content cell painting experiments, train deep-learning models to classify cellular/subcellular phenotypes, simulate, and analyze pooled CRISPR-Cas9 imaging screens.
18
18
 
19
19
  Features
20
20
  --------
@@ -23,9 +23,9 @@ Features
23
23
 
24
24
  - **Object Measurements:** Measurements for each object including scikit-image-regionprops, intensity percentiles, shannon-entropy, pearsons and manders correlations, homogeneity, and radial distribution. Measurements are saved to a SQL database in object-level tables.
25
25
 
26
- - **Crop Images:** Objects (e.g., cells) can be saved as PNGs from the object area or bounding box area of each object. Object paths are saved in a SQL database that can be annotated and used to train CNNs/Transformer models for classification tasks.
26
+ - **Crop Images:** Save objects (cells, nuclei, pathogen, cytoplasm) as images. Object image paths are saved in a SQL database.
27
27
 
28
- - **Train CNNs or Transformers:** Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images. Train Torch models with IRM/ERM, checkpointing.
28
+ - **Train CNNs or Transformers:** Train Torch models to classify single object images.
29
29
 
30
30
  - **Manual Annotation:** Supports manual annotation of single-cell images and segmentation to refine training datasets for training CNNs/Transformers or cellpose, respectively.
31
31
 
@@ -42,29 +42,20 @@ Features
42
42
  Installation
43
43
  ------------
44
44
 
45
- Requires Tkinter for graphical user interface features.
45
+ If using Windows, switch to Linux—it's free, open-source, and better.
46
46
 
47
- Ubuntu
48
- ~~~~~~
47
+ Before installing SpaCr on OSX ensure OpenMP is installed::
49
48
 
50
- Before installing SpaCr, ensure Tkinter is installed:
49
+ brew install libomp
51
50
 
52
- (Tkinter is included with the standard Python installation on macOS, and Windows)
53
-
54
- On Linux:
55
-
56
- ::
51
+ SpaCr GUI requires Tkinter. On Linux, ensure Tkinter is installed. (Tkinter is included with the standard Python installation on macOS and Windows)::
57
52
 
58
53
  sudo apt-get install python3-tk
59
54
 
60
- Install spacr with pip
61
-
62
- ::
55
+ Install SpaCr with pip::
63
56
 
64
57
  pip install spacr
65
58
 
66
- Run spacr GUI:
67
-
68
- ::
59
+ Run SpaCr GUI::
69
60
 
70
- gui
61
+ spacr
@@ -10,19 +10,11 @@ def get_cuda_version():
10
10
  except (subprocess.CalledProcessError, FileNotFoundError):
11
11
  return None
12
12
 
13
- cuda_version = get_cuda_version()
14
-
15
- if cuda_version:
16
- dgl_dependency = f'dgl-cu{cuda_version}==0.9.1' # Specify the version of DGL compatible with your setup
17
- else:
18
- dgl_dependency = 'dgl==0.9.1' # Fallback to CPU version if no CUDA is detected
19
-
20
13
  # Ensure you have read the README.rst content into a variable, e.g., `long_description`
21
14
  with open("README.rst", "r", encoding="utf-8") as fh:
22
15
  long_description = fh.read()
23
16
 
24
17
  dependencies = [
25
- dgl_dependency,
26
18
  'torch>=2.2.1,<3.0',
27
19
  'torchvision>=0.17.1,<1.0',
28
20
  'torch-geometric>=2.5.1,<3.0',
@@ -52,16 +44,12 @@ dependencies = [
52
44
  'ttf_opensans>=2020.10.30',
53
45
  'customtkinter>=5.2.2,<6.0',
54
46
  'biopython>=1.80,<2.0',
55
- 'lxml>=5.1.0,<6.0',
56
- 'qtpy>=2.4.1,<2.5',
57
- 'superqt>=0.6.7,<0.7',
58
- 'pyqt6>=6.7.1,<6.8',
59
- 'pyqtgraph>=0.13.7,<0.14'
47
+ 'lxml>=5.1.0,<6.0'
60
48
  ]
61
49
 
62
50
  setup(
63
51
  name="spacr",
64
- version="0.1.11",
52
+ version="0.1.16",
65
53
  author="Einar Birnir Olafsson",
66
54
  author_email="olafsson@med.umich.com",
67
55
  description="Spatial phenotype analysis of crisp screens (SpaCr)",
@@ -73,18 +61,17 @@ setup(
73
61
  install_requires=dependencies,
74
62
  entry_points={
75
63
  'console_scripts': [
76
- 'mask=spacr.gui_mask_app:gui_mask',
77
- 'measure=spacr.gui_measure_app:gui_measure',
78
- 'make_masks=spacr.gui_make_mask_app:gui_make_masks',
79
- 'make_masks2=spacr.gui_make_mask_app_v2:gui_make_masks',
80
- 'annotate=spacr.annotate_app_v2:gui_annotate',
81
- 'classify=spacr.gui_classify_app:gui_classify',
82
- 'sim=spacr.gui_sim_app:gui_sim',
83
- 'gui=spacr.gui:gui_app',
64
+ 'mask=spacr.app_mask:gui_mask',
65
+ 'measure=spacr.app_measure:gui_measure',
66
+ 'make_masks=spacr.app_make_mask:gui_make_masks',
67
+ 'annotate=spacr.app_annotate:gui_annotate',
68
+ 'classify=spacr.app_classify:gui_classify',
69
+ 'sim=spacr.app_sim:gui_sim',
70
+ 'spacr=spacr.gui:gui_app',
84
71
  ],
85
72
  },
86
73
  extras_require={
87
- 'dev': ['pytest>=3.9'],
74
+ 'dev': ['pytest>=3.9,<3.11'],
88
75
  'headless': ['opencv-python-headless'],
89
76
  'full': ['opencv-python'],
90
77
  },
@@ -93,4 +80,32 @@ setup(
93
80
  "License :: OSI Approved :: MIT License",
94
81
  "Operating System :: OS Independent",
95
82
  ]
96
- )
83
+ )
84
+
85
+ cuda_version = get_cuda_version()
86
+
87
+ if cuda_version:
88
+ dgl = f'dgl-cu{cuda_version}==0.9.1'
89
+ else:
90
+ dgl = 'dgl==0.9.1' # Fallback to CPU version if no CUDA is detected
91
+ try:
92
+ subprocess.run(['pip', 'install', dgl], check=True)
93
+ except subprocess.CalledProcessError:
94
+ subprocess.run(['pip', 'install', 'dgl'], check=True)
95
+
96
+ deps = ['pyqtgraph>=0.13.7,<0.14',
97
+ 'pyqt6>=6.7.1,<6.8',
98
+ 'pyqt6.sip',
99
+ 'qtpy>=2.4.1,<2.5',
100
+ 'superqt>=0.6.7,<0.7',
101
+ 'pyqtgraph',
102
+ 'pyqt6',
103
+ 'pyqt6.sip',
104
+ 'qtpy',
105
+ 'superqt']
106
+
107
+ for dep in deps:
108
+ try:
109
+ subprocess.run(['pip', 'install', dep], check=True)
110
+ except subprocess.CalledProcessError:
111
+ pass
@@ -12,14 +12,13 @@ from . import sim
12
12
  from . import sequencing
13
13
  from . import timelapse
14
14
  from . import deep_spacr
15
- from . import annotate_app
16
- from . import annotate_app_v2
15
+ from . import app_annotate
17
16
  from . import gui_utils
18
- from . import gui_make_masks_app
19
- from . import gui_make_masks_app_v2
20
- from . import gui_mask_app
21
- from . import gui_measure_app
22
- from . import gui_classify_app
17
+ from . import app_make_masks
18
+ from . import app_make_masks_v2
19
+ from . import app_mask
20
+ from . import app_measure
21
+ from . import app_classify
23
22
  from . import logger
24
23
 
25
24
 
@@ -34,14 +33,13 @@ __all__ = [
34
33
  "sequencing"
35
34
  "timelapse",
36
35
  "deep_spacr",
37
- "annotate_app",
38
- "annotate_app_v2",
36
+ "app_annotate",
39
37
  "gui_utils",
40
- "gui_make_masks_app",
41
- "gui_make_masks_app_v2",
42
- "gui_mask_app",
43
- "gui_measure_app",
44
- "gui_classify_app",
38
+ "app_make_masks",
39
+ "app_make_masks_v2",
40
+ "app_mask",
41
+ "app_measure",
42
+ "app_classify",
45
43
  "logger"
46
44
  ]
47
45
 
@@ -11,29 +11,12 @@ from PIL import ImageTk
11
11
  from skimage.exposure import rescale_intensity
12
12
  from IPython.display import display, HTML
13
13
  from tkinter import font as tkFont
14
+ from tkinter import TclError
14
15
 
15
- from .gui_utils import ScrollableFrame, CustomButton, set_dark_style, set_default_font, style_text_boxes, create_menu_bar
16
+ from .gui_utils import ScrollableFrame, CustomButton, set_dark_style, set_default_font, set_dark_style, create_menu_bar
16
17
 
17
18
  class ImageApp:
18
19
  def __init__(self, root, db_path, src, image_type=None, channels=None, grid_rows=None, grid_cols=None, image_size=(200, 200), annotation_column='annotate', normalize=False, percentiles=(1,99), measurement=None, threshold=None):
19
- """
20
- Initializes an instance of the ImageApp class.
21
-
22
- Parameters:
23
- - root (tkinter.Tk): The root window of the application.
24
- - db_path (str): The path to the SQLite database.
25
- - src (str): The source directory that should be upstream of 'data' in the paths.
26
- - image_type (str): The type of images to display.
27
- - channels (list): The channels to filter in the images.
28
- - grid_rows (int): The number of rows in the image grid.
29
- - grid_cols (int): The number of columns in the image grid.
30
- - image_size (tuple): The size of the displayed images.
31
- - annotation_column (str): The column name for image annotations in the database.
32
- - normalize (bool): Whether to normalize images to their 2nd and 98th percentiles. Defaults to False.
33
- - measurement (str): The measurement column to filter by.
34
- - threshold (float): The threshold value for filtering the measurement column.
35
- """
36
-
37
20
  self.root = root
38
21
  self.db_path = db_path
39
22
  self.src = src
@@ -69,9 +52,6 @@ class ImageApp:
69
52
  self.labels.append(label)
70
53
 
71
54
  def prefilter_paths_annotations(self):
72
- """
73
- Pre-filters the paths and annotations based on the specified measurement and threshold.
74
- """
75
55
  from .io import _read_and_join_tables
76
56
  from .utils import is_list_of_lists
77
57
 
@@ -159,21 +139,6 @@ class ImageApp:
159
139
  conn.close()
160
140
 
161
141
  def load_images(self):
162
- """
163
- Loads and displays images with annotations.
164
-
165
- This method retrieves image paths and annotations from a pre-filtered list,
166
- loads the images using a ThreadPoolExecutor for parallel processing,
167
- adds colored borders to images based on their annotations,
168
- and displays the images in the corresponding labels.
169
-
170
- Args:
171
- None
172
-
173
- Returns:
174
- None
175
- """
176
-
177
142
  for label in self.labels:
178
143
  label.config(image='')
179
144
 
@@ -213,16 +178,6 @@ class ImageApp:
213
178
  self.root.update()
214
179
 
215
180
  def load_single_image(self, path_annotation_tuple):
216
- """
217
- Loads a single image from the given path and annotation tuple.
218
-
219
- Args:
220
- path_annotation_tuple (tuple): A tuple containing the image path and its annotation.
221
-
222
- Returns:
223
- img (PIL.Image.Image): The loaded image.
224
- annotation: The annotation associated with the image.
225
- """
226
181
  path, annotation = path_annotation_tuple
227
182
  img = Image.open(path)
228
183
  img = self.normalize_image(img, self.normalize, self.percentiles)
@@ -233,18 +188,6 @@ class ImageApp:
233
188
 
234
189
  @staticmethod
235
190
  def normalize_image(img, normalize=False, percentiles=(1, 99)):
236
- """
237
- Normalize the pixel values of an image based on the 2nd and 98th percentiles or the image min and max values,
238
- and ensure the image is exported as 8-bit.
239
-
240
- Parameters:
241
- - img: PIL.Image.Image. The input image to be normalized.
242
- - normalize: bool. Whether to normalize based on the 2nd and 98th percentiles.
243
- - percentiles: tuple. The percentiles to use for normalization.
244
-
245
- Returns:
246
- - PIL.Image.Image. The normalized and 8-bit converted image.
247
- """
248
191
  img_array = np.array(img)
249
192
 
250
193
  if normalize:
@@ -261,17 +204,6 @@ class ImageApp:
261
204
  return Image.fromarray(img_array)
262
205
 
263
206
  def add_colored_border(self, img, border_width, border_color):
264
- """
265
- Adds a colored border to an image.
266
-
267
- Args:
268
- img (PIL.Image.Image): The input image.
269
- border_width (int): The width of the border in pixels.
270
- border_color (str): The color of the border in RGB format.
271
-
272
- Returns:
273
- PIL.Image.Image: The image with the colored border.
274
- """
275
207
  top_border = Image.new('RGB', (img.width, border_width), color=border_color)
276
208
  bottom_border = Image.new('RGB', (img.width, border_width), color=border_color)
277
209
  left_border = Image.new('RGB', (border_width, img.height), color=border_color)
@@ -287,15 +219,6 @@ class ImageApp:
287
219
  return bordered_img
288
220
 
289
221
  def filter_channels(self, img):
290
- """
291
- Filters the channels of an image based on the specified channels.
292
-
293
- Args:
294
- img (PIL.Image.Image): The input image.
295
-
296
- Returns:
297
- PIL.Image.Image: The filtered image.
298
- """
299
222
  r, g, b = img.split()
300
223
  if self.channels:
301
224
  if 'r' not in self.channels:
@@ -312,17 +235,6 @@ class ImageApp:
312
235
  return Image.merge("RGB", (r, g, b))
313
236
 
314
237
  def get_on_image_click(self, path, label, img):
315
- """
316
- Returns a callback function that handles the click event on an image.
317
-
318
- Parameters:
319
- path (str): The path of the image file.
320
- label (tkinter.Label): The label widget to update with the annotated image.
321
- img (PIL.Image.Image): The image object.
322
-
323
- Returns:
324
- function: The callback function for the image click event.
325
- """
326
238
  def on_image_click(event):
327
239
  new_annotation = 1 if event.num == 1 else (2 if event.num == 3 else None)
328
240
 
@@ -356,11 +268,6 @@ class ImageApp:
356
268
  """))
357
269
 
358
270
  def update_database_worker(self):
359
- """
360
- Worker function that continuously updates the database with pending updates from the update queue.
361
- It retrieves the pending updates from the queue, updates the corresponding records in the database,
362
- and resets the text in the HTML and status label.
363
- """
364
271
  conn = sqlite3.connect(self.db_path)
365
272
  c = conn.cursor()
366
273
 
@@ -383,51 +290,24 @@ class ImageApp:
383
290
  c.execute(f'UPDATE png_list SET {self.annotation_column} = ? WHERE png_path = ?', (new_annotation, path))
384
291
  conn.commit()
385
292
 
386
- # Reset the text
387
293
  ImageApp.update_html('')
388
294
  self.status_label.config(text='')
389
295
  self.root.update()
390
296
  time.sleep(0.1)
391
297
 
392
298
  def update_gui_text(self, text):
393
- """
394
- Update the text of the status label in the GUI.
395
-
396
- Args:
397
- text (str): The new text to be displayed in the status label.
398
-
399
- Returns:
400
- None
401
- """
402
299
  self.status_label.config(text=text)
403
300
  self.root.update()
404
301
 
405
302
  def next_page(self):
406
- """
407
- Moves to the next page of images in the grid.
408
-
409
- If there are pending updates in the dictionary, they are added to the update queue.
410
- The pending updates dictionary is then cleared.
411
- The index is incremented by the number of rows multiplied by the number of columns in the grid.
412
- Finally, the images are loaded for the new page.
413
- """
414
- if self.pending_updates: # Check if the dictionary is not empty
303
+ if self.pending_updates:
415
304
  self.update_queue.put(self.pending_updates.copy())
416
305
  self.pending_updates.clear()
417
306
  self.index += self.grid_rows * self.grid_cols
418
307
  self.load_images()
419
308
 
420
309
  def previous_page(self):
421
- """
422
- Move to the previous page in the grid.
423
-
424
- If there are pending updates in the dictionary, they are added to the update queue.
425
- The dictionary of pending updates is then cleared.
426
- The index is decremented by the number of rows multiplied by the number of columns in the grid.
427
- If the index becomes negative, it is set to 0.
428
- Finally, the images are loaded for the new page.
429
- """
430
- if self.pending_updates: # Check if the dictionary is not empty
310
+ if self.pending_updates:
431
311
  self.update_queue.put(self.pending_updates.copy())
432
312
  self.pending_updates.clear()
433
313
  self.index -= self.grid_rows * self.grid_cols
@@ -436,23 +316,15 @@ class ImageApp:
436
316
  self.load_images()
437
317
 
438
318
  def shutdown(self):
439
- """
440
- Shuts down the application.
441
-
442
- This method sets the terminate flag to True, clears the pending updates,
443
- updates the database, and quits the application.
444
-
445
- """
446
- self.terminate = True # Set terminate first
319
+ self.terminate = True
447
320
  self.update_queue.put(self.pending_updates.copy())
448
321
  self.pending_updates.clear()
449
- self.db_update_thread.join() # Join the thread to make sure database is updated
322
+ self.db_update_thread.join()
450
323
  self.root.quit()
451
324
  self.root.destroy()
452
325
  print(f'Quit application')
453
326
 
454
327
  def get_annotate_default_settings(settings):
455
-
456
328
  settings.setdefault('image_type', 'cell_png')
457
329
  settings.setdefault('channels', ['r', 'g', 'b'])
458
330
  settings.setdefault('geom', "3200x2000")
@@ -468,24 +340,6 @@ def get_annotate_default_settings(settings):
468
340
  return settings
469
341
 
470
342
  def annotate(settings):
471
- """
472
- Annotates images in a database using a graphical user interface.
473
-
474
- Args:
475
- db (str): The path to the SQLite database.
476
- src (str): The source directory that should be upstream of 'data' in the paths.
477
- image_type (str, optional): The type of images to load from the database. Defaults to None.
478
- channels (str, optional): The channels of the images to load from the database. Defaults to None.
479
- geom (str, optional): The geometry of the GUI window. Defaults to "1000x1100".
480
- img_size (tuple, optional): The size of the images to display in the GUI. Defaults to (200, 200).
481
- rows (int, optional): The number of rows in the image grid. Defaults to 5.
482
- columns (int, optional): The number of columns in the image grid. Defaults to 5.
483
- annotation_column (str, optional): The name of the annotation column in the database table. Defaults to 'annotate'.
484
- normalize (bool, optional): Whether to normalize images to their 2nd and 98th percentiles. Defaults to False.
485
- measurement (str, optional): The measurement column to filter by.
486
- threshold (float, optional): The threshold value for filtering the measurement column.
487
- """
488
-
489
343
  settings = get_annotate_default_settings(settings)
490
344
  src = settings['src']
491
345
 
@@ -518,7 +372,6 @@ global_image_refs = []
518
372
  def initiate_annotation_app_root(parent_frame):
519
373
  style = ttk.Style(parent_frame)
520
374
  set_dark_style(style)
521
- style_text_boxes(style)
522
375
  set_default_font(parent_frame, font_name="Arial", size=8)
523
376
 
524
377
  parent_frame.configure(bg='black')
@@ -604,6 +457,9 @@ def annotate_app(parent_frame, settings):
604
457
  annotate_with_image_refs(settings, root, lambda: load_next_app(root))
605
458
 
606
459
  def annotate_with_image_refs(settings, root, shutdown_callback):
460
+ from .gui_utils import proceed_with_app
461
+ from .gui import gui_app
462
+
607
463
  settings = get_annotate_default_settings(settings)
608
464
  src = settings['src']
609
465
 
@@ -632,9 +488,9 @@ def annotate_with_image_refs(settings, root, shutdown_callback):
632
488
  app.load_images()
633
489
 
634
490
  # Store the shutdown function and next app details in the root
635
- root.current_app_exit_func = app.shutdown
636
- root.next_app_func = None
637
- root.next_app_args = ()
491
+ root.current_app_exit_func = lambda: [app.shutdown(), shutdown_callback()]
492
+ root.next_app_func = proceed_with_app
493
+ root.next_app_args = ("Main App", gui_app) # Specify the main app function
638
494
 
639
495
  def load_next_app(root):
640
496
  # Get the next app function and arguments
@@ -642,7 +498,19 @@ def load_next_app(root):
642
498
  next_app_args = root.next_app_args
643
499
 
644
500
  if next_app_func:
645
- next_app_func(*next_app_args)
501
+ try:
502
+ if not root.winfo_exists():
503
+ raise tk.TclError
504
+ next_app_func(root, *next_app_args)
505
+ except tk.TclError:
506
+ # Reinitialize root if it has been destroyed
507
+ new_root = tk.Tk()
508
+ width = new_root.winfo_screenwidth()
509
+ height = new_root.winfo_screenheight()
510
+ new_root.geometry(f"{width}x{height}")
511
+ new_root.title("SpaCr Application")
512
+ next_app_func(new_root, *next_app_args)
513
+
646
514
 
647
515
  def gui_annotate():
648
516
  root = tk.Tk()