当前位置:   article > 正文

【Opencv-Python】数字图像处理(四)——图像恢复_cv2.inpaint模糊图像修复

cv2.inpaint模糊图像修复

 


>>文章导航

1 实验目的和要求

2 实验环境与配置

3.1 图像去噪

3.1.1 几何均值滤波器

(1)自己编程实现上述几何均值滤波器,并且与 算术均值滤波器的去噪效果进行对比(可调用:cv2.blur),描述和分析两者的效果差异。

实现代码:

滤波效果:

(2) 滤波器大小要尝试:3 ×3 、5 ×5 。

滤波器大小为3 ×3 :

滤波器大小为5 ×5 :

3.1.2 混合滤波器 (均值+ 中值滤波)

实现代码:

运行效果:

3.2 图像修补

(1)尝试修补文件夹 images 里的以下图像:lena_inpaint 、lena_inpaint3 、lena_inpaint6、lena_inpaint7 。

(2)对上述各图像的修补情况,描述以下因素对修补效果的影响:

(3)另外找一幅图像夹 (不限于文件夹 images ) 内的图像,可以自己找) ,并抹掉不同尺度、位置的区域后进行修补,观察修补的效果。

(4)加分:在给定原图和待修补图像的情况下, 另想一个与程序不同的办法计算修补用的 mask_binary;

4 实验心得与思考

4.1 实验心得总结

4.2 实验问题与解决

修改前运行结果:

修改后运行效果:


1 实验目的和要求

(1)掌握图像去噪的基本原理和滤波器的作用,了解不同滤波器的特点和应用范围;

(2)理解并掌握图像恢复的处理方法;

(3)了解并掌握图像运算的基本概念和方法等;

(4)通过实验,提高对图像处理基本概念的理解,培养实际操作和解决问题的能力。

2 实验环境与配置

(1)计算机;

(2)Python及Anaconda软件;

(3)典型的灰度、彩色图像等文件。

3.1 图像去噪

试验以下滤波器对图像噪声的滤除效果。

3.1.1 几何均值滤波器

image-20231115191713585

该滤波器的实质:将模板所覆盖像素的灰度值相乘,然后开 mn 次方。

(1)自己编程实现上述几何均值滤波器,并且与 算术均值滤波器的去噪效果进行对比(可调用:cv2.blur),描述和分析两者的效果差异。
实现代码:

滤波器大小为3 ×3 :

  1. import cv2
  2. import numpy as np
  3. def geometric_mean_filter(image, kernel_size):
  4.    rows, cols = image.shape
  5.    result = np.zeros_like(image, dtype=np.float32)
  6.    # 边界处理
  7.    border = kernel_size // 2
  8.    padded_image = cv2.copyMakeBorder(image, border, border, border, border, cv2.BORDER_REFLECT)
  9.    for i in range(border, rows + border):
  10.        for j in range(border, cols + border):
  11.            # 获取 kernel_size x kernel_size 区域内的像素值
  12.            neighborhood = padded_image[i - border:i + border + 1, j - border:j + border + 1]
  13.            # 计算几何平均值的对数
  14.            log_product = np.sum(np.log(neighborhood + 1e-10))  # Adding a small epsilon to avoid log(0)
  15.            result[i - border, j - border] = np.exp(log_product / (kernel_size ** 2))
  16.    # 将结果截断到[0, 255]范围内
  17.    result = np.clip(result, 0, 255).astype(np.uint8)
  18.    return result
  19. # 算术均值滤波器(调用cv2.blur)
  20. def arithmetic_mean_filter(image, kernel_size):
  21.    return cv2.blur(image, (kernel_size, kernel_size))
  22. # 读取图像
  23. image = cv2.imread("D:\images\lena_color_512_saltpepper.jpg", cv2.IMREAD_GRAYSCALE)
  24. if image is None:
  25.    print("Error: Unable to read the image.")
  26. else:
  27.    # 设置滤波器的核大小
  28.    kernel_size = 3
  29.    # 应用几何均值滤波器
  30.    geometric_filtered_image = geometric_mean_filter(image, kernel_size)
  31.    # 应用算术均值滤波器
  32.    arithmetic_filtered_image = arithmetic_mean_filter(image, kernel_size)
  33.    # 显示原始图像和处理后的图像
  34.    cv2.imshow("Original Image", image)
  35.    cv2.imshow("Geometric Filtered Image", geometric_filtered_image)
  36.    cv2.imshow("Arithmetic Filtered Image", arithmetic_filtered_image)
  37.    cv2.waitKey(0)
  38.    cv2.destroyAllWindows()
滤波效果:

