bm3dornl 0.3.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.
bm3dornl-0.3.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Neutron Scattering Software
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.1
2
+ Name: bm3dornl
3
+ Version: 0.3.1
4
+ Summary: BM3D for streak artifact removal in neutron imaging
5
+ License: MIT
6
+ Project-URL: homepage, https://github.com/neutrons/python_project_template/
7
+ Project-URL: repository, https://github.com/ornlneutronimaging/bm3dornl
8
+ Project-URL: documentation, https://bm3dornl.readthedocs.io/en/latest/
9
+ Project-URL: issues, https://github.com/ornlneutronimaging/bm3dornl/issues
10
+ Keywords: BM3D,image processing,neutron imaging,denoising,CuPy,Numba
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: numpy
15
+ Requires-Dist: scipy<1.13
16
+ Requires-Dist: numba
17
+ Requires-Dist: cupy-cuda12x
18
+ Requires-Dist: scikit-image
19
+
20
+ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/ornlneutronimaging/bm3dornl/next.svg)](https://results.pre-commit.ci/latest/github/ornlneutronimaging/bm3dornl/next)
21
+ [![Documentation Status](https://readthedocs.org/projects/bm3dornl/badge/?version=latest)](https://bm3dornl.readthedocs.io/en/latest/?badge=latest)
22
+
23
+ BM3D ORNL
24
+ =========
25
+
26
+ This repository contains the BM3D ORNL code, which is a Python implementation of the BM3D denoising algorithm. The BM3D algorithm was originally proposed by K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian in the paper "Image Denoising by Sparse 3D Transform-Domain Collaborative Filtering" (2007).
27
+ The BM3D algorithm is a state-of-the-art denoising algorithm that is widely used in the image processing community.
28
+ The BM3D ORNL code is a Python implementation of the BM3D algorithm that has been optimized for performance using both `Numba` and `CuPy`.
29
+ The BM3D ORNL code is designed to be easy to use and easy to integrate into existing Python workflows.
30
+ The BM3D ORNL code is released under an open-source license, and is freely available for download and use.
31
+
32
+ For more information, check out our [FAQ](docs/FAQ.md).
@@ -0,0 +1,13 @@
1
+ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/ornlneutronimaging/bm3dornl/next.svg)](https://results.pre-commit.ci/latest/github/ornlneutronimaging/bm3dornl/next)
2
+ [![Documentation Status](https://readthedocs.org/projects/bm3dornl/badge/?version=latest)](https://bm3dornl.readthedocs.io/en/latest/?badge=latest)
3
+
4
+ BM3D ORNL
5
+ =========
6
+
7
+ This repository contains the BM3D ORNL code, which is a Python implementation of the BM3D denoising algorithm. The BM3D algorithm was originally proposed by K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian in the paper "Image Denoising by Sparse 3D Transform-Domain Collaborative Filtering" (2007).
8
+ The BM3D algorithm is a state-of-the-art denoising algorithm that is widely used in the image processing community.
9
+ The BM3D ORNL code is a Python implementation of the BM3D algorithm that has been optimized for performance using both `Numba` and `CuPy`.
10
+ The BM3D ORNL code is designed to be easy to use and easy to integrate into existing Python workflows.
11
+ The BM3D ORNL code is released under an open-source license, and is freely available for download and use.
12
+
13
+ For more information, check out our [FAQ](docs/FAQ.md).
@@ -0,0 +1,82 @@
1
+ [project]
2
+ name = "bm3dornl"
3
+ description = "BM3D for streak artifact removal in neutron imaging"
4
+ dynamic = ["version"]
5
+ requires-python = ">=3.10"
6
+ dependencies = [
7
+ "numpy",
8
+ "scipy<1.13", # avoid a bug
9
+ "numba",
10
+ "cupy-cuda12x", # use pre-built wheels
11
+ "scikit-image",
12
+ ]
13
+ license = { text = "MIT" }
14
+ keywords = ["BM3D", "image processing", "neutron imaging", "denoising", "CuPy", "Numba"]
15
+ readme = "README.md"
16
+
17
+ [project.urls]
18
+ homepage = "https://github.com/neutrons/python_project_template/"
19
+ repository = "https://github.com/ornlneutronimaging/bm3dornl"
20
+ documentation = "https://bm3dornl.readthedocs.io/en/latest/"
21
+ issues = "https://github.com/ornlneutronimaging/bm3dornl/issues"
22
+
23
+
24
+ [build-system]
25
+ requires = [
26
+ "setuptools >= 40.6.0",
27
+ "wheel",
28
+ "toml",
29
+ "versioningit"
30
+ ]
31
+ build-backend = "setuptools.build_meta"
32
+
33
+ [tool.black]
34
+ line-length = 119
35
+
36
+ [tool.versioningit.vcs]
37
+ method = "git"
38
+ default-tag = "0.0.1"
39
+
40
+ [tool.versioningit.next-version]
41
+ method = "minor"
42
+
43
+ [tool.versioningit.format]
44
+ distance = "{next_version}.dev{distance}"
45
+ dirty = "{version}+d{build_date:%Y%m%d}"
46
+ distance-dirty = "{next_version}.dev{distance}+d{build_date:%Y%m%d%H%M}"
47
+
48
+ [tool.versioningit.write]
49
+ file = "src/bm3dornl/_version.py"
50
+
51
+ [tool.setuptools.packages.find]
52
+ where = ["src"]
53
+ exclude = ["tests*", "scripts*", "docs*", "notebooks*"]
54
+
55
+ [tool.setuptools.package-data]
56
+ "*" = ["*.yml","*.yaml","*.ini"]
57
+
58
+ [project.scripts]
59
+ packagename-cli = "packagenamepy.packagename:main"
60
+
61
+ [project.gui-scripts]
62
+ packagenamepy = "packagenamepy.packagename:gui"
63
+
64
+ [tool.pytest.ini_options]
65
+ pythonpath = [
66
+ ".", "src", "scripts"
67
+ ]
68
+ testpaths = ["tests"]
69
+ python_files = ["test*.py"]
70
+ norecursedirs = [".git", "tmp*", "_tmp*", "__pycache__", "*dataset*", "*data_set*"]
71
+ markers = [
72
+ "cuda_required: test requires cuda to run."
73
+ ]
74
+
75
+ [tool.pylint]
76
+ max-line-length = 120
77
+ disable = ["too-many-locals",
78
+ "too-many-statements",
79
+ "too-many-instance-attributes",
80
+ "too-many-arguments",
81
+ "duplicate-code"
82
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,10 @@
1
+ """
2
+ Contains the entry point for the application
3
+ """
4
+
5
+ try:
6
+ from ._version import __version__ # noqa: F401
7
+ except ImportError:
8
+ __version__ = "0.0.1"
9
+
10
+ from .bm3d import bm3d_ring_artifact_removal # noqa: F401
@@ -0,0 +1 @@
1
+ __version__ = "0.3.1"
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env python3
2
+ """Functions for aggregating hyper patch block into a single image."""
3
+
4
+ import numpy as np
5
+ from typing import Tuple
6
+ from numba import njit, prange
7
+
8
+
9
+ @njit(parallel=True)
10
+ def aggregate_block_to_image(
11
+ image_shape: Tuple[int, int],
12
+ hyper_blocks: np.ndarray,
13
+ hyper_block_indices: np.ndarray,
14
+ variance_blocks: np.ndarray,
15
+ ) -> np.ndarray:
16
+ """
17
+ Aggregate patches into the denoised image matrix and update the corresponding weights matrix using smart weighting.
18
+
19
+ Parameters
20
+ ----------
21
+ image_shape : tuple
22
+ The shape of the image to be denoised.
23
+ hyper_blocks : np.ndarray
24
+ A 4D numpy array of patches to be aggregated. Shape is (num_blocks, num_patches_per_block, patch_height, patch_width).
25
+ hyper_block_indices : np.ndarray
26
+ A 3D numpy array containing the top-left indices (row, column) for each patch in the `hyper_blocks`.
27
+ Shape is (num_blocks, num_patches_per_block, 2).
28
+ variance_blocks : np.ndarray
29
+ A 4D numpy array of the variances for each patch. Shape is the same as `hyper_blocks`.
30
+
31
+ Returns
32
+ -------
33
+ np.ndarray
34
+ The denoised image.
35
+ """
36
+ estimate_denoised_image = np.zeros(image_shape)
37
+ weights = np.zeros(image_shape)
38
+
39
+ num_blocks, num_patches, ph, pw = hyper_blocks.shape
40
+
41
+ for i in prange(num_blocks):
42
+ for p in range(num_patches):
43
+ patch = hyper_blocks[i, p]
44
+ variance = variance_blocks[i, p]
45
+ weight = 1 / (variance + 1e-8) # Small epsilon to avoid division by zero
46
+ i_pos, j_pos = hyper_block_indices[i, p]
47
+ for ii in range(ph):
48
+ for jj in range(pw):
49
+ estimate_denoised_image[i_pos + ii, j_pos + jj] += (
50
+ patch[ii, jj] * weight[ii, jj]
51
+ )
52
+ weights[i_pos + ii, j_pos + jj] += weight[ii, jj]
53
+
54
+ # Normalize the denoised image by the sum of weights
55
+ estimate_denoised_image /= np.maximum(weights, 1)
56
+
57
+ return estimate_denoised_image
58
+
59
+
60
+ @njit(parallel=True)
61
+ def aggregate_denoised_block_to_image(
62
+ image_shape: Tuple[int, int],
63
+ denoised_patches: np.ndarray,
64
+ patch_positions: np.ndarray,
65
+ ) -> np.ndarray:
66
+ """
67
+ Aggregate denoised patches into the final denoised image.
68
+
69
+ Parameters
70
+ ----------
71
+ image_shape : tuple
72
+ The shape of the final denoised image (height, width).
73
+ denoised_patches : np.ndarray
74
+ A 4D numpy array of denoised patches. Shape is (num_blocks, num_patches_per_block, patch_height, patch_width).
75
+ patch_positions : np.ndarray
76
+ A 3D numpy array containing the top-left indices (row, column) for each patch in the `denoised_patches`.
77
+ Shape is (num_blocks, num_patches_per_block, 2).
78
+
79
+ Returns
80
+ -------
81
+ np.ndarray
82
+ The final denoised image.
83
+ """
84
+ denoised_image = np.zeros(image_shape, dtype=np.float32)
85
+ weights = np.zeros(image_shape, dtype=np.float32)
86
+
87
+ num_blocks, num_patches_per_block, patch_height, patch_width = (
88
+ denoised_patches.shape
89
+ )
90
+
91
+ for block_idx in prange(num_blocks):
92
+ for patch_idx in range(num_patches_per_block):
93
+ patch = denoised_patches[block_idx, patch_idx]
94
+ top_left_row, top_left_col = patch_positions[block_idx, patch_idx]
95
+ for i in range(patch_height):
96
+ for j in range(patch_width):
97
+ denoised_image[top_left_row + i, top_left_col + j] += patch[i, j]
98
+ weights[top_left_row + i, top_left_col + j] += 1
99
+
100
+ # Normalize the denoised image by the sum of weights
101
+ denoised_image /= np.maximum(weights, 1)
102
+
103
+ return denoised_image