图像处理四种边界条件Python轮子实现——ZeroPadding、WrapAround、EdgeCopy、Reflect(同时应对各种size——valid、same、full)

我们首先要了解图像输出的size
图像处理四种边界条件Python轮子实现——ZeroPadding、WrapAround、EdgeCopy、Reflect(同时应对各种size——valid、same、full)_第1张图片

(图源见图片右下角)
(尽管上图已将valid、same、full说得很清楚了,但我还是画蛇添足再描述一下)
  • full,full嘛,全部的意思,将边界填充,使得,kernel四个角的像素点能与原图像的像素点刚好对应重合,output会变大
  • same,same嘛,相同的意思,原图像的四个角像素点与kernel中心像素点重合,output与原图一样大
  • valid,图片不需要填充,直接进行运算,会使图片变小

接下来再上手边界条件

这是原图:
图像处理四种边界条件Python轮子实现——ZeroPadding、WrapAround、EdgeCopy、Reflect(同时应对各种size——valid、same、full)_第2张图片

ZeroPadding:
图像处理四种边界条件Python轮子实现——ZeroPadding、WrapAround、EdgeCopy、Reflect(同时应对各种size——valid、same、full)_第3张图片


WrapAround
图像处理四种边界条件Python轮子实现——ZeroPadding、WrapAround、EdgeCopy、Reflect(同时应对各种size——valid、same、full)_第4张图片


EdgeCopy
图像处理四种边界条件Python轮子实现——ZeroPadding、WrapAround、EdgeCopy、Reflect(同时应对各种size——valid、same、full)_第5张图片


Reflect

图像处理四种边界条件Python轮子实现——ZeroPadding、WrapAround、EdgeCopy、Reflect(同时应对各种size——valid、same、full)_第6张图片

为了减少代码冗余,我写了一个装饰器filterDecorator,在我写的函数boundaryZeroPaddingboundaryWrapAround中,留存了部分注释以帮助大家理解


# 为减少部分代码,定义装饰器函数
def filterDecorator(someFilter):
    '''
    someFilter : 传入的滤波器函数
    '''
    def inner(img, kernel_size, shape='same'):
        # 1.检查 shape 参数正确与否
        assert shape in ['full', 'same','valid'], "弟弟, shape参数填错了!"
        # 2.valid 情况无需填充
        if shape == 'valid':
            return img.copy()
        if shape == 'full':
            # k -> 2K 即和 'same' 一样
            kernel_size = np.array(kernel_size) * 2 - 1
        # 调用被装饰函数
        result_img = someFilter(img, kernel_size, 'same')
        
        return result_img
    return inner



# 全零填充函数
@filterDecorator
def boundaryZeroPadding(img, kernel_size, shape):
    '''
    img         :   要填充的img
    kernel_size :   你kernel的形状,(先行后列)与kernel.shape顺序相反
    shape       :   填充的类型, 你懂得:full, same, valid
    返回值为 padding 后的图片
    '''
#    # -------------------------代码冗余,转至装饰器中----------------------------
#    # 检查 shape 参数正确与否
#    assert shape in ['full', 'same','valid'], "弟弟, shape参数填错了!"
#    if shape == 'valid':
#        # 此情况无需 padding
#        return img.copy()
#    # -------------------------代码冗余,转至装饰器中----------------------------
    
    # 获取图片的高,宽
    img_height, img_width = img.shape
    # 获得 K 值
    kx, ky = kernel_size[1]//2, kernel_size[0]//2


    if shape == 'same': # 不加 if 非常可
        # 左右需要填充的 zero
        # 此处必须加上 dtype=np.uint8,否则 cv2 float 显示全白
        zeros_array = np.zeros((img_height, kx), dtype=np.uint8)
        # 给图片左右两边添加 0
        img_copy = np.concatenate([zeros_array, img, zeros_array], axis=1)
        # 上下需要填充的 zero
        zeros_array = np.zeros((ky, 2*kx + img_width), dtype=np.uint8)
        # 给图片上下两边添加 0
        img_result = np.concatenate([zeros_array, img_copy, zeros_array], axis=0)
        return img_result
    
#    # -------------------------代码冗余,转至装饰器中----------------------------
#    else:
#        # k -> 2K 即和 'same' 一样
#        kernel_size = np.array(kernel_size) * 2 - 1
#        return boundaryZeroPadding(img, kernel_size, 'same')
#    # -------------------------代码冗余,转至装饰器中----------------------------