image-20231118212950816

  • 几何均值滤波器的优点

    • 对于椒盐噪声等强噪声具有较好的去噪效果,因为几何均值滤波器对异常值的敏感度较低。

    • 在保存图像边缘信息的同时,能够有效地去除一些高频噪声。

  • 算术均值滤波器的优点

    • 实现简单,计算速度较快。

    • 在一些轻度噪声的情况下,可以有效地平滑图像。

  • 两者的差异

    • 几何均值滤波器在处理椒盐噪声等强噪声时通常比算术均值滤波器效果更好,但在一些轻度噪声情况下,可能导致图像细节损失较多。

    • 算术均值滤波器可能会模糊图像,特别是对于边缘等细节部分。

在实际应用中,滤波器的选择取决于图像的特点以及对于噪声和细节的处理要求。

(2) 滤波器大小要尝试:3 ×3 、5 ×5 。
滤波器大小为3 ×3 :

实现代码:

  1. import cv2
  2. import numpy as np
  3. def geometric_mean_filter(image, kernel_size):
  4.    rows, cols = image.shape
  5.    result = np.zeros_like(image, dtype=np.float32)
  6.    # 边界处理
  7.    border = kernel_size // 2
  8.    padded_image = cv2.copyMakeBorder(image, border, border, border, border, cv2.BORDER_REFLECT)
  9.    for i in range(border, rows + border):
  10.        for j in range(border, cols + border):
  11.            # 获取 kernel_size x kernel_size 区域内的像素值
  12.            neighborhood = padded_image[i - border:i + border + 1, j - border:j + border + 1]
  13.            # 计算几何平均值的对数
  14.            log_product = np.sum(np.log(neighborhood + 1e-10))  # Adding a small epsilon to avoid log(0)
  15.            result[i - border, j - border] = np.exp(log_product / (kernel_size ** 2))
  16.    # 将结果截断到[0, 255]范围内
  17.    result = np.clip(result, 0, 255).astype(np.uint8)
  18.    return result
  19. # 算术均值滤波器(调用cv2.blur)
  20. def arithmetic_mean_filter(image, kernel_size):
  21.    return cv2.blur(image, (kernel_size, kernel_size))
  22. # 读取图像
  23. image = cv2.imread("D:\images\lena_color_512_saltpepper.jpg", cv2.IMREAD_GRAYSCALE)
  24. if image is None:
  25.    print("Error: Unable to read the image.")
  26. else:
  27.    # 设置滤波器的核大小
  28.    kernel_size = 3
  29.    # 应用几何均值滤波器
  30.    geometric_filtered_image = geometric_mean_filter(image, kernel_size)
  31.    # 应用算术均值滤波器
  32.    arithmetic_filtered_image = arithmetic_mean_filter(image, kernel_size)
  33.    # 显示原始图像和处理后的图像
  34.    cv2.imshow("Original Image", image)
  35.    cv2.imshow("Geometric Filtered Image", geometric_filtered_image)
  36.    cv2.imshow("Arithmetic Filtered Image", arithmetic_filtered_image)
  37.    cv2.waitKey(0)
  38.    cv2.destroyAllWindows()

滤波效果:

image-20231118213035292

滤波器大小为5 ×5 :

实现代码:

  1. import cv2
  2. import numpy as np
  3. def geometric_mean_filter(image, kernel_size):
  4.    rows, cols = image.shape
  5.    result = np.zeros_like(image, dtype=np.float32)
  6.    # 边界处理
  7.    border = kernel_size // 2
  8.    padded_image = cv2.copyMakeBorder(image, border, border, border, border, cv2.BORDER_REFLECT)
  9.    for i in range(border, rows + border):
  10.        for j in range(border, cols + border):
  11.            # 获取 kernel_size x kernel_size 区域内的像素值
  12.            neighborhood = padded_image[i - border:i + border + 1, j - border:j + border + 1]
  13.            # 计算几何平均值的对数
  14.            log_product = np.sum(np.log(neighborhood + 1e-10))  # Adding a small epsilon to avoid log(0)
  15.            result[i - border, j - border] = np.exp(log_product / (kernel_size ** 2))
  16.    # 将结果截断到[0, 255]范围内
  17.    result = np.clip(result, 0, 255).astype(np.uint8)
  18.    return result
  19. # 算术均值滤波器(调用cv2.blur)
  20. def arithmetic_mean_filter(image, kernel_size):
  21.    return cv2.blur(image, (kernel_size, kernel_size))
  22. # 读取图像
  23. image = cv2.imread("D:\images\lena_color_512_saltpepper.jpg", cv2.IMREAD_GRAYSCALE)
  24. if image is None:
  25.    print("Error: Unable to read the image.")
  26. else:
  27.    # 设置滤波器的核大小
  28.    kernel_size = 5
  29.    # 应用几何均值滤波器
  30.    geometric_filtered_image = geometric_mean_filter(image, kernel_size)
  31.    # 应用算术均值滤波器
  32.    arithmetic_filtered_image = arithmetic_mean_filter(image, kernel_size)
  33.    # 显示原始图像和处理后的图像
  34.    cv2.imshow("Original Image", image)
  35.    cv2.imshow("Geometric Filtered Image", geometric_filtered_image)
  36.    cv2.imshow("Arithmetic Filtered Image", arithmetic_filtered_image)
  37.    cv2.waitKey(0)
  38.    cv2.destroyAllWindows()

