【Opencv-Python】数字图像处理(四)——图像恢复

 


>>文章导航

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 :

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

image-20231118212950816

  • 几何均值滤波器的优点

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

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

  • 算术均值滤波器的优点

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

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

  • 两者的差异

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

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

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

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

实现代码:

import cv2
import numpy as np
​
def geometric_mean_filter(image, kernel_size):
    rows, cols = image.shape
    result = np.zeros_like(image, dtype=np.float32)
    # 边界处理
    border = kernel_size // 2
    padded_image = cv2.copyMakeBorder(image, border, border, border, border, cv2.BORDER_REFLECT)
    for i in range(border, rows + border):
        for j in range(border, cols + border):
            # 获取 kernel_size x kernel_size 区域内的像素值
            neighborhood = padded_image[i - border:i + border + 1, j - border:j + border + 1]
            # 计算几何平均值的对数
            log_product = np.sum(np.log(neighborhood + 1e-10))  # Adding a small epsilon to avoid log(0)
            result[i - border, j - border] = np.exp(log_product / (kernel_size ** 2))
    # 将结果截断到[0, 255]范围内
    result = np.clip(result, 0, 255).astype(np.uint8)
    return result
​
# 算术均值滤波器(调用cv2.blur)
def arithmetic_mean_filter(image, kernel_size):
    return cv2.blur(image, (kernel_size, kernel_size))
​
# 读取图像
image = cv2.imread("D:\images\lena_color_512_saltpepper.jpg", cv2.IMREAD_GRAYSCALE)
if image is None:
    print("Error: Unable to read the image.")
