cellfinder 1.6.0__tar.gz → 1.7.0__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 (72) hide show
  1. {cellfinder-1.6.0 → cellfinder-1.7.0}/.github/workflows/test_and_deploy.yml +9 -0
  2. {cellfinder-1.6.0 → cellfinder-1.7.0}/PKG-INFO +4 -3
  3. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/cli_migration_warning.py +3 -1
  4. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/classify/classify.py +2 -2
  5. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/classify/tools.py +13 -3
  6. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/detect.py +13 -7
  7. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/download/download.py +2 -1
  8. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/main.py +2 -2
  9. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/tools/threading.py +4 -3
  10. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/train/train_yaml.py +4 -13
  11. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/curation.py +16 -0
  12. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/detect/detect.py +4 -1
  13. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/detect/detect_containers.py +12 -2
  14. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/input_container.py +14 -4
  15. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/train/train.py +3 -7
  16. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/train/train_containers.py +0 -2
  17. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder.egg-info/PKG-INFO +4 -3
  18. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder.egg-info/requires.txt +1 -1
  19. {cellfinder-1.6.0 → cellfinder-1.7.0}/pyproject.toml +4 -2
  20. {cellfinder-1.6.0 → cellfinder-1.7.0}/.github/workflows/test_include_guard.yaml +0 -0
  21. {cellfinder-1.6.0 → cellfinder-1.7.0}/.gitignore +0 -0
  22. {cellfinder-1.6.0 → cellfinder-1.7.0}/.napari/config.yml +0 -0
  23. {cellfinder-1.6.0 → cellfinder-1.7.0}/CITATION.cff +0 -0
  24. {cellfinder-1.6.0 → cellfinder-1.7.0}/LICENSE +0 -0
  25. {cellfinder-1.6.0 → cellfinder-1.7.0}/MANIFEST.in +0 -0
  26. {cellfinder-1.6.0 → cellfinder-1.7.0}/README.md +0 -0
  27. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/__init__.py +0 -0
  28. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/__init__.py +0 -0
  29. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/classify/__init__.py +0 -0
  30. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/classify/augment.py +0 -0
  31. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/classify/cube_generator.py +0 -0
  32. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/classify/resnet.py +0 -0
  33. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/config/__init__.py +0 -0
  34. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/config/cellfinder.conf +0 -0
  35. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/__init__.py +0 -0
  36. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/filters/__init__.py +0 -0
  37. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/filters/plane/__init__.py +0 -0
  38. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/filters/plane/classical_filter.py +0 -0
  39. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/filters/plane/plane_filter.py +0 -0
  40. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/filters/plane/tile_walker.py +0 -0
  41. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/filters/setup_filters.py +0 -0
  42. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/filters/volume/__init__.py +0 -0
  43. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/filters/volume/ball_filter.py +0 -0
  44. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/filters/volume/structure_detection.py +0 -0
  45. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/filters/volume/structure_splitting.py +0 -0
  46. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/detect/filters/volume/volume_filter.py +0 -0
  47. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/download/__init__.py +0 -0
  48. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/download/cli.py +0 -0
  49. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/tools/IO.py +0 -0
  50. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/tools/__init__.py +0 -0
  51. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/tools/array_operations.py +0 -0
  52. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/tools/geometry.py +0 -0
  53. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/tools/image_processing.py +0 -0
  54. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/tools/prep.py +0 -0
  55. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/tools/source_files.py +0 -0
  56. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/tools/system.py +0 -0
  57. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/tools/tiff.py +0 -0
  58. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/tools/tools.py +0 -0
  59. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/train/__init__.py +0 -0
  60. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/core/types.py +0 -0
  61. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/__init__.py +0 -0
  62. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/detect/__init__.py +0 -0
  63. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/detect/thread_worker.py +0 -0
  64. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/napari.yaml +0 -0
  65. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/sample_data.py +0 -0
  66. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/train/__init__.py +0 -0
  67. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder/napari/utils.py +0 -0
  68. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder.egg-info/SOURCES.txt +0 -0
  69. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder.egg-info/dependency_links.txt +0 -0
  70. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder.egg-info/entry_points.txt +0 -0
  71. {cellfinder-1.6.0 → cellfinder-1.7.0}/cellfinder.egg-info/top_level.txt +0 -0
  72. {cellfinder-1.6.0 → cellfinder-1.7.0}/setup.cfg +0 -0
