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.
- rescale4dl-0.1.1/LICENSE.txt +22 -0
- rescale4dl-0.1.1/PKG-INFO +118 -0
- rescale4dl-0.1.1/README.md +75 -0
- rescale4dl-0.1.1/pyproject.toml +62 -0
- rescale4dl-0.1.1/setup.cfg +4 -0
- rescale4dl-0.1.1/src/rescale4dl/__init__.py +0 -0
- rescale4dl-0.1.1/src/rescale4dl/batch.py +209 -0
- rescale4dl-0.1.1/src/rescale4dl/blurring.py +14 -0
- rescale4dl-0.1.1/src/rescale4dl/downscaling.py +67 -0
- rescale4dl-0.1.1/src/rescale4dl/metrics/__init__.py +0 -0
- rescale4dl-0.1.1/src/rescale4dl/metrics/binary.py +0 -0
- rescale4dl-0.1.1/src/rescale4dl/metrics/fov_estimates.py +111 -0
- rescale4dl-0.1.1/src/rescale4dl/metrics/instance.py +0 -0
- rescale4dl-0.1.1/src/rescale4dl/metrics/metrics.py +2111 -0
- rescale4dl-0.1.1/src/rescale4dl/metrics/metrics_skeleton.py +354 -0
- rescale4dl-0.1.1/src/rescale4dl/metrics/model_pdf_metrics.py +23 -0
- rescale4dl-0.1.1/src/rescale4dl/metrics/plot.py +59 -0
- rescale4dl-0.1.1/src/rescale4dl/metrics/properties.py +60 -0
- rescale4dl-0.1.1/src/rescale4dl/metrics/semantic.py +0 -0
- rescale4dl-0.1.1/src/rescale4dl/morphology.py +2892 -0
- rescale4dl-0.1.1/src/rescale4dl/upscaling.py +72 -0
- rescale4dl-0.1.1/src/rescale4dl/utils.py +320 -0
- rescale4dl-0.1.1/src/rescale4dl.egg-info/PKG-INFO +118 -0
- rescale4dl-0.1.1/src/rescale4dl.egg-info/SOURCES.txt +26 -0
- rescale4dl-0.1.1/src/rescale4dl.egg-info/dependency_links.txt +1 -0
- rescale4dl-0.1.1/src/rescale4dl.egg-info/requires.txt +32 -0
- rescale4dl-0.1.1/src/rescale4dl.egg-info/top_level.txt +1 -0
- 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
|
+
[](https://www.python.org/downloads/)
|
|
47
|
+
[](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
|
+
[](https://www.python.org/downloads/)
|
|
4
|
+
[](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"]
|
|
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
|