napari-tmidas 0.3.3__py3-none-any.whl → 0.3.5__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.
@@ -257,3 +257,22 @@ class TestCLAHE:
257
257
  assert result_float32.dtype == np.float32
258
258
  assert result_float32.max() <= 1.0
259
259
  assert result_float32.min() >= 0.0
260
+
261
+ def test_clahe_max_workers_parameter(self):
262
+ """Test that max_workers parameter is respected"""
263
+ # Create a 4D image that will trigger parallel processing
264
+ image_4d = np.random.rand(10, 50, 100, 100) * 0.5
265
+
266
+ # Test with different worker counts
267
+ result_1 = equalize_histogram(image_4d, max_workers=1)
268
+ result_4 = equalize_histogram(image_4d, max_workers=4)
269
+ result_8 = equalize_histogram(image_4d, max_workers=8)
270
+
271
+ # All should produce same shape
272
+ assert result_1.shape == image_4d.shape
273
+ assert result_4.shape == image_4d.shape
274
+ assert result_8.shape == image_4d.shape
275
+
276
+ # Results should be nearly identical (some floating point differences are OK)
277
+ np.testing.assert_allclose(result_1, result_4, rtol=1e-5)
278
+ np.testing.assert_allclose(result_1, result_8, rtol=1e-5)
napari_tmidas/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.3.3'
32
- __version_tuple__ = version_tuple = (0, 3, 3)
31
+ __version__ = version = '0.3.5'
32
+ __version_tuple__ = version_tuple = (0, 3, 5)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -3,7 +3,6 @@
3
3
  Processing functions that depend on scikit-image.
4
4
  """
5
5
  import concurrent.futures
6
- import os
7
6
 
8
7
  import numpy as np
9
8
 
@@ -49,10 +48,18 @@ if SKIMAGE_AVAILABLE:
49
48
  "default": 0,
50
49
  "description": "Size of the local region (0 = auto-calculate based on image size). For small features use smaller values (e.g., 32), for large features use larger values (e.g., 128)",
51
50
  },
51
+ "max_workers": {
52
+ "type": int,
53
+ "default": 4,
54
+ "description": "Maximum number of parallel workers (default: 4). Lower values use less memory but are slower. Range: 1-16",
55
+ },
52
56
  },
53
57
  )
54
58
  def equalize_histogram(
55
- image: np.ndarray, clip_limit: float = 0.01, kernel_size: int = 0
59
+ image: np.ndarray,
60
+ clip_limit: float = 0.01,
61
+ kernel_size: int = 0,
62
+ max_workers: int = 4,
56
63
  ) -> np.ndarray:
57
64
  """
58
65
  Apply CLAHE (Contrast Limited Adaptive Histogram Equalization) to enhance local contrast.
@@ -70,23 +77,29 @@ if SKIMAGE_AVAILABLE:
70
77
  Higher values give more contrast but may amplify noise
71
78
  kernel_size : int
72
79
  Size of the contextual regions (0 = auto-calculate based on image size)
80
+ max_workers : int
81
+ Maximum number of parallel workers for processing large datasets (default: 4)
82
+ Lower values reduce memory usage, higher values increase speed
73
83
 
74
84
  Returns
75
85
  -------
76
86
  np.ndarray
77
87
  CLAHE-enhanced image with same dtype as input
78
-
88
+
79
89
  Notes
80
90
  -----
81
91
  For large multi-dimensional datasets (TZYX), processing is parallelized across