else:
    # 设置滤波器的核大小
    kernel_size = 3
    # 应用几何均值滤波器
    geometric_filtered_image = geometric_mean_filter(image, kernel_size)
    # 应用算术均值滤波器
    arithmetic_filtered_image = arithmetic_mean_filter(image, kernel_size)
    # 显示原始图像和处理后的图像
    cv2.imshow("Original Image", image)
    cv2.imshow("Geometric Filtered Image", geometric_filtered_image)
    cv2.imshow("Arithmetic Filtered Image", arithmetic_filtered_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

滤波效果:

image-20231118213035292

滤波器大小为5 ×5 :

实现代码:

import cv2
import numpy as np
​
def geometric_mean_filter(image, kernel_size):
    rows, cols = image.shape
    result = np.zeros_like(image, dtype=np.float32)
    # 边界处理
    border = kernel_size // 2
    padded_image = cv2.copyMakeBorder(image, border, border, border, border, cv2.BORDER_REFLECT)
    for i in range(border, rows + border):
        for j in range(border, cols + border):
            # 获取 kernel_size x kernel_size 区域内的像素值
            neighborhood = padded_image[i - border:i + border + 1, j - border:j + border + 1]
            # 计算几何平均值的对数
            log_product = np.sum(np.log(neighborhood + 1e-10))  # Adding a small epsilon to avoid log(0)
            result[i - border, j - border] = np.exp(log_product / (kernel_size ** 2))
    # 将结果截断到[0, 255]范围内
    result = np.clip(result, 0, 255).astype(np.uint8)
    return result
​
# 算术均值滤波器(调用cv2.blur)
def arithmetic_mean_filter(image, kernel_size):
    return cv2.blur(image, (kernel_size, kernel_size))
​
# 读取图像
image = cv2.imread("D:\images\lena_color_512_saltpepper.jpg", cv2.IMREAD_GRAYSCALE)
if image is None:
    print("Error: Unable to read the image.")
else:
    # 设置滤波器的核大小
    kernel_size = 5
    # 应用几何均值滤波器
    geometric_filtered_image = geometric_mean_filter(image, kernel_size)
    # 应用算术均值滤波器
    arithmetic_filtered_image = arithmetic_mean_filter(image, kernel_size)
    # 显示原始图像和处理后的图像
    cv2.imshow("Original Image", image)
    cv2.imshow("Geometric Filtered Image", geometric_filtered_image)
    cv2.imshow("Arithmetic Filtered Image", arithmetic_filtered_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

滤波效果:

image-20231118213113079

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

image-20231115191953329

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

实现代码:
import cv2
import numpy as np
import time
​
def custom_mixed_filter(image, kernel_size, alpha):
    # 中值滤波
    median_filtered = cv2.medianBlur(image, kernel_size)
    # 算术均值滤波
    arithmetic_filtered = cv2.blur(image, (kernel_size, kernel_size))
    # 混合滤波
    result = alpha * median_filtered + (1 - alpha) * arithmetic_filtered
    # 将结果截断到[0, 255]范围内
    result = np.clip(result, 0, 255).astype(np.uint8)
    return result
# 读取图像
image = cv2.imread("D:\myimages\cat.jfif", cv2.IMREAD_GRAYSCALE)
if image is None:
    print("Error: Unable to read the image.")
else:
    # 设置滤波器的核大小
    kernel_size = 3
    # 设置混合滤波的权重参数
    alpha = 0.5
    # 记录开始时间
    start_time = time.time()
    # 应用混合滤波器
    mixed_filtered_image = custom_mixed_filter(image, kernel_size, alpha)
    # 记录结束时间
    end_time = time.time()
    # 显示原始图像和处理后的图像
    cv2.imshow("Original Image", image)
    cv2.imshow("Mixed Filtered Image", mixed_filtered_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    # 输出程序运行时间
    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:

实现代码:

import cv2
​
img_origin = cv2.imread("D:/images/lena.bmp")
img_inpaint = cv2.imread('D:/images/lena_inpaint.bmp ') # 读取图片(小尺度)
#img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
​
img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
mask = img_origin_gray - img_inpaint_gray
​
# 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
threshold = 20
ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
​
# 调用 cv2.inpaint()进行图像修补
dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_TELEA)
dst_NS = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_NS)
​
# 检验修补后的图像与原图的差距(用图像减法)
img_diff_TELEA = img_origin - dst_TELEA
img_diff_NS = img_origin - dst_NS
​
# 显示图像
cv2.imshow("origin image ", img_origin)
cv2.imshow("inpaint image ", img_inpaint)
cv2.imshow("mask ", mask)
cv2.imshow("mask_binary ", mask_binary)
cv2.imshow("result image of TELEA ", dst_TELEA)
cv2.imshow("result image of NS ", dst_NS)
cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
cv2.imshow("difference between origin and NS images ", img_diff_NS)
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行效果:

image-20231118201208798

image-20231118201328952

image-20231118201400161

lena_inpaint3:

实现代码:

import cv2
​
img_origin = cv2.imread("D:/images/lena.bmp")
img_inpaint = cv2.imread('D:/images/lena_inpaint3.bmp ') # 读取图片(小尺度)
#img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
​
img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
mask = img_origin_gray - img_inpaint_gray
​
# 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
threshold = 20
ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
​
# 调用 cv2.inpaint()进行图像修补
dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_TELEA)
dst_NS = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_NS)
​
# 检验修补后的图像与原图的差距(用图像减法)
img_diff_TELEA = img_origin - dst_TELEA
img_diff_NS = img_origin - dst_NS
​
# 显示图像
cv2.imshow("origin image ", img_origin)
cv2.imshow("inpaint image ", img_inpaint)
cv2.imshow("mask ", mask)
cv2.imshow("mask_binary ", mask_binary)
cv2.imshow("result image of TELEA ", dst_TELEA)
cv2.imshow("result image of NS ", dst_NS)
cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
cv2.imshow("difference between origin and NS images ", img_diff_NS)
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行效果:

image-20231118202516775

image-20231118202637970

image-20231118202659928

lena_inpaint6:

实现代码:

import cv2
​
img_origin = cv2.imread("D:/images/lena.bmp")
img_inpaint = cv2.imread('D:/images/lena_inpaint6.bmp ') # 读取图片(小尺度)
#img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
​
img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
mask = img_origin_gray - img_inpaint_gray
​
# 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
threshold = 20
ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
​
# 调用 cv2.inpaint()进行图像修补
dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_TELEA)
dst_NS = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_NS)
​
# 检验修补后的图像与原图的差距(用图像减法)
img_diff_TELEA = img_origin - dst_TELEA
img_diff_NS = img_origin - dst_NS
​
# 显示图像
cv2.imshow("origin image ", img_origin)
cv2.imshow("inpaint image ", img_inpaint)
cv2.imshow("mask ", mask)
cv2.imshow("mask_binary ", mask_binary)
cv2.imshow("result image of TELEA ", dst_TELEA)
cv2.imshow("result image of NS ", dst_NS)
cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
cv2.imshow("difference between origin and NS images ", img_diff_NS)
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行效果:

image-20231118202747018

image-20231118202842670

image-20231118202904206

lena_inpaint7:

实现代码:

