数字图像处理 - 实验作业四 - Python

第五章 图像复原与重建

我们所看到的事物并不是事物的本身……物体本身是什么对我们而言完全是未知的,并且远离我们的感知。除了对物体的感知,我们什么也不知道——伊曼努尔·康德

特别说明:

第三个和第四个实验的代码大部分都不是原创,因为自己理解不是很深,借助了网上的资料,文末给出了参考来源

图像退化/复原过程的模型:

在这里插入图片描述

退化图像:
g ( x , y ) = f ( x , y ) ∗ h ( x , y ) + η ( x , y ) g(x, y)=f(x, y) * h(x, y)+\eta(x, y) g(x,y)=f(x,y)h(x,y)+η(x,y)

G ( u , v ) = F ( u , v ) H ( u , v ) + N ( u , v ) G(u, v)=F(u, v) H(u, v)+N(u, v) G(u,v)=F(u,v)H(u,v)+N(u,v)

我们研究的就是在退化之后如何进行复原

1、实现自适应均值滤波,并和算术均值滤波的结果做对比

算术均值滤波器的表达式为
f ^ ( x , y ) = 1 m n ∑ ( s , t ) ∈ S m n g ( s , t ) \hat{f}(x, y)=\frac{1}{m n} \sum_{(s, t) \in S_{mn}} g(s, t) f^(x,y)=mn1(s,t)Smng(s,t)

其中的Smn代表中心在点(x,y)处,大小为m×n的矩形子图像窗口(邻域)的一组坐标

算术均值滤波会平滑一幅图像中的局部变化。虽然模糊了结果,但是降低了噪声

自适应均值滤波的表达式为
f ^ ( x , y ) = g ( x , y ) − σ η 2 σ L 2 [ g ( x , y ) − m L ] \hat{f}(x, y)=g(x, y)-\frac{\sigma_{\eta}^{2}}{\sigma_{L}^{2}}\left[g(x, y)-m_{L}\right] f^(x,y)=g(x,y)σL2ση2[g(x,y)mL]

参数以及处理的原则为

在这里插入图片描述

其中需要估计的就是噪声的方差

实现的结果如下所示:

在这里插入图片描述

可见自适应均值滤波在算术均值滤波的基础上,对于边缘的处理更加好了。消除了原有的模糊信息,保留的更多的边缘的部分

实现的代码如下所示

# 自适应均值滤波和算术均值滤波
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image


def mean_filter(src_image, kernel_size=3):
    """The arithmetic average filter"""
    edge_size = int(kernel_size / 2)
    padded_img = np.pad(src_image, edge_size, 'edge')

    height, width = src_image.shape

    src_after_filter = np.zeros_like(src_image, dtype="float64")

    for i in range(height):
        for j in range(width):
            box = padded_img[i:i + kernel_size, j:j + kernel_size]
            src_after_filter[i, j] = np.mean(box)

    return src_after_filter


def self_adaptaion_mean_filter(src_image, kernel_size=3, variance=1000):
    """self adaptaion mean filter"""
    edge_size = int(kernel_size / 2)
    padded_img = np.pad(src_image, edge_size, 'edge')

    height, width = src_image.shape

    src_after_filter = np.zeros_like(src_image, dtype="float64")

    for i in range(height):
        for j in range(width):
            box = padded_img[i:i + kernel_size, j:j + kernel_size]
            box_mean = np.mean(box)
            box_var = np.var(box)
            src_after_filter[i, j] = src_image[i, j] - (variance / box_var) * (src_image[i, j] - box_mean)

    return src_after_filter


def main(img_path):
    src = np.array(Image.open(img_path).convert("L"))
    noise = np.random.normal(0, 15, size=src.shape)
    noise_src = src + noise

    img_mean_filter = mean_filter(noise_src, 7)
    img_self_adap_mean_filter = self_adaptaion_mean_filter(noise_src, 7, 225)

    img_list = [src, noise_src, img_mean_filter, img_self_adap_mean_filter]
    img_name = ["原图像", "被加性高斯噪声污染的图像", "7×7算术均值滤波", "7×7自适应均值滤波"]

    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    _, axs = plt.subplots(2, 2, figsize=(12, 12))

    for i in range(2):
        for j in range(2):
            axs[i][j].imshow(img_list[i * 2 + j], cmap='gray')
            axs[i][j].set_title(img_name[i * 2 + j])
            axs[i][j].axes.get_xaxis().set_visible(False)
            axs[i][j].axes.get_yaxis().set_visible(False)

    plt.show()


if __name__ == '__main__':
    main('man.jpg')