# 块复制,相当于搞一个周期函数
@filterDecorator
def boundaryWrapAround(img, kernel_size, shape):
    '''
    img         :   要填充的img
    kernel_size :   你kernel的形状,(先行后列)与kernel.shape顺序相反
    shape       :   填充的类型, 你懂得:full, same, valid
    返回值为 WrapAround 后的图片
    '''
    
    # 获取图片的高,宽
    img_height, img_width = img.shape
    # 获得 K 值
    kx, ky = kernel_size[1]//2, kernel_size[0]//2
    
    '''
    块复制 位置分布定义, X为原图片
      E |     D     | F
    --------------------
        | 1 | 2 | 3 |
        | - | - | - |
      A | 4 | X | 5 | B
        | - | - | - |
        | 6 | 7 | 8 |
    --------------------
      G |     C     | H
    '''
    if shape == 'same': # 不加 if 非常可
        # 求上下
        part_D = img[-ky:]
        part_C = img[: ky]
        # part_A = img[:, -kx:]
        # part_B = img[:,  :kx]
        # 中间合并
        part_DXC = np.concatenate([part_D, img, part_C], axis=0)
        # 复制 DXC 的 右边
        part_EAG = part_DXC[:, -kx:]
        # 复制 DXC 的 左边
        part_FBH = part_DXC[:,  :kx]
        # 整体合并
        img_result = np.concatenate([part_EAG, part_DXC, part_FBH], axis=1)
        return img_result
#    # -------------------------代码冗余,转至装饰器中----------------------------
#    else:
#        # k -> 2K 即和 'same' 一样
#        kernel_size = np.array(kernel_size) * 2 - 1
#        return boundaryWrapAround(img, kernel_size, 'same')
#    # -------------------------代码冗余,转至装饰器中----------------------------



# 边界复制,就是把边界拉出去
@filterDecorator
def boundaryEdgeCopy(img, kernel_size, shape):
    '''
    img         :   要填充的img
    kernel_size :   你kernel的形状,(先行后列)与kernel.shape顺序相反
    shape       :   填充的类型,你懂得:full, same, valid
    返回值为 EdgeCopy 后的图片
    '''
    
    # 获取图片的高,宽
    img_height, img_width = img.shape
    # 获得 K 值
    kx, ky = kernel_size[1]//2, kernel_size[0]//2
    
    '''
    边界复制 位置分布 X为图片
    | 1 | 2 | 3 |
    | - | - | - |
    | 4 | X | 5 |
    | - | - | - |
    | 6 | 7 | 8 |
    '''
    if shape == 'same': # 不加 if 非常可
        # 求上下左右
        part_2 = np.concatenate([img[ 0].reshape(1, -1)]*ky, axis=0)
        part_7 = np.concatenate([img[-1].reshape(1, -1)]*ky, axis=0)
        part_4 = np.concatenate([img[:, 0].reshape(-1, 1)]*kx, axis=1)
        part_5 = np.concatenate([img[:,-1].reshape(-1, 1)]*kx, axis=1)
        # 求四个角
        # 此处必须加上 dtype=np.uint8,否则 cv2 float 显示全白
        part_1 = np.ones(shape=(kx, ky), dtype=np.uint8) * img[0,0]
        part_3 = np.ones_like(part_1, dtype=np.uint8) * img[0, -1]
        part_6 = np.ones_like(part_1, dtype=np.uint8) * img[-1, 0]
        part_8 = np.ones_like(part_1, dtype=np.uint8) * img[-1,-1]
        # 三列合并
        part_146 = np.concatenate([part_1, part_4, part_6], axis=0)
        part_2X7 = np.concatenate([part_2, img   , part_7], axis=0)
        part_358 = np.concatenate([part_3, part_5, part_8], axis=0)
        # 整体合并
        img_result = np.concatenate([part_146, part_2X7, part_358], axis=1)
        
        return img_result



# 边界镜像对称函数
@filterDecorator
def boundaryReflect(img, kernel_size, shape):
    '''
    img         :   要填充的img
    kernel_size :   你kernel的形状,(先行后列)与kernel.shape顺序相反
    shape       :   填充的类型
    返回值为 Reflect 后的图片
    '''
    # 获取图片的高,宽
    img_height, img_width = img.shape
    # 获得 K 值
    kx, ky = kernel_size[1]//2, kernel_size[0]//2
    
    '''
    镜像对称 位置分布定义, X为原图片
      E |     D     | F
    --------------------
        | 1 | 2 | 3 |
        | - | - | - |
      A | 4 | X | 5 | B
        | - | - | - |
        | 6 | 7 | 8 |
    --------------------
      G |     C     | H
    '''
    if shape == 'same': # 不加 if 非常可
        # 先通过堆成求 DC
        part_D = img[(ky-1)::-1, :]
        part_C = img[-1:-(ky+1):-1, :]
        # DC 与 X 合并
        part_DXC = np.concatenate([part_D, img, part_C], axis=0)
        # 通过对称求 EAG 和 FBH
        part_EAG = part_DXC[:, (kx-1)::-1]
        part_FBH = part_DXC[:, -1:(-kx-1):-1]
        # 合并
        img_result = np.concatenate([part_EAG, part_DXC, part_FBH], axis=1)
        
        return img_result

你可能感兴趣的:(cv常规操作,python,opencv)