(七)滤波与卷积

滤波与卷积

一、滤波与卷积的区别

图像处理中滤波和卷积原理上相似,但是在实现的细节上存在一些区别。
滤波操作就是图像对应像素与掩膜(mask)的对应元素相乘相加。而卷积操作是图像对应像素与旋转180度的卷积核对应元素相乘相加。
在这里插入图片描述
下面是一个卷积示意图(卷积核已经旋转180°)
在这里插入图片描述

二、卷积

卷积操作也是卷积核与图像对应位置的乘积和。但是卷积操作在做乘积之前,需要先
将卷积核翻转180度,之后再做乘积。其数学定义为:
在这里插入图片描述
一般称g为作用在f上的filter或kernel。f是图像,g叫滤波器或卷积核

三、卷积核的要求

对于滤波器,也有一定的规则要求:

1)滤波器的大小应该是奇数,这样它才有一个中心,例如3x3,5x5或者7x7。有中心了,也有
了半径的称呼,例如5x5大小的核的半径就是2。
2)滤波器矩阵所有的元素之和应该要等于1,这是为了保证滤波前后图像的亮度保持不变。但
这不是硬性要求。
3)如果滤波器矩阵所有元素之和大于1,那么滤波后的图像就会比原图像更亮,反之,如果小
于1,那么得到的图像就会变暗。如果和为0,图像不会变黑,但也会非常暗。
4)对于滤波后的结构,可能会出现负数或者大于255的数值。对这种情况,我们将他们直接截 断到0和255之间即可。对于负数,也可以取绝对值。

四、卷积的意义

在具体应用中,往往有多个卷积核,可以认为,每个卷积核代表了一种图像模式。
如果某个图像块与此卷积核卷积出的值大,则认为此图像块十分接近于此卷积核。
例如,如果我们设计了6个卷积核,可以理解:我们认为这个图像上有6种底层纹理模式,
也就是我们用6种基础模式就能描绘出一副图像。
例如:
用Gx来卷积下面这张图的话,就会在中间黑白边界获得比较大的值。
在这里插入图片描述

五、常用的卷积核

1、一个没有任何效果的卷积核:

在这里插入图片描述
将原像素中间像素值乘1,其余全部乘0。
显然像素值不会发生任何变化。

2、平滑均值滤波

在这里插入图片描述
取九个值的平均值代替中间像素值,用于过滤高频噪声点
在这里插入图片描述

— 起到平滑的效果。

3、高斯平滑

在这里插入图片描述
高斯平滑水平和垂直方向呈现高斯分布,更突出了中心点在像素平滑后的权重,相比于均值滤波而言,有着更好的平滑效果。

4、图像锐化

图像锐化使用的是拉普拉斯变换核函数:
在这里插入图片描述

5、Soble边缘检测

在这里插入图片描述
Soble更强调了和边缘相邻的像素点对边缘的影响。
在这里插入图片描述

六、卷积解决的问题

卷积负责提取图像中的局部特征

七、滤波or卷积的填充

如果用 (f, f) 的过滤器来卷积一张 (h, w) 大小的图片,每次移动一个像素的话,那么得出的结果就是 (h-f+1, w-f+1) 的输出结果。
f是过滤器大小,
hw分别是图片的高宽。
如果每次不止移动一个像素,而是s个像素,那么结果就会变为:
在这里插入图片描述
其中这个s就叫做步长。

此时的卷积存在的问题:
• 只要是fs的值比1要大的话,那么每次卷积之后结果的长宽,要比卷积前小一些。
• 丢失信息(图片边缘的像素卷积卷不到)
解决问题的方法–在卷积之前在原图周围进行补0填充
在这里插入图片描述

1、Same填充

