stouputils 1.2.40__tar.gz → 1.2.41__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.
- {stouputils-1.2.40 → stouputils-1.2.41}/PKG-INFO +27 -2
- {stouputils-1.2.40 → stouputils-1.2.41}/README.md +26 -1
- {stouputils-1.2.40 → stouputils-1.2.41}/pyproject.toml +3 -1
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/all_doctests.py +28 -7
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/ctx.py +2 -2
- stouputils-1.2.41/stouputils/data_science/config/get.py +51 -0
- stouputils-1.2.41/stouputils/data_science/config/set.py +103 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/__init__.py +66 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/auto_contrast.py +79 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/axis_flip.py +58 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/bias_field_correction.py +74 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/binary_threshold.py +73 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/blur.py +59 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/brightness.py +54 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/canny.py +110 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/clahe.py +92 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/common.py +30 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/contrast.py +53 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/curvature_flow_filter.py +74 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/denoise.py +378 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/histogram_equalization.py +123 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/invert.py +64 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/laplacian.py +60 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/median_blur.py +52 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/noise.py +59 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/normalize.py +65 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/random_erase.py +66 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/resize.py +69 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/rotation.py +80 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/salt_pepper.py +68 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/sharpening.py +55 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/shearing.py +64 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/threshold.py +64 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/translation.py +71 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image/zoom.py +83 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image_augmentation.py +118 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/image_preprocess.py +183 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/prosthesis_detection.py +359 -0
- stouputils-1.2.41/stouputils/data_science/data_processing/technique.py +481 -0
- stouputils-1.2.41/stouputils/data_science/dataset/__init__.py +45 -0
- stouputils-1.2.41/stouputils/data_science/dataset/dataset.py +292 -0
- stouputils-1.2.41/stouputils/data_science/dataset/dataset_loader.py +135 -0
- stouputils-1.2.41/stouputils/data_science/dataset/grouping_strategy.py +296 -0
- stouputils-1.2.41/stouputils/data_science/dataset/image_loader.py +100 -0
- stouputils-1.2.41/stouputils/data_science/dataset/xy_tuple.py +696 -0
- stouputils-1.2.41/stouputils/data_science/metric_dictionnary.py +94 -0
- stouputils-1.2.41/stouputils/data_science/metric_utils.py +692 -0
- stouputils-1.2.41/stouputils/data_science/mlflow_utils.py +200 -0
- stouputils-1.2.41/stouputils/data_science/models/abstract_model.py +149 -0
- stouputils-1.2.41/stouputils/data_science/models/all.py +99 -0
- stouputils-1.2.41/stouputils/data_science/models/base_keras.py +765 -0
- stouputils-1.2.41/stouputils/data_science/models/keras/all.py +56 -0
- stouputils-1.2.41/stouputils/data_science/models/keras/convnext.py +62 -0
- stouputils-1.2.41/stouputils/data_science/models/keras/densenet.py +50 -0
- stouputils-1.2.41/stouputils/data_science/models/keras/efficientnet.py +60 -0
- stouputils-1.2.41/stouputils/data_science/models/keras/mobilenet.py +56 -0
- stouputils-1.2.41/stouputils/data_science/models/keras/resnet.py +99 -0
- stouputils-1.2.41/stouputils/data_science/models/keras/squeezenet.py +233 -0
- stouputils-1.2.41/stouputils/data_science/models/keras/vgg.py +42 -0
- stouputils-1.2.41/stouputils/data_science/models/keras/xception.py +38 -0
- stouputils-1.2.41/stouputils/data_science/models/keras_utils/callbacks/__init__.py +18 -0
- stouputils-1.2.41/stouputils/data_science/models/keras_utils/callbacks/colored_progress_bar.py +219 -0
- stouputils-1.2.41/stouputils/data_science/models/keras_utils/callbacks/learning_rate_finder.py +148 -0
- stouputils-1.2.41/stouputils/data_science/models/keras_utils/callbacks/progressive_unfreezing.py +249 -0
- stouputils-1.2.41/stouputils/data_science/models/keras_utils/callbacks/warmup_scheduler.py +66 -0
- stouputils-1.2.41/stouputils/data_science/models/keras_utils/losses/__init__.py +12 -0
- stouputils-1.2.41/stouputils/data_science/models/keras_utils/losses/next_generation_loss.py +56 -0
- stouputils-1.2.41/stouputils/data_science/models/keras_utils/visualizations.py +413 -0
- stouputils-1.2.41/stouputils/data_science/models/model_interface.py +887 -0
- stouputils-1.2.41/stouputils/data_science/models/sandbox.py +116 -0
- stouputils-1.2.41/stouputils/data_science/range_tuple.py +234 -0
- stouputils-1.2.41/stouputils/data_science/scripts/augment_dataset.py +77 -0
- stouputils-1.2.41/stouputils/data_science/scripts/exhaustive_process.py +128 -0
- stouputils-1.2.41/stouputils/data_science/scripts/preprocess_dataset.py +70 -0
- stouputils-1.2.41/stouputils/data_science/scripts/routine.py +168 -0
- stouputils-1.2.41/stouputils/data_science/utils.py +218 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/io.py +32 -2
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/parallel.py +312 -294
- {stouputils-1.2.40 → stouputils-1.2.41}/.gitignore +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/LICENSE +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/__init__.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/applications/__init__.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/applications/automatic_docs.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/applications/upscaler/__init__.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/applications/upscaler/config.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/applications/upscaler/image.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/applications/upscaler/video.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/archive.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/backup.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/collections.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/continuous_delivery/__init__.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/continuous_delivery/cd_utils.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/continuous_delivery/github.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/continuous_delivery/pypi.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/continuous_delivery/pyproject.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/decorators.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/dont_look/zip_file_override.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/image.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/installer/__init__.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/installer/common.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/installer/downloader.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/installer/linux.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/installer/main.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/installer/windows.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/print.py +0 -0
- {stouputils-1.2.40 → stouputils-1.2.41}/stouputils/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stouputils
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.41
|
|
4
4
|
Summary: Stouputils is a collection of utility modules designed to simplify and enhance the development process. It includes a range of tools for tasks such as execution of doctests, display utilities, decorators, as well as context managers, and many more.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Stoupy51/stouputils
|
|
6
6
|
Project-URL: Issues, https://github.com/Stoupy51/stouputils/issues
|
|
@@ -62,7 +62,7 @@ It includes a range of tools for tasks such as execution of doctests, display ut
|
|
|
62
62
|
<pre class="code-tree">stouputils/
|
|
63
63
|
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.applications.html">applications/</a>
|
|
64
64
|
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.applications.automatic_docs.html">automatic_docs.py</a> <span class="comment"># 📚 Documentation generation utilities (used to create this documentation)</span>
|
|
65
|
-
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.applications.upscaler.html">upscaler/</a>
|
|
65
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.applications.upscaler.html">upscaler/</a> <span class="comment"># 🔎 Image & Video upscaler (configurable)</span>
|
|
66
66
|
│ └── ...
|
|
67
67
|
│
|
|
68
68
|
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.continuous_delivery.html">continuous_delivery/</a>
|
|
@@ -72,6 +72,31 @@ It includes a range of tools for tasks such as execution of doctests, display ut
|
|
|
72
72
|
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.continuous_delivery.pyproject.html">pyproject.py</a> <span class="comment"># 📝 Pyproject.toml utilities</span>
|
|
73
73
|
│ └── ...
|
|
74
74
|
│
|
|
75
|
+
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.html">data_science/</a>
|
|
76
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.config.html">config/</a> <span class="comment"># ⚙️ Configuration utilities for data science</span>
|
|
77
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.dataset.html">dataset/</a> <span class="comment"># 📊 Dataset handling (dataset, dataset_loader, grouping_strategy)</span>
|
|
78
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.data_processing.html">data_processing/</a> <span class="comment"># 🔄 Data processing utilities (image augmentation, preprocessing)</span>
|
|
79
|
+
│ │ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.data_processing.image.html">image/</a> <span class="comment"># 🖼️ Image processing techniques</span>
|
|
80
|
+
│ │ └── ...
|
|
81
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.models.html">models/</a> <span class="comment"># 🧠 ML/DL model interfaces and implementations</span>
|
|
82
|
+
│ │ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.models.keras.html">keras/</a> <span class="comment"># 🤖 Keras model implementations</span>
|
|
83
|
+
│ │ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.models.keras_utils.html">keras_utils/</a> <span class="comment"># 🛠️ Keras utilities (callbacks, losses, visualizations)</span>
|
|
84
|
+
│ │ └── ...
|
|
85
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.scripts.html">scripts/</a> <span class="comment"># 📜 Data science scripts (augment, preprocess, routine)</span>
|
|
86
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.metric_utils.html">metric_utils.py</a> <span class="comment"># 📏 Metrics utilities for ML/DL models</span>
|
|
87
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.mlflow_utils.html">mlflow_utils.py</a> <span class="comment"># 📊 MLflow integration utilities</span>
|
|
88
|
+
│ └── ...
|
|
89
|
+
│
|
|
90
|
+
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.dont_look.html">dont_look/</a> <span class="comment"># 🙈 Internal utilities (zip_file_override)</span>
|
|
91
|
+
│
|
|
92
|
+
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.installer.html">installer/</a>
|
|
93
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.installer.common.html">common.py</a> <span class="comment"># 🔧 Common installer utilities</span>
|
|
94
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.installer.downloader.html">downloader.py</a> <span class="comment"># ⬇️ File download utilities</span>
|
|
95
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.installer.linux.html">linux.py</a> <span class="comment"># 🐧 Linux-specific installer utilities</span>
|
|
96
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.installer.main.html">main.py</a> <span class="comment"># 🚀 Main installer functionality</span>
|
|
97
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.installer.windows.html">windows.py</a> <span class="comment"># 💻 Windows-specific installer utilities</span>
|
|
98
|
+
│ └── ...
|
|
99
|
+
│
|
|
75
100
|
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.all_doctests.html">all_doctests.py</a> <span class="comment"># ✅ Execution of all doctests for a given path</span>
|
|
76
101
|
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.archive.html">archive.py</a> <span class="comment"># 📦 Archive utilities (zip, repair_zip)</span>
|
|
77
102
|
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.backup.html">backup.py</a> <span class="comment"># 📦 Backup utilities (delta backup, consolidate)</span>
|
|
@@ -41,7 +41,7 @@ It includes a range of tools for tasks such as execution of doctests, display ut
|
|
|
41
41
|
<pre class="code-tree">stouputils/
|
|
42
42
|
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.applications.html">applications/</a>
|
|
43
43
|
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.applications.automatic_docs.html">automatic_docs.py</a> <span class="comment"># 📚 Documentation generation utilities (used to create this documentation)</span>
|
|
44
|
-
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.applications.upscaler.html">upscaler/</a>
|
|
44
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.applications.upscaler.html">upscaler/</a> <span class="comment"># 🔎 Image & Video upscaler (configurable)</span>
|
|
45
45
|
│ └── ...
|
|
46
46
|
│
|
|
47
47
|
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.continuous_delivery.html">continuous_delivery/</a>
|
|
@@ -51,6 +51,31 @@ It includes a range of tools for tasks such as execution of doctests, display ut
|
|
|
51
51
|
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.continuous_delivery.pyproject.html">pyproject.py</a> <span class="comment"># 📝 Pyproject.toml utilities</span>
|
|
52
52
|
│ └── ...
|
|
53
53
|
│
|
|
54
|
+
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.html">data_science/</a>
|
|
55
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.config.html">config/</a> <span class="comment"># ⚙️ Configuration utilities for data science</span>
|
|
56
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.dataset.html">dataset/</a> <span class="comment"># 📊 Dataset handling (dataset, dataset_loader, grouping_strategy)</span>
|
|
57
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.data_processing.html">data_processing/</a> <span class="comment"># 🔄 Data processing utilities (image augmentation, preprocessing)</span>
|
|
58
|
+
│ │ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.data_processing.image.html">image/</a> <span class="comment"># 🖼️ Image processing techniques</span>
|
|
59
|
+
│ │ └── ...
|
|
60
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.models.html">models/</a> <span class="comment"># 🧠 ML/DL model interfaces and implementations</span>
|
|
61
|
+
│ │ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.models.keras.html">keras/</a> <span class="comment"># 🤖 Keras model implementations</span>
|
|
62
|
+
│ │ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.models.keras_utils.html">keras_utils/</a> <span class="comment"># 🛠️ Keras utilities (callbacks, losses, visualizations)</span>
|
|
63
|
+
│ │ └── ...
|
|
64
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.scripts.html">scripts/</a> <span class="comment"># 📜 Data science scripts (augment, preprocess, routine)</span>
|
|
65
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.metric_utils.html">metric_utils.py</a> <span class="comment"># 📏 Metrics utilities for ML/DL models</span>
|
|
66
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.data_science.mlflow_utils.html">mlflow_utils.py</a> <span class="comment"># 📊 MLflow integration utilities</span>
|
|
67
|
+
│ └── ...
|
|
68
|
+
│
|
|
69
|
+
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.dont_look.html">dont_look/</a> <span class="comment"># 🙈 Internal utilities (zip_file_override)</span>
|
|
70
|
+
│
|
|
71
|
+
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.installer.html">installer/</a>
|
|
72
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.installer.common.html">common.py</a> <span class="comment"># 🔧 Common installer utilities</span>
|
|
73
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.installer.downloader.html">downloader.py</a> <span class="comment"># ⬇️ File download utilities</span>
|
|
74
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.installer.linux.html">linux.py</a> <span class="comment"># 🐧 Linux-specific installer utilities</span>
|
|
75
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.installer.main.html">main.py</a> <span class="comment"># 🚀 Main installer functionality</span>
|
|
76
|
+
│ ├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.installer.windows.html">windows.py</a> <span class="comment"># 💻 Windows-specific installer utilities</span>
|
|
77
|
+
│ └── ...
|
|
78
|
+
│
|
|
54
79
|
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.all_doctests.html">all_doctests.py</a> <span class="comment"># ✅ Execution of all doctests for a given path</span>
|
|
55
80
|
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.archive.html">archive.py</a> <span class="comment"># 📦 Archive utilities (zip, repair_zip)</span>
|
|
56
81
|
├── <a href="https://stoupy51.github.io/stouputils/latest/modules/stouputils.backup.html">backup.py</a> <span class="comment"># 📦 Backup utilities (delta backup, consolidate)</span>
|
|
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "stouputils"
|
|
8
|
-
version = "1.2.
|
|
8
|
+
version = "1.2.41"
|
|
9
9
|
description = "Stouputils is a collection of utility modules designed to simplify and enhance the development process. It includes a range of tools for tasks such as execution of doctests, display utilities, decorators, as well as context managers, and many more."
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -64,6 +64,8 @@ ignore = [
|
|
|
64
64
|
"W191",
|
|
65
65
|
"N803",
|
|
66
66
|
"N806",
|
|
67
|
+
"F403",
|
|
68
|
+
"F405",
|
|
67
69
|
]
|
|
68
70
|
|
|
69
71
|
[tool.hatch.build]
|
|
@@ -16,6 +16,7 @@ from types import ModuleType
|
|
|
16
16
|
from . import decorators
|
|
17
17
|
from .decorators import LogLevels, handle_error, measure_time
|
|
18
18
|
from .print import error, info, progress
|
|
19
|
+
from .io import clean_path, relative_path
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
def test_module_with_progress(module: ModuleType, separator: str) -> TestResults:
|
|
@@ -60,19 +61,39 @@ def launch_tests(root_dir: str, importing_errors: LogLevels = LogLevels.WARNING_
|
|
|
60
61
|
old_value: bool = strict
|
|
61
62
|
decorators.force_raise_exception = True
|
|
62
63
|
strict = old_value
|
|
63
|
-
|
|
64
|
+
|
|
65
|
+
# Get the path of the directory to check modules from
|
|
66
|
+
working_dir: str = clean_path(os.getcwd())
|
|
67
|
+
root_dir = clean_path(os.path.abspath(root_dir))
|
|
68
|
+
dir_to_check: str = os.path.dirname(root_dir) if working_dir != root_dir else root_dir
|
|
64
69
|
|
|
65
70
|
# Get all modules from folder
|
|
66
|
-
sys.path.insert(0,
|
|
71
|
+
sys.path.insert(0, dir_to_check)
|
|
67
72
|
modules_file_paths: list[str] = []
|
|
68
73
|
for directory_path, _, _ in os.walk(root_dir):
|
|
74
|
+
directory_path = clean_path(directory_path)
|
|
69
75
|
for module_info in pkgutil.walk_packages([directory_path]):
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
|
|
77
|
+
# Extract root and module name
|
|
78
|
+
module_root: str = str(module_info.module_finder.path) # type: ignore
|
|
79
|
+
module_name: str = module_info.name.split(".")[-1]
|
|
80
|
+
|
|
81
|
+
# Get the absolute path
|
|
82
|
+
absolute_module_path: str = clean_path(os.path.join(module_root, module_name))
|
|
83
|
+
|
|
84
|
+
# Check if the module is in the root directory that we want to check
|
|
85
|
+
if root_dir in absolute_module_path:
|
|
86
|
+
|
|
87
|
+
# Get the path of the module like 'stouputils.io'
|
|
88
|
+
path: str = absolute_module_path.split(dir_to_check, 1)[1].replace("/", ".")[1:]
|
|
89
|
+
|
|
90
|
+
# If the module is not already in the list, add it
|
|
91
|
+
if path not in modules_file_paths:
|
|
92
|
+
modules_file_paths.append(path)
|
|
93
|
+
|
|
94
|
+
# If no modules are found, raise an error
|
|
74
95
|
if not modules_file_paths:
|
|
75
|
-
raise ValueError(f"No modules found in '{root_dir}'")
|
|
96
|
+
raise ValueError(f"No modules found in '{relative_path(root_dir)}'")
|
|
76
97
|
|
|
77
98
|
# Find longest module path for alignment
|
|
78
99
|
max_length: int = max(len(path) for path in modules_file_paths)
|
|
@@ -222,8 +222,8 @@ class MeasureTime:
|
|
|
222
222
|
total_ms: float = total_ns / 1_000_000
|
|
223
223
|
total_s: float = total_ns / 1_000_000_000
|
|
224
224
|
|
|
225
|
-
# Print the execution time (nanoseconds if less than 0.
|
|
226
|
-
if total_ms <
|
|
225
|
+
# Print the execution time (nanoseconds if less than 0.1s, seconds otherwise)
|
|
226
|
+
if total_ms < 100:
|
|
227
227
|
self.print_func(f"{self.message}: {total_ms:.3f}ms ({total_ns}ns)")
|
|
228
228
|
elif total_s < 60:
|
|
229
229
|
self.print_func(f"{self.message}: {(total_s):.5f}s")
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
""" Load configuration from the set.py file and handle some special cases.
|
|
2
|
+
|
|
3
|
+
Proper way to get the configuration is by importing this module, not the set.py file directly.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# pyright: reportUnknownMemberType=false
|
|
7
|
+
# pyright: reportUnknownVariableType=false
|
|
8
|
+
# pyright: reportMissingTypeStubs=false
|
|
9
|
+
|
|
10
|
+
# Imports
|
|
11
|
+
import os
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from .set import DataScienceConfig
|
|
15
|
+
|
|
16
|
+
# Special cases
|
|
17
|
+
# Hide GPU when using CPU
|
|
18
|
+
if DataScienceConfig.TENSORFLOW_DEVICE.lower().startswith("/cpu"):
|
|
19
|
+
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
|
|
20
|
+
|
|
21
|
+
# Precise which GPU we use
|
|
22
|
+
elif DataScienceConfig.TENSORFLOW_DEVICE.lower().startswith("/gpu"):
|
|
23
|
+
os.environ["CUDA_VISIBLE_DEVICES"] = DataScienceConfig.TENSORFLOW_DEVICE.split(":")[-1]
|
|
24
|
+
|
|
25
|
+
# Configure TensorFlow (if available)
|
|
26
|
+
try:
|
|
27
|
+
from tensorflow import config as tf_config
|
|
28
|
+
|
|
29
|
+
# Get the physical devices
|
|
30
|
+
physical_devices: list[Any] = tf_config.list_physical_devices("GPU")
|
|
31
|
+
|
|
32
|
+
# Configure TensorFlow GPU memory management to allocate memory dynamically
|
|
33
|
+
# This prevents TensorFlow from allocating all GPU memory upfront
|
|
34
|
+
# Instead, memory will grow as needed, allowing better resource sharing
|
|
35
|
+
for device in physical_devices:
|
|
36
|
+
tf_config.experimental.set_memory_growth(device, True)
|
|
37
|
+
|
|
38
|
+
# Disable eager execution mode in TensorFlow
|
|
39
|
+
# This improves performance by allowing TensorFlow to create an optimized graph
|
|
40
|
+
# of operations instead of executing operations one by one (at the cost of debugging difficulty)
|
|
41
|
+
tf_config.run_functions_eagerly(False)
|
|
42
|
+
except ImportError:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
# Enable mixed precision training (if available)
|
|
46
|
+
try:
|
|
47
|
+
from keras import mixed_precision
|
|
48
|
+
mixed_precision.set_global_policy(DataScienceConfig.MIXED_PRECISION_POLICY)
|
|
49
|
+
except ImportError:
|
|
50
|
+
pass
|
|
51
|
+
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
""" Configuration file for the project. """
|
|
2
|
+
|
|
3
|
+
# Imports
|
|
4
|
+
import os
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from stouputils.decorators import LogLevels
|
|
8
|
+
from stouputils.io import get_root_path
|
|
9
|
+
|
|
10
|
+
# Environment variables
|
|
11
|
+
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "9" # Suppress TensorFlow logging
|
|
12
|
+
os.environ["GRPC_VERBOSITY"] = "ERROR" # Suppress gRPC logging
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Configuration class
|
|
16
|
+
class DataScienceConfig:
|
|
17
|
+
""" Configuration class for the project. """
|
|
18
|
+
|
|
19
|
+
# Common
|
|
20
|
+
SEED: int = 42
|
|
21
|
+
""" Seed for the random number generator. """
|
|
22
|
+
|
|
23
|
+
ERROR_LOG: LogLevels = LogLevels.WARNING_TRACEBACK
|
|
24
|
+
""" Log level for errors for all functions. """
|
|
25
|
+
|
|
26
|
+
AUGMENTED_FILE_SUFFIX: str = "_aug_"
|
|
27
|
+
""" Suffix for augmented files, ex: 'image_008_aug_1.png'. """
|
|
28
|
+
|
|
29
|
+
AUGMENTED_DIRECTORY_PREFIX: str = "aug_"
|
|
30
|
+
""" Prefix for augmented directories, ex: 'data/hip_implant' -> 'data/aug_hip_implant'. """
|
|
31
|
+
|
|
32
|
+
PREPROCESSED_DIRECTORY_SUFFIX: str = "_preprocessed"
|
|
33
|
+
""" Suffix for preprocessed directories, ex: 'data/hip_implant' -> 'data/hip_implant_preprocessed'. """
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Directories
|
|
37
|
+
ROOT: str = get_root_path(__file__, go_up=3)
|
|
38
|
+
""" Root directory of the project. """
|
|
39
|
+
|
|
40
|
+
MLFLOW_FOLDER: str = f"{ROOT}/mlruns"
|
|
41
|
+
""" Folder containing the mlflow data. """
|
|
42
|
+
MLFLOW_URI: str = f"file://{MLFLOW_FOLDER}"
|
|
43
|
+
""" URI to the mlflow data. """
|
|
44
|
+
|
|
45
|
+
DATA_FOLDER: str = f"{ROOT}/data"
|
|
46
|
+
""" Folder containing all the data (e.g. subfolders containing images). """
|
|
47
|
+
|
|
48
|
+
TEMP_FOLDER: str = f"{ROOT}/temp"
|
|
49
|
+
""" Folder containing temporary files (e.g. models checkpoints, plots, etc.). """
|
|
50
|
+
|
|
51
|
+
LOGS_FOLDER: str = f"{ROOT}/logs"
|
|
52
|
+
""" Folder containing the logs. """
|
|
53
|
+
|
|
54
|
+
TENSORBOARD_FOLDER: str = f"{ROOT}/tensorboard"
|
|
55
|
+
""" Folder containing the tensorboard logs. """
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Behaviours
|
|
59
|
+
TEST_SIZE: float = 0.2
|
|
60
|
+
""" Size of the test set by default (0.2 means 80% training, 20% test). """
|
|
61
|
+
|
|
62
|
+
VALIDATION_SIZE: float = 0.2
|
|
63
|
+
""" Size of the validation set by default (0.2 means 80% training, 20% validation). """
|
|
64
|
+
|
|
65
|
+
# Machine learning
|
|
66
|
+
SAVE_MODEL: bool = False
|
|
67
|
+
""" If the model should be saved in the mlflow folder using mlflow.*.save_model. """
|
|
68
|
+
|
|
69
|
+
DO_SALIENCY_AND_GRADCAM: bool = True
|
|
70
|
+
""" If the saliency and gradcam should be done during the run. """
|
|
71
|
+
|
|
72
|
+
DO_LEARNING_RATE_FINDER: Literal[0, 1, 2] = 1
|
|
73
|
+
""" If the learning rate finder should be done during the run.
|
|
74
|
+
0: no, 1: only plot, 2: plot and use value for the remaining run
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
DO_UNFREEZE_FINDER: Literal[0, 1, 2] = 0
|
|
78
|
+
""" If the unfreeze finder should be done during the run.
|
|
79
|
+
0: no, 1: only plot, 2: plot and use value for the remaining run
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
DO_FIT_IN_SUBPROCESS: bool = True
|
|
83
|
+
""" If the model should be fitted in a subprocess.
|
|
84
|
+
Is memory efficient, and more stable. Turn it off only if you are having issues.
|
|
85
|
+
|
|
86
|
+
Note: This allow a program to make lots of runs without getting killed by the OS for using too much resources.
|
|
87
|
+
(e.g. LeaveOneOut Cross Validation, Grid Search, etc.)
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
MIXED_PRECISION_POLICY: Literal["mixed_float16", "mixed_bfloat16", "float32"] = "mixed_float16"
|
|
91
|
+
""" Mixed precision policy to use. Turn back to "float32" if you are having issues with a specific model or metrics.
|
|
92
|
+
See: https://www.tensorflow.org/guide/mixed_precision
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
TENSORFLOW_DEVICE: str = "/gpu:1"
|
|
96
|
+
""" TensorFlow device to use. """
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# Fix MLFLOW_URI for Windows by adding a missing slash
|
|
101
|
+
if os.name == "nt":
|
|
102
|
+
DataScienceConfig.MLFLOW_URI = DataScienceConfig.MLFLOW_URI.replace("file:", "file:/")
|
|
103
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
from .auto_contrast import auto_contrast_image
|
|
4
|
+
from .axis_flip import flip_image
|
|
5
|
+
from .bias_field_correction import bias_field_correction_image
|
|
6
|
+
from .binary_threshold import binary_threshold_image
|
|
7
|
+
from .blur import blur_image
|
|
8
|
+
from .brightness import brightness_image
|
|
9
|
+
from .canny import canny_image
|
|
10
|
+
from .clahe import clahe_image
|
|
11
|
+
from .contrast import contrast_image
|
|
12
|
+
from .curvature_flow_filter import curvature_flow_filter_image
|
|
13
|
+
from .denoise import (
|
|
14
|
+
adaptive_denoise_image,
|
|
15
|
+
bilateral_denoise_image,
|
|
16
|
+
nlm_denoise_image,
|
|
17
|
+
tv_denoise_image,
|
|
18
|
+
wavelet_denoise_image,
|
|
19
|
+
)
|
|
20
|
+
from .invert import invert_image
|
|
21
|
+
from .laplacian import laplacian_image
|
|
22
|
+
from .median_blur import median_blur_image
|
|
23
|
+
from .noise import noise_image
|
|
24
|
+
from .normalize import normalize_image
|
|
25
|
+
from .random_erase import random_erase_image
|
|
26
|
+
from .resize import resize_image
|
|
27
|
+
from .rotation import rotate_image
|
|
28
|
+
from .salt_pepper import salt_pepper_image
|
|
29
|
+
from .sharpening import sharpen_image
|
|
30
|
+
from .shearing import shear_image
|
|
31
|
+
from .threshold import threshold_image
|
|
32
|
+
from .translation import translate_image
|
|
33
|
+
from .zoom import zoom_image
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"adaptive_denoise_image",
|
|
37
|
+
"auto_contrast_image",
|
|
38
|
+
"bias_field_correction_image",
|
|
39
|
+
"bilateral_denoise_image",
|
|
40
|
+
"binary_threshold_image",
|
|
41
|
+
"blur_image",
|
|
42
|
+
"brightness_image",
|
|
43
|
+
"canny_image",
|
|
44
|
+
"clahe_image",
|
|
45
|
+
"contrast_image",
|
|
46
|
+
"curvature_flow_filter_image",
|
|
47
|
+
"flip_image",
|
|
48
|
+
"invert_image",
|
|
49
|
+
"laplacian_image",
|
|
50
|
+
"median_blur_image",
|
|
51
|
+
"nlm_denoise_image",
|
|
52
|
+
"noise_image",
|
|
53
|
+
"normalize_image",
|
|
54
|
+
"random_erase_image",
|
|
55
|
+
"resize_image",
|
|
56
|
+
"rotate_image",
|
|
57
|
+
"salt_pepper_image",
|
|
58
|
+
"sharpen_image",
|
|
59
|
+
"shear_image",
|
|
60
|
+
"threshold_image",
|
|
61
|
+
"translate_image",
|
|
62
|
+
"tv_denoise_image",
|
|
63
|
+
"wavelet_denoise_image",
|
|
64
|
+
"zoom_image",
|
|
65
|
+
]
|
|
66
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
|
|
2
|
+
# pyright: reportUnusedImport=false
|
|
3
|
+
# ruff: noqa: F401
|
|
4
|
+
|
|
5
|
+
# Imports
|
|
6
|
+
from .common import Any, NDArray, check_image, cv2, np
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Functions
|
|
10
|
+
def auto_contrast_image(image: NDArray[Any], ignore_dtype: bool = False) -> NDArray[Any]:
|
|
11
|
+
""" Adjust the contrast of an image.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
image (NDArray[Any]): Image to adjust contrast
|
|
15
|
+
ignore_dtype (bool): Ignore the dtype check
|
|
16
|
+
Returns:
|
|
17
|
+
NDArray[Any]: Image with adjusted contrast
|
|
18
|
+
|
|
19
|
+
>>> ## Basic tests
|
|
20
|
+
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.uint8)
|
|
21
|
+
>>> adjusted = auto_contrast_image(image)
|
|
22
|
+
>>> adjusted.tolist()
|
|
23
|
+
[[0, 36, 73], [109, 146, 182], [219, 255, 255]]
|
|
24
|
+
>>> adjusted.shape == image.shape
|
|
25
|
+
True
|
|
26
|
+
>>> adjusted.dtype == image.dtype
|
|
27
|
+
True
|
|
28
|
+
|
|
29
|
+
>>> ## Test invalid inputs
|
|
30
|
+
>>> auto_contrast_image("not an image")
|
|
31
|
+
Traceback (most recent call last):
|
|
32
|
+
...
|
|
33
|
+
AssertionError: Image must be a numpy array
|
|
34
|
+
"""
|
|
35
|
+
# Check input data
|
|
36
|
+
check_image(image, ignore_dtype=ignore_dtype)
|
|
37
|
+
|
|
38
|
+
# Perform histogram clipping
|
|
39
|
+
clip_hist_percent: float = 1.0
|
|
40
|
+
|
|
41
|
+
# Calculate the histogram of the image
|
|
42
|
+
hist: NDArray[Any] = cv2.calcHist([image], [0], None, [256], [0, 256])
|
|
43
|
+
|
|
44
|
+
# Create an accumulator list to store the cumulative histogram
|
|
45
|
+
accumulator: list[float] = []
|
|
46
|
+
accumulator.append(hist[0])
|
|
47
|
+
for i in range(1, 256):
|
|
48
|
+
accumulator.append(accumulator[i - 1] + hist[i])
|
|
49
|
+
|
|
50
|
+
# Find the maximum value in the accumulator
|
|
51
|
+
max_value: float = accumulator[-1]
|
|
52
|
+
|
|
53
|
+
# Calculate the clipping threshold
|
|
54
|
+
clip_hist_percent = clip_hist_percent * (max_value / 100.0)
|
|
55
|
+
clip_hist_percent = clip_hist_percent / 2.0
|
|
56
|
+
|
|
57
|
+
# Find the minimum and maximum gray levels after clipping
|
|
58
|
+
min_gray: int = 0
|
|
59
|
+
while accumulator[min_gray] < clip_hist_percent:
|
|
60
|
+
min_gray = min_gray + 1
|
|
61
|
+
max_gray: int = 256 - 1
|
|
62
|
+
while (max_gray >= 0 and accumulator[max_gray] >= (max_value - clip_hist_percent)):
|
|
63
|
+
max_gray = max_gray - 1
|
|
64
|
+
|
|
65
|
+
# Calculate the input range after clipping
|
|
66
|
+
input_range: int = max_gray - min_gray
|
|
67
|
+
|
|
68
|
+
# If the input range is 0, return the original image
|
|
69
|
+
if input_range == 0:
|
|
70
|
+
return image
|
|
71
|
+
|
|
72
|
+
# Calculate the scaling factors for contrast adjustment
|
|
73
|
+
alpha: float = (256 - 1) / input_range
|
|
74
|
+
beta: float = -min_gray * alpha
|
|
75
|
+
|
|
76
|
+
# Apply the contrast adjustment
|
|
77
|
+
adjusted: NDArray[Any] = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)
|
|
78
|
+
return adjusted
|
|
79
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
|
|
2
|
+
# pyright: reportUnusedImport=false
|
|
3
|
+
# ruff: noqa: F401
|
|
4
|
+
|
|
5
|
+
# Imports
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
from .common import Any, NDArray, check_image, cv2, np
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Functions
|
|
12
|
+
def flip_image(
|
|
13
|
+
image: NDArray[Any], axis: Literal["horizontal", "vertical", "both"], ignore_dtype: bool = True
|
|
14
|
+
) -> NDArray[Any]:
|
|
15
|
+
""" Flip an image along specified axis
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
image (NDArray[Any]): Image to flip
|
|
19
|
+
axis (str): Axis along which to flip ("horizontal" or "vertical" or "both")
|
|
20
|
+
ignore_dtype (bool): Ignore the dtype check
|
|
21
|
+
Returns:
|
|
22
|
+
NDArray[Any]: Flipped image
|
|
23
|
+
|
|
24
|
+
>>> ## Basic tests
|
|
25
|
+
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
26
|
+
>>> flip_image(image, "horizontal").tolist()
|
|
27
|
+
[[3, 2, 1], [6, 5, 4], [9, 8, 7]]
|
|
28
|
+
|
|
29
|
+
>>> flip_image(image, "vertical").tolist()
|
|
30
|
+
[[7, 8, 9], [4, 5, 6], [1, 2, 3]]
|
|
31
|
+
|
|
32
|
+
>>> flip_image(image, "both").tolist()
|
|
33
|
+
[[9, 8, 7], [6, 5, 4], [3, 2, 1]]
|
|
34
|
+
|
|
35
|
+
>>> ## Test invalid inputs
|
|
36
|
+
>>> flip_image(image, "diagonal")
|
|
37
|
+
Traceback (most recent call last):
|
|
38
|
+
AssertionError: axis must be either 'horizontal' or 'vertical' or 'both', got 'diagonal'
|
|
39
|
+
|
|
40
|
+
>>> flip_image("not an image", "horizontal")
|
|
41
|
+
Traceback (most recent call last):
|
|
42
|
+
...
|
|
43
|
+
AssertionError: Image must be a numpy array
|
|
44
|
+
"""
|
|
45
|
+
# Check input data
|
|
46
|
+
check_image(image, ignore_dtype=ignore_dtype)
|
|
47
|
+
assert axis in ("horizontal", "vertical", "both"), (
|
|
48
|
+
f"axis must be either 'horizontal' or 'vertical' or 'both', got '{axis}'"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Apply the flip
|
|
52
|
+
if axis == "horizontal":
|
|
53
|
+
return cv2.flip(image, 1) # 1 for horizontal flip
|
|
54
|
+
elif axis == "vertical":
|
|
55
|
+
return cv2.flip(image, 0) # 0 for vertical flip
|
|
56
|
+
else:
|
|
57
|
+
return cv2.flip(image, -1) # -1 for both flips
|
|
58
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
|
|
2
|
+
# pyright: reportUnknownMemberType=false
|
|
3
|
+
# pyright: reportUnknownArgumentType=false
|
|
4
|
+
# pyright: reportUnknownVariableType=false
|
|
5
|
+
|
|
6
|
+
# Imports
|
|
7
|
+
import SimpleITK as Sitk
|
|
8
|
+
|
|
9
|
+
from .common import Any, NDArray, check_image, np
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Functions
|
|
13
|
+
def bias_field_correction_image(image: NDArray[Any], ignore_dtype: bool = False) -> NDArray[Any]:
|
|
14
|
+
""" Apply a bias field correction to an image. (N4 Filter)
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
image (NDArray[Any]): Image to apply the bias field correction (can't be 8-bit unsigned integer)
|
|
18
|
+
ignore_dtype (bool): Ignore the dtype check
|
|
19
|
+
Returns:
|
|
20
|
+
NDArray[Any]: Image with the curvature flow filter applied
|
|
21
|
+
|
|
22
|
+
>>> ## Basic tests
|
|
23
|
+
>>> image = np.random.randint(0, 255, size=(10,10), dtype=np.uint8) / 255
|
|
24
|
+
>>> corrected = bias_field_correction_image(image)
|
|
25
|
+
>>> corrected.shape == image.shape
|
|
26
|
+
True
|
|
27
|
+
>>> corrected.dtype == np.float64
|
|
28
|
+
True
|
|
29
|
+
|
|
30
|
+
>>> ## Test invalid inputs
|
|
31
|
+
>>> bias_field_correction_image("not an image")
|
|
32
|
+
Traceback (most recent call last):
|
|
33
|
+
...
|
|
34
|
+
AssertionError: Image must be a numpy array
|
|
35
|
+
"""
|
|
36
|
+
# Check input data
|
|
37
|
+
check_image(image, ignore_dtype=ignore_dtype)
|
|
38
|
+
|
|
39
|
+
# If the image is 3D, convert to grayscale first
|
|
40
|
+
if image.ndim == 3:
|
|
41
|
+
image = np.mean(image, axis=-1)
|
|
42
|
+
|
|
43
|
+
# Convert numpy array to SimpleITK image
|
|
44
|
+
image_sitk: Sitk.Image = Sitk.GetImageFromArray(image)
|
|
45
|
+
|
|
46
|
+
# Create binary mask of the head region
|
|
47
|
+
transformed: Sitk.Image = Sitk.RescaleIntensity(image_sitk) # Normalize intensities
|
|
48
|
+
transformed = Sitk.LiThreshold(transformed, 0, 1) # Apply Li thresholding
|
|
49
|
+
head_mask: Sitk.Image = transformed
|
|
50
|
+
|
|
51
|
+
# Downsample images to speed up bias field estimation
|
|
52
|
+
shrink_factor: int = 4 # Reduce image size by factor of 4
|
|
53
|
+
input_image: Sitk.Image = Sitk.Shrink(
|
|
54
|
+
image_sitk,
|
|
55
|
+
[shrink_factor] * image_sitk.GetDimension() # Apply shrink factor to all dimensions
|
|
56
|
+
)
|
|
57
|
+
mask_image: Sitk.Image = Sitk.Shrink(
|
|
58
|
+
head_mask,
|
|
59
|
+
[shrink_factor] * image_sitk.GetDimension()
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Apply N4 bias field correction
|
|
63
|
+
corrector = Sitk.N4BiasFieldCorrectionImageFilter()
|
|
64
|
+
corrector.Execute(input_image, mask_image)
|
|
65
|
+
|
|
66
|
+
# Get estimated bias field and apply correction
|
|
67
|
+
log_bias_field: Sitk.Image = Sitk.Cast(
|
|
68
|
+
corrector.GetLogBiasFieldAsImage(image_sitk), Sitk.sitkFloat64
|
|
69
|
+
)
|
|
70
|
+
corrected_image_full_resolution: Sitk.Image = image_sitk / Sitk.Exp(log_bias_field)
|
|
71
|
+
|
|
72
|
+
# Convert back to numpy array and return
|
|
73
|
+
return Sitk.GetArrayFromImage(corrected_image_full_resolution)
|
|
74
|
+
|