@@ -86,6 +86,15 @@ jobs:
86
86
  secret-codecov-token: ${{ secrets.CODECOV_TOKEN }}
87
87
  use-xvfb: true
88
88
 
89
+ # Run tests on napari main if this is a scheduled run
90
+ - name: Run tests on napari main
91
+ if: github.event_name == 'schedule'
92
+ uses: neuroinformatics-unit/actions/test@v2
93
+ with:
94
+ python-version: ${{ matrix.python-version }}
95
+ secret-codecov-token: ${{ secrets.CODECOV_TOKEN }}
96
+ tox-args: '-e napari-dev'
97
+
89
98
  - name: Notify slack on scheduled failure
90
99
  if: failure() && github.event_name == 'schedule'
91
100
  uses: ravsamhq/notify-slack-action@v2
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: cellfinder
3
- Version: 1.6.0
3
+ Version: 1.7.0
4
4
  Summary: Automated 3D cell detection in large microscopy images
5
5
  Author-email: "Adam Tyson, Christian Niedworok, Charly Rousseau" <code@adamltyson.com>
6
6
  License: BSD-3-Clause
@@ -33,7 +33,7 @@ Requires-Dist: numpy
33
33
  Requires-Dist: scikit-image
34
34
  Requires-Dist: scikit-learn
35
35
  Requires-Dist: keras>=3.7.0
36
- Requires-Dist: torch!=2.4,>=2.1.0
36
+ Requires-Dist: torch>=2.4.1
37
37
  Requires-Dist: tifffile
38
38
  Requires-Dist: tqdm
39
39
  Requires-Dist: qt-niu
@@ -56,6 +56,7 @@ Requires-Dist: napari-plugin-engine>=0.1.4; extra == "napari"
56
56
  Requires-Dist: napari[pyqt5]; extra == "napari"
57
57
  Requires-Dist: pooch>=1; extra == "napari"
58
58
  Requires-Dist: qtpy; extra == "napari"
59
+ Dynamic: license-file
59
60
 
