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 +21 -0
- bm3dornl-0.3.1/PKG-INFO +32 -0
- bm3dornl-0.3.1/README.md +13 -0
- bm3dornl-0.3.1/pyproject.toml +82 -0
- bm3dornl-0.3.1/setup.cfg +4 -0
- bm3dornl-0.3.1/src/bm3dornl/__init__.py +10 -0
- bm3dornl-0.3.1/src/bm3dornl/_version.py +1 -0
- bm3dornl-0.3.1/src/bm3dornl/aggregation.py +103 -0
- bm3dornl-0.3.1/src/bm3dornl/block_matching.py +432 -0
- bm3dornl-0.3.1/src/bm3dornl/bm3d.py +784 -0
- bm3dornl-0.3.1/src/bm3dornl/denoiser_gpu.py +254 -0
- bm3dornl-0.3.1/src/bm3dornl/noise_analysis.py +85 -0
- bm3dornl-0.3.1/src/bm3dornl/phantom.py +241 -0
- bm3dornl-0.3.1/src/bm3dornl/plot.py +24 -0
- bm3dornl-0.3.1/src/bm3dornl/signal.py +31 -0
- bm3dornl-0.3.1/src/bm3dornl/utils.py +203 -0
- bm3dornl-0.3.1/src/bm3dornl.egg-info/PKG-INFO +32 -0
- bm3dornl-0.3.1/src/bm3dornl.egg-info/SOURCES.txt +20 -0
- bm3dornl-0.3.1/src/bm3dornl.egg-info/dependency_links.txt +1 -0
- bm3dornl-0.3.1/src/bm3dornl.egg-info/entry_points.txt +5 -0
- bm3dornl-0.3.1/src/bm3dornl.egg-info/requires.txt +5 -0
- bm3dornl-0.3.1/src/bm3dornl.egg-info/top_level.txt +1 -0
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.
|
bm3dornl-0.3.1/PKG-INFO
ADDED
|
@@ -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
|
+
[](https://results.pre-commit.ci/latest/github/ornlneutronimaging/bm3dornl/next)
|
|
21
|
+
[](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).
|
bm3dornl-0.3.1/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[](https://results.pre-commit.ci/latest/github/ornlneutronimaging/bm3dornl/next)
|
|
2
|
+
[](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
|
+
]
|
bm3dornl-0.3.1/setup.cfg
ADDED
|
@@ -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
|