在没有填充时,卷积输出的图像大小为:
在这里插入图片描述
假设,我们在原图像四周填充了宽度为P的0(也可以根据需要填充其他值)进行填充,这样原图像的大小就从 (h,w) 变成 (h+2p,w+2p), 有了填充之后,每次卷积之后的图像大小::
在这里插入图片描述
此时我们让输图像的宽高等于输入图像的宽高,即可求出p,以高为例:
在这里插入图片描述
即计算出了四周填充宽度p。
特别的 当步长 s=1:
在这里插入图片描述

当p是分数怎么办?
一般的处理是,只取整数部分(向上取整)
假设 p求出的是高度h上需要填充的总宽度,
p = s(h-1)-h+f
则图像高方向的上半部分需要填充的宽度为
p_top =int(p/2)
p_bottom = p-p_top
图片宽度方向处理方法相同。
以上的填充过程就是same填充

2、Valid(有效)填充

不进行任何填充,直接进行卷积,即p=0,叫做"Valid(有效)填充"
在这里插入图片描述由此可见 卷积时,padding设置为valid会使得图片变小,设置为same不会改变图片大小
vaild输出图片大小:
在这里插入图片描述
same输出图片大小:
在这里插入图片描述

八、多通道卷积(重要)

在这里插入图片描述
理解图:
在这里插入图片描述
对多通道图像进行卷积需要使用与图像图像相同的卷积核进行卷积,卷积核的每个通道对应着图像中相应的通道图像,一对一卷积,最后将每个通道的卷积后图像对应位置的卷积值相加,得到一个1通道的特征图。
多通道卷积结果的通道数==卷积核数
在这里插入图片描述
滤波的实现


'''
滤波与卷积
'''

import cv2
import numpy as np
import matplotlib.pyplot as plt

def Padding(image,kernels_size,stride = [1,1],padding = "same"):
    '''
    对图像进行padding
    :param image: 要padding的图像矩阵
    :param kernels_size: list 卷积核大小[h,w]
    :param stride: 卷积步长 [左右步长,上下步长]
    :param padding: padding方式
    :return: padding后的图像
    '''
    if padding == "same":
        h,w = image.shape
        p_h =max((stride[0]*(h-1)-h+kernels_size[0]),0)  # 高度方向要补的0
        p_w =max((stride[1]*(w-1)-w+kernels_size[1]),0)  # 宽度方向要补的0
        p_h_top = p_h//2                                 # 上边要补的0
        p_h_bottom = p_h-p_h_top                         # 下边要补的0
        p_w_left = p_w//2                                # 左边要补的0
        p_w_right = p_w-p_w_left                         # 右边要补的0
        # print(p_h_top,p_h_bottom,p_w_left,p_w_right)     # 输出padding方式
        padding_image = np.zeros((h+p_h, w+p_w), dtype=np.uint8)
        for i in range(h):
            for j in range(w):
                    padding_image[i+p_h_top][j+p_w_left] = image[i][j] # 将原来的图像放入新图中做padding
        return padding_image
    else:
        return image