滤波效果:

image-20231118213113079

3.1.2 混合滤波器 (均值+ 中值滤波)

image-20231115191953329

(1)自己编程实现上述混合滤波器,并且与只用中值滤波器的去噪效果和运行速度进行对比(可调用:cv2.medianBlur ),描述和分析两者的对比结果 。要提供自己实现的代码和滤波效果。统计程序的运行时间可使用 python 的 time 库;

实现代码:
  1. import cv2
  2. import numpy as np
  3. import time
  4. def custom_mixed_filter(image, kernel_size, alpha):
  5.    # 中值滤波
  6.    median_filtered = cv2.medianBlur(image, kernel_size)
  7.    # 算术均值滤波
  8.    arithmetic_filtered = cv2.blur(image, (kernel_size, kernel_size))
  9.    # 混合滤波
  10.    result = alpha * median_filtered + (1 - alpha) * arithmetic_filtered
  11.    # 将结果截断到[0, 255]范围内
  12.    result = np.clip(result, 0, 255).astype(np.uint8)
  13.    return result
  14. # 读取图像
  15. image = cv2.imread("D:\myimages\cat.jfif", cv2.IMREAD_GRAYSCALE)
  16. if image is None:
  17.    print("Error: Unable to read the image.")
  18. else:
  19.    # 设置滤波器的核大小
  20.    kernel_size = 3
  21.    # 设置混合滤波的权重参数
  22.    alpha = 0.5
  23.    # 记录开始时间
  24.    start_time = time.time()
  25.    # 应用混合滤波器
  26.    mixed_filtered_image = custom_mixed_filter(image, kernel_size, alpha)
  27.    # 记录结束时间
  28.    end_time = time.time()
  29.    # 显示原始图像和处理后的图像
  30.    cv2.imshow("Original Image", image)
  31.    cv2.imshow("Mixed Filtered Image", mixed_filtered_image)
  32.    cv2.waitKey(0)
  33.    cv2.destroyAllWindows()
  34.    # 输出程序运行时间
  35.    print(f"Processing time: {end_time - start_time} seconds.")
运行效果:

image-20231115202207093

  • 中值滤波器的优势

    • 对于椒盐噪声等强噪声有较好的去噪效果。

    • 能够在一定程度上保留图像的边缘信息。

  • 混合滤波器的优势

    • 结合了均值滤波和中值滤波的优点,可以在去噪的同时保留更多图像细节。

    • 可以通过调整权重来平衡两者的影响,以满足特定的应用需求。

  • 对比结果

    • 中值滤波器可能在去除椒盐噪声方面效果更好,但可能会在一些轻度噪声情况下丧失一些图像细节。

    • 混合滤波器通过结合均值滤波和中值滤波的结果,可以在一些场景中提供更好的平衡,既能去噪又能保留更多细节。

  • 运行速度对比

    • 通常情况下,中值滤波器相对于均值滤波速度较慢,因为中值滤波需要对像素进行排序。混合滤波器的速度取决于权重的组合。

在实际应用中,选择滤波器的类型和参数应根据图像的特性和去噪的需求进行调整。

3.2 图像修补

在图像中抹掉某个(些)区域,然后通过 opencv 库的 inpaint 函数对该区域进行修补。

inpaint 函数的用法: dst = cv2.inpaint(src, mask, inpaintRadius, flags)

参数: src :输入 8 位 1 通道或 3 通道的待修补图像,如图 3.1(1)。 mask :二值修复掩码,8 位 1 通道图像。非零像素表示需要修复的区域; dst :输出与 src 具有相同大小和类型的图像。 inpaintRadius: :算法考虑的每个像素点的圆形邻域的半径(该邻域内像素的视觉信息将被用于修补该像素点)。 flags 有两种取值,分别表示两种修补算法: cv2.INPAINT_NS:INPAINT_NS 基于 Navier-Stokes 的方法; cv2.INPAINT_TELEA:AlexandruTelea 的 INPAINT_TELEA 方法;

示例:

(1)尝试修补文件夹 images 里的以下图像:lena_inpaint 、lena_inpaint3 、lena_inpaint6、lena_inpaint7;

lena_inpaint:

实现代码:

  1. import cv2
  2. img_origin = cv2.imread("D:/images/lena.bmp")
  3. img_inpaint = cv2.imread('D:/images/lena_inpaint.bmp ') # 读取图片(小尺度)
  4. #img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
  5. img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
  6. img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
  7. mask = img_origin_gray - img_inpaint_gray
  8. # 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
  9. threshold = 20
  10. ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
  11. # 调用 cv2.inpaint()进行图像修补
  12. dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_TELEA)
  13. dst_NS = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_NS)
  14. # 检验修补后的图像与原图的差距(用图像减法)
  15. img_diff_TELEA = img_origin - dst_TELEA
  16. img_diff_NS = img_origin - dst_NS
  17. # 显示图像
  18. cv2.imshow("origin image ", img_origin)
  19. cv2.imshow("inpaint image ", img_inpaint)
  20. cv2.imshow("mask ", mask)
  21. cv2.imshow("mask_binary ", mask_binary)
  22. cv2.imshow("result image of TELEA ", dst_TELEA)
  23. cv2.imshow("result image of NS ", dst_NS)
  24. cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
  25. cv2.imshow("difference between origin and NS images ", img_diff_NS)
  26. # 等待显示
  27. cv2.waitKey(0)
  28. cv2.destroyAllWindows()

运行效果:

image-20231118201208798

image-20231118201328952

image-20231118201400161

lena_inpaint3:

实现代码:

  1. import cv2
  2. img_origin = cv2.imread("D:/images/lena.bmp")
  3. img_inpaint = cv2.imread('D:/images/lena_inpaint3.bmp ') # 读取图片(小尺度)
  4. #img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
  5. img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
  6. img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
  7. mask = img_origin_gray - img_inpaint_gray
  8. # 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
  9. threshold = 20
  10. ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
  11. # 调用 cv2.inpaint()进行图像修补
  12. dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_TELEA)
  13. dst_NS = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_NS)
  14. # 检验修补后的图像与原图的差距(用图像减法)
  15. img_diff_TELEA = img_origin - dst_TELEA
  16. img_diff_NS = img_origin - dst_NS
  17. # 显示图像
  18. cv2.imshow("origin image ", img_origin)
  19. cv2.imshow("inpaint image ", img_inpaint)
  20. cv2.imshow("mask ", mask)
  21. cv2.imshow("mask_binary ", mask_binary)
  22. cv2.imshow("result image of TELEA ", dst_TELEA)
  23. cv2.imshow("result image of NS ", dst_NS)
  24. cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
  25. cv2.imshow("difference between origin and NS images ", img_diff_NS)
  26. # 等待显示
  27. cv2.waitKey(0)
  28. cv2.destroyAllWindows()

运行效果:

image-20231118202516775

image-20231118202637970

image-20231118202659928

lena_inpaint6:

实现代码:

  1. import cv2
  2. img_origin = cv2.imread("D:/images/lena.bmp")
  3. img_inpaint = cv2.imread('D:/images/lena_inpaint6.bmp ') # 读取图片(小尺度)
  4. #img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
  5. img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
  6. img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
  7. mask = img_origin_gray - img_inpaint_gray
  8. # 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
  9. threshold = 20
  10. ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
  11. # 调用 cv2.inpaint()进行图像修补
  12. dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_TELEA)
  13. dst_NS = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_NS)
  14. # 检验修补后的图像与原图的差距(用图像减法)
  15. img_diff_TELEA = img_origin - dst_TELEA
  16. img_diff_NS = img_origin - dst_NS
  17. # 显示图像
  18. cv2.imshow("origin image ", img_origin)
  19. cv2.imshow("inpaint image ", img_inpaint)
  20. cv2.imshow("mask ", mask)
  21. cv2.imshow("mask_binary ", mask_binary)
  22. cv2.imshow("result image of TELEA ", dst_TELEA)
  23. cv2.imshow("result image of NS ", dst_NS)
  24. cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
  25. cv2.imshow("difference between origin and NS images ", img_diff_NS)
  26. # 等待显示
  27. cv2.waitKey(0)
  28. cv2.destroyAllWindows()

运行效果:

image-20231118202747018

image-20231118202842670

image-20231118202904206

lena_inpaint7:

实现代码:

  1. import cv2
  2. img_origin = cv2.imread("D:/images/lena.bmp")
  3. img_inpaint = cv2.imread('D:/images/lena_inpaint7.bmp ') # 读取图片(小尺度)
  4. #img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
  5. img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
  6. img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
  7. mask = img_origin_gray - img_inpaint_gray
  8. # 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
  9. threshold = 20
  10. ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
  11. # 调用 cv2.inpaint()进行图像修补
  12. dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_TELEA)
  13. dst_NS = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_NS)
  14. # 检验修补后的图像与原图的差距(用图像减法)
  15. img_diff_TELEA = img_origin - dst_TELEA
  16. img_diff_NS = img_origin - dst_NS
  17. # 显示图像
  18. cv2.imshow("origin image ", img_origin)
  19. cv2.imshow("inpaint image ", img_inpaint)
  20. cv2.imshow("mask ", mask)
  21. cv2.imshow("mask_binary ", mask_binary)
  22. cv2.imshow("result image of TELEA ", dst_TELEA)
  23. cv2.imshow("result image of NS ", dst_NS)
  24. cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
  25. cv2.imshow("difference between origin and NS images ", img_diff_NS)
  26. # 等待显示
  27. cv2.waitKey(0)
  28. cv2.destroyAllWindows()

运行效果:

image-20231118203006250

image-20231118203042105

image-20231118203116926

(2)对上述各图像的修补情况,描述以下因素对修补效果的影响:

2.1 被抹掉区域的尺度大小、纹理多少(丰富程度);

被抹掉区域的尺度大小以及其纹理多少会对图像修补的效果产生重要影响。修补算法通常依赖于周围像素的信息来填补缺失区域,因此这些因素会直接影响修复后图像的质量和准确性。

1.尺度大小:

小尺度缺失:小尺度的缺失通常意味着周围的像素更加连续、更容易获得相关信息来进行修补。对于较小的缺失,算法可能能够从周围的纹理或结构中推断并填充缺失部分,因此修复效果可能较好。 大尺度缺失:大尺度的缺失通常意味着涉及更多的像素或者更广泛的纹理和结构,这可能增加修复的难度。填补大尺度缺失可能需要更多的上下文信息和更复杂的算法来合理地还原缺失区域,因此修复效果可能不如小尺度缺失那么理想。

2.纹理丰富程度:

低纹理区域:在纹理较少或均匀的区域,修复算法可能会面临更大的挑战。因为缺失区域周围的信息可能不足以提供准确的纹理或结构信息,这可能导致修复结果显得模糊或不自然。 高纹理区域:相比于低纹理区域,纹理丰富的区域可能提供更多细节和上下文信息,使得算法能够更准确地推断缺失部分的内容。修复结果可能更接近原始纹理,并且更难察觉修复痕迹。

小尺度、高纹理的缺失通常会更容易修复,而大尺度、低纹理的缺失则可能需要更复杂的算法和更多的上下文信息来获得满意的修复效果。修补算法的准确性和效果也受到图像内容、算法选择以及参数设置等因素的影响。

2.2 inpaint() 的第 3 个参数 inpaintRadius 取不同的值 (至少 3 个)。

inpaintRadius = 2:

实现代码:

  1. import cv2
  2. img_origin = cv2.imread("D:/images/lena.bmp")
  3. img_inpaint = cv2.imread('D:/images/lena_inpaint.bmp ') # 读取图片(小尺度)
  4. #img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
  5. img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
  6. img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
  7. mask = img_origin_gray - img_inpaint_gray
  8. # 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
  9. threshold = 20
  10. ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
  11. # 调用 cv2.inpaint()进行图像修补
  12. dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 2, cv2.INPAINT_TELEA)
  13. dst_NS = cv2.inpaint(img_inpaint, mask_binary, 2, cv2.INPAINT_NS)
  14. # 检验修补后的图像与原图的差距(用图像减法)
  15. img_diff_TELEA = img_origin - dst_TELEA
  16. img_diff_NS = img_origin - dst_NS
  17. # 显示图像
  18. cv2.imshow("origin image ", img_origin)
  19. cv2.imshow("inpaint image ", img_inpaint)
  20. cv2.imshow("mask ", mask)
  21. cv2.imshow("mask_binary ", mask_binary)
  22. cv2.imshow("result image of TELEA ", dst_TELEA)
  23. cv2.imshow("result image of NS ", dst_NS)
  24. cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
  25. cv2.imshow("difference between origin and NS images ", img_diff_NS)
  26. # 等待显示
  27. cv2.waitKey(0)
  28. cv2.destroyAllWindows()

运行效果:

image-20231118204612556

image-20231118204733510

image-20231118204758721

inpaintRadius = 20:

实现代码:

  1. import cv2
  2. img_origin = cv2.imread("D:/images/lena.bmp")
  3. img_inpaint = cv2.imread('D:/images/lena_inpaint.bmp ') # 读取图片(小尺度)
  4. #img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
  5. img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
  6. img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
  7. mask = img_origin_gray - img_inpaint_gray
  8. # 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
  9. threshold = 20
  10. ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
  11. # 调用 cv2.inpaint()进行图像修补
  12. dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 20, cv2.INPAINT_TELEA)
  13. dst_NS = cv2.inpaint(img_inpaint, mask_binary, 20, cv2.INPAINT_NS)
  14. # 检验修补后的图像与原图的差距(用图像减法)
  15. img_diff_TELEA = img_origin - dst_TELEA
  16. img_diff_NS = img_origin - dst_NS
  17. # 显示图像
  18. cv2.imshow("origin image ", img_origin)
  19. cv2.imshow("inpaint image ", img_inpaint)
  20. cv2.imshow("mask ", mask)
  21. cv2.imshow("mask_binary ", mask_binary)
  22. cv2.imshow("result image of TELEA ", dst_TELEA)
  23. cv2.imshow("result image of NS ", dst_NS)
  24. cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
  25. cv2.imshow("difference between origin and NS images ", img_diff_NS)
  26. # 等待显示
  27. cv2.waitKey(0)
  28. cv2.destroyAllWindows()

运行效果:

image-20231118205037462

image-20231118205120195

image-20231118205153961

inpaintRadius = 40:

实现代码:

  1. import cv2
  2. img_origin = cv2.imread("D:/images/lena.bmp")
  3. img_inpaint = cv2.imread('D:/images/lena_inpaint.bmp ') # 读取图片(小尺度)
  4. #img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
  5. img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
  6. img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
  7. mask = img_origin_gray - img_inpaint_gray
  8. # 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
  9. threshold = 20
  10. ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
  11. # 调用 cv2.inpaint()进行图像修补
  12. dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 40, cv2.INPAINT_TELEA)
  13. dst_NS = cv2.inpaint(img_inpaint, mask_binary, 40, cv2.INPAINT_NS)
  14. # 检验修补后的图像与原图的差距(用图像减法)
  15. img_diff_TELEA = img_origin - dst_TELEA
  16. img_diff_NS = img_origin - dst_NS
  17. # 显示图像
  18. cv2.imshow("origin image ", img_origin)
  19. cv2.imshow("inpaint image ", img_inpaint)
  20. cv2.imshow("mask ", mask)
  21. cv2.imshow("mask_binary ", mask_binary)
  22. cv2.imshow("result image of TELEA ", dst_TELEA)
  23. cv2.imshow("result image of NS ", dst_NS)
  24. cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
  25. cv2.imshow("difference between origin and NS images ", img_diff_NS)
  26. # 等待显示
  27. cv2.waitKey(0)
  28. cv2.destroyAllWindows()

运行效果:

image-20231118205426649

image-20231118205507300

image-20231118205700962

inpaintRadius参数表示修复区域的半径大小。较大的半径意味着更广泛的邻域被用于计算修复值,这可能导致更加平滑的修复结果,但也可能损失更多的细节。较小的半径则可能更好地保留图像细节,但在某些情况下可能不能有效修复大的缺陷。

  • 小的 inpaintRadius

    • 修复过程主要关注缺陷的局部细节,可能能够更好地保留图像中的细节。

    • 适用于较小的缺陷,不太适合大面积损坏的情况。

  • 大的 inpaintRadius

    • 修复算法将考虑更广泛的邻域,可能导致更平滑的修复效果。

    • 适用于大面积损坏或需要更广泛修复的情况,但可能会损失一些细节。

具体的最佳取值取决于图像的特性、缺陷的大小和位置,以及用户的修复偏好。在实际应用中,可以尝试不同的 inpaintRadius 值,通过视觉观察和定量评估来选择最适合特定任务的值。

(3)另外找一幅图像夹 (不限于文件夹 images ) 内的图像,可以自己找) ,并抹掉不同尺度、位置的区域后进行修补,观察修补的效果。

实现代码:

  1. import cv2
  2. img_origin = cv2.imread("D:/myimages/cat_origin.bmp")
  3. img_inpaint = cv2.imread('D:/myimages/cat_inpaint.bmp ') # 读取图片(小尺度)
  4. #img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
  5. img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
  6. img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
  7. mask = img_origin_gray - img_inpaint_gray
  8. # 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
  9. threshold = 20
  10. ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
  11. # 调用 cv2.inpaint()进行图像修补
  12. dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_TELEA)
  13. dst_NS = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_NS)
  14. # 检验修补后的图像与原图的差距(用图像减法)
  15. img_diff_TELEA = img_origin - dst_TELEA
  16. img_diff_NS = img_origin - dst_NS
  17. # 显示图像
  18. cv2.imshow("origin image ", img_origin)
  19. cv2.imshow("inpaint image ", img_inpaint)
  20. cv2.imshow("mask ", mask)
  21. cv2.imshow("mask_binary ", mask_binary)
  22. cv2.imshow("result image of TELEA ", dst_TELEA)
  23. cv2.imshow("result image of NS ", dst_NS)
  24. cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
  25. cv2.imshow("difference between origin and NS images ", img_diff_NS)
  26. # 等待显示
  27. cv2.waitKey(0)
  28. cv2.destroyAllWindows()