2、实现自适应中值滤波,并和中值滤波的结果做对比

中值滤波器的表达式为
f ^ ( x , y ) = m e d i a n { g ( s , t ) } ( s , t ) ∈ S x y \hat{f}(x, y)=median\{g(s,t)\} \quad (s,t)∈S_{xy} f^(x,y)=median{g(s,t)}(s,t)Sxy

不同于平均值,这里取的是中值,对于椒盐噪声类型的噪声,可以提供更好的去噪能力,且比相同尺寸的线性平滑滤波器引起的模糊更少

自适应中值滤波的参数为

  • S x y S_{xy} Sxy:滤波器的作用区域,该区域中心点为图像中第y行第x列个像素点;
  • Z m i n Z_{min} Zmin S x y S_{xy} Sxy中最小的灰度值;
  • Z m a x Z_{max} Zmax S : x y S_:{xy} Sxy中最大的灰度值;
  • Z m e d Z_{med} Zmed S x y S_{xy} Sxy中所有灰度值的中值;
  • Z x y Z_{xy} Zxy:表示图像中第y行第x列个像素点的灰度值;
  • S m a x S_{max} Smax S x y S_{xy} Sxy所允许的最大窗口尺寸;

自适应中值滤波器分为以下两个过程,A和B:

Process A

  1. A 1 A1 A1 = Z m e d − Z m i n Z_{med}-Z_{min} ZmedZmin
  2. A 2 A2 A2 = Z m e d − Z m a x Z_{med}-Z_{max} ZmedZmax
  3. 如果 A 1 > 0 A1>0 A1>0 且$ A2<0$,则跳转到B
  4. 否则,增大窗口的尺寸
  5. 如果增大后的尺寸 ≤ S m a x ≤S_{max} Smax,则重复A
  6. 否则,直接输出 Z m e d Z_{med} Zmed

Process B

  1. B 1 B1 B1 = Z x y − Z m i n Z_{xy}-Z_{min} ZxyZmin
  2. B 2 B2 B2 = Z x y − Z m a x Z_{xy}-Z_{max} ZxyZmax
  3. 如果 B 1 > 0 B1>0 B1>0 B 2 < 0 B2<0 B2<0,则输出 Z − x y Z-{xy} Zxy
  4. 否则输出 Z m e d Z_{med} Zmed

实现结果如下所示:

在这里插入图片描述

可见自适应中值滤波对于被污染的图像的去噪有着更好的效果,而一般的中值滤波残留的噪声还是比较多的。

实现代码如下所示:

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import sys


def salt(image, prob):
    """prob:Noise Scale"""
    output = np.zeros(image.shape, np.float64)
    prob_other = 1 - prob
    for i in range(image.shape[0]):
        for j in range(image.shape[1]):
            rdn = np.random.random()
            if rdn < prob:
                output[i][j] = 0
            elif rdn > prob_other:
                output[i][j] = 255
            else:
                output[i][j] = image[i][j]
    return output


def median_filter(src_image, kernel_size):
    """Median filter"""
    edge_size = int(kernel_size / 2)
    padded_img = np.pad(src_image, edge_size, 'edge')

    height, width = src_image.shape

    src_after_filter = np.zeros_like(src_image, dtype="float64")

    for i in range(height):
        for j in range(width):
            box = padded_img[i:i + kernel_size, j:j + kernel_size]
            src_after_filter[i, j] = np.median(box)

    return src_after_filter


def self_adaptation_median_filter(src_image, kernel_size, output):
    """self-adaptation median filter"""

    height, width = src_image.shape

    num = height * width
    loc = 0

    src_after_filter = np.zeros_like(src_image, dtype="float64")

    for i in range(height):
        for j in range(width):
            rst = filter_process(src_image, kernel_size, i, j, 7)
            src_after_filter[i][j] = rst
            loc = loc + 1
            count = loc / num * 100
            output.write(f'\rcomplete percent:{count:.0f}')

    return src_after_filter


def filter_process(src_image, kernel_size, i, j, s_max=7):
    edge_size = int(kernel_size / 2)
    padded_img = np.pad(src_image, edge_size, 'edge')

    box = padded_img[i:i + kernel_size, j:j + kernel_size]
    z_min = np.min(box)
    z_max = np.max(box)
    z_med = np.median(box)
    z_xy = src_image[i, j]

    rst = a_process(src_image, kernel_size, i, j, s_max, z_xy, z_min, z_max, z_med)

    return rst


def a_process(src_image, kernel_size, i, j, s_max, z_xy, z_min, z_max, z_med):
    A1 = z_med - z_min
    A2 = z_med - z_max

    if A1 > 0 and A2 < 0:
        return b_process(z_xy, z_min, z_max, z_med)
    elif (kernel_size + 2) <= s_max:
        return filter_process(src_image, kernel_size + 2, i, j, s_max)
    else:
        return z_med


def b_process(z_xy, z_min, z_max, z_med):
    B1 = z_xy - z_min
    B2 = z_xy - z_max

    if B1 > 0 and B2 < 0:
        return z_xy
    else:
        return z_med


def add_noise(src_image, noise_method, prob):
    return noise_method(src_image, prob)


def main(img_path):
    src = np.array(Image.open(img_path).convert("L"))
    noise_src = add_noise(src, salt, 0.1)

    output = sys.stdout

    img_median_filter = median_filter(noise_src, 3)
    img_self_adap_median_filter = self_adaptation_median_filter(noise_src, 3, output)

    output.flush()

    img_list = [src, noise_src, img_median_filter, img_self_adap_median_filter]
    img_name = ["原图像", "被椒盐噪声污染的图像", "3×3中值滤波", "自适中值滤波"]

    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    _, axs = plt.subplots(2, 2, figsize=(12, 12))

    for i in range(2):
        for j in range(2):
            axs[i][j].imshow(img_list[i * 2 + j], cmap='gray')
            axs[i][j].set_title(img_name[i * 2 + j])
            axs[i][j].axes.get_xaxis().set_visible(False)
            axs[i][j].axes.get_yaxis().set_visible(False)

    plt.show()


if __name__ == '__main__':
    main('man.jpg')

3、逆滤波与维纳滤波

原题:用下式对图像进行模糊处理,然后加高斯白噪声,得到降质图像。用逆滤波和维纳滤波恢复图像,对结果进行分析

题目中所要求的模糊处理的式子为
H ( u , v ) = e − k ( u 2 + v 2 ) 5 / 6 H(u, v)=e^{-k\left(u^{2}+v^{2}\right)^{5 / 6}} H(u,v)=ek(u2+v2)5/6

模糊处理可以理解为我们在摄像的过程中,由于抖动导致图像在某个方向上变得模糊了,这个模糊反应在我们的式子中就是 H ( u , v ) H(u,v) H(u,v)

而逆滤波的原理非常简单,怎么变过来就这么回去,F代表原图,G代表模糊后的图,N为噪声

在这里插入图片描述

可以看出来的是,在没有噪声的时候,逆滤波就可以很好的复原,但是如果有噪声,对原图像的影响还是很大的。为了应对这种问题,我们引入了维纳滤波

维纳滤波用来去除含有噪声的模糊图像,其目标是找到未污染图像的一个估计,使它们之间的均方差最小,可以去除噪声,同时清晰化模糊图像

具体原理可以参考《数字图像处理》或文末给出的连接

其对应的公式为
G ( f ) = 1 H ( f ) [ ∣ H ( f ) ∣ 2 ∣ H ( f ) ∣ 2 + N ( f ) S ( f ) ] = 1 H ( f ) [ ∣ H ( f ) ∣ 2 ∣ H ( f ) ∣ 2 + 1 S N R ( f ) ] G(f)=\frac{1}{H(f)}\left[\frac{|H(f)|^{2}}{|H(f)|^{2}+\frac{N(f)}{S(f)}}\right]=\frac{1}{H(f)}\left[\frac{|H(f)|^{2}}{|H(f)|^{2}+\frac{1}{S N R(f)}}\right] G(f)=H(f)1H(f)2+S(f)N(f)H(f)2=H(f)1[H(f)2+SNR(f)1H(f)2]

可见噪声为0的时候,等同与逆滤波

实现的效果如下所示

在这里插入图片描述

可见逆滤波在有噪声的时候不能很好的复原图像,但是维纳滤波可以,虽然在画质和边缘上有一些损失,但是还是很好的复原了图像

实现代码如下所示:

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image


def motion_process(height, weight, k=0.001):
    h_uv = np.zeros((height, weight), dtype='complex128')
    for u in range(height):
        for v in range(weight):
            q = np.power((u ** 2 + v ** 2), (5.0 / 6.0))
            h_uv[u][v] = np.exp(-(k * q))
    return h_uv


def blur_noise(src, h_uv):
    noise = np.random.normal(0, 1, size=src.shape)
    f_uv = np.fft.fft2(src)
    arr = np.multiply(f_uv, h_uv) + noise
    return arr, noise


def winner(src, h_uv, k=0.001):
    p_uv = np.conj(h_uv) / (np.abs(h_uv) ** 2 + 0.00005*k)
    rst = np.multiply(src, p_uv)
    return np.abs(np.fft.ifft2(rst))


def inv_filter(src, h_uv):
    p_uv = 1 / h_uv
    rst = np.multiply(src, p_uv)
    return np.abs(np.fft.ifft2(rst))


def main(img_path):
    src = np.array(Image.open(img_path).convert("L"))
    height, weight = src.shape
    h_uv = motion_process(height, weight, k=0.001)
    noise_src, noise = blur_noise(src, h_uv)
    mov_noi_src = np.abs(np.fft.ifft2(noise_src))
    k = np.power(np.abs(np.fft.fft2(noise)), 2) / np.power(np.abs(np.fft.fft2(src)), 2)

    src_processed_inv_filter = inv_filter(noise_src, h_uv)
    src_processed_winner = winner(noise_src, h_uv, k)

    img_list = [src, mov_noi_src, src_processed_inv_filter, src_processed_winner]
    img_name = ["原图像", "运动模糊+高斯噪声", "逆滤波", "维纳滤波"]

    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    _, axs = plt.subplots(2, 2, figsize=(12, 12))

    for i in range(2):
        for j in range(2):
            axs[i][j].imshow(img_list[i * 2 + j], cmap='gray')
            axs[i][j].set_title(img_name[i * 2 + j])
            axs[i][j].axes.get_xaxis().set_visible(False)
            axs[i][j].axes.get_yaxis().set_visible(False)

    plt.show()


if __name__ == '__main__':
    main('man.jpg')

4、维纳滤波和有约束最小二乘方滤波

原题:用运动模糊对图像进行模糊处理,然后加白高斯噪声,得到降质图像。用维纳滤波和有约束最小二乘方滤波恢复图像,约束线性算子用拉普拉斯算子。对实验结果进行对比分析

维纳滤波基本原理不赘述了

约束最小二乘方滤波核心是H对噪声的敏感性问题。减少噪声敏感新问题的一种方法是以平滑度量的最佳复原为基础的,建立约束式
C = ∑ 0 M − 1 ∑ 0 N − 1 [ ∇ 2 f ( x , y ) ] 2 C=\sum_{0}^{M-1} \sum_{0}^{N-1}\left[\nabla^{2} f(x, y)\right]^{2} C=0M10N1[2f(x,y)]2

约束条件为
∥ G − H F ^ ∥ 2 2 = ∥ N ∥ 2 2 \|G-H \hat{F}\|_{2}^{2}=\|N\|_{2}^{2} GHF^22=N22

将上式表示成矩阵形式,同时将约束项转换成拉格朗日乘子项,并最小化代价函数

具体过程可以见《数字信号处理》或文末给出的连接

最后可得到
F ^ = λ H ∗ G λ H ∗ H + P ∗ P = [ H ∗ ∥ H ∥ 2 2 + λ ∥ P ∥ 2 2 ] G \hat{F}=\frac{\lambda H^{*} G}{\lambda H^{*} H+P^{*} P}=\left[\frac{H^{*}}{\|H\|_{2}^{2}+\lambda\|P\|_{2}^{2}}\right] G F^=λHH+PPλHG=[H22+λP22H]G

P是函数
p = [ 0 − 1 0 − 1 4 − 1 0 − 1 0 ] p=\left[\begin{array}{ccc} 0 & -1 & 0 \\ -1 & 4 & -1 \\ 0 & -1 & 0 \end{array}\right] p=010141010

的傅里叶变换

实现的效果如下所示

在这里插入图片描述

可以看出在没有噪声的时候还是逆滤波的效果最好,但是在有噪声的时候,维纳滤波和约束最小二乘方滤波从两个角度给出了解答,虽然都具有一定的模糊,但是对图像都能够成功的修复,在我所选的因子下,维纳滤波的效果好一点,这个和调参也有关系

实现的代码如下所示:

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from numpy import fft


def motion_process(image_size, motion_angle):
    PSF = np.zeros(image_size)
    center_position = (image_size[0] - 1) / 2

    slope_tan = np.tan(motion_angle * np.pi / 180)
    slope_cot = 1 / slope_tan
    if slope_tan <= 1:
        for i in range(15):
            offset = round(i * slope_tan)  # ((center_position-i)*slope_tan)
            PSF[int(center_position + offset), int(center_position - offset)] = 1
        return PSF / PSF.sum()  # 对点扩散函数进行归一化亮度
    else:
        for i in range(15):
            offset = round(i * slope_cot)
            PSF[int(center_position - offset), int(center_position + offset)] = 1
        return PSF / PSF.sum()


