实习背景与目的意义¶
本次实习旨在通过数字图像处理的若干基础操作,让学生深入理解图像滤波的相关理论与实践内容,以提升对数字图像处理基础知识的掌握。本次实验内容涵盖了图像的彩色合成、噪声模拟、滤波器实现、二维傅里叶变换及频率域滤波操作,着重训练学生对图像基本操作的程序实现,并为后续复杂图像处理打下基础。具体目标包括掌握不同滤波技术(如中值滤波、平滑滤波、拉普拉斯滤波)的应用效果,以及通过频域操作(如高通和低通滤波)分析和处理图像特性,进一步提升对图像特性与滤波方法的理解。
实习内容与步骤¶
假彩色合成与保存:通过读取影像数据,调整波段顺序以实现标准假彩色合成。将近红外波段(NIR)、红波段(Red)和绿波段(Green)分别赋予RGB三个通道,合成为彩色影像并保存。
图像加噪:加入椒盐噪声和高斯噪声两种随机噪声,模拟含噪图像。其中,椒盐噪声通过随机极端值替换图像像素,高斯噪声通过高斯分布上的随机值叠加像素值,实现不同噪声效果的影像生成。
空间滤波:针对不同噪声图像进行滤波处理:椒盐噪声图像采用中值滤波降低噪声干扰,并保留边缘细节。高斯噪声图像使用均值滤波实现平滑处理。原始灰度图像通过拉普拉斯滤波进行锐化处理,增强边缘特征。
傅里叶变换:对原始影像和椒盐噪声图像进行频域变换,输出影像的频谱特性,便于分析图像的高低频信息分布。
高通与低通滤波:利用傅里叶变换设计高通滤波器和低通滤波器,分别用于突出高频边缘特征和降低平滑噪声干扰。 滤波后通过逆变换还原图像,验证频域滤波效果并保存滤波结果。
去噪结果定量评价,不同的去噪方法横向评价
(1)彩色合成与保存¶
任务:读取data目录下GF2.tif数据,将标准假彩色合成结果保存到./result/GF2-false.tif
import rasterio
import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")
with rasterio.open('data/GF2.tiff') as src:
meta = src.meta.copy()
img = src.read().astype(np.float32)
meta.update({'dtype': 'float32'})
def normized(data):
min_val = np.min(data)
max_val = np.max(data)
normed_data = (data - min_val) / (max_val - min_val)
return normed_data
for i in range(img.shape[0]):
img[i, :, :] = normized(img[i, :, :])
blue = img[0, :, :]
green = img[1, :, :]
red = img[2, :, :]
nir = img[3, :, :]
true_rgb = np.stack([red, green, blue])
false_rgb = np.stack([nir, red, green])
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title('True Color Composite')
plt.imshow(np.transpose(true_rgb, (1, 2, 0)))
plt.axis('off')
plt.subplot(1, 2, 2)
plt.title('False Color Composite')
plt.imshow(np.transpose(false_rgb, (1, 2, 0)))
plt.axis('off')
false_rgb_meta = meta.copy()
false_rgb_meta.update({'count': 3})
with rasterio.open('result/GF2-false.tif', 'w', **false_rgb_meta) as src:
src.write(false_rgb)
# free memory
del img
del blue
del green
del red
del nir
del true_rgb
del false_rgb
del meta
del false_rgb_meta
(2)图像加噪¶
任务:读取data目录下的JAX_gray.tif数据,对影像分别添加椒盐噪声和高斯噪声,保存到data/JAX_noise_xxx.tif
with rasterio.open('data/JAX_gray.tif') as src:
meta = src.meta.copy()
img = src.read(1).astype(np.float32)
meta.update({'dtype': 'float32'})
img = normized(img)
def add_salt_and_pepper_noise(image, salt_prob, pepper_prob):
noisy_image = np.copy(image)
total_pixels = image.size
num_salt = int(total_pixels * salt_prob)
num_pepper = int(total_pixels * pepper_prob)
salt_coords = [np.random.randint(0, i - 1, num_salt) for i in image.shape]
noisy_image[salt_coords[0], salt_coords[1]] = 1
pepper_coords = [np.random.randint(0, i - 1, num_pepper) for i in image.shape]
noisy_image[pepper_coords[0], pepper_coords[1]] = 0
return noisy_image
def add_gaussian_noise(image, mean=0, var=0.01):
sigma = var ** 0.5
gaussian_noise = np.random.normal(mean, sigma, image.shape)
noisy_image = image + gaussian_noise
noisy_image = np.clip(noisy_image, 0, 1)
return noisy_image
salt_pepper_noisy_img = add_salt_and_pepper_noise(img, salt_prob=0.02, pepper_prob=0.02)
gaussian_noisy_img = add_gaussian_noise(img, mean=0, var=0.01)
# 显示三个图像
plt.figure(figsize=(15, 5))
plt.subplot(1, 3, 1)
plt.title('Original Image')
plt.imshow(img, cmap='gray')
plt.axis('off')
plt.subplot(1, 3, 2)
plt.title('Salt and Pepper Noise')
plt.imshow(salt_pepper_noisy_img, cmap='gray')
plt.axis('off')
plt.subplot(1, 3, 3)
plt.title('Gaussian Noise')
plt.imshow(gaussian_noisy_img, cmap='gray')
plt.axis('off')
with rasterio.open('result/JAX_noise_salt_pepper.tif', 'w', **meta) as src:
src.write(salt_pepper_noisy_img, 1)
with rasterio.open('result/JAX_noise_gaussian.tif', 'w', **meta) as src:
src.write(gaussian_noisy_img, 1)
del img
del salt_pepper_noisy_img
del gaussian_noisy_img
del meta
(3)空间滤波¶
任务:对(2)中生成的椒盐噪声图像进行中值滤波,对(2)中生成的高斯噪声图像进行均值滤波,对原始灰度图像进行拉普拉斯滤波,将滤波后的结果保存到data/JAX_filter_xx.tif。
def median_filter(image):
padded_image = np.pad(image, pad_width=1, mode='edge')
filtered_image = np.zeros_like(image)
for i in range(image.shape[0]):
for j in range(image.shape[1]):
neighborhood = padded_image[i:i+3, j:j+3]
filtered_image[i, j] = np.median(neighborhood)
return filtered_image
def mean_filter(image):
padded_image = np.pad(image, pad_width=1, mode='edge')
filtered_image = np.zeros_like(image)
for i in range(image.shape[0]):
for j in range(image.shape[1]):
neighborhood = padded_image[i:i+3, j:j+3]
filtered_image[i, j] = np.mean(neighborhood)
return filtered_image
def laplacian_filter(image):
kernel = np.array([[0, -1, 0],
[-1, 4, -1],
[0, -1, 0]])
padded_image = np.pad(image, pad_width=1, mode='edge')
filtered_image = np.zeros_like(image)
for i in range(image.shape[0]):
for j in range(image.shape[1]):
neighborhood = padded_image[i:i+3, j:j+3]
filtered_image[i, j] = np.sum(neighborhood * kernel)
filtered_image = np.clip(filtered_image, 0, 1)
return filtered_image
with rasterio.open('data/JAX_gray.tif') as src:
img = src.read(1).astype(np.float32)
meta = src.meta.copy()
meta.update({'dtype': 'float32'})
img = normized(img)
laplacian_filtered_img = laplacian_filter(img)
with rasterio.open('result/JAX_noise_salt_pepper.tif') as src:
salt_pepper_noisy_img = src.read(1).astype(np.float32)
salt_pepper_filtered_img = median_filter(salt_pepper_noisy_img)
with rasterio.open('result/JAX_noise_gaussian.tif') as src:
gaussian_noisy_img = src.read(1).astype(np.float32)
gaussian_filtered_img = mean_filter(gaussian_noisy_img)
plt.figure(figsize=(15, 5))
plt.subplot(1, 3, 1)
plt.title('Median Filter on Salt and Pepper Noise')
plt.imshow(salt_pepper_filtered_img, cmap='gray')
plt.axis('off')
plt.subplot(1, 3, 2)
plt.title('Mean Filter on Gaussian Noise')
plt.imshow(gaussian_filtered_img, cmap='gray')
plt.axis('off')
plt.subplot(1, 3, 3)
plt.title('Laplacian Filter on Original Image')
plt.imshow(laplacian_filtered_img, cmap='gray')
plt.axis('off')
with rasterio.open('result/JAX_filter_median.tif', 'w', **meta) as src:
src.write(salt_pepper_filtered_img, 1)
with rasterio.open('result/JAX_filter_mean.tif', 'w', **meta) as src:
src.write(gaussian_filtered_img, 1)
with rasterio.open('result/JAX_filter_laplacian.tif', 'w', **meta) as src:
src.write(laplacian_filtered_img, 1)
del img
del salt_pepper_filtered_img
del gaussian_filtered_img
del laplacian_filtered_img
del meta
(4)傅里叶变换¶
任务:对原始影像和椒盐噪声图像进行傅里叶变换,输出频率谱。
def compute_fft(image):
f = np.fft.fft2(image)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 20 * np.log(np.abs(fshift) + 1)
return magnitude_spectrum
with rasterio.open('data/JAX_gray.tif') as src:
img = src.read(1).astype(np.float32)
meta = src.meta.copy()
meta.update({'dtype': 'float32'})
img = normized(img)
original_fft = compute_fft(img)
salt_pepper_fft = compute_fft(salt_pepper_noisy_img)
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title('FFT of Original Image')
plt.imshow(original_fft, cmap='gray')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.title('FFT of Salt and Pepper Noisy Image')
plt.imshow(salt_pepper_fft, cmap='gray')
plt.axis('off')
with rasterio.open('result/JAX_fft_original.tif', 'w', **meta) as src:
src.write(original_fft, 1)
with rasterio.open('result/JAX_fft_salt_pepper.tif', 'w', **meta) as src:
src.write(salt_pepper_fft, 1)
del img
del salt_pepper_noisy_img
del original_fft
del salt_pepper_fft
del meta
with rasterio.open('data/GF2.tiff') as src:
meta = src.meta.copy()
img = src.read(1).astype(np.float32)
meta.update({'dtype': 'float32'})
img = normized(img)
def high_pass_filter(image, cutoff_frequency):
f = np.fft.fft2(image)
fshift = np.fft.fftshift(f)
rows, cols = image.shape
crow, ccol = rows // 2 , cols // 2
mask = np.ones((rows, cols), np.uint8)
r = cutoff_frequency
center = (crow, ccol)
y, x = np.ogrid[:rows, :cols]
mask_area = (x - center[1])**2 + (y - center[0])**2 <= r*r
mask[mask_area] = 0
fshift_filtered = fshift * mask
f_ishift = np.fft.ifftshift(fshift_filtered)
img_back = np.fft.ifft2(f_ishift)
img_back = np.abs(img_back)
img_back = normized(img_back)
return img_back
def low_pass_filter(image, cutoff_frequency):
f = np.fft.fft2(image)
fshift = np.fft.fftshift(f)
rows, cols = image.shape
crow, ccol = rows // 2 , cols // 2
mask = np.zeros((rows, cols), np.uint8)
r = cutoff_frequency
center = (crow, ccol)
y, x = np.ogrid[:rows, :cols]
mask_area = (x - center[1])**2 + (y - center[0])**2 <= r*r
mask[mask_area] = 1
fshift_filtered = fshift * mask
f_ishift = np.fft.ifftshift(fshift_filtered)
img_back = np.fft.ifft2(f_ishift)
img_back = np.abs(img_back)
img_back = normized(img_back)
return img_back
high_pass_filtered_img = high_pass_filter(img, cutoff_frequency=30)
high_pass_filtered_img = normized(high_pass_filtered_img)
low_pass_filtered_img = low_pass_filter(img, cutoff_frequency=30)
low_pass_filtered_img = normized(low_pass_filtered_img)
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.title('High Pass Filtered Image')
plt.imshow(high_pass_filtered_img, cmap='gray')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.title('Low Pass Filtered Image')
plt.imshow(low_pass_filtered_img, cmap='gray')
plt.axis('off')
with rasterio.open('data/high_pass_result.png', 'w', **meta) as src:
src.write(high_pass_filtered_img, 1)
with rasterio.open('data/low_pass_result.png', 'w', **meta) as src:
src.write(low_pass_filtered_img, 1)
del img
del high_pass_filtered_img
del meta
遇到的问题与解决策略¶
循环处理效率低:原先逐像素双重 for 循环计算中值滤波耗时过长,改用 NumPy 的 stride tricks 或 OpenCV 中位数函数显著提升速度。
频谱可视化问题:直接对幅值取对数后显示对比度更佳
边缘效应与零填充:空间滤波需对图像边界作对称或零填充,避免卷积时尺寸不匹配或边缘失真。
实验收获与心得¶
理论与实践结合
通过编程实现空间域与频域滤波,深刻理解低频对应图像的整体亮度与缓变部分,高频对应边缘与细节; 卷积核、掩模设计与信号处理理论相互印证,增强了对数字图像处理原理的掌握。 工具与优化
熟练掌握 NumPy、Matplotlib 等常用 Python 库; 体会到向量化与库函数调用在大规模数据处理中的重要性。 问题解决能力
在实验过程中遇到性能瓶颈与调试难题,提高了查阅文档、测试与调优的综合能力; 学会分步骤分析问题,将复杂任务拆分为子模块逐个攻克。 团队协作与时间管理