-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathamf.py
151 lines (127 loc) · 3.85 KB
/
amf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
from typing import Tuple
import numpy as np
def build_kernel(img: np.ndarray, ii: int, jj: int, kernel_size: int) -> np.ndarray:
"""Builds kernel from image at coords ii, jj with provided kernel size.
Parameters
----------
img : np.ndarray
input image array
ii : int
row coordinate
jj : int
col coordinate
kernel_size : int
size of kernel
Returns
-------
np.ndarray
kernel created from image
"""
# find kernel widths
kernel_width = int(kernel_size / 2)
# build and return kernel
return img[
max(0, ii - kernel_width) : min(ii + kernel_width, img.shape[0]),
max(0, jj - kernel_width) : min(jj + kernel_width, img.shape[1]),
]
def get_kernel_stats(kernel: np.ndarray) -> Tuple[int, int, int]:
"""Returns minimum, median, and maximum of kernel.
Parameters
----------
kernel : np.ndaray
kernel to generate statistics for
Returns
-------
Tuple[int, int, int]
minimum, median, and maximum value of kernel
"""
return np.min(kernel), np.median(kernel), np.max(kernel)
def level_b(z_xy: int, z_min: int, z_med: int, z_max: int) -> int:
"""Performs level B of AMF procedure
Parameters
----------
z_xy : int
intensity value at center of kernel
z_min : int
minimum intensity in kernel
z_med : int
median intensity in kernel
z_max : int
maximum intensity in kernel
Returns
-------
int
filtered intensity value
"""
# if intensity is in range of kernel, return intensity
if z_min < z_xy < z_max:
return z_xy
# otherwise return median
return z_med
def level_a(
orig_img: np.ndarray, ii: int, jj: int, kernel_size: int, max_kernel_size: int
) -> int:
"""Performs level A of AMF procedure
Parameters
----------
orig_img : np.ndarray
image to perform AMF on
ii : int
row coordinate
jj : int
col coordinate
kernel_size : int
size of median filter kernel
max_kernel_size : int
maximum size of median filter kernel
Returns
-------
int
filtered intensity value at coordinate
"""
# create kernel
kernel = build_kernel(orig_img, ii, jj, kernel_size)
# get kernel statistics
z_min, z_med, z_max = get_kernel_stats(kernel)
# if median is between min and max, return level b result
if z_min < z_med < z_max:
return level_b(orig_img[ii, jj], z_min, z_med, z_max)
# increase the kernel size
kernel_size += 2
# if kernel size is less than max, repeat level A
if kernel_size <= max_kernel_size:
return level_a(orig_img, ii, jj, kernel_size, max_kernel_size)
# otherwise return median
return z_med
def amf(
orig_img: np.ndarray, init_kernel_size: int = 3, max_kernel_size: int = 7
) -> np.ndarray:
"""Performs adaptive median filtering on original image.
Parameters
----------
orig_img : np.ndarray
image to perform AMF on
init_kernel_size : int
initial size of median filter kernel, by default 3
max_kernel_size : int
maximum size of median filter kernel, by default 7
Returns
-------
np.ndarray
adaptive median filtered image
"""
if(len(orig_img.shape) == 2):
return
# assert that kernel sizes are odd
assert init_kernel_size % 2 == 1, "Initial kernel size must be odd"
assert max_kernel_size % 2 == 1, "Max kernel size must be odd"
# construct output image
amf_img = np.empty_like(orig_img)
# iterate over pixels in image
for ii, jj, kk in np.ndindex(*orig_img.shape):
# set filtered value of pixel
amf_img[ii, jj, kk] = level_a(
orig_img[:, :, kk], ii, jj, init_kernel_size, max_kernel_size
)
# return filtered image
return amf_img