def filtering_and_convolution(image,kernels,stride,padding = "same"):
    '''
    :param image: 要卷积的图像
    :param kernels: 卷积核 列表
    :param stride: 卷积步长 [左右步长,上下步长]
    :param padding: padding方式 “same”or“valid”
    :return:
    '''
    image_h,image_w = image.shape
    kernels_h,kernels_w = np.array(kernels).shape
    # 获取卷积核的中心点
    kernels_h_core = int(kernels_h/2+0.5)-1
    kernels_w_core = int(kernels_w/2+0.5)-1
    if padding == "valid":
        # 计算卷积后的图像大小
        h = int((image_h-kernels_h)/stride[0]+1)
        w = int((image_w-kernels_w)/stride[1]+1)
        # 生成卷积后的图像
        conv_image = np.zeros((h,w),dtype=np.uint8)
        # 计算遍历起始点
        h1_start = kernels_h//2
        w1_start = kernels_w//2
        ii=-1
        for i in range(h1_start,image_h - h1_start,stride[0]):
            ii += 1
            jj = 0
            for j in range(w1_start,image_w - w1_start,stride[1]):
                sum = 0
                for x in range(kernels_h):
                    for y in range(kernels_w):
                        # print(i,j,int((i/image_h)*h),int((j/image_w)*w),  i-kernels_h_core + x,  j-kernels_w_core+y,x,y)
                        sum += int(image[i-kernels_h_core+x][j-kernels_w_core+y]*kernels[x][y])
                conv_image[ii][jj] = sum
                jj += 1
        return conv_image

    if padding == "same":
        # 对原图进行padding
        kernels_size = [kernels_h, kernels_w]
        pad_image = Padding(image,kernels_size,stride,padding="same")
        # 计算卷积后的图像大小
        h = image_h
        w = image_w
        # 生成卷积后的图像
        conv_image = np.zeros((h,w),dtype=np.uint8)
        # # 计算遍历起始点
        h1_start = kernels_h//2
        w1_start = kernels_w//2
        ii=-1
        for i in range(h1_start,image_h - h1_start,stride[0]):
            ii +=1
            jj = 0
            for j in range(w1_start,image_w - w1_start,stride[1]):
                sum = 0
                for x in range(kernels_h):
                    for y in range(kernels_w):
                        sum += int(image[i-kernels_h_core+x][j-kernels_w_core+y]*kernels[x][y])
                conv_image[ii][jj] = sum
                jj += 1
        return conv_image

def sobel_filter(image):
    h = image.shape[0]
    w = image.shape[1]
    image_new = np.zeros(image.shape, np.uint8)

    for i in range(1, h - 1):
        for j in range(1, w - 1):
            sx = (image[i + 1][j - 1] + 2 * image[i + 1][j] + image[i + 1][j + 1]) - \
                 (image[i - 1][j - 1] + 2 * image[i - 1][j] + image[i - 1][j + 1])
            sy = (image[i - 1][j + 1] + 2 * image[i][j + 1] + image[i + 1][j + 1]) - \
                 (image[i - 1][j - 1] + 2 * image[i][j - 1] + image[i + 1][j - 1])
            image_new[i][j] = np.sqrt(np.square(sx) + np.square(sy))
            # image_new[i][j] = sy
    return image_new

# 设置matplotlib正常显示中文和负号
plt.rcParams['font.sans-serif']=['SimHei']   # 用黑体显示中文
plt.rcParams['axes.unicode_minus']=False     # 正常显示负号
img = cv2.imread('lenna.png',1)
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
plt.subplot(331)
plt.imshow(img_gray,cmap="gray")
plt.title("原图")


sobel_Gy = [[-1,0,1],[-2,0,2],[-1,0,1]]
Average = [[1/9,1/9,1/9],[1/9,1/9,1/9],[1/9,1/9,1/9]]
Gaussian = [[1/16,2/16,1/16],[2/16,4/16,2/16],[1/16,2/16,1/16]]
Laplace = [[-1,-1,-1],[-1,9,-1],[-1,-1,-1]]
stride=[1,1]
img_sobel_Gy = filtering_and_convolution(img_gray,sobel_Gy,stride,padding="same")
img_Average = filtering_and_convolution(img_gray,Average,stride,padding="same")
img_Gaussian = filtering_and_convolution(img_gray,Gaussian,stride,padding="same")
img_Laplace = filtering_and_convolution(img_gray,Laplace,stride,padding="same")
plt.subplot(332)
plt.imshow(img_sobel_Gy,cmap = "gray")
plt.title("sobel_Gy")
plt.subplot(333)
plt.imshow(img_Average,cmap = "gray")
plt.title("Average")
plt.subplot(334)
plt.imshow(img_Gaussian,cmap = "gray")
plt.title("Gaussian")
plt.subplot(335)
plt.imshow(img_Laplace,cmap = "gray")
plt.title("Laplace")
plt.show()


  • 10
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值