运行效果:

image-20231118203901267

image-20231118203928534

实现代码:

  1. import cv2
  2. img_origin = cv2.imread("D:/myimages/cat_origin.bmp")
  3. img_inpaint = cv2.imread('D:/myimages/cat_inpaint2.bmp ') # 读取图片(小尺度)
  4. #img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
  5. img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
  6. img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
  7. mask = img_origin_gray - img_inpaint_gray
  8. # 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
  9. threshold = 20
  10. ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
  11. # 调用 cv2.inpaint()进行图像修补
  12. dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_TELEA)
  13. dst_NS = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_NS)
  14. # 检验修补后的图像与原图的差距(用图像减法)
  15. img_diff_TELEA = img_origin - dst_TELEA
  16. img_diff_NS = img_origin - dst_NS
  17. # 显示图像
  18. cv2.imshow("origin image ", img_origin)
  19. cv2.imshow("inpaint image ", img_inpaint)
  20. cv2.imshow("mask ", mask)
  21. cv2.imshow("mask_binary ", mask_binary)
  22. cv2.imshow("result image of TELEA ", dst_TELEA)
  23. cv2.imshow("result image of NS ", dst_NS)
  24. cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
  25. cv2.imshow("difference between origin and NS images ", img_diff_NS)
  26. # 等待显示
  27. cv2.waitKey(0)
  28. cv2.destroyAllWindows()

运行效果:

image-20231118204057316

image-20231118204129672

(4)加分:在给定原图和待修补图像的情况下, 另想一个与程序不同的办法计算修补用的 mask_binary;

实现代码:

  1. import cv2
  2. # 读取原始图像和待修补图像
  3. img_origin = cv2.imread("D:/images/lena.bmp")
  4. img_inpaint = cv2.imread("D:/images/lena_inpaint3.bmp")
  5. # 转换图像为灰度
  6. img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
  7. img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
  8. # 使用高斯模糊减少噪音
  9. img_origin_blur = cv2.GaussianBlur(img_origin_gray, (5, 5), 0)
  10. img_inpaint_blur = cv2.GaussianBlur(img_inpaint_gray, (5, 5), 0)
  11. # 计算两个模糊图像的差异
  12. mask = cv2.absdiff(img_origin_blur, img_inpaint_blur)
  13. # 根据差异设定阈值,创建二值掩模
  14. threshold = 20
  15. _, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
  16. # 显示原始图像、待修补图像和生成的二值掩模
  17. cv2.imshow('Original Image', img_origin)
  18. cv2.imshow('Inpainted Image', img_inpaint)
  19. cv2.imshow('Mask Binary', mask_binary)
  20. cv2.waitKey(0)
  21. cv2.destroyAllWindows()

运行效果:

image-20231118212326179

4 实验心得与思考

4.1 实验心得总结

在本次实验中,我深入学习了图像处理的基本原理,特别是图像去噪和平滑的方法。在图像去噪方面,我着重研究了几何均值滤波器,并通过自己编写代码实现了该滤波器。与算术均值滤波器相比,几何均值滤波器在去噪效果上表现出更好的特性。我进行了不同滤波器大小的尝试,观察到滤波器大小的变化对去噪效果的影响。

通过观察滤波效果,我发现几何均值滤波器在去除图像噪声方面具有一定优势,尤其在处理椒盐噪声时表现良好。在实验中,我还尝试了混合滤波器,结合了均值和中值滤波的特性。这种方法在一定程度上能够克服各自滤波器的缺点,既能有效去除噪声又能保持图像的清晰度。实验中,我通过调整均值和中值滤波器的权重,观察到混合滤波器在图像去噪方面表现出较好的平衡。在图像修补方面,通过理论学习了图像运算的基本概念和方法,为更深入的图像处理工作奠定了基础。

总的来说,通过本次实验,我不仅学到了图像处理的基本原理和常见滤波器的应用,还锻炼了编程和实验分析的能力。在未来的学习中,我将继续深入图像处理领域,探索更多先进的方法和技术。

4.2 实验问题与解决

在实验中遇到预想之外的问题(比如得到的图像处理效果与预期不同),自己找到原因,并想到办法加以解决。

出现问题: geometric_filtered_image全黑;

问题原因: 在geometric_mean_filter函数直接计算了邻域像素的乘积,结果是,当乘积被提升到1 / (kernel_size ** 2)的幂时,对于大多数区域来说会变成零。 问题解决: 对乘积取对数,计算对数值的平均,然后对结果进行指数运算。避免乘法运算中出现的数值下溢问题。

