a) 噪声定义文件 noise.py
from abc import ABC
from typing import Tuple
import numpy as np
from functools import singledispatchmethod
import cv2
# from IPython import embed
# from numpy.typing import ArrayLike
__all__ = [
"NoiseGenerator",
"UniformNoiseGenerator",
"NormalNoiseGenerator",
"GaussianNoiseGenerator",
"SaltPepperNoiseGenerator",
]
class NoiseGenerator:
"""The base distribution (without uniform noise)."""
__slots__ = []
def __init__(self):
pass
@singledispatchmethod
def generate_noise(self):
"""Generating noise which should be implemented"""
raise NotImplementedError
def add_noise(self, image: np.ndarray) -> np.ndarray:
"""Adding noise to image"""
image_shape = image.shape
return image + self.generate_noise(image_shape=image_shape)
- __all__定义将定义的所有类,从而更好的供后续使用,定义噪声基类与虚函数。
- 这里本来想用 python 3.8 提供的 @singledispatchmethod 装饰器,在抽象基类里定义两个虚函数,然后在继承的时候重载函数及调用方式,但是发现如果是继承的类好像不能直接继承(?)因为会报没有这个方法的错,只能把 @singledispatchmethod 写在类内部?不知道能否有人解答我的疑惑。
class UniformNoiseGenerator(NoiseGenerator, ABC):
"""The base distribution with uniform noise."""
__slots__ = ['min_val', 'max_val']
def __init__(self, min_val: float = 0, max_val: float = 1):
super(UniformNoiseGenerator, self).__init__()
self.min_val = min_val
self.max_val = max_val
def generate_uniform_noise(self,
min_val: float = 0,
max_val: float = 1,
image_shape: Tuple[int, int] = None) -> np.ndarray:
"""Generating noise
:param: image_shape: Image shape of generated noise
:return: output_noise: ArrayLike noise or single noise
"""
if min_val is None and max_val is None:
min_val = self.min_val
max_val = self.max_val
assert (min_val < max_val) # assertion
distribution_range = max_val - min_val # getting range of Uniform(0, b - a)
moving_factor = distribution_range - max_val # getting move of Uniform(a, b) from Uniform(0, b - a)
if image_shape is None: # just generate one noise
return np.random.rand() * distribution_range - moving_factor
return np.random.rand(*image_shape) * distribution_range - moving_factor # ArrayLike output
def add_noise(self, image: np.ndarray) -> np.ndarray:
"""Adding noise to image"""
image_shape = image.shape
return image + self.generate_uniform_noise(image_shape=image_shape)
定义均匀噪声类,使用numpy.random.rand生成0,1之间的均匀噪声,同时利用平移和放缩的到任意范围的均匀噪声。
由于前面定义的的是抽象类,因此后面继承的时候需要同时继承 ABC 抽象类。
感觉这里写的有些问题,可能应该写成
class Noise(abc.ABCMeta): @abc.abstractmethod def read(self): pass
不太确定。
用__slots__减少类初始化和定义时的时间消耗。
numpy.random.rand(*image_shape) 时利用了拆包,类似于 *args 的用法
class NormalNoiseGenerator(UniformNoiseGenerator, ABC):
"""The base distribution with additive i.i.d. uniform noise."""
def __init__(self):
super(NormalNoiseGenerator, self).__init__()
def generate_normal_noise(self, image_shape: Tuple[int, int]) -> np.ndarray:
"""Generating Normal noise"""
uniform_noise_1 = self.generate_uniform_noise(0, 1, image_shape)
uniform_noise_2 = self.generate_uniform_noise(0, 1, image_shape)
normal_noise = np.cos(2.0 * np.pi * uniform_noise_1) * np.sqrt(-2.0 * np.log(uniform_noise_2))
return normal_noise
def add_noise(self, image: np.ndarray) -> np.ndarray:
"""Adding noise to image"""
image_shape = image.shape
return image + self.generate_normal_noise(image_shape=image_shape)
- 继承均匀噪声类,使用 Box-Muller 变换得到标准正态分布的噪声。
- Box-Muller 的核心就是通过极坐标变换将正态分布的指数变换为极坐标的辐角的均匀分布。
class GaussianNoiseGenerator(NormalNoiseGenerator, ABC):
"""Gaussian distribution with reparameterization trick."""
def __init__(self):
super(GaussianNoiseGenerator, self).__init__()
def generate_gaussian_noise(self, image_shape: Tuple[int, int], mean: float = 0, var: float = 1) -> np.ndarray:
"""
Generating Normal noise
:param image_shape: Shape of input image
:param mean: Mean of gaussian
:param var: Variance of gaussian
:return: gaussian_noise: Output noise with same size
"""
normal_noise = self.generate_normal_noise(image_shape)
gaussian_noise = normal_noise * np.sqrt(var) + mean
return gaussian_noise
def add_all_channel_noise(self, image: np.ndarray, mean: float = 0, var: float = 1) -> np.ndarray:
"""Adding gaussian noise to image with SAME noise in each channel
:param image: input image
:param mean: Mean of gaussian
:param var: Variance of gaussian
:return: output_img: Output image with same size adding gaussian noise
"""
image_shape = image.shape
input_img = image / 255
gaussian_noise = np.expand_dims(self.generate_gaussian_noise(image_shape[:2], mean, var), -1)
gaussian_noise = gaussian_noise.repeat(3, axis=-1)
output_img = input_img + gaussian_noise
output_img[input_img > 1] = 1
output_img[input_img < 0] = 0
return output_img * 255
def add_channel_wise_noise(self, image: np.ndarray, mean: float = 0, var: float = 1) -> np.ndarray:
"""Adding gaussian noise to image with DIFFERENT noise in each channel
:param image: input image
:param mean: Mean of gaussian
:param var: Variance of gaussian
:return: output_img: Output image with same size adding channel-wise gaussian noise
"""
image_shape = image.shape
input_img = image / 255
gaussian_noise = self.generate_gaussian_noise(image_shape, mean, var)
output_img = input_img + gaussian_noise
output_img[input_img > 1] = 1
output_img[input_img < 0] = 0
return output_img * 255
通过重参数化技巧(re-parameterization trick),从标准正态分布噪声生成带有均值和方差的高斯噪声分布。同时设置两种加噪声方式:分通道添加或整体添加。
注意,这里如果想直接使用 cv2 或其他方式直接可视化,有两种方式,
一种是除 255 变换到 [0, 1] 之间,
第二种是通过 uint8 的方式转化为整值。
如果直接可视化的话,会产生过亮的情况,因为会自动判定为 0-1 从而被 clip 成高亮图像。
class SaltPepperNoiseGenerator(UniformNoiseGenerator, ABC):
"""Gaussian distribution with additive i.i.d. uniform noise."""
def __init__(self):
super(SaltPepperNoiseGenerator, self).__init__()
def generate_saltpepper_noise(self, image_shape: Tuple[int, int]) -> np.ndarray:
"""
:param: prob_1: Probability of "Salt" noise.
:param: prob_2: Probability of "Pepper" noise.
:param: image_shape: Shape of input image.
:return: saltpepper_noise: ArrayLike output salt pepper noise.
"""
uniform_noise = self.generate_uniform_noise(0, 1, image_shape)
saltpepper_noise = uniform_noise.copy()
return saltpepper_noise
def add_saltpepper_noise(self, prob_1: float, prob_2: float, image: np.ndarray) -> np.ndarray:
"""Adding noise to image"""
output_img = image.copy()
image_shape = image.shape
saltpepper_noise = self.generate_saltpepper_noise(image_shape[:2])
output_img[saltpepper_noise > 1 - prob_1] = 255
output_img[saltpepper_noise < prob_2] = 0
return output_img
- 继承均匀噪声类,生成均匀噪声,根据均匀噪声的值确定为原始图、胡椒噪声或原始噪声。
if __name__ == '__main__':
uniform_noise_generator = UniformNoiseGenerator()
print(uniform_noise_generator.generate_uniform_noise(2, 4, (2, 3)),
uniform_noise_generator.generate_uniform_noise())
normal_noise_generator = NormalNoiseGenerator()
print(normal_noise_generator.generate_normal_noise((2, 3)))
gaussian_noise_generator = GaussianNoiseGenerator()
print(gaussian_noise_generator.generate_gaussian_noise((2, 3)))
input_image = cv2.imread('./test_image/test3.jpg', 1)
print(type(input_image))
cv2.namedWindow('input_image', cv2.WINDOW_AUTOSIZE)
cv2.imshow('input_image', input_image)
# saltpepper_noise_generator = SaltPepperNoiseGenerator()
# tar = saltpepper_noise_generator.add_saltpepper_noise(0.12, 0.1, input_image)
# cv2.imshow('saltpepper_noise', tar)
gaussian_noise_generator = GaussianNoiseGenerator()
tar2 = gaussian_noise_generator.add_all_channel_noise(input_image, 0, 0.05)
tar2_channel_wise = gaussian_noise_generator.add_channel_wise_noise(input_image, 0, 0.05)
cv2.imshow('gaussian_noise', tar2)
cv2.imshow('gaussian_noise_channel', tar2_channel_wise)
cv2.waitKey(0)
b) 空间滤波器定义文件 spatial_filter.py
from typing import Tuple, List, Optional
import numpy as np
import logging
import sys
import functools
import cv2
from IPython import embed
level = logging.DEBUG
fmt = "[%(levelname)s] %(asctime)s - %(message)s"
logging.basicConfig(level=level, format=fmt)
def get_median(noised_image: np.ndarray, x_min: int, x_max: int, y_min: int, y_max: int, c: int) -> float:
median_area = noised_image[x_min:x_max + 1, y_min:y_max + 1, c].flatten()
sorted_area = sorted(median_area)
n = len(median_area)
median = sorted_area[n // 2 + 1] if n % 2 else (sorted_area[n // 2] + sorted_area[n // 2 + 1]) / 2
return median
def get_adaptive_median(noised_image: np.ndarray, x: int, y: int, h: int, w: int, c: int,
max_size: int = None) -> float:
median_area = noised_image[max(0, x - h):x + h + 1, max(0, y - w):y + w + 1, c].flatten()
max_val, min_val = np.max(median_area), np.min(median_area)
sorted_area = sorted(median_area)
n = len(median_area)
median = sorted_area[n // 2 + 1] if n % 2 else (int(sorted_area[n // 2]) + int(sorted_area[n // 2 + 1])) // 2
if min_val < median < max_val:
return noised_image[x][y][c] if min_val < noised_image[x][y][c] < max_val else median
elif h < max_size and w < max_size:
return get_adaptive_median(noised_image, x, y, h + 1, w + 1, c, max_size)
else:
return noised_image[x][y][c] if min_val < noised_image[x][y][c] < max_val else median
def get_mean(noised_image: np.ndarray, x_min: int, x_max: int, y_min: int, y_max: int) -> np.ndarray:
return np.mean(noised_image[x_min:x_max + 1, y_min:y_max + 1], axis=(0, 1))
- 定义静态函数,完成计算规定kernel内的均值,中值与自适应中值。
- 我有一个坏习惯,经常使用 print debug(虽然我现在还是这样),但我尝试使用 log 来输出。
class SpatialFilter:
"""
This is description which will be showed
The Class of Padding Adder
"""
instance = None # There should be ONLY ONE Spatial Filter once
__slots__ = ['noised_image', 'kernel_size']
def __new__(cls, *args):
if cls.instance is None: # Once no adder exists
cls.instance = super().__new__(cls)
return cls.instance
def __init__(self, noised_image: np.ndarray, kernel_size: int = 3):
self.noised_image = noised_image
self.kernel_size = kernel_size
def reverse_image(self, image: np.ndarray, reflect_size: int = None, axis: int = 0) -> np.ndarray:
"""
reverse image in one axis for reflect padding
:param: image: Input image for reflection
:param: reflect_size: The size of reflect area edge + 1
:param: axis: The axis that will be reflected
:return: reversed_img: Reversed image at axis
"""
if reflect_size is None:
reflect_size = self.kernel_size
logging.info(f"No kernel size input, automatically use size = {self.kernel_size}")
if axis == 0:
reflect_area_left = image[reflect_size - 1::-1]
reflect_area_right = image[-1 * reflect_size:][::-1]
else:
reflect_area_left = image[:, reflect_size - 1::-1]
reflect_area_right = image[:, -1 * reflect_size:][:, ::-1]
reversed_img = np.concatenate([reflect_area_left, image, reflect_area_right], axis=axis)
return reversed_img
def add_reflect_padding(self, image: np.ndarray, kernel_size: int = None) -> np.ndarray:
"""
Adding reflect padding
:param: image: Input image for reflection
:param: kernel_size: The size of reflect kernel
:return: reversed_img: Padded image.
"""
if kernel_size is None:
kernel_size = self.kernel_size
logging.info(f"No kernel size input, automatically use size = {self.kernel_size}")
horizontal_flip = self.reverse_image(image, kernel_size, 0)
reversed_img = self.reverse_image(horizontal_flip, kernel_size, 1)
return reversed_img
def add_zero_padding(self, image: np.ndarray, kernel_size: int = None):
"""
Adding zero padding
:param: image: Input image for padding
:param: kernel_size: The size of padding kernel
:return: output_img: Padded image.
"""
if kernel_size is None:
kernel_size = self.kernel_size
logging.info(f"No kernel size input, automatically use size = {self.kernel_size}")
if len(image.shape) > 2:
h, w, c = image.shape
output_img = np.zeros((h + 2 * kernel_size, w + 2 * kernel_size, c))
else:
h, w = image.shape
output_img = np.zeros((h + 2 * kernel_size, w + 2 * kernel_size))
output_img[kernel_size:h + kernel_size, kernel_size:w + kernel_size] = image
return output_img
定义Spatial Filter类,同时,由于防止复用时覆盖掉特性,以及在要求的三个函数中不必每次初始化类,因此重载__new__函数,保证只需要使用同一个Spatial Filter,同时实现将图像翻转的功能,供后续对称padding使用。
定义两种padding方式,对称填充和零填充。
对输入参数做预处理,填充未给出的值。
- (主要是 pycharm 提示,duplicate too long)所以我就写进一个函数了。
def _preprocess_args(self,
noised_image: np.ndarray = None,
kernel_size: int = None,
padding_func: Optional = None,
padding_type: str = None,
max_size: int = None):
assert (padding_type is None or padding_func is None)
assert (kernel_size % 2)
if noised_image is None:
noised_image = self.noised_image
if kernel_size is None:
kernel_size = self.kernel_size
if max_size is None:
max_size = self.kernel_size + 4
if padding_func is not None:
padded_image = padding_func(noised_image, (kernel_size - 1) // 2)
elif padding_type is not None:
padding_func = getattr(sys.modules[__name__].SpatialFilter, "add_%s_padding" % padding_type)
padded_image = padding_func(self, noised_image, (kernel_size - 1) // 2)
else:
padded_image = self.add_zero_padding(noised_image, (kernel_size - 1) // 2)
depth = (kernel_size - 1) // 2
c_img = 1
if len(padded_image.shape) > 2:
h_img, w_img, c_img = noised_image.shape
else:
h_img, w_img = noised_image.shape
output_img = padded_image.copy()
return noised_image, padded_image, kernel_size, padding_func, max_size, depth, h_img, w_img, c_img, output_img
- 实现中值滤波与均值滤波。
def mean_filter(self,
noised_image: np.ndarray = None,
kernel_size: int = None,
padding_func: Optional = None,
padding_type: str = None):
"""
Mean filter
Support two type of padding:
1. define it yourself and put it with padding_func
2. use the predefined type
:param: noised_image: Input image for padding
:param: kernel_size: The size of padding kernel
:param: padding_func: Padding function defined by yourself
:param: padding_type: Type of padding, use in ["reflect", "zero"]
:return: output_img: Output image after filter
"""
noised_image, padded_image, kernel_size, padding_func, _, depth, h_img, w_img, c_img, output_img = \
self._preprocess_args(noised_image, kernel_size, padding_func, padding_type)
for h in range(depth, h_img + depth):
for w in range(depth, w_img + depth):
output_img[h, w, :] = get_mean(padded_image, h - depth, h + depth, w - depth, w + depth)
output_img = output_img[depth:h_img + depth, depth:w_img + depth]
return np.uint8(output_img)
def median_filter(self,
noised_image: np.ndarray = None,
kernel_size: int = None,
padding_func: Optional = None,
padding_type: str = None):
noised_image, padded_image, kernel_size, padding_func, _, depth, h_img, w_img, c_img, output_img = \
self._preprocess_args(noised_image, kernel_size, padding_func, padding_type)
for h in range(depth, h_img + depth):
for w in range(depth, w_img + depth):
for c in range(c_img):
output_img[h, w, c] = get_median(padded_image, h - depth, h + depth, w - depth, w + depth, c)
output_img = output_img[depth:h_img + depth, depth:w_img + depth]
return np.uint8(output_img)
def adaptive_median_filter(self,
noised_image: np.ndarray = None,
kernel_size: int = None,
max_size: int = None,
padding_func: Optional = None,
padding_type: str = None):
noised_image, padded_image, kernel_size, padding_func, _, depth, h_img, w_img, c_img, output_img = \
self._preprocess_args(noised_image, kernel_size, padding_func, padding_type)
for h in range(depth, h_img + depth):
for w in range(depth, w_img + depth):
for c in range(c_img):
output_img[h, w, c] = get_adaptive_median(padded_image, h, w, depth, depth, c, max_size)
output_img = output_img[depth:h_img + depth, depth:w_img + depth]
return np.uint8(output_img)
- 实现自适应中值滤波。
c) 测量指标文件 metric.py
- 由于并非任务要求,因此这里使用了库函数。
- 本来想用 sys.modules 调用变量,后来才知道类和函数是存在 sys.modules 里的,变量是存在 locals() 或者 globals() 里。
# The implementation of digital image processing assignment 2.
# Random Noise and Spatial Filter
# Metrics
# author: leafy
# 2022-12-5
# last_modified: 2022-12-5
from itertools import product
import math
import numpy as np
from skimage.metrics import structural_similarity
import cv2
def mean_squared_error(img1: np.ndarray, img2: np.ndarray) -> float:
mse = np.mean((img1 / 1.0 - img2 / 1.0) ** 2)
return float(mse)
def peak_signal_noise_ratio(mse: float) -> float:
if mse < 1.0e-10:
return 100
return 10 * math.log10(255.0 ** 2 / mse)
# Just borrow it from skimage
# will implement soon(for assignment request)
def compare(img1, img2):
mse = mean_squared_error(img1, img2)
psnr = peak_signal_noise_ratio(mse)
ssim = structural_similarity(img1, img2, multichannel=True)
# print('PSNR:{},SSIM:{},MSE:{}'.format(psnr, ssim, mse))
return psnr, ssim, mse
if __name__ == "__main__":
for i in range(1, 5):
globals()[f"input_image{i}"] = cv2.imread(f'./result/input_image_{i}.png')
# Recording gaussian noise images
for i in range(1, 5):
for types in ["channel", "full"]:
globals()[f"gaussian_img_{types}_{i}"] = cv2.imread(f'./result/gaussian_img_{types}_{i}.png')
# recording saltpepper noise images
for i in range(1, 5):
globals()[f"sp_img_full_{i}"] = cv2.imread(f'./result/sp_img_full_{i}.png',
)
for i in range(1, 5):
globals()[f"low_sp_img_full_{i}"] = cv2.imread(f'./result/low_sp_img_full_{i}.png')
for i in range(1, 5):
for f_type, noise in product(["mean", "median", "median_adaptive"], ["gaussian", "sp", "low_sp"]):
# for noise in ["gaussian", "sp", "low_sp"]:
for types in ["channel", "full"]:
if "sp" in noise:
types = "full"
for pad in ["_reflect", ""]:
print(f"Reading images in ./result/{f_type}_{noise}_{types}{pad}_{i}.png")
globals()[f"{f_type}_{noise}_{types}{pad}_{i}"] = cv2.imread(
f'./result/{f_type}_{noise}_{types}{pad}_{i}.png')
for f_type, noise in product(["mean", "median", "median_adaptive", "no_filter"], ["gaussian", "sp", "low_sp"]):
for types in ["channel", "full"]:
if "sp" in noise and types == "channel":
continue
for pad in ["_reflect", ""]:
cnt_psnr, cnt_ssim, cnt_mse = 0, 0, 0
if f_type == "no_filter":
filt = ""
img = "img_"
else:
filt = f_type + "_"
img = ""
if f_type == "no_filter" and pad != "":
continue
for i in range(1, 5):
cur_psnr, cur_ssim, cur_mse = compare(globals()[f"input_image{i}"],
globals()[f"{filt}{noise}_{img}{types}{pad}_{i}"])
for metric in ["psnr", "ssim", "mse"]:
globals()[f"cnt_{metric}"] += globals()[f"cur_{metric}"]
cnt_psnr, cnt_ssim, cnt_mse = cnt_psnr / 4, cnt_ssim / 4, cnt_mse / 4
print(f"Difference between {filt}{noise}_{img}{types}{pad}.png and input is "
f"\n{cnt_psnr} \n{cnt_ssim} \n{cnt_mse}")
- 完成了所有图像的指标测试。
d) 主文件(五个函数及主函数调用)
import os
from typing import Tuple
import cv2
import numpy as np
import sys
from itertools import product
from noise import GaussianNoiseGenerator, SaltPepperNoiseGenerator
from spatial_filter import SpatialFilter
from metric import compare
from IPython import embed
def generate_gaussian_noise(input_img: np.ndarray, mean: float, var: float) -> Tuple[np.ndarray, np.ndarray]:
"""
:param: input_img: Input image.
:param: mean: Mean of gaussian noise.
:param: var: Variance of gaussian noise.
:return: output_image: Output image with gaussian noise added. output_img, output_img_channel_wise
"""
inner_gaussian_noise_generator = GaussianNoiseGenerator()
output_img = inner_gaussian_noise_generator.add_all_channel_noise(input_img, mean, var)
output_img_channel_wise = inner_gaussian_noise_generator.add_channel_wise_noise(input_img, mean, var)
return output_img, output_img_channel_wise
def generate_saltpepper_noise(prob_1: float, prob_2: float, input_img: np.ndarray) -> np.ndarray:
"""
:param: prob_1: prob_1 of saltpepper noise.
:param: prob_2: prob_2 of saltpepper noise.
:param: input_img: Input image.
:return: output_image: Output image with saltpepper noise added.
"""
saltpepper_noise_generator = SaltPepperNoiseGenerator()
output_img = saltpepper_noise_generator.add_saltpepper_noise(prob_1, prob_2, input_img)
return output_img
def mean_filter(noised_img: np.ndarray, kernel_size: int = None) -> Tuple[np.ndarray, np.ndarray]:
"""
:param: input_img: Input image.
:param: kernel_size: kernel size of filter.
:return: output_image: Output filtered image. output_img_zero, output_img_reflect
"""
sp_filter = SpatialFilter(noised_img)
output_img_zero = sp_filter.mean_filter(noised_img, kernel_size=kernel_size, padding_type="zero")
output_img_reflect = sp_filter.mean_filter(noised_img, kernel_size=kernel_size, padding_type="reflect")
return output_img_zero, output_img_reflect
def median_filter(noised_img: np.ndarray, kernel_size: int = None) -> Tuple[np.ndarray, np.ndarray]:
"""
:param: input_img: Input image.
:param: kernel_size: kernel size of filter.
:return: output_image: Output filtered image. output_img_zero, output_img_reflect
"""
sp_filter = SpatialFilter(noised_img)
output_img_zero = sp_filter.median_filter(noised_img, kernel_size=kernel_size, padding_type="zero")
output_img_reflect = sp_filter.median_filter(noised_img, kernel_size=kernel_size, padding_type="reflect")
return output_img_zero, output_img_reflect
def median_adaptive_filter(noised_img: np.ndarray, kernel_size: int = None,
max_size: int = None) -> Tuple[np.ndarray, np.ndarray]:
"""
:param: input_img: Input image.
:param: kernel_size: kernel size of filter.
:param: max_size: Max size of adaptive filter.
:return: output_image: Output filtered image. output_img_zero, output_img_reflect
"""
sp_filter = SpatialFilter(noised_img)
output_img_zero = sp_filter.adaptive_median_filter(noised_img, kernel_size=kernel_size,
max_size=max_size, padding_type="zero")
output_img_reflect = sp_filter.adaptive_median_filter(noised_img, kernel_size=kernel_size,
max_size=max_size, padding_type="reflect")
return output_img_zero, output_img_reflect
两种噪声的生成。
三种滤波结果,同时包括对称填充和零填充。
主函数——图像读入与加噪声,添加了高斯噪声、高概率椒盐噪声和低概率椒盐噪声。
def main(test_dir: str = "./test_image"):
# Reading images
input_images = os.listdir(test_dir)
for idx, path in input_images:
locals()[f"input_image{idx}"] = cv2.imread(os.path.join(test_dir, path), 1)
# Recording input images
for i in range(1, 5):
cv2.imwrite(f'./result/input_image_{i}.png',
locals()[f"input_image{i}"])
# Recording gaussian noise images
for i in range(1, 5):
locals()[f"gaussian_img_full_{i}"], locals()[f"gaussian_img_channel_{i}"] = \
generate_gaussian_noise(locals()[f"input_image{i}"], 0, 0.05)
for types in ["channel", "full"]:
cv2.imwrite(f'./result/gaussian_img_{types}_{i}.png',
locals()[f"gaussian_img_{types}_{i}"])
# recording saltpepper noise images
for i in range(1, 5):
locals()[f"sp_img_full_{i}"] = generate_saltpepper_noise(0.1, 0.1,
locals()[f"input_image{i}"])
cv2.imwrite(f'./result/sp_img_full_{i}.png',
locals()[f"sp_img_full_{i}"])
for i in range(1, 5):
locals()[f"low_sp_img_full_{i}"] = generate_saltpepper_noise(0.01, 0.01,
locals()[f"input_image{i}"])
cv2.imwrite(f'./result/low_sp_img_full_{i}.png',
locals()[f"low_sp_img_full_{i}"])
# Filtering and recording the output images
print(locals().keys())
for i in range(1, 5):
for f_type, noise in product(["mean", "median", "median_adaptive"], ["gaussian", "sp", "low_sp"]):
# for noise in ["gaussian", "sp", "low_sp"]:
for types in ["channel", "full"]:
if "sp" in noise and types == "channel":
continue
print(f"Generating images {f_type}_{noise}_{types}_{i}")
if f_type == "median_adaptive":
locals()[f"{f_type}_{noise}_{types}_{i}"], locals()[f"{f_type}_{noise}_{types}_reflect_{i}"] = \
getattr(sys.modules[__name__], f"{f_type}_filter")(locals()[f"{noise}_img_{types}_{i}"],
kernel_size=3,
max_size=7)
else:
locals()[f"{f_type}_{noise}_{types}_{i}"], locals()[f"{f_type}_{noise}_{types}_reflect_{i}"] = \
getattr(sys.modules[__name__], f"{f_type}_filter")(locals()[f"{noise}_img_{types}_{i}"],
kernel_size=3)
for pad in ["_reflect", ""]:
print(f"Saving images in ./result/{f_type}_{noise}_{types}{pad}_{i}.png")
cv2.imwrite(f'./result/{f_type}_{noise}_{types}{pad}_{i}.png',
locals()[f"{f_type}_{noise}_{types}{pad}_{i}"])
for f_type, noise in product(["mean", "median", "median_adaptive", "no_filter"], ["gaussian", "sp", "low_sp"]):
for types in ["channel", "full"]:
if "sp" in noise and types == "channel":
continue
for pad in ["_reflect", ""]:
cnt_psnr, cnt_ssim, cnt_mse = 0, 0, 0
if f_type == "no_filter":
filt = ""
img = "img_"
else:
filt = f_type + "_"
img = ""
if f_type == "no_filter" and pad != "":
continue
for i in range(1, 5):
cur_psnr, cur_ssim, cur_mse = compare(globals()[f"input_image{i}"],
globals()[f"{filt}{noise}_{img}{types}{pad}_{i}"])
for metric in ["psnr", "ssim", "mse"]:
globals()[f"cnt_{metric}"] += globals()[f"cur_{metric}"]
cnt_psnr, cnt_ssim, cnt_mse = cnt_psnr / 4, cnt_ssim / 4, cnt_mse / 4
print(f"Difference between {filt}{noise}_{img}{types}{pad}.png and input is "
f"\n{cnt_psnr} \n{cnt_ssim} \n{cnt_mse}")
if __name__ == '__main__':
# input_image3 = cv2.imread('test3.jpg', 1)
# a, b = generate_gaussian_noise(input_image3, 0, 0.05)
# cv2.imshow('gaussian_noise', a)
# cv2.imshow('gaussian_noise_channel', b)
# cv2.waitKey(0)
main()
- 主函数——滤波与指标计算。