def psf2otf(psf, outSize):
    psfSize = np.array(psf.shape)
    outSize = np.array(outSize)
    padSize = outSize - psfSize
    psf = np.pad(psf, ((0, padSize[0]), (0, padSize[1])), 'constant')
    for i in range(len(psfSize)):
        psf = np.roll(psf, -int(psfSize[i] / 2), i)
    otf = np.fft.fftn(psf)
    nElem = np.prod(psfSize)
    nOps = 0
    for k in range(len(psfSize)):
        nffts = nElem / psfSize[k]
        nOps = nOps + psfSize[k] * np.log2(psfSize[k]) * nffts
    if np.max(np.abs(np.imag(otf))) / np.max(np.abs(otf)) <= nOps * np.finfo(np.float32).eps:
        otf = np.real(otf)
    return otf


def CLSF(blurred, PSF, gamma=0.05):
    PF = psf2otf(PSF,blurred.shape)
    size = blurred.shape
    kernel = np.array([[0, -1, 0],
                       [-1, 4, -1],
                       [0, -1, 0]])

    PF_kernel = psf2otf(kernel, size)
    IF_noisy = fft.fft2(blurred)

    numerator = np.conj(PF)
    denominator = PF ** 2 + gamma * (PF_kernel ** 2)
    CLSF_deblurred = fft.ifft2(numerator * IF_noisy / denominator)
    CLSF_deblurred = np.real(CLSF_deblurred)
    return CLSF_deblurred


def inverse(blurred_image, PSF, eps):  # 逆滤波
    input_fft = fft.fft2(blurred_image)
    PSF_fft = fft.fft2(PSF) + eps  # 噪声功率,这是已知的,考虑epsilon
    result = fft.ifft2(input_fft / PSF_fft)  # 计算F(u,v)的傅里叶反变换
    result = np.abs(fft.fftshift(result))
    return result


def wiener(blurred_img, PSF, eps, k=0.01):  # 维纳滤波,K=0.01
    input_fft = fft.fft2(blurred_img)
    PSF_fft = fft.fft2(PSF) + eps
    PSF_fft_1 = np.conj(PSF_fft) / (np.abs(PSF_fft) ** 2 + k)
    result = fft.ifft2(input_fft * PSF_fft_1)
    result = np.abs(fft.fftshift(result))
    return result


def make_blurred(image, PSF, eps):
    input_fft = fft.fft2(image)  # 进行二维数组的傅里叶变换
    PSF_fft = fft.fft2(PSF) + eps
    blurred = fft.ifft2(input_fft * PSF_fft)
    blurred = np.abs(fft.fftshift(blurred))
    return blurred


def main(img_path):
    img = np.array(Image.open(img_path).convert("L"))

    img_size = img.shape
    PSF = motion_process(img_size, 60)

    blurred = make_blurred(img, PSF, 1e-3)
    blurred_inv = inverse(blurred, PSF, 1e-3)
    blurred_wiener = wiener(blurred, PSF, 1e-3, 0.01)
    blurred_CLSF = CLSF(blurred, PSF)

    blurred_noise = make_blurred(img, PSF, 1e-3) + np.random.normal(0, 5, img_size)
    blurred_noise_inv = inverse(blurred_noise, PSF, 1e-3)
    blurred_noise_wiener = wiener(blurred_noise, PSF, 1e-3, 0.01)
    blurred_noise_CLSF = CLSF(blurred_noise, PSF,0.03)

    img_list = [img, blurred, blurred_inv, blurred_wiener, blurred_CLSF,
                blurred_noise, blurred_noise_inv,blurred_noise_wiener, blurred_noise_CLSF]
    img_name = ["原图像", "运动模糊", "逆滤波", "维纳滤波", "约束最小二乘方",
                "运动模糊(噪声)", "逆滤波(噪声)", "维纳滤波(噪声)", "约束最小二乘方(噪声)"]

    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    _, axs = plt.subplots(3, 3, figsize=(12, 12))

    for i in range(3):
        for j in range(3):
            axs[i][j].imshow(img_list[i * 3 + j], cmap='gray')
            axs[i][j].set_title(img_name[i * 3 + j])
            axs[i][j].axes.get_xaxis().set_visible(False)
            axs[i][j].axes.get_yaxis().set_visible(False)

    plt.show()


if __name__ == '__main__':
    main('man.jpg')

参考链接

Python实现psf2otf_python

图像去模糊(约束最小二乘方滤波)

python实现逆滤波与维纳滤波客

Matlab 函数circShift、psf2otf 的 python(Numpy)实现

OpenCV—Python 图像去模糊(维纳滤波,约束最小二乘方滤波)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值