import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(20, 6))
index = 1
def plot_noise(image, noise, noisy, title):
global index
plt.subplot(2, 8, index)
plt.imshow(noisy, cmap='gray')
plt.title(title)
plt.axis('off')
index += 1
plt.subplot(2, 8, index)
plt.hist(noise.ravel(), bins=100)
plt.title("Noise Dist")
plt.tight_layout()
index += 1
image = np.zeros((256, 256), dtype=np.uint8)
noise = np.random.uniform(-50, 50, image.shape)
noisy = np.clip(image + noise, 0, 255).astype(np.uint8)
plot_noise(image, noise, noisy, "Uniform Noise")
image = np.zeros((256, 256), dtype=np.uint8)
noise = np.random.normal(0, 25, image.shape)
noisy = np.clip(image + noise, 0, 255).astype(np.uint8)
plot_noise(image, noise, noisy, "Gaussian Noise")
image = np.zeros((256, 256), dtype=np.uint8)
prob = 0.05
rnd = np.random.rand(*image.shape)
noisy = image.copy()
noisy[rnd < prob / 2] = 0
noisy[rnd > 1 - prob / 2] = 255
noise = rnd
plot_noise(image, noise, noisy, "Salt-Pepper Noise")
image = np.full((256, 256), 128, dtype=np.uint8)
noise = np.random.poisson(image / 255.0 * 10) / 10 * 255 - image
noisy = np.clip(image + noise, 0, 255).astype(np.uint8)
plot_noise(image, noise, noisy, "Poisson Noise")
image = np.zeros((256, 256), dtype=np.uint8)
noise = np.random.rayleigh(scale=25, size=image.shape)
noisy = np.clip(image + noise, 0, 255).astype(np.uint8)
plot_noise(image, noise, noisy, "Rayleigh Noise")
image = np.zeros((256, 256), dtype=np.uint8)
shape_k = 2.0
scale_theta = 20.0
noise = np.random.gamma(shape_k, scale_theta, image.shape)
noisy = np.clip(image + noise, 0, 255).astype(np.uint8)
plot_noise(image, noise, noisy, "Gamma Noise")
image = np.full((256, 256), 128, dtype=np.uint8)
noise = np.random.normal(0, 0.1, image.shape)
noisy = np.clip(image + image * noise, 0, 255).astype(np.uint8)
plot_noise(image, noise, noisy, "Speckle Noise")
image = np.zeros((256, 256), dtype=np.uint8)
prob = 0.01
noisy = image.copy()
mask = np.random.rand(*image.shape) < prob
noisy[mask] = 255
noise = mask.astype(np.uint8)
plot_noise(image, noise, noisy, "Impulse Noise")
plt.show()
import os
import cv2
import tifffile
import numpy as np
def add_uniform_noise(image, intensity=50, noise_ratio=1.0):
""" 添加均匀随机噪声(Uniform Noise),并控制受影响像素的比例。
支持 uint8, uint16 和 float32 图像。
参数:
image (numpy.ndarray): 输入的灰度图像。
intensity (int): 噪声强度,取值范围为 [0, max_val // 2],其中 max_val 为图像数据类型最大值。
noise_ratio (float): 噪声覆盖率,取值范围为 (0, 1]。
返回:
numpy.ndarray: 添加均匀随机噪声后的图像,数据类型与输入保持一致。
"""
if image.ndim != 2:
raise ValueError("只支持灰度图像")
if not (0 < noise_ratio <= 1.0):
raise ValueError("噪声覆盖率必须在 (0, 1] 之间")
if np.issubdtype(image.dtype, np.integer):
max_val = np.iinfo(image.dtype).max
elif np.issubdtype(image.dtype, np.floating):
max_val = 1.0
else:
raise TypeError("不支持的图像数据类型")
assert 0 < intensity <= max_val // 2 if isinstance(max_val, int) else intensity <= 0.5
noisy_image = image.copy()
h, w = image.shape
total_pixels = h * w
num_noisy_pixels = int(total_pixels * noise_ratio)
indices = np.random.choice(total_pixels, num_noisy_pixels, replace=False)
coords = np.unravel_index(indices, (h, w))
noise = np.random.uniform(-intensity, intensity, num_noisy_pixels)
if np.issubdtype(image.dtype, np.integer):
noise = noise.astype(np.int32)
temp = noisy_image[coords].astype(np.int32) + noise
noisy_image[coords] = np.clip(temp, 0, max_val).astype(image.dtype)
else:
temp = noisy_image[coords] + noise
noisy_image[coords] = np.clip(temp, 0.0, max_val).astype(image.dtype)
return noisy_image
def add_gaussian_noise(image, mean=0.0, sigma=25.0):
""" 添加高斯噪声(Gaussian Noise) — 遵循正态分布,用于模拟感光元件噪声。
支持 uint8、uint16、float32 的灰度图像。
参数:
image (numpy.ndarray): 输入的灰度图像。
mean (float): 均值,决定噪声分布的中心位置(默认 0.0)。
sigma (float): 标准差,决定噪声分布的离散程度(默认 25.0)。
返回:
numpy.ndarray: 添加高斯噪声后的图像,数据类型与输入保持一致。
"""
if image.ndim != 2:
raise ValueError("只支持灰度图像")
if sigma < 0:
raise ValueError("sigma 必须为非负数")
if np.issubdtype(image.dtype, np.integer):
max_val = np.iinfo(image.dtype).max
elif np.issubdtype(image.dtype, np.floating):
max_val = 1.0
else:
raise TypeError("不支持的图像数据类型")
noise = np.random.normal(mean, sigma, image.shape)
if np.issubdtype(image.dtype, np.integer):
temp = image.astype(np.int32) + noise.astype(np.int32)
noisy_image = np.clip(temp, 0, max_val).astype(image.dtype)
else:
temp = image + noise
noisy_image = np.clip(temp, 0.0, max_val).astype(image.dtype)
return noisy_image
def add_salt_pepper_noise(image, salt_prob=0.02, pepper_prob=0.02):
""" 添加椒盐噪声(Salt-and-Pepper Noise)——随机选择部分像素,将其值设为最大值或最小值。
支持 uint8、uint16、float32 灰度图。
参数:
image (numpy.ndarray): 输入的灰度图像。
salt_prob (float): 椒噪声概率,推荐取值范围为 (0, 1](默认 0.02)。
pepper_prob (float): 胡椒噪声概率,推荐取值范围为 (0, 1](默认 0.02)。
返回:
numpy.ndarray: 添加椒盐噪声后的图像,数据类型与输入保持一致。
"""
if image.ndim != 2:
raise ValueError("只支持灰度图像")
if not (0 <= salt_prob <= 1) or not (0 <= pepper_prob <= 1):
raise ValueError("salt_prob 和 pepper_prob 应在 [0,1] 范围内")
if salt_prob + pepper_prob > 1:
raise ValueError("salt_prob 与 pepper_prob 总和不能超过 1")
if np.issubdtype(image.dtype, np.integer):
max_val = np.iinfo(image.dtype).max
elif np.issubdtype(image.dtype, np.floating):
max_val = 1.0
else:
raise TypeError("不支持的图像数据类型")
noisy_image = image.copy()
total_pixels = image.size
num_salt = int(total_pixels * salt_prob)
num_pepper = int(total_pixels * pepper_prob)
coords = np.random.choice(total_pixels, num_salt + num_pepper, replace=False)
coords = np.unravel_index(coords, image.shape)
salt_coords = (coords[0][:num_salt], coords[1][:num_salt])
pepper_coords = (coords[0][num_salt:], coords[1][num_salt:])
max_value = np.max(image)
noisy_image[salt_coords] = max_value
noisy_image[pepper_coords] = 0
return noisy_image
def add_poisson_noise(image):
""" 添加泊松噪声(Poisson Noise)——模拟基于光子统计的成像噪声。
支持 uint8、uint16、float32 灰度图。
参数:
image (numpy.ndarray): 输入的灰度图像。
返回:
numpy.ndarray: 添加泊松噪声后的图像,数据类型与输入保持一致。
"""
if np.issubdtype(image.dtype, np.integer):
max_val = np.iinfo(image.dtype).max
elif np.issubdtype(image.dtype, np.floating):
max_val = 1.0
else:
raise TypeError("不支持的图像数据类型")
max_value = np.max(image)
image_float = image.astype(np.float64)
scale_factor = 255.0
scaled = image_float / max_value * scale_factor
noisy = np.random.poisson(scaled)
rescaled = noisy / scale_factor * max_value
noisy_image = np.clip(rescaled, 0, max_value).astype(image.dtype)
return noisy_image
def add_rayleigh_noise(image, scale=30, mode='multiplicative'):
""" 添加瑞利噪声(Rayleigh Noise) ———— 常用于通信模拟,默认使用乘性噪声建模。
参数:
image (numpy.ndarray): 输入灰度图像。
scale (float): 瑞利分布的尺度参数,推荐范围为 10 ~ 50。
mode (str): 模式选择,可选 'additive' 或 'multiplicative'。
返回:
numpy.ndarray: 添加瑞利噪声后的图像。
"""
if np.issubdtype(image.dtype, np.integer):
max_val = np.iinfo(image.dtype).max
elif np.issubdtype(image.dtype, np.floating):
max_val = 1.0
else:
raise TypeError("不支持的图像数据类型")
noise = np.random.rayleigh(scale, image.shape)
image_float = image.astype(np.float64)
if mode == 'additive':
noisy = image_float + noise
elif mode == 'multiplicative':
noisy = image_float * noise
else:
raise ValueError("mode 参数应为 'additive' 或 'multiplicative'")
noisy_image = np.clip(noisy, 0, max_val).astype(image.dtype)
return noisy_image
def add_gamma_noise(image, shape=2.0, scale=30, mode='multiplicative'):
""" 添加伽马噪声(Gamma Noise) ———— 可选择加性或乘性噪声,常用于放射性图像建模。
参数:
image (numpy.ndarray): 输入的灰度图像。
shape (float): 伽马分布的形状参数,推荐范围为 0.5 ~ 2.0。
scale (float): 伽马分布的尺度参数,推荐范围为 10 ~ 50。
mode (str): 'additive' 或 'multiplicative',控制噪声叠加方式。
返回:
numpy.ndarray: 添加伽马噪声后的图像。
"""
if np.issubdtype(image.dtype, np.integer):
max_val = np.iinfo(image.dtype).max
elif np.issubdtype(image.dtype, np.floating):
max_val = 1.0
else:
raise TypeError("不支持的图像数据类型")
gamma_noise = np.random.gamma(shape, scale, image.shape)
image_float = image.astype(np.float64)
if mode == 'additive':
noisy = image_float + gamma_noise
elif mode == 'multiplicative':
noisy = image_float * gamma_noise
else:
raise ValueError("mode 参数仅支持 'additive' 或 'multiplicative'")
noisy_image = np.clip(noisy, 0, max_val).astype(image.dtype)
return noisy_image
def add_speckle_noise(image, scale=0.2):
""" 添加斑点噪声(Speckle Noise) ———— 数学形式为 f(x) = x + x·n,其中 n 为高斯分布噪声项。
参数:
image (numpy.ndarray): 输入的灰度图像。
scale (float): 噪声强度的标准差(默认 0.2),推荐范围为 0.05 ~ 0.3。
返回:
numpy.ndarray: 添加斑点噪声后的图像,数据类型与输入保持一致。
"""
if np.issubdtype(image.dtype, np.integer):
max_val = np.iinfo(image.dtype).max
elif np.issubdtype(image.dtype, np.floating):
max_val = 1.0
else:
raise TypeError("不支持的图像数据类型")
image_float = image.astype(np.float32)
noise = np.random.randn(*image.shape) * scale
noisy = image_float + image_float * noise
noisy_image = np.clip(noisy, 0, max_val).astype(image.dtype)
return noisy_image
def add_impulse_noise(image, prob=0.02):
""" 添加脉冲噪声(Impulse Noise) ———— 以一定概率将像素值替换为最大值或最小值,模拟传感器失真。
参数:
image (numpy.ndarray): 输入的灰度图像。
prob (float): 每个像素被替换为噪声值(0 或最大值)的概率(默认 0.02)。
返回:
numpy.ndarray: 添加脉冲噪声后的图像,数据类型与输入保持一致。
"""
if np.issubdtype(image.dtype, np.integer):
max_val = np.iinfo(image.dtype).max
elif np.issubdtype(image.dtype, np.floating):
max_val = 1.0
else:
raise TypeError("不支持的图像数据类型")
noisy_image = image.copy()
h, w = image.shape
total_pixels = h * w
num_impulse = int(total_pixels * prob)
indices = np.random.choice(total_pixels, num_impulse, replace=False)
coords = np.unravel_index(indices, (h, w))
max_value = np.max(image)
salt_or_pepper = np.random.randint(0, 2, num_impulse)
noisy_image[coords] = salt_or_pepper * max_value
return noisy_image.astype(image.dtype)
def add_noise(image, mode='gaussian', **kwargs):
if mode == 'uniform':
return add_uniform_noise(image, **kwargs)
elif mode == 'gaussian':
return add_gaussian_noise(image, **kwargs)
elif mode == 'salt_pepper':
return add_salt_pepper_noise(image, **kwargs)
elif mode == 'poisson':
return add_poisson_noise(image, **kwargs)
elif mode == 'rayleigh':
return add_rayleigh_noise(image, **kwargs)
elif mode == 'gamma':
return add_gamma_noise(image, **kwargs)
elif mode == 'speckle':
return add_speckle_noise(image, **kwargs)
elif mode == 'impulse':
return add_impulse_noise(image, **kwargs)
else:
raise ValueError("不支持的噪声类型")
def read_image(image_path):
if not os.path.exists(image_path):
raise ValueError("文件不存在,请检查文件路径")
if not os.path.isfile(image_path):
raise ValueError("路径不是文件,请检查文件路径")
if not os.path.splitext(image_path)[1] in ['.tif', '.tiff', '.bmp', '.jpg', '.jpeg', '.png']:
raise ValueError("不支持的文件格式,请检查文件路径")
if image_path.endswith('.tif') or image_path.endswith('.tiff'):
image = tifffile.imread(image_path)
elif image_path.endswith('.bmp') or image_path.endswith('.jpg') or image_path.endswith('.jpeg') or image_path.endswith('.png'):
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
else:
raise ValueError("不支持的文件格式,请检查文件路径")
return image
if __name__ == '__main__':
import random
random.seed(0)
np.random.seed(0)
image_path = r"image.tif"
image = read_image(image_path)
uniform_noise = add_uniform_noise(image, intensity=50, noise_ratio=0.1)
gaussian_noise = add_gaussian_noise(image, mean=0.0, sigma=25.0)
salt_pepper_noise = add_salt_pepper_noise(image, salt_prob=0.02, pepper_prob=0.02)
poisson_noise = add_poisson_noise(image)
rayleigh_noise = add_rayleigh_noise(image, scale=30, mode='multiplicative')
gamma_noise = add_gamma_noise(image, shape=2.0, scale=30, mode='multiplicative')
speckle_noise = add_speckle_noise(image, scale=0.2)
impulse_noise = add_impulse_noise(image, prob=0.02)
path = os.path.dirname(image_path)
name = os.path.splitext(os.path.basename(image_path))[0]
print(os.path.join(path, name))
import matplotlib.pyplot as plt
plt.subplot(2, 5, 1), plt.imshow(image, cmap='gray'), plt.title('Original Image'), plt.axis('off')
plt.subplot(2, 5, 2), plt.imshow(uniform_noise, cmap='gray'), plt.title('Uniform Noise'), plt.axis('off')
plt.subplot(2, 5, 3), plt.imshow(gaussian_noise, cmap='gray'), plt.title('Gaussian Noise'), plt.axis('off')
plt.subplot(2, 5, 4), plt.imshow(salt_pepper_noise, cmap='gray'), plt.title('Salt & Pepper Noise'), plt.axis('off')
plt.subplot(2, 5, 5), plt.imshow(poisson_noise, cmap='gray'), plt.title('Poisson Noise'), plt.axis('off')
plt.subplot(2, 5, 6), plt.imshow(rayleigh_noise, cmap='gray'), plt.title('Rayleigh Noise'), plt.axis('off')
plt.subplot(2, 5, 7), plt.imshow(gamma_noise, cmap='gray'), plt.title('Gamma Noise'), plt.axis('off')
plt.subplot(2, 5, 8), plt.imshow(speckle_noise, cmap='gray'), plt.title('Speckle Noise'), plt.axis('off')
plt.subplot(2, 5, 9), plt.imshow(impulse_noise, cmap='gray'), plt.title('Impulse Noise'), plt.axis('off')
plt.show()