60
61
  [![Python Version](https://img.shields.io/pypi/pyversions/cellfinder.svg)](https://pypi.org/project/cellfinder)
61
62
  [![PyPI](https://img.shields.io/pypi/v/cellfinder.svg)](https://pypi.org/project/cellfinder)
@@ -1,5 +1,7 @@
1
1
  import argparse
2
2
 
3
+ from cellfinder.core import logger
4
+
3
5
  BRAINGLOBE_WORKFLOWS = "https://github.com/brainglobe/brainglobe-workflows"
4
6
  NEW_NAME = "brainmapper"
5
7
  BLOG_POST = "https://brainglobe.info/blog/version1/core_and_napari_merge.html"
@@ -36,7 +38,7 @@ def cli_catch() -> None:
36
38
  ),
37
39
  )
38
40
 
39
- print(
41
+ logger.warning(
40
42
  "Hey, it looks like you're trying to run the old command-line tool.",
41
43
  "This workflow has been renamed and moved -",
42
44
  " you can now find it in the brainglobe-workflows package:\n",
@@ -70,7 +70,7 @@ def main(
70
70
  )
71
71
 
72
72
  if trained_model and Path(trained_model).suffix == ".h5":
73
- print(
73
+ logger.warning(
74
74
  "Weights provided in place of the model, "
75
75
  "loading weights into default model."
76
76
  )
@@ -103,7 +103,7 @@ def main(
103
103
  points_list.append(cell)
104
104
 
105
105
  time_elapsed = datetime.now() - start_time
106
- print(
106
+ logger.info(
107
107
  "Classfication complete - all points done in : {}".format(time_elapsed)
108
108
  )
109
109
 
@@ -47,9 +47,19 @@ def get_model(
47
47
  f"Setting model weights according to: {model_weights}",
48
48
  )
49
49
  if model_weights is None:
50
- raise OSError("`model_weights` must be provided")
51
- model.load_weights(model_weights)
52
- return model
50
+ raise OSError(
51
+ "`model_weights` must be provided for inference "
52
+ "or continued training."
53
+ )
54
+ try:
55
+ model.load_weights(model_weights)
56
+ except (OSError, ValueError) as e:
57
+ raise ValueError(
58
+ f"Error loading weights: {model_weights}.\n"
59
+ "Provided weights don't match the model architecture.\n"
60
+ ) from e
61
+
62
+ return model
53
63
 
54
64
 
55
65
  def make_lists(
@@ -48,8 +48,7 @@ def main(
48
48
  save_planes: bool = False,
49
49
  plane_directory: Optional[str] = None,
50
50
  batch_size: Optional[int] = None,
51
- torch_device: str = "cpu",
52
- use_scipy: bool = True,
51
+ torch_device: Optional[str] = None,
53
52
  split_ball_xy_size: int = 3,
54
53
  split_ball_z_size: int = 3,
55
54
  split_ball_overlap_fraction: float = 0.8,
@@ -121,9 +120,9 @@ def main(
121
120
  becomes slower.
122
121
 
123
122
  torch_device : str, optional
124
- The device on which to run the computation. By default, it's "cpu".
125
- To run on a gpu, specify the PyTorch device name, such as "cuda" to
126
- run on the first GPU.
123
+ The device on which to run the computation. If not specified (None),
124
+ "cuda" will be used if a GPU is available, otherwise "cpu".
125
+ You can also manually specify "cuda" or "cpu".
127
126
 
128
127
  callback : Callable[int], optional
129
128
  A callback function that is called every time a plane has finished
@@ -135,6 +134,8 @@ def main(
135
134
  List of detected cells.
136
135
  """
137
136
  start_time = datetime.now()
137
+ if torch_device is None:
138
+ torch_device = "cuda" if torch.cuda.is_available() else "cpu"
138
139
  if batch_size is None:
139
140
  if torch_device == "cpu":
140
141
  batch_size = 4
@@ -155,6 +156,12 @@ def main(
155
156
  end_plane = min(len(signal_array), end_plane)
156
157
 
157
158
  torch_device = torch_device.lower()
159
+ # Use SciPy filtering on CPU (better performance); use PyTorch on GPU
160
+ if torch_device != "cuda":
161
+ use_scipy = True
162
+ else:
163
+ use_scipy = False
164
+
158
165
  batch_size = max(batch_size, 1)
159
166
  # brainmapper can pass them in as str
160
167
  voxel_sizes = list(map(float, voxel_sizes))
@@ -231,6 +238,5 @@ def main(
231
238
 
232
239
  time_elapsed = datetime.now() - start_time
233
240
  s = f"Detection complete. Found {len(cells)} cells in {time_elapsed}"
234
- logger.debug(s)
235
- print(s)
241
+ logger.info(s)
236
242
  return cells
@@ -6,6 +6,7 @@ import pooch
6
6
  from brainglobe_utils.general.config import get_config_obj
7
7
 
8
8
  from cellfinder import DEFAULT_CELLFINDER_DIRECTORY
9
+ from cellfinder.core import logger
9
10
  from cellfinder.core.tools.source_files import (
10
11
  default_configuration_path,
11
12
  user_specific_configuration_path,
@@ -74,7 +75,7 @@ def amend_user_configuration(new_model_path=None) -> None:
74
75
  new_model_path : Path, optional
75
76
  The path to the new model configuration.
76
77
  """
77
- print("(Over-)writing custom user configuration")
78
+ logger.info("(Over-)writing custom user configuration")
78
79
 
79
80
  original_config = default_configuration_path()
80
81
  new_config = user_specific_configuration_path()
@@ -37,7 +37,7 @@ def main(
37
37
  skip_classification: bool = False,
38
38
  detected_cells: List[Cell] = None,
39
39
  classification_batch_size: Optional[int] = None,
40
- classification_torch_device: str = "cpu",
40
+ torch_device: Optional[str] = None,
41
41
  *,
42
42
  detect_callback: Optional[Callable[[int], None]] = None,
43
43
  classify_callback: Optional[Callable[[int], None]] = None,
@@ -77,7 +77,7 @@ def main(
77
77
  log_sigma_size,
78
78
  n_sds_above_mean_thresh,
79
79
  batch_size=classification_batch_size,
80
- torch_device=classification_torch_device,
80
+ torch_device=torch_device,
81
81
  callback=detect_callback,
82
82
  )
83
83
 
@@ -15,6 +15,7 @@ Typical example::
15
15
 
16
16
  from cellfinder.core.tools.threading import ThreadWithException, \\
17
17
  EOFSignal, ProcessWithException
18
+ from cellfinder.core import logger
18
19
  import torch
19
20
 
20
21
 
@@ -63,7 +64,7 @@ Typical example::
63
64
  # thread exited for whatever reason (not exception)
64
65
  break
65
66
 
66
- print(f"Thread processed tensor {i}")
67
+ logger.debug(f"Thread processed tensor {i}")
67
68
  finally:
68
69
  # whatever happens, make sure thread is told to finish so it
69
70
  # doesn't get stuck
@@ -248,8 +249,8 @@ class ExceptionWithQueueMixIn:
248
249
  ... # do something with the msg
249
250
  ... pass
250
251
  ... except ExecutionFailure as e:
251
- ... print(f"got exception {type(e.__cause__)}")
252
- ... print(f"with message {e.__cause__.args[0]}")
252
+ ... logger.error(f"got exception {type(e.__cause__)}")
253
+ ... logger.error(f"with message {e.__cause__.args[0]}")
253
254
  """
254
255
  msg, value = self.from_thread_queue.get(block=True, timeout=timeout)
255
256
  if msg == "eof":
@@ -3,9 +3,6 @@ main
3
3
  ===============
4
4
 
5
5
  Trains a network based on a yaml file specifying cubes of cells/non cells.
6
-
7
- N.B imports are within functions to prevent tensorflow being imported before
8
- it's warnings are silenced
9
6
  """
10
7
 
11
8
  import os
@@ -29,12 +26,16 @@ from brainglobe_utils.general.system import (
29
26
  from brainglobe_utils.IO.cells import find_relevant_tiffs
30
27
  from brainglobe_utils.IO.yaml import read_yaml_section
31
28
  from fancylog import fancylog
29
+ from keras.callbacks import CSVLogger, ModelCheckpoint, TensorBoard
32
30
  from sklearn.model_selection import train_test_split
33
31
 
34
32
  import cellfinder.core as program_for_log
35
33
  from cellfinder.core import logger
34
+ from cellfinder.core.classify.cube_generator import CubeGeneratorFromDisk
36
35
  from cellfinder.core.classify.resnet import layer_type
36
+ from cellfinder.core.classify.tools import get_model, make_lists
37
37
  from cellfinder.core.download.download import DEFAULT_DOWNLOAD_DIRECTORY
38
+ from cellfinder.core.tools.prep import prep_model_weights
38
39
 
39
40
  depth_type = Literal["18", "34", "50", "101", "152"]
40
41
 
@@ -316,16 +317,6 @@ def run(
316
317
  save_progress=False,
317
318
  epochs=100,
318
319
  ):
319
- from keras.callbacks import (
320
- CSVLogger,
321
- ModelCheckpoint,
322
- TensorBoard,
323
- )
324
-
325
- from cellfinder.core.classify.cube_generator import CubeGeneratorFromDisk
326
- from cellfinder.core.classify.tools import get_model, make_lists
327
- from cellfinder.core.tools.prep import prep_model_weights
328
-
329
320
  start_time = datetime.now()
330
321
 
331
322
  ensure_directory_exists(output_dir)
@@ -95,6 +95,22 @@ class CurationWidget(QWidget):
95
95
  self.training_data_non_cell_choice, self.point_layer_names
96
96
  )
97
97
 
98
+ @self.viewer.layers.events.removed.connect
99
+ def _remove_selection_layers(event: QtCore.QEvent):
100
+ """
101
+ Set internal background, signal, training data cell,
102
+ and training data non-cell layers to None when they
103
+ are removed from the napari viewer GUI.
104
+ """
105
+ if event.value == self.signal_layer:
106
+ self.signal_layer = None
107
+ if event.value == self.background_layer:
108
+ self.background_layer = None
109
+ if event.value == self.training_data_cell_layer:
110
+ self.training_data_cell_layer = None
111
+ if event.value == self.training_data_non_cell_layer:
112
+ self.training_data_non_cell_layer = None
113
+
98
114
  @staticmethod
99
115
  def _update_combobox_options(combobox: QComboBox, options_list: List[str]):
100
116
  original_text = combobox.currentText()
@@ -261,6 +261,7 @@ def detect_widget() -> FunctionGui:
261
261
  end_plane: int,
262
262
  n_free_cpus: int,
263
263
  analyse_local: bool,
264
+ use_gpu: bool,
264
265
  debug: bool,
265
266
  reset_button,
266
267
  ) -> None:
@@ -315,6 +316,8 @@ def detect_widget() -> FunctionGui:
315
316
  How many CPU cores to leave free
316
317
  analyse_local : bool
317
318
  Only analyse planes around the current position
319
+ use_gpu : bool
320
+ If True, use GPU for processing (if available); otherwise, use CPU.
318
321
  debug : bool
319
322
  Increase logging
320
323
  reset_button :
@@ -389,7 +392,7 @@ def detect_widget() -> FunctionGui:
389
392
  end_plane = len(signal_image.data)
390
393
 
391
394
  misc_inputs = MiscInputs(
392
- start_plane, end_plane, n_free_cpus, analyse_local, debug
395
+ start_plane, end_plane, n_free_cpus, analyse_local, use_gpu, debug
393
396
  )
394
397
 
395
398
  worker = Worker(
@@ -1,8 +1,9 @@
1
- from dataclasses import dataclass
1
+ from dataclasses import dataclass, field
2
2
  from pathlib import Path
3
3
  from typing import List, Optional
4
4
 
5
5
  import numpy
6
+ import torch
6
7
  from brainglobe_utils.cells.cells import Cell
7
8
 
8
9
  from cellfinder.napari.input_container import InputContainer
@@ -30,7 +31,7 @@ class DataInputs(InputContainer):
30
31
  self.voxel_size_x,
31
32
  )
32
33
  # del operator doesn't affect self, because asdict creates a copy of
33
- # fields.
34
+ # the dict.
34
35
  del data_input_dict["voxel_size_z"]
35
36
  del data_input_dict["voxel_size_y"]
36
37
  del data_input_dict["voxel_size_x"]
@@ -144,10 +145,13 @@ class MiscInputs(InputContainer):
144
145
  end_plane: int = 0
145
146
  n_free_cpus: int = 2
146
147
  analyse_local: bool = False
148
+ use_gpu: bool = field(default_factory=lambda: torch.cuda.is_available())
147
149
  debug: bool = False
148
150
 
149
151
  def as_core_arguments(self) -> dict:
150
152
  misc_input_dict = super().as_core_arguments()
153
+ misc_input_dict["torch_device"] = "cuda" if self.use_gpu else "cpu"
154
+ del misc_input_dict["use_gpu"]
151
155
  del misc_input_dict["debug"]
152
156
  del misc_input_dict["analyse_local"]
153
157
  return misc_input_dict
@@ -162,5 +166,11 @@ class MiscInputs(InputContainer):
162
166
  "n_free_cpus", custom_label="Number of free CPUs"
163
167
  ),
164
168
  analyse_local=dict(value=cls.defaults()["analyse_local"]),
169
+ use_gpu=dict(
170
+ widget_type="CheckBox",
171
+ label="Use GPU",
172
+ value=cls.defaults()["use_gpu"],
173
+ enabled=torch.cuda.is_available(),
174
+ ),
165
175
  debug=dict(value=cls.defaults()["debug"]),
166
176
  )
@@ -1,8 +1,18 @@
1
1
  from abc import abstractmethod
2
- from dataclasses import asdict, dataclass
2
+ from dataclasses import dataclass, fields
3
3
  from typing import Optional
4
4
 
5
5
 
6
+ def asdict_no_copy(obj: dataclass) -> dict:
7
+ """
8
+ Similar to `asdict`, except it makes no copies of the field values.
9
+ asdict will do a deep copy of field values that are non-basic objects.
10
+
11
+ It still creates a new dict to return, though.
12
+ """
13
+ return {field.name: getattr(obj, field.name) for field in fields(obj)}
14
+
15
+
6
16
  @dataclass
7
17
  class InputContainer:
8
18
  """Base for classes that contain inputs
@@ -23,7 +33,7 @@ class InputContainer:
23
33
  # Derived classes are not expected to be particularly
24
34
  # slow to instantiate, so use the default constructor
25
35
  # to avoid code repetition.
26
- return asdict(cls())
36
+ return asdict_no_copy(cls())
27
37
 
28
38
  @abstractmethod
29
39
  def as_core_arguments(self) -> dict:
@@ -32,10 +42,10 @@ class InputContainer:
32
42
  The implementation provided here can be re-used in derived classes, if
33
43
  convenient.
34
44
  """
35
- # note that asdict returns a new instance of a dict,
45
+ # note that asdict_no_copy returns a new instance of a dict,
36
46
  # so any subsequent modifications of this dict won't affect the class
37
47
  # instance
38
- return asdict(self)
48
+ return asdict_no_copy(self)
39
49
 
40
50
  @classmethod
41
51
  def _custom_widget(
@@ -25,14 +25,14 @@ def run_training(
25
25
  optional_training_inputs: OptionalTrainingInputs,
26
26
  misc_training_inputs: MiscTrainingInputs,
27
27
  ):
28
- print("Running training")
28
+ show_info("Running training...")
29
29
  train_yaml(
30
30
  **training_data_inputs.as_core_arguments(),
31
31
  **optional_network_inputs.as_core_arguments(),
32
32
  **optional_training_inputs.as_core_arguments(),
33
33
  **misc_training_inputs.as_core_arguments(),
34
34
  )
35
- print("Finished!")
35
+ show_info("Training finished!")
36
36
 
37
37
 
38
38
  def training_widget() -> FunctionGui:
@@ -60,7 +60,6 @@ def training_widget() -> FunctionGui:
60
60
  continue_training: bool,
61
61
  augment: bool,
62
62
  tensorboard: bool,
63
- save_weights: bool,
64
63
  save_checkpoints: bool,
65
64
  save_progress: bool,
66
65
  epochs: int,
@@ -96,9 +95,6 @@ def training_widget() -> FunctionGui:
96
95
  Augment the training data to improve generalisation
97
96
  tensorboard : bool
98
97
  Log to output_directory/tensorboard
99
- save_weights : bool
100
- Only store the model weights, and not the full model
101
- Useful to save storage space
102
98
  save_checkpoints : bool
103
99
  Store the model at intermediate points during training
104
100
  save_progress : bool
@@ -133,7 +129,6 @@ def training_widget() -> FunctionGui:
133
129
  continue_training,
134
130
  augment,
135
131
  tensorboard,
136
- save_weights,
137
132
  save_checkpoints,
138
133
  save_progress,
139
134
  epochs,
@@ -147,6 +142,7 @@ def training_widget() -> FunctionGui:
147
142
  if yaml_files[0] == Path.home(): # type: ignore
148
143
  show_info("Please select a YAML file for training")
149
144
  else:
145
+ show_info("Starting training process...")
150
146
  worker = run_training(
151
147
  training_data_inputs,
152
148
  optional_network_inputs,
@@ -75,7 +75,6 @@ class OptionalTrainingInputs(InputContainer):
75
75
  continue_training: bool = False
76
76
  augment: bool = True
77
77
  tensorboard: bool = False
78
- save_weights: bool = False
79
78
  save_checkpoints: bool = True
80
79
  save_progress: bool = True
81
80
  epochs: int = 100
@@ -98,7 +97,6 @@ class OptionalTrainingInputs(InputContainer):
98
97
  continue_training=cls._custom_widget("continue_training"),
99
98
  augment=cls._custom_widget("augment"),
100
99
  tensorboard=cls._custom_widget("tensorboard"),
101
- save_weights=cls._custom_widget("save_weights"),
102
100
  save_checkpoints=cls._custom_widget("save_checkpoints"),
103
101
  save_progress=cls._custom_widget("save_progress"),
104
102
  epochs=cls._custom_widget("epochs"),
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: cellfinder
3
- Version: 1.6.0
3
+ Version: 1.7.0
4
4
  Summary: Automated 3D cell detection in large microscopy images
5
5
  Author-email: "Adam Tyson, Christian Niedworok, Charly Rousseau" <code@adamltyson.com>
6
6
  License: BSD-3-Clause
@@ -33,7 +33,7 @@ Requires-Dist: numpy
33
33
  Requires-Dist: scikit-image
34
34
  Requires-Dist: scikit-learn
35
35
  Requires-Dist: keras>=3.7.0
36
- Requires-Dist: torch!=2.4,>=2.1.0
36
+ Requires-Dist: torch>=2.4.1
37
37
  Requires-Dist: tifffile
38
38
  Requires-Dist: tqdm
39
39
  Requires-Dist: qt-niu
@@ -56,6 +56,7 @@ Requires-Dist: napari-plugin-engine>=0.1.4; extra == "napari"
56
56
  Requires-Dist: napari[pyqt5]; extra == "napari"
57
57
  Requires-Dist: pooch>=1; extra == "napari"
58
58
  Requires-Dist: qtpy; extra == "napari"
59
+ Dynamic: license-file
59
60
 
60
61
  [![Python Version](https://img.shields.io/pypi/pyversions/cellfinder.svg)](https://pypi.org/project/cellfinder)
61
62
  [![PyPI](https://img.shields.io/pypi/v/cellfinder.svg)](https://pypi.org/project/cellfinder)
@@ -8,7 +8,7 @@ numpy
8
8
  scikit-image
9
9
  scikit-learn
10
10
  keras>=3.7.0
11
- torch!=2.4,>=2.1.0
11
+ torch>=2.4.1
12
12
  tifffile
13
13
  tqdm
14
14
  qt-niu
@@ -31,7 +31,7 @@ dependencies = [
31
31
  "scikit-image",
32
32
  "scikit-learn",
33
33
  "keras>=3.7.0",
34
- "torch>=2.1.0,!=2.4",
34
+ "torch>=2.4.1",
35
35
  "tifffile",
36
36
  "tqdm",
37
37
  "qt-niu"
@@ -113,7 +113,7 @@ markers = ["slow: marks tests as slow (deselect with '-m \"not slow\"')"]
113
113
  legacy_tox_ini = """
114
114
  # For more information about tox, see https://tox.readthedocs.io/en/latest/
115
115
  [tox]
116
- envlist = py{311,312,313}
116
+ envlist = py{311,312,313}, napari-dev
117
117
  isolated_build = true
118
118
 
119
119
  [gh-actions]
@@ -139,4 +139,6 @@ passenv =
139
139
  NUMPY_EXPERIMENTAL_ARRAY_FUNCTION
140
140
  PYVISTA_OFF_SCREEN
141
141
  BRAINGLOBE_TEST_DATA_DIR
142
+ deps =
143
+ napari-dev: git+https://github.com/napari/napari
142
144
  """
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes