rescale4dl 0.1.1__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 (28) hide show
  1. rescale4dl-0.1.1/LICENSE.txt +22 -0
  2. rescale4dl-0.1.1/PKG-INFO +118 -0
  3. rescale4dl-0.1.1/README.md +75 -0
  4. rescale4dl-0.1.1/pyproject.toml +62 -0
  5. rescale4dl-0.1.1/setup.cfg +4 -0
  6. rescale4dl-0.1.1/src/rescale4dl/__init__.py +0 -0
  7. rescale4dl-0.1.1/src/rescale4dl/batch.py +209 -0
  8. rescale4dl-0.1.1/src/rescale4dl/blurring.py +14 -0
  9. rescale4dl-0.1.1/src/rescale4dl/downscaling.py +67 -0
  10. rescale4dl-0.1.1/src/rescale4dl/metrics/__init__.py +0 -0
  11. rescale4dl-0.1.1/src/rescale4dl/metrics/binary.py +0 -0
  12. rescale4dl-0.1.1/src/rescale4dl/metrics/fov_estimates.py +111 -0
  13. rescale4dl-0.1.1/src/rescale4dl/metrics/instance.py +0 -0
  14. rescale4dl-0.1.1/src/rescale4dl/metrics/metrics.py +2111 -0
  15. rescale4dl-0.1.1/src/rescale4dl/metrics/metrics_skeleton.py +354 -0
  16. rescale4dl-0.1.1/src/rescale4dl/metrics/model_pdf_metrics.py +23 -0
  17. rescale4dl-0.1.1/src/rescale4dl/metrics/plot.py +59 -0
  18. rescale4dl-0.1.1/src/rescale4dl/metrics/properties.py +60 -0
  19. rescale4dl-0.1.1/src/rescale4dl/metrics/semantic.py +0 -0
  20. rescale4dl-0.1.1/src/rescale4dl/morphology.py +2892 -0
  21. rescale4dl-0.1.1/src/rescale4dl/upscaling.py +72 -0
  22. rescale4dl-0.1.1/src/rescale4dl/utils.py +320 -0
  23. rescale4dl-0.1.1/src/rescale4dl.egg-info/PKG-INFO +118 -0
  24. rescale4dl-0.1.1/src/rescale4dl.egg-info/SOURCES.txt +26 -0
  25. rescale4dl-0.1.1/src/rescale4dl.egg-info/dependency_links.txt +1 -0
  26. rescale4dl-0.1.1/src/rescale4dl.egg-info/requires.txt +32 -0
  27. rescale4dl-0.1.1/src/rescale4dl.egg-info/top_level.txt +1 -0
  28. rescale4dl-0.1.1/tests/test_blurring.py +16 -0
@@ -0,0 +1,22 @@
1
+
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2025 rescale4dl
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.4
2
+ Name: rescale4dl
3
+ Version: 0.1.1
4
+ Summary: My python package package
5
+ Author-email: Mariana G Ferreira <mariana.g.ferreira@itqb.unl.pt>, Bruno Saraiva <bruno.msaraiva2@gmail.com>, Ricardo Henriques <ricardo.jv.henriques@gmail.com>, Estibaliz Gómez-de-Mariscal <esgodema@gmail.com>
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3.10
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: <=3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE.txt
13
+ Requires-Dist: imageio>=2.33.1
14
+ Requires-Dist: jupyter>=1.1.1
15
+ Requires-Dist: matplotlib>=3.9.2
16
+ Requires-Dist: pandas>=2.2.3
17
+ Requires-Dist: pillow>=11.0.0
18
+ Requires-Dist: pypdf>=4.2.0
19
+ Requires-Dist: scikit-image>=0.22.0
20
+ Requires-Dist: scikit-learn>=1.5.2
21
+ Requires-Dist: scipy>=1.14.1
22
+ Requires-Dist: seaborn>=0.13.2
23
+ Requires-Dist: setuptools>=68.2.2
24
+ Requires-Dist: tifffile>=2024.12.12
25
+ Requires-Dist: nanopyx>=1.0.0
26
+ Provides-Extra: all
27
+ Requires-Dist: rescale4dl[dev,test]; extra == "all"
28
+ Provides-Extra: dev
29
+ Requires-Dist: pre-commit>=3.7.0; extra == "dev"
30
+ Requires-Dist: ipykernel>=6.29.4; extra == "dev"
31
+ Requires-Dist: ruff>=0.4.3; extra == "dev"
32
+ Requires-Dist: gptrepo>=1.0.3; extra == "dev"
33
+ Requires-Dist: pdoc>=12.3.1; extra == "dev"
34
+ Requires-Dist: build; extra == "dev"
35
+ Requires-Dist: gptrepo; extra == "dev"
36
+ Provides-Extra: test
37
+ Requires-Dist: pytest>=8.2.0; extra == "test"
38
+ Requires-Dist: pytest-xdist>=3.6.1; extra == "test"
39
+ Requires-Dist: pytest-cov>=5.0.0; extra == "test"
40
+ Requires-Dist: nbmake>=1.5.3; extra == "test"
41
+ Requires-Dist: mypy>=1.10.0; extra == "test"
42
+ Dynamic: license-file
43
+
44
+ # ReScale4DL: Balancing Pixel and Contextual Information for Enhanced Bioimage Segmentation
45
+
46
+ [![Python 3.8+](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/)
47
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
48
+
49
+ <img src="https://raw.githubusercontent.com/HenriquesLab/ReScale4DL/refs/heads/main/.github/logo.png?token=GHSAT0AAAAAAC5PYSXBX34C76FL7KUWDO62Z7VHF5A" align="right" width="150"/>
50
+
51
+ A systematic approach for determining optimal image resolution in deep learning-based microscopy segmentation, balancing accuracy with acquisition/storage costs.
52
+
53
+ ## Key Features
54
+ - **Resolution simulation**: Rescale images and their respective annotations (upsample and downsample)
55
+ - **Segmentation evaluation**: Compare performance across resolutions using:
56
+ - Mean Intersection-over-Union (IoU)
57
+ - Morphological features
58
+ - Potential throughput
59
+ - Personalised metrics
60
+ - **Visualization tools**: Generate comparative plots and sample outputs
61
+
62
+ ## Installation
63
+
64
+ ReScale4DL is available as a Python package through pip.
65
+ Activate your conda environment or create one and install it with `pip`:
66
+
67
+ ```terminal
68
+ pip install rescale4dl
69
+ ```
70
+
71
+ ### Manual installation
72
+ Manual installation from using GitHub repository
73
+ ```terminal
74
+ git clone https://github.com/HenriquesLab/ReScale4DL.git
75
+ cd rescale4dl
76
+ conda create -n rescale4dl "python<=3.12"
77
+ conda activate rescale4dl
78
+ python -m pip install .
79
+ ```
80
+
81
+
82
+ ## Usage
83
+
84
+ ### 1. Image Rescaling
85
+ Notebook: `Rescale_Images.ipynb`
86
+
87
+ ### 2. Segmentation Analysis
88
+ Notebook: `Evaluate_Segmentation.ipynb`
89
+
90
+ ### 3. Rescale and crop
91
+ Notebook: `Rescale_Foundation_Models.ipynb`
92
+
93
+
94
+ ## Contributing
95
+ We welcome contributions through:
96
+ - [Issue reporting](https://github.com/HenriquesLab/ReScale4D/issues)
97
+ - [Pull requests](https://github.com/HenriquesLab/ReScale4D/pulls)
98
+
99
+ ## License
100
+ MIT License - See [LICENSE](LICENSE) for details
101
+
102
+ ## Citation
103
+ If using this work in research, please cite:
104
+ ```
105
+ @article{gferreira2025rescale4dl,
106
+ title={ReScale4DL: Balancing Pixel and Contextual Information for Enhanced Bioimage Segmentation},
107
+ author={Ferreira, Mariana G. and Saraiva, Bruno M. and Brito, Antonio D. and Pinho, Mariana G. and Henriques, Ricardo and G{\'o}mez-de-Mariscal, Estibaliz },
108
+ journal={bioRxiv},
109
+ year={2025},
110
+ publisher = {Cold Spring Harbor Laboratory},
111
+ URL = ,
112
+ eprint = ,
113
+ }
114
+ ```
115
+
116
+
117
+
118
+
@@ -0,0 +1,75 @@
1
+ # ReScale4DL: Balancing Pixel and Contextual Information for Enhanced Bioimage Segmentation
2
+
3
+ [![Python 3.8+](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ <img src="https://raw.githubusercontent.com/HenriquesLab/ReScale4DL/refs/heads/main/.github/logo.png?token=GHSAT0AAAAAAC5PYSXBX34C76FL7KUWDO62Z7VHF5A" align="right" width="150"/>
7
+
8
+ A systematic approach for determining optimal image resolution in deep learning-based microscopy segmentation, balancing accuracy with acquisition/storage costs.
9
+
10
+ ## Key Features
11
+ - **Resolution simulation**: Rescale images and their respective annotations (upsample and downsample)
12
+ - **Segmentation evaluation**: Compare performance across resolutions using:
13
+ - Mean Intersection-over-Union (IoU)
14
+ - Morphological features
15
+ - Potential throughput
16
+ - Personalised metrics
17
+ - **Visualization tools**: Generate comparative plots and sample outputs
18
+
19
+ ## Installation
20
+
21
+ ReScale4DL is available as a Python package through pip.
22
+ Activate your conda environment or create one and install it with `pip`:
23
+
24
+ ```terminal
25
+ pip install rescale4dl
26
+ ```
27
+
28
+ ### Manual installation
29
+ Manual installation from using GitHub repository
30
+ ```terminal
31
+ git clone https://github.com/HenriquesLab/ReScale4DL.git
32
+ cd rescale4dl
33
+ conda create -n rescale4dl "python<=3.12"
34
+ conda activate rescale4dl
35
+ python -m pip install .
36
+ ```
37
+
38
+
39
+ ## Usage
40
+
41
+ ### 1. Image Rescaling
42
+ Notebook: `Rescale_Images.ipynb`
43
+
44
+ ### 2. Segmentation Analysis
45
+ Notebook: `Evaluate_Segmentation.ipynb`
46
+
47
+ ### 3. Rescale and crop
48
+ Notebook: `Rescale_Foundation_Models.ipynb`
49
+
50
+
51
+ ## Contributing
52
+ We welcome contributions through:
53
+ - [Issue reporting](https://github.com/HenriquesLab/ReScale4D/issues)
54
+ - [Pull requests](https://github.com/HenriquesLab/ReScale4D/pulls)
55
+
56
+ ## License
57
+ MIT License - See [LICENSE](LICENSE) for details
58
+
59
+ ## Citation
60
+ If using this work in research, please cite:
61
+ ```
62
+ @article{gferreira2025rescale4dl,
63
+ title={ReScale4DL: Balancing Pixel and Contextual Information for Enhanced Bioimage Segmentation},
64
+ author={Ferreira, Mariana G. and Saraiva, Bruno M. and Brito, Antonio D. and Pinho, Mariana G. and Henriques, Ricardo and G{\'o}mez-de-Mariscal, Estibaliz },
65
+ journal={bioRxiv},
66
+ year={2025},
67
+ publisher = {Cold Spring Harbor Laboratory},
68
+ URL = ,
69
+ eprint = ,
70
+ }
71
+ ```
72
+
73
+
74
+
75
+
@@ -0,0 +1,62 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "rescale4dl"
7
+ version = "0.1.1"
8
+ description = "My python package package"
9
+ authors = [
10
+ { name = "Mariana G Ferreira", email = "mariana.g.ferreira@itqb.unl.pt" },
11
+ { name = "Bruno Saraiva", email = "bruno.msaraiva2@gmail.com" },
12
+ { name = "Ricardo Henriques", email = "ricardo.jv.henriques@gmail.com" },
13
+ { name = "Estibaliz Gómez-de-Mariscal", email = "esgodema@gmail.com" }
14
+ ]
15
+ readme = { file = "README.md", content-type = "text/markdown" }
16
+ license = { text = "MIT" }
17
+ classifiers = [
18
+ "Programming Language :: Python :: 3.10",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: OS Independent"
21
+ ]
22
+ requires-python = "<= 3.12"
23
+ dependencies = [
24
+ "imageio>=2.33.1",
25
+ "jupyter>=1.1.1",
26
+ "matplotlib>=3.9.2",
27
+ "pandas>=2.2.3",
28
+ "pillow>=11.0.0",
29
+ "pypdf>=4.2.0",
30
+ "scikit-image>=0.22.0",
31
+ "scikit-learn>=1.5.2",
32
+ "scipy>=1.14.1",
33
+ "seaborn>=0.13.2",
34
+ "setuptools>=68.2.2",
35
+ "tifffile>=2024.12.12",
36
+ "nanopyx>=1.0.0",
37
+ ]
38
+
39
+ [project.optional-dependencies]
40
+ all = ["rescale4dl[dev, test]"]
41
+ dev = [
42
+ "pre-commit>=3.7.0",
43
+ "ipykernel>=6.29.4",
44
+ "ruff>=0.4.3",
45
+ "gptrepo>=1.0.3",
46
+ "pdoc>=12.3.1",
47
+ "build",
48
+ "gptrepo"
49
+ ]
50
+ test = [
51
+ "pytest>=8.2.0",
52
+ "pytest-xdist>=3.6.1",
53
+ "pytest-cov>=5.0.0",
54
+ "nbmake>=1.5.3",
55
+ "mypy>=1.10.0"
56
+ ]
57
+
58
+ [tool.setuptools.packages.find]
59
+ where = ["src"]
60
+
61
+ [tool.setuptools.package-data]
62
+ "*" = ["*.yaml"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,209 @@
1
+ import os
2
+ import numpy as np
3
+
4
+ from typing import List
5
+ from tkinter import filedialog as fd
6
+ from tifffile import imread, imwrite
7
+
8
+ from .utils import check_crop_img, crop_with_padding
9
+ from .blurring import gaussian_blur
10
+ from .downscaling import binning_img, binning_label
11
+ from .upscaling import upsample_img, upsample_labels
12
+ from tqdm import tqdm
13
+
14
+
15
+
16
+ def downsample_batch(input_folder_path: str, input_folder_name: str, downsampling_factor: int, keep_dims: bool = False, mode: str = "sum"):
17
+ """Downsamples a batch of images by a given factor. The last two dimensions of the array are binned.
18
+ Creates new folders outside input_folder to store the results.
19
+ :param input_folder_path: path to folder containing an "images" folder and a "labels" folder. Images inside both folders should have the same name.
20
+ :param downsampling_factor: factor used to bin dimensions
21
+ :para keep_dims: whether to keep the original dimensions or just blur the image (defaults to False)
22
+ :param mode: can be either sum, max or mean, defaults to sum if not specified or not valid mode
23
+ """
24
+
25
+ if keep_dims:
26
+ new_dataset_path = os.path.join(os.path.dirname(os.path.dirname(input_folder_path)), "Processed", f"{input_folder_name}_downsampled_{downsampling_factor}_mode_{mode}_same_dims")
27
+ else:
28
+ new_dataset_path = os.path.join(os.path.dirname(os.path.dirname(input_folder_path)), "Processed", f"{input_folder_name}_downsampled_{downsampling_factor}_mode_{mode}_diff_dims")
29
+
30
+ new_images_path = os.path.join(new_dataset_path, "Images")
31
+ new_labels_path = os.path.join(new_dataset_path, "Labels")
32
+
33
+ if not os.path.exists(new_dataset_path):
34
+ os.mkdir(new_dataset_path)
35
+ os.mkdir(new_images_path)
36
+ os.mkdir(new_labels_path)
37
+
38
+ for img_name in os.listdir(os.path.join(input_folder_path, "Images")):
39
+ img = imread(os.path.join(input_folder_path, "Images", img_name)).astype(np.float32)
40
+ img = check_crop_img(img, downsampling_factor)
41
+ lbl = imread(os.path.join(input_folder_path, "Labels", img_name)).astype(np.float32)
42
+ lbl = check_crop_img(lbl, downsampling_factor)
43
+ imwrite(os.path.join(new_images_path, img_name), binning_img(img, downsampling_factor, keep_dims=keep_dims, mode=mode))
44
+ if keep_dims:
45
+ imwrite(os.path.join(new_labels_path, img_name), lbl.astype(np.uint16))
46
+ else:
47
+ imwrite(os.path.join(new_labels_path, img_name), binning_label(lbl, downsampling_factor).astype(np.uint16))
48
+
49
+
50
+ def upsample_batch(input_folder_path: str, input_folder_name: str, magnification: int, keep_dims: bool = False):
51
+ """Upsamples a batch of images by the magnification param using Catmull-rom interpolation and labels using Nearest-neighbor.
52
+ Creates new folders outside input_folder to store the results.
53
+ :param input_folder_path: path to folder containing an "images" folder and a "labels" folder. Images inside both folders should have the same name.
54
+ :param magnification: upscaling factor
55
+ :para keep_dims: whether to keep the original dimensions or just blur the image (defaults to False)
56
+ """
57
+
58
+ if keep_dims:
59
+ new_dataset_path = os.path.join(os.path.dirname(os.path.dirname(input_folder_path)), "Processed", f"{input_folder_name}_upsampled_{magnification}_same_dims")
60
+ else:
61
+ new_dataset_path = os.path.join(os.path.dirname(os.path.dirname(input_folder_path)), "Processed", f"{input_folder_name}_upsampled_{magnification}_diff_dims")
62
+
63
+ new_images_path = os.path.join(new_dataset_path, "Images")
64
+ new_labels_path = os.path.join(new_dataset_path, "Labels")
65
+
66
+ if not os.path.exists(new_dataset_path):
67
+ os.mkdir(new_dataset_path)
68
+ os.mkdir(new_images_path)
69
+ os.mkdir(new_labels_path)
70
+
71
+ for img_name in os.listdir(os.path.join(input_folder_path, "Images")):
72
+ img = imread(os.path.join(input_folder_path, "Images", img_name)).astype(np.float32)
73
+ lbl = imread(os.path.join(input_folder_path, "Labels", img_name)).astype(np.float32)
74
+ imwrite(os.path.join(new_images_path, img_name), upsample_img(img, magnification, keep_dims=keep_dims))
75
+ imwrite(os.path.join(new_labels_path, img_name), upsample_labels(lbl, magnification, keep_dims=keep_dims).astype(np.uint16))
76
+
77
+
78
+ def blur_batch(input_folder_path: str, input_folder_name: str, gaussian_sigma: float):
79
+ """Applies Gaussian blur to a batch of images.
80
+ Creates new folders outside input_folder to store the results.
81
+ :param input_folder_path: path to folder containing an "images" folder and a "labels" folder. Images inside both folders should have the same name.
82
+ :param gaussians: list of standard deviations
83
+ """
84
+
85
+ new_dataset_path = os.path.join(os.path.dirname(os.path.dirname(input_folder_path)), "Processed", f"{input_folder_name}_blurred_{gaussian_sigma}")
86
+ new_images_path = os.path.join(new_dataset_path, "Images")
87
+ new_labels_path = os.path.join(new_dataset_path, "Labels")
88
+
89
+ if not os.path.exists(new_dataset_path):
90
+ os.mkdir(new_dataset_path)
91
+ os.mkdir(new_images_path)
92
+ os.mkdir(new_labels_path)
93
+
94
+ for img_name in os.listdir(os.path.join(input_folder_path, "Images")):
95
+ img = imread(os.path.join(input_folder_path, "Images", img_name)).astype(np.float32)
96
+ lbl = imread(os.path.join(input_folder_path, "Labels", img_name)).astype(np.float32)
97
+ imwrite(os.path.join(new_images_path, img_name), gaussian_blur(img, gaussian_sigma))
98
+ imwrite(os.path.join(new_labels_path, img_name), lbl.astype(np.uint16))
99
+
100
+
101
+ def process_batch(input_folder_path: str, input_folder_name: str, downsampling_factors: List[int], magnifications: List[int], gaussians: List[float], modes: List[str] = ["sum", "mean"]):
102
+ """Performs all downstream preprocessing on a single dataset"""
103
+
104
+ for mag in magnifications:
105
+ upsample_batch(
106
+ input_folder_path,
107
+ input_folder_name,
108
+ mag
109
+ )
110
+ upsample_batch(
111
+ input_folder_path,
112
+ input_folder_name,
113
+ mag,
114
+ keep_dims=True
115
+ )
116
+
117
+ for mode in modes:
118
+ for dsf in downsampling_factors:
119
+ downsample_batch(
120
+ input_folder_path,
121
+ input_folder_name,
122
+ dsf,
123
+ keep_dims=True,
124
+ mode=mode
125
+ )
126
+ downsample_batch(
127
+ input_folder_path,
128
+ input_folder_name,
129
+ dsf,
130
+ keep_dims=False,
131
+ mode=mode
132
+ )
133
+
134
+ for gau in gaussians:
135
+ blur_batch(
136
+ input_folder_path,
137
+ input_folder_name,
138
+ gau
139
+ )
140
+
141
+
142
+ def process_all_datasets(datasets_path: str, downsampling_factor: List[int], magnification: List[int], gaussians: List[float], modes: List[str] = ["sum", "mean"]):
143
+ """Performs all downstream preprocessing on all datasets in a folder"""
144
+
145
+ if datasets_path is None:
146
+ datasets_path = fd.askdirectory()
147
+
148
+ if not os.path.exists(os.path.join(os.path.dirname(datasets_path), "Processed")):
149
+ os.mkdir(os.path.join(os.path.dirname(datasets_path), "Processed"))
150
+
151
+ for fld in os.listdir(datasets_path):
152
+ if os.path.isdir(os.path.join(datasets_path, fld)):
153
+ process_batch(
154
+ os.path.join(datasets_path, fld),
155
+ fld,
156
+ downsampling_factor,
157
+ magnification,
158
+ gaussians,
159
+ modes
160
+ )
161
+
162
+ # Core Processing Functions
163
+ def rescale_image(image: np.ndarray, factor: int, mode: str) -> np.ndarray:
164
+ """Rescale image using specified method"""
165
+ if mode == "down":
166
+ return binning_img(image, factor, keep_dims=False, mode="mean")
167
+ elif mode == "up":
168
+ return upsample_img(image, factor, keep_dims=False)
169
+ else:
170
+ raise ValueError(f"Invalid scale mode: {mode}. Use 'up' or 'down'")
171
+
172
+
173
+ # Processing Pipeline
174
+ def rescale_and_crop_image(INPUT_DIR, OUTPUT_DIR, SCALE_FACTOR, SCALE_MODE, TARGET_SHAPE, SAVE_SCALED):
175
+ # Create output directories
176
+ os.makedirs(os.path.join(OUTPUT_DIR, "scaled"), exist_ok=True)
177
+ os.makedirs(os.path.join(OUTPUT_DIR, "final"), exist_ok=True)
178
+ # Get image list
179
+ images = [f for f in os.listdir(INPUT_DIR) if f.lower().endswith('.tif')]
180
+
181
+ for filename in images:
182
+ # Load image
183
+ img_path = os.path.join(INPUT_DIR, filename)
184
+ image = np.squeeze(imread(img_path))
185
+
186
+ # Rescale
187
+ scaled = rescale_image(image, SCALE_FACTOR, SCALE_MODE)
188
+
189
+ # Save intermediate
190
+ if SAVE_SCALED:
191
+ imwrite(os.path.join(OUTPUT_DIR, "scaled", f"scaled_{SCALE_MODE}_{SCALE_FACTOR}_{filename}"), scaled)
192
+
193
+ # Crop/Pad
194
+ final_image = crop_with_padding(scaled, TARGET_SHAPE)
195
+
196
+ # Save result
197
+ imwrite(os.path.join(OUTPUT_DIR, "final", f"processed_{SCALE_MODE}_{SCALE_FACTOR}_{filename}"), final_image)
198
+
199
+
200
+ def rescale_and_crop(INPUT_DIR, OUTPUT_DIR, SCALE_FACTOR, TARGET_SHAPE, SAVE_SCALED):
201
+ for s in tqdm(SCALE_FACTOR, desc="Rescaling and cropping"):
202
+ SCALE_MODE = "down" if s<1 else "up"
203
+ if s < 1:
204
+ SCALE_MODE = "down"
205
+ s = np.floor(1/s)
206
+ else:
207
+ SCALE_MODE = "up"
208
+ print(s)
209
+ rescale_and_crop_image(INPUT_DIR, OUTPUT_DIR, np.int8(s), SCALE_MODE, TARGET_SHAPE, SAVE_SCALED)
@@ -0,0 +1,14 @@
1
+ import numpy as np
2
+ from skimage.filters import gaussian
3
+
4
+
5
+ def gaussian_blur(img: np.array, sigma: float):
6
+ """
7
+ Apply Gaussian blur to an image.
8
+ Parameters:
9
+ img (ndarray): The input image.
10
+ sigma (float): The standard deviation for Gaussian kernel.
11
+ Returns:
12
+ ndarray: The blurred image.
13
+ """
14
+ return gaussian(img, sigma=sigma)
@@ -0,0 +1,67 @@
1
+ import numpy as np
2
+
3
+ from skimage.transform import rescale
4
+ from nanopyx.core.transform.binning import rebin_2d
5
+ from nanopyx.core.transform._le_convolution import Convolution
6
+
7
+
8
+
9
+ def binning_downsize(img: np.array, downsampling_factor: int, mode: str = "sum"):
10
+ """Bins a 2D array by a given factor. The last two dimensions of the array are binned.
11
+ :param arr: numpy array with any shape as long as last two dimensions are y, x (example: time, channel, z, y, x)
12
+ :param bin_factor: factor used to bin dimensions
13
+ :param mode: can be either sum or mean, defaults to sum if not specified or not valid mode
14
+ :return: binned array
15
+ """
16
+ return rebin_2d(img, downsampling_factor, mode=mode)
17
+
18
+
19
+ def binning_blur(img: np.array, downsampling_factor: int, mode: str = "sum"):
20
+ """Blurs a 2D array by a given factor using binning. The last two dimensions of the array are binned.
21
+ :param arr: numpy array with any shape as long as last two dimensions are y, x (example: time, channel, z, y, x)
22
+ :param bin_factor: factor used to bin dimensions
23
+ :param mode: can be either sum or mean, defaults to sum if not specified or not valid mode
24
+ :return: binned array
25
+ """
26
+ conv = Convolution()
27
+
28
+ if mode not in ["sum", "mean"]:
29
+ mode = "sum"
30
+
31
+ if mode == "sum":
32
+ kernel = np.ones((downsampling_factor, downsampling_factor), dtype=np.float32)
33
+ return np.asarray(
34
+ conv.run(img.astype(np.float32), kernel),
35
+ dtype=np.float32
36
+ )
37
+
38
+ elif mode == "mean":
39
+ kernel = np.ones((
40
+ downsampling_factor, downsampling_factor),
41
+ dtype=np.float32) / (
42
+ downsampling_factor**2
43
+ )
44
+ return np.asarray(
45
+ conv.run(img.astype(np.float32), kernel),
46
+ dtype=np.float32
47
+ )
48
+
49
+
50
+ def binning_img(img: np.array, downsampling_factor: int, keep_dims: bool = False, mode: str = "sum"):
51
+ """Bins a 2D array by a given factor. The last two dimensions of the array are binned.
52
+ :param arr: numpy array with any shape as long as last two dimensions are y, x (example: time, channel, z, y, x)
53
+ :param bin_factor: factor used to bin dimensions
54
+ :param keep_dims: whether to keep the original dimensions or just blur the image (defaults to False)
55
+ :param mode: can be either sum or mean, defaults to sum if not specified or not valid mode
56
+ :return: binned array
57
+ """
58
+ if keep_dims:
59
+ return binning_blur(img, downsampling_factor, mode=mode)
60
+ else:
61
+ return binning_downsize(img, downsampling_factor, mode=mode)
62
+
63
+ def binning_label(img: np.array, downsampling_factor: int):
64
+ """Bins a 2D array by a given factor using Nearest-neighbor.
65
+ :params img: Input image, should be a 2-d np array.
66
+ :params downsampling_factor: factor used to bin dimensions"""
67
+ return rescale(img, 1/downsampling_factor, anti_aliasing=False, order=0).astype(np.uint16)
File without changes
File without changes