import cv2
​
img_origin = cv2.imread("D:/images/lena.bmp")
img_inpaint = cv2.imread('D:/images/lena_inpaint7.bmp ') # 读取图片(小尺度)
#img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
​
img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
mask = img_origin_gray - img_inpaint_gray
​
# 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
threshold = 20
ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
​
# 调用 cv2.inpaint()进行图像修补
dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_TELEA)
dst_NS = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_NS)
​
# 检验修补后的图像与原图的差距(用图像减法)
img_diff_TELEA = img_origin - dst_TELEA
img_diff_NS = img_origin - dst_NS
​
# 显示图像
cv2.imshow("origin image ", img_origin)
cv2.imshow("inpaint image ", img_inpaint)
cv2.imshow("mask ", mask)
cv2.imshow("mask_binary ", mask_binary)
cv2.imshow("result image of TELEA ", dst_TELEA)
cv2.imshow("result image of NS ", dst_NS)
cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
cv2.imshow("difference between origin and NS images ", img_diff_NS)
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行效果:

image-20231118203006250

image-20231118203042105

image-20231118203116926

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

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

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

1.尺度大小:

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

2.纹理丰富程度:

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

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

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

inpaintRadius = 2:

实现代码:

import cv2
​
img_origin = cv2.imread("D:/images/lena.bmp")
img_inpaint = cv2.imread('D:/images/lena_inpaint.bmp ') # 读取图片(小尺度)
#img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
​
img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
mask = img_origin_gray - img_inpaint_gray
​
# 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
threshold = 20
ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
​
# 调用 cv2.inpaint()进行图像修补
dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 2, cv2.INPAINT_TELEA)
dst_NS = cv2.inpaint(img_inpaint, mask_binary, 2, cv2.INPAINT_NS)
​
# 检验修补后的图像与原图的差距(用图像减法)
img_diff_TELEA = img_origin - dst_TELEA
img_diff_NS = img_origin - dst_NS
​
# 显示图像
cv2.imshow("origin image ", img_origin)
cv2.imshow("inpaint image ", img_inpaint)
cv2.imshow("mask ", mask)
cv2.imshow("mask_binary ", mask_binary)
cv2.imshow("result image of TELEA ", dst_TELEA)
cv2.imshow("result image of NS ", dst_NS)
cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
cv2.imshow("difference between origin and NS images ", img_diff_NS)
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行效果:

image-20231118204612556

image-20231118204733510

image-20231118204758721

inpaintRadius = 20:

实现代码:

import cv2
​
img_origin = cv2.imread("D:/images/lena.bmp")
img_inpaint = cv2.imread('D:/images/lena_inpaint.bmp ') # 读取图片(小尺度)
#img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
​
img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
mask = img_origin_gray - img_inpaint_gray
​
# 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
threshold = 20
ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
​
# 调用 cv2.inpaint()进行图像修补
dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 20, cv2.INPAINT_TELEA)
dst_NS = cv2.inpaint(img_inpaint, mask_binary, 20, cv2.INPAINT_NS)
​
# 检验修补后的图像与原图的差距(用图像减法)
img_diff_TELEA = img_origin - dst_TELEA
img_diff_NS = img_origin - dst_NS
​
# 显示图像
cv2.imshow("origin image ", img_origin)
cv2.imshow("inpaint image ", img_inpaint)
cv2.imshow("mask ", mask)
cv2.imshow("mask_binary ", mask_binary)
cv2.imshow("result image of TELEA ", dst_TELEA)
cv2.imshow("result image of NS ", dst_NS)
cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
cv2.imshow("difference between origin and NS images ", img_diff_NS)
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行效果:

image-20231118205037462

image-20231118205120195

image-20231118205153961

inpaintRadius = 40:

实现代码:

import cv2
​
img_origin = cv2.imread("D:/images/lena.bmp")
img_inpaint = cv2.imread('D:/images/lena_inpaint.bmp ') # 读取图片(小尺度)
#img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
​
img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
mask = img_origin_gray - img_inpaint_gray
​
# 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
threshold = 20
ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
​
# 调用 cv2.inpaint()进行图像修补
dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 40, cv2.INPAINT_TELEA)
dst_NS = cv2.inpaint(img_inpaint, mask_binary, 40, cv2.INPAINT_NS)
​
# 检验修补后的图像与原图的差距(用图像减法)
img_diff_TELEA = img_origin - dst_TELEA
img_diff_NS = img_origin - dst_NS
​
# 显示图像
cv2.imshow("origin image ", img_origin)
cv2.imshow("inpaint image ", img_inpaint)
cv2.imshow("mask ", mask)
cv2.imshow("mask_binary ", mask_binary)
cv2.imshow("result image of TELEA ", dst_TELEA)
cv2.imshow("result image of NS ", dst_NS)
cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
cv2.imshow("difference between origin and NS images ", img_diff_NS)
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行效果:

image-20231118205426649

image-20231118205507300

image-20231118205700962

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

  • 小的 inpaintRadius

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

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

  • 大的 inpaintRadius

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

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

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

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

实现代码:

import cv2
​
img_origin = cv2.imread("D:/myimages/cat_origin.bmp")
img_inpaint = cv2.imread('D:/myimages/cat_inpaint.bmp ') # 读取图片(小尺度)
#img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
​
img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
mask = img_origin_gray - img_inpaint_gray
​
# 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
threshold = 20
ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
​
# 调用 cv2.inpaint()进行图像修补
dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_TELEA)
dst_NS = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_NS)
​
# 检验修补后的图像与原图的差距(用图像减法)
img_diff_TELEA = img_origin - dst_TELEA
img_diff_NS = img_origin - dst_NS
​
# 显示图像
cv2.imshow("origin image ", img_origin)
cv2.imshow("inpaint image ", img_inpaint)
cv2.imshow("mask ", mask)
cv2.imshow("mask_binary ", mask_binary)
cv2.imshow("result image of TELEA ", dst_TELEA)
cv2.imshow("result image of NS ", dst_NS)
cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
cv2.imshow("difference between origin and NS images ", img_diff_NS)
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行效果:

image-20231118203901267

image-20231118203928534

实现代码:

import cv2
​
img_origin = cv2.imread("D:/myimages/cat_origin.bmp")
img_inpaint = cv2.imread('D:/myimages/cat_inpaint2.bmp ') # 读取图片(小尺度)
#img_inpaint = cv2.imread('D:\images/lena_inpaint2.bmp ') # 读取图片(大尺度)
​
img_origin_gray = cv2.cvtColor(img_origin, cv2.COLOR_BGR2GRAY)
img_inpaint_gray = cv2.cvtColor(img_inpaint, cv2.COLOR_BGR2GRAY)
mask = img_origin_gray - img_inpaint_gray
​
# 把 mask 变为二值化的 mask_binary,因为后面 cv2.inpaint()的第二个参数必须是二值图像
threshold = 20
ret, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
​
# 调用 cv2.inpaint()进行图像修补
dst_TELEA = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_TELEA)
dst_NS = cv2.inpaint(img_inpaint, mask_binary, 15, cv2.INPAINT_NS)
​
# 检验修补后的图像与原图的差距(用图像减法)
img_diff_TELEA = img_origin - dst_TELEA
img_diff_NS = img_origin - dst_NS
​
# 显示图像
cv2.imshow("origin image ", img_origin)
cv2.imshow("inpaint image ", img_inpaint)
cv2.imshow("mask ", mask)
cv2.imshow("mask_binary ", mask_binary)
cv2.imshow("result image of TELEA ", dst_TELEA)
cv2.imshow("result image of NS ", dst_NS)
cv2.imshow("difference between origin and TELEA images ", img_diff_TELEA)
cv2.imshow("difference between origin and NS images ", img_diff_NS)
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

运行效果:

image-20231118204057316

image-20231118204129672

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

实现代码:

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

运行效果:

image-20231118212326179

4 实验心得与思考

4.1 实验心得总结

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

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

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

4.2 实验问题与解决

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

出现问题: geometric_filtered_image全黑;

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

修改前代码:

import cv2
import numpy as np
def geometric_mean_filter(image, kernel_size):
    rows, cols = image.shape
    result = np.zeros_like(image, dtype=np.float32)
    # 边界处理
    border = kernel_size // 2
    padded_image = cv2.copyMakeBorder(image, border, border, border, border, cv2.BORDER_REFLECT)
    for i in range(border, rows + border):
        for j in range(border, cols + border):
            # 获取 kernel_size x kernel_size 区域内的像素值
            neighborhood = padded_image[i - border:i + border + 1, j - border:j + border + 1]
            # 计算几何平均值
            product = np.prod(neighborhood)
            result[i - border, j - border] = product ** (1 / (kernel_size ** 2))
    # 将结果截断到[0, 255]范围内
    result = np.clip(result, 0, 255).astype(np.uint8)
    return result
# 算术均值滤波器(调用cv2.blur)
def arithmetic_mean_filter(image, kernel_size):
    return cv2.blur(image, (kernel_size, kernel_size))
# 读取图像
image = cv2.imread("D:\images\lena_color_512_saltpepper.jpg", cv2.IMREAD_GRAYSCALE)
if image is None:
    print("Error: Unable to read the image.")