82
- the first dimension to utilize multiple CPU cores effectively.
92
+ the first dimension to utilize multiple CPU cores effectively. The max_workers
93
+ parameter controls memory usage vs speed tradeoff.
83
94
  """
84
95
  # Store original dtype to convert back later
85
96
  original_dtype = image.dtype
86
-
97
+
87
98
  # Print diagnostic info for multi-dimensional data
88
99
  if image.ndim > 2:
89
- print(f"Applying CLAHE to {image.ndim}D image with shape {image.shape}")
100
+ print(
101
+ f"Applying CLAHE to {image.ndim}D image with shape {image.shape}"
102
+ )
90
103
 
91
104
  # Auto-calculate kernel size if not specified
92
105
  if kernel_size <= 0:
@@ -99,14 +112,19 @@ if SKIMAGE_AVAILABLE:
99
112
  # Ensure kernel_size is odd
100
113
  if kernel_size % 2 == 0:
101
114
  kernel_size += 1
102
-
115
+
103
116
  if image.ndim > 2:
104
117
  print(f"Using kernel_size={kernel_size}, clip_limit={clip_limit}")
105
-
118
+
119
+ # Clamp max_workers to reasonable range
120
+ max_workers = max(1, min(max_workers, 16))
121
+
106
122
  # For 4D data (TZYX), parallelize across first dimension for better performance
107
123
  if image.ndim == 4 and image.shape[0] > 1:
108
- print(f"Parallelizing CLAHE across {image.shape[0]} timepoints/slices using {max(1, os.cpu_count() - 1)} workers...")
109
-
124
+ print(
125
+ f"Parallelizing CLAHE across {image.shape[0]} timepoints/slices using {max_workers} workers..."
126
+ )
127
+
110
128
  def process_slice(idx):
111
129
  """Process a single slice along first dimension"""
112
130
  result_slice = skimage.exposure.equalize_adapthist(
@@ -115,21 +133,26 @@ if SKIMAGE_AVAILABLE:
115
133
  if (idx + 1) % max(1, image.shape[0] // 10) == 0:
116
134
  print(f" Processed {idx + 1}/{image.shape[0]} slices")
117
135
  return result_slice
118
-
119
- # Process in parallel
136
+
137
+ # Process in parallel with limited workers to control memory usage
120
138
  with concurrent.futures.ThreadPoolExecutor(
121
- max_workers=max(1, os.cpu_count() - 1)
139
+ max_workers=max_workers
122
140
  ) as executor:
123
- futures = [executor.submit(process_slice, i) for i in range(image.shape[0])]
141
+ futures = [
142
+ executor.submit(process_slice, i)
143
+ for i in range(image.shape[0])
144
+ ]
124
145
  results = [future.result() for future in futures]
125
-
146
+
126
147
  result = np.stack(results, axis=0)
127
148
  print("CLAHE processing complete!")
128
-
149
+
129
150
  elif image.ndim == 3 and image.shape[0] > 5:
130
151
  # For 3D data with many slices, also parallelize
131
- print(f"Parallelizing CLAHE across {image.shape[0]} slices using {max(1, os.cpu_count() - 1)} workers...")
132
-
152
+ print(
153
+ f"Parallelizing CLAHE across {image.shape[0]} slices using {max_workers} workers..."
154
+ )
155
+
133
156
  def process_slice(idx):
134
157
  """Process a single 2D slice"""
135
158
  result_slice = skimage.exposure.equalize_adapthist(
@@ -138,14 +161,17 @@ if SKIMAGE_AVAILABLE:
138
161
  if (idx + 1) % max(1, image.shape[0] // 10) == 0:
139
162
  print(f" Processed {idx + 1}/{image.shape[0]} slices")
140
163
  return result_slice
141
-
142
- # Process in parallel
164
+
165
+ # Process in parallel with limited workers to control memory usage
143
166
  with concurrent.futures.ThreadPoolExecutor(
144
- max_workers=max(1, os.cpu_count() - 1)
167
+ max_workers=max_workers
145
168
  ) as executor:
146
- futures = [executor.submit(process_slice, i) for i in range(image.shape[0])]
169
+ futures = [
170
+ executor.submit(process_slice, i)
171
+ for i in range(image.shape[0])
172
+ ]
147
173
  results = [future.result() for future in futures]
148
-
174
+
149
175
  result = np.stack(results, axis=0)
150
176
  print("CLAHE processing complete!")
151
177
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: napari-tmidas
3
- Version: 0.3.3
3
+ Version: 0.3.5
4
4
  Summary: A plugin for batch processing of confocal and whole-slide microscopy images of biological tissues
5
5
  Author: Marco Meer
6
6
  Author-email: marco.meer@pm.me
@@ -161,7 +161,8 @@ Then find napari-tmidas in the **Plugins** menu. [Watch video tutorials →](htt
161
161
 
162
162
  - [Batch Crop Anything](docs/crop_anything.md) - Interactive object cropping with SAM2
163
163
  - [Batch Label Inspection](docs/batch_label_inspection.md) - Manual label verification and editing
164
- - [Advanced Filters](docs/advanced_processing.md) - SciPy/scikit-image filters
164
+ - [SciPy Filters](docs/advanced_processing.md#scipy-filters) - Gaussian, median, morphological operations
165
+ - [Scikit-Image Filters](docs/advanced_processing.md#scikit-image-filters) - CLAHE, thresholding, edge detection
165
166
 
166
167
  ## 💻 Installation
167
168
 
@@ -10,7 +10,7 @@ napari_tmidas/_registry.py,sha256=yunbEoDe1JZREMab4BeP7wka17IwK1toV5g1imju30c,21
10
10
  napari_tmidas/_roi_colocalization.py,sha256=0ZSs7JlJKPhGibnETf6Rj746T3YV4AUgynWmZbmNjHw,92257
11
11
  napari_tmidas/_sample_data.py,sha256=khuv1jemz_fCjqNwEKMFf83Ju0EN4S89IKydsUMmUxw,645
12
12
  napari_tmidas/_ui_utils.py,sha256=wBmaR-3wdgizb234atsjUU2DElsM5-tf4TIsxGLaHzI,1499
13
- napari_tmidas/_version.py,sha256=lemL_4Kl75FgrO6lVuFrrtw6-Dcf9wtXBalKkXuzkO4,704
13
+ napari_tmidas/_version.py,sha256=UAb2Toi6SAdScDfq1uKRRv5QpMUuRtJqqwNxTMGe5Q4,704
14
14
  napari_tmidas/_widget.py,sha256=Uab5WuJK2fgdlGga6iNnHsiZjRMUq2KM3u0N5JJW8DA,5495
15
15
  napari_tmidas/_writer.py,sha256=wbVfHFjjHdybSg37VR4lVmL-kdCkDZsUPDJ66AVLaFQ,1941
16
16
  napari_tmidas/napari.yaml,sha256=1Am1dA0-ZtCXk6veIT6jrMz3zwQ7dF8_p9tZTFx_vTg,2641
@@ -28,7 +28,7 @@ napari_tmidas/_tests/test_regionprops_analysis.py,sha256=IGAEtdg83R9aH8wsINR8VCv
28
28
  napari_tmidas/_tests/test_registry.py,sha256=0dVpeGE273YDPDy6KMInfkz2JAaJ_SUKFOySKn160_c,4795
29
29
  napari_tmidas/_tests/test_sample_data.py,sha256=D1HU_C3hWpO3mlSW_7Z94xaYHDtxz0XUrMjQoYop9Ag,104
30
30
  napari_tmidas/_tests/test_scipy_filters.py,sha256=SUcgyFh2IY4YVJvVO6_J1JdgkCsYdW36RgpHLkkp2vg,6268
31
- napari_tmidas/_tests/test_skimage_filters.py,sha256=TgvwcE_1kpdRzg5Hex9z9IoJc5Mt7IJjbpsdmZFiOGc,9869
31
+ napari_tmidas/_tests/test_skimage_filters.py,sha256=yj0DrfOYyyyjNps3Yzvwwh_wtR4Mmz4_L5DNjFNBbSs,10739
32
32
  napari_tmidas/_tests/test_split_channels.py,sha256=aMvjppoHlPAuwWLGYa1UcfBM_fvu0zM7Elam5JWaAQw,7448
33
33
  napari_tmidas/_tests/test_spotiflow.py,sha256=BLSaD8z8r2zbkYEGmfQ3JkSgw5OkvEORnSzpbkIIz4Y,2578
34
34
  napari_tmidas/_tests/test_tyx_display_fix.py,sha256=rhKbmM1rkPAAUeV3x3VuJkzXwEoqgi3Gtrxi1PP4ijs,4804
@@ -51,16 +51,16 @@ napari_tmidas/processing_functions/regionprops_analysis.py,sha256=ySmzlY_F8uXWQI
51
51
  napari_tmidas/processing_functions/sam2_env_manager.py,sha256=w-X493XdHWAE8UhyHhEEVJ3uvLi2VdS-UFU7yPqnagg,2569
52
52
  napari_tmidas/processing_functions/sam2_mp4.py,sha256=lEdrqQP36_kw2g3soyu81CCRXCkI5DdSExfq5Bc5kig,11523
53
53
  napari_tmidas/processing_functions/scipy_filters.py,sha256=1Y69F5Pe-MEJPwQEs_6Ci3ncFuTKiRAuKRvDxmOQUPw,17871
54
- napari_tmidas/processing_functions/skimage_filters.py,sha256=og2mrEHNLBGQA6j07DHX94rhXJs4fRt5nzjpLyUmbeM,28154
54
+ napari_tmidas/processing_functions/skimage_filters.py,sha256=dLEKw8Nw9-9b6hbPLebifmYj6l5x7KeA28DthWaMBP0,28920
55
55
  napari_tmidas/processing_functions/spotiflow_detection.py,sha256=2FLnDNXLc0eNj8vhp_XBv_bukX5pJOEhuiyXbUzzcyU,32811
56
56
  napari_tmidas/processing_functions/spotiflow_env_manager.py,sha256=07J_tYADMvIVIr_afniSNt8uEduecqpNblSWEj9aH7Q,20323
57
57
  napari_tmidas/processing_functions/timepoint_merger.py,sha256=7pXyfcI2rXZz6_TP3v_WejmMFivNVyUzkzBmifMiFKA,27424
58
58
  napari_tmidas/processing_functions/trackastra_tracking.py,sha256=IkFk5HoEZmKdcu5jXri4WMhHN1KTADDMxSpeYfPgSbo,9976
59
59
  napari_tmidas/processing_functions/viscy_env_manager.py,sha256=eJ9NsyrtypvxRAFVir9n9RtKaaj6GTpIrOFNLScoVDY,11999
60
60
  napari_tmidas/processing_functions/viscy_virtual_staining.py,sha256=Aa__YweYzSFYUTkbneDZ2lxRBplozrQvXGSiMFzUhA4,12422
61
- napari_tmidas-0.3.3.dist-info/licenses/LICENSE,sha256=tSjiOqj57exmEIfP2YVPCEeQf0cH49S6HheQR8IiY3g,1485
62
- napari_tmidas-0.3.3.dist-info/METADATA,sha256=aahB9CKda7grLnpGSUtqS9B-SrP1qrhOPg_0Rp_CSMo,10348
63
- napari_tmidas-0.3.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
64
- napari_tmidas-0.3.3.dist-info/entry_points.txt,sha256=fbVjzbJTm4aDMIBtel1Lyqvq-CwXY7wmCOo_zJ-jtRY,60
65
- napari_tmidas-0.3.3.dist-info/top_level.txt,sha256=63ybdxCZ4SeT13f_Ou4TsivGV_2Gtm_pJOXToAt30_E,14
66
- napari_tmidas-0.3.3.dist-info/RECORD,,
61
+ napari_tmidas-0.3.5.dist-info/licenses/LICENSE,sha256=tSjiOqj57exmEIfP2YVPCEeQf0cH49S6HheQR8IiY3g,1485
62
+ napari_tmidas-0.3.5.dist-info/METADATA,sha256=-wGsfY5Pn_4fJPeF5Y8dCNnGnw-0kSDQe-vLVWKzvcQ,10488
63
+ napari_tmidas-0.3.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
64
+ napari_tmidas-0.3.5.dist-info/entry_points.txt,sha256=fbVjzbJTm4aDMIBtel1Lyqvq-CwXY7wmCOo_zJ-jtRY,60
65
+ napari_tmidas-0.3.5.dist-info/top_level.txt,sha256=63ybdxCZ4SeT13f_Ou4TsivGV_2Gtm_pJOXToAt30_E,14
66
+ napari_tmidas-0.3.5.dist-info/RECORD,,