修改前代码:

  1. import cv2
  2. import numpy as np
  3. def geometric_mean_filter(image, kernel_size):
  4.    rows, cols = image.shape
  5.    result = np.zeros_like(image, dtype=np.float32)
  6.    # 边界处理
  7.    border = kernel_size // 2
  8.    padded_image = cv2.copyMakeBorder(image, border, border, border, border, cv2.BORDER_REFLECT)
  9.    for i in range(border, rows + border):
  10.        for j in range(border, cols + border):
  11.            # 获取 kernel_size x kernel_size 区域内的像素值
  12.            neighborhood = padded_image[i - border:i + border + 1, j - border:j + border + 1]
  13.            # 计算几何平均值
  14.            product = np.prod(neighborhood)
  15.            result[i - border, j - border] = product ** (1 / (kernel_size ** 2))
  16.    # 将结果截断到[0, 255]范围内
  17.    result = np.clip(result, 0, 255).astype(np.uint8)
  18.    return result
  19. # 算术均值滤波器(调用cv2.blur)
  20. def arithmetic_mean_filter(image, kernel_size):
  21.    return cv2.blur(image, (kernel_size, kernel_size))
  22. # 读取图像
  23. image = cv2.imread("D:\images\lena_color_512_saltpepper.jpg", cv2.IMREAD_GRAYSCALE)
  24. if image is None:
  25.    print("Error: Unable to read the image.")
  26. else:
  27.    # 设置滤波器的核大小
  28.    kernel_size = 3
  29.    # 应用几何均值滤波器
  30.    geometric_filtered_image = geometric_mean_filter(image, kernel_size)
  31.    # 应用算术均值滤波器
  32.    arithmetic_filtered_image = arithmetic_mean_filter(image, kernel_size)
  33.    # 显示原始图像和处理后的图像
  34.    cv2.imshow("Original Image", image)
  35.    cv2.imshow("Geometric Filtered Image", geometric_filtered_image)
  36.    cv2.imshow("Arithmetic Filtered Image", arithmetic_filtered_image)
  37.    cv2.waitKey(0)
  38.    cv2.destroyAllWindows()
修改前运行结果:

image-20231118200142091

修改后代码:

  1. import cv2
  2. import numpy as np
  3. def geometric_mean_filter(image, kernel_size):
  4.    rows, cols = image.shape
  5.    result = np.zeros_like(image, dtype=np.float32)
  6.    # 边界处理
  7.    border = kernel_size // 2
  8.    padded_image = cv2.copyMakeBorder(image, border, border, border, border, cv2.BORDER_REFLECT)
  9.    for i in range(border, rows + border):
  10.        for j in range(border, cols + border):
  11.            # 获取 kernel_size x kernel_size 区域内的像素值
  12.            neighborhood = padded_image[i - border:i + border + 1, j - border:j + border + 1]
  13.            # 计算几何平均值的对数
  14.            log_product = np.sum(np.log(neighborhood + 1e-10))  # Adding a small epsilon to avoid log(0)
  15.            result[i - border, j - border] = np.exp(log_product / (kernel_size ** 2))
  16.    # 将结果截断到[0, 255]范围内
  17.    result = np.clip(result, 0, 255).astype(np.uint8)
  18.    return result
  19. # 算术均值滤波器(调用cv2.blur)
  20. def arithmetic_mean_filter(image, kernel_size):
  21.    return cv2.blur(image, (kernel_size, kernel_size))
  22. # 读取图像
  23. image = cv2.imread("D:\images\lena_color_512_saltpepper.jpg", cv2.IMREAD_GRAYSCALE)
  24. if image is None:
  25.    print("Error: Unable to read the image.")
  26. else:
  27.    # 设置滤波器的核大小
  28.    kernel_size = 3
  29.    # 应用几何均值滤波器
  30.    geometric_filtered_image = geometric_mean_filter(image, kernel_size)
  31.    # 应用算术均值滤波器
  32.    arithmetic_filtered_image = arithmetic_mean_filter(image, kernel_size)
  33.    # 显示原始图像和处理后的图像
  34.    cv2.imshow("Original Image", image)
  35.    cv2.imshow("Geometric Filtered Image", geometric_filtered_image)
  36.    cv2.imshow("Arithmetic Filtered Image", arithmetic_filtered_image)
  37.    cv2.waitKey(0)
  38.    cv2.destroyAllWindows()
修改后运行效果:

image-20231118212950816


如果觉得作者写得还不错的话, 点赞 / 收藏 / 评论 / 转发 四连支持一下吧~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/975356
推荐阅读
相关标签