else:
    # 设置滤波器的核大小
    kernel_size = 3
    # 应用几何均值滤波器
    geometric_filtered_image = geometric_mean_filter(image, kernel_size)
    # 应用算术均值滤波器
    arithmetic_filtered_image = arithmetic_mean_filter(image, kernel_size)
    # 显示原始图像和处理后的图像
    cv2.imshow("Original Image", image)
    cv2.imshow("Geometric Filtered Image", geometric_filtered_image)
    cv2.imshow("Arithmetic Filtered Image", arithmetic_filtered_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
修改前运行结果:

image-20231118200142091

修改后代码:

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

image-20231118212950816


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

最重要的是点一个大大的关注, 你的支持就是作者创作的最大动力!!!


  • 20
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Retouch Pilot v3.10.2是一款非常完美的相片修复工具,能够快速的将一系列老旧照片来进行修复,是一款非常实用的旧照片翻新修复工具,通过它可以对一些损坏的照片进行修复,可以修复旧片的污点刮痕等细小的瑕疵,除此之外还可以将它当做水印去除工具来使用,可以完美地清除图片的LOGO水印等,即使在复杂的背景下也可以完成。可真是老照片的救星啊! 以前我们修复破旧的老照片都是使用PS,对技术要求比较高,怎么办呢?后来出现了一款专门修复老照片的工具Retouch Pilot,使用简单。 在胶片摄影时代(俗称“传统摄影”时代),修复一张严重破损的旧照片,步骤非常复杂:首先把旧照片翻拍放大成低反差照片,之后用铅笔、毛笔和刀片进行修 整,之后再翻拍放大出照片;在新翻拍的照片上修整,再根据照片破损的程度和修复的效果决定翻拍和修整的次数,直到修好、翻拍放大出成品照片为止。 如今,有了电子计算机这个高效的工具,图像可以转为数字文件进行后期数字化处理,旧照片的修复就得容易和简单了。不过,这是单纯从技术方面,或者说是修整工具方面来说的。如果不懂得修像的原理和方法,用再高级的电子计算机也是不行的,旧照片的修复都不会得容易和简单。 使用方法可以看软件界面提供的DEMO。
OpenCV是一款非常强大的计算机视觉库,其包含了很多功能强大的图像处理和计算机视觉算法。而在这个系列的第三篇文章,我们将重点介绍如何在OpenCV绘制图形和文本。 OpenCV绘制图形的函数包括cv2.line(直线),cv2.rectangle(矩形),cv2.circle(圆),cv2.ellipse(椭圆)和cv2.putText(文本)。这些函数的使用非常简单,只需要指定图像,开始点和结束点(或心点和半径),颜色和线宽即可。 例如,我们可以使用cv2.rectangle函数在图像上绘制一个矩形: ``` import cv2 import numpy as np img = np.zeros((512,512,3), np.uint8) # 创建一个黑色的图像 cv2.rectangle(img,(384,0),(510,128),(0,255,0),3) # 绘制矩形 cv2.imshow("image",img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 在这个例子,我们首先创建了一个512x512的黑色图像,然后使用cv2.rectangle函数在图像上绘制了一个矩形,并将其显示出来。在这里,矩形的左上角坐标是(384,0),右下角坐标是(510,128),颜色是绿色(0,255,0),线宽为3。 除了绘制图形,我们也可以使用cv2.putText函数在图像上绘制文本。这个函数的使用也非常简单,只需要指定图像,文本内容,位置,字体,字体大小和颜色即可。 例如,我们可以使用cv2.putText函数在图像上写入一个“Hello World!”的文本: ``` import cv2 import numpy as np img = np.zeros((512,512,3), np.uint8) # 创建一个黑色的图像 font = cv2.FONT_HERSHEY_SIMPLEX # 设置字体 cv2.putText(img,'Hello World!',(10,500), font, 1,(255,255,255),2,cv2.LINE_AA) # 绘制文本 cv2.imshow("image",img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 在这个例子,我们使用cv2.putText函数在左下角绘制了一个“Hello World!”的文本,并将其显示出来。在这里,我们首先选取了字体类型为cv2.FONT_HERSHEY_SIMPLEX,然后指定了文本的位置(10,500),字体大小为1,颜色为白色(255,255,255),线宽为2,并且使用了cv2.LINE_AA参数,以实现更好的文本渲染效果。 综上所述,OpenCV可以很方便地对图像进行绘制和文本处理,这对于图像处理和计算机视觉应用来说是非常重要的一步。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值