python中值滤波、最大池化、平均池化、canny边缘检测(石原里美系列一)

6 篇文章 1 订阅

一、常见三种滤波器介绍

中值滤波:取卷积区域内的中位数
最大池化:取卷积区域内的最大值
平均池化:取卷积区域内的均值
边缘检测:边缘检测就是找到图像的边缘信息(轮廓)

二、故事背景

有一天,石原里美小姐姐出去玩,拍了一张美美的照片,回来的路上看到了路边有一个十元快速洗照片的摊,于是就花了十元把照片洗了下。回家之后掏出来一看,黑心的老板没有给照片加膜,照片出现了椒盐噪点,于是找你来求助。
在这里插入图片描述

三、修复图像(中值滤波)

聪明的你,立马想到了中值滤波,于是三下五除二就给搞好了。另外还实现了均值池化(滤波)、最大池化(滤波)。

import numpy as np
from PIL import Image
import copy
def Fliter(src,dst,k=3,filter_type='median'):
    # src: 读入图像路径
    # dst: 输出图像路径
    # k:   卷积核大小
    # filter_type: median, maxpooling, avgpooling
    imarray = np.array(Image.open(src))
    new_img = copy.deepcopy(imarray)
    height, width, depth = imarray.shape
    filter_dict = {'median':np.median,'maxpooling':np.max,'avgpooling':np.mean}
    if filter_type in filter_dict:
        filter_conv = filter_dict[filter_type]
    else:
        raise ValueError('filter_type: median, maxpooling, avgpooling')
    for i in range(height):
        for j in range(width):
            b_i = max(0,i-k//2)
            e_i = min(height-1,i+k//2)
            b_j = max(0,j-k//2)
            e_j = min(width-1,j+k//2)
            tmp = imarray[b_i:e_i+1,b_j:e_j+1,:]
            new_img[i][j][0] = filter_conv(tmp[:,:,0])
            new_img[i][j][1] = filter_conv(tmp[:,:,1])
            new_img[i][j][2] = filter_conv(tmp[:,:,2])
    new_img = Image.fromarray(new_img)
    new_img.save(dst)
    return new_img

中值滤波结果:
在这里插入图片描述
最大池化结果(怎么有点像卸妆了呢):
在这里插入图片描述
平均池化结果:
在这里插入图片描述

四、追求进步的石原里美(均值滤波加速)

石原里美用了均值滤波之后,总是感觉好慢,就说,均值滤波用一次耗时很长,能不能快一点。于是,聪明的你想到了累加和来做这个。思路就是,二维数组中每个位置保留为从左上角开始到当前位置的累加和
a r r a y [ i ] [ j ] = ∑ i m ∑ j n a r r [ i ] [ j ] array[i][j] = ∑_i^{m}∑_j^{n}arr[i][j] array[i][j]=imjnarr[i][j]
在这里插入图片描述
那么在计算均值滤波时,每个滤波器的值就变成了如下表示,其中k表示滤波器大小:
a v g [ i ] [ j ] = ( a r r a y [ i ] [ j ] − a r r a y [ i − k ] [ j ] − a r r a y [ i ] [ j − k ] + a r r a y [ i − k ] [ j − k ] ) / ( k ∗ k ) avg[i][j]=(array[i][j]-array[i-k][j]-array[i][j-k]+array[i-k][j-k])/(k*k) avg[i][j]=(array[i][j]array[ik][j]array[i][jk]+array[ik][jk])/(kk)
在这里插入图片描述

def AvgPooling(src,dst,k=3):
    # src: 读入图像路径
    # dst: 输出图像路径
    # k:   卷积核大小
    # filter_type: median, maxpooling, avgpooling
    imarray = np.array(Image.open(src),dtype=int)
    new_img = copy.deepcopy(imarray)
    height, width, depth = imarray.shape
    tmp_array = np.zeros((height,width,depth))
    tmp_array[0,0,:] = imarray[0,0,:]
    for i in range(1,height):
        tmp_array[i,0,:] = tmp_array[i-1,0,:] + imarray[i,0,:]
    for j in range(1,width):
        tmp_array[0,j,:] = tmp_array[0,j-1,:] + imarray[0,j,:]
    for i in range(1,height):
        for j in range(1,width):
            tmp_array[i,j,:] = tmp_array[i-1,j,:] + tmp_array[i,j-1,:] + imarray[i,j,:] - tmp_array[i-1,j-1,:]
    for i in range(height):
        for j in range(width):
            b_i = max(0,i-k//2)
            e_i = min(height-1,i+k//2+1)
            b_j = max(0,j-k//2)
            e_j = min(width-1,j+k//2+1)
            kernel = (e_i-b_i)*(e_j-b_j)
            new_img[i,j,:] = (tmp_array[e_i,e_j,:] + tmp_array[b_i,b_j,:] - tmp_array[e_i,b_j,:] - tmp_array[b_i,e_j,:])/kernel
    new_img = Image.fromarray(np.uint8(new_img))
    new_img.save(dst)
    return new_img

下面比较一下时间,用未优化的Fliter与Avgpooling进行速度比较。

t = time.time()
Fliter('shiyuan.png','avg_shiyuan.jpg',k=3,filter_type='avgpooling')
print('未优化的时间:',time.time()-t)
# 未优化的时间: 3.712080478668213
t = time.time()
AvgPooling('shiyuan.png','avg_shiyuan.jpg',k=3)
print('优化之后的时间:',time.time()-t)
# 优化之后的时间: 1.0790929794311523

五、故事反转(椒盐噪声)

其实,事实的真相是,十元小姐姐的照片根本就没有被椒盐噪声模糊,她只是喜欢技术宅而已。为了和你找话题,自己把照片添加了椒盐噪声,然后让你帮忙修复。

import numpy as np
from PIL import Image
def AddSalt(src,dst,probility=0.02):
    imarray = np.array(Image.open(src))
    height, width, detp = imarray.shape
    for i in range(height):
        for j in range(width):
            if np.random.random(1) < probility:
                if np.random.random(1) < 0.5:
                    imarray[i, j] = [0,0,0]
                else:
                    imarray[i, j] = [255,255,255]
    new_im = Image.fromarray(imarray)
    new_im.save(dst)

原图:
在这里插入图片描述

六、续集(边缘检测)

经过上面的几次交流,石原里美小姐姐对你的好感大大增加,又找到一个理由想和你进一步交流。微信问你,有没有什么好的方法把我的照骗变成线条简笔画。身为技术宅的你,听到之后,大腿一拍,这明明就是边缘检测嘛。
canny边缘检测是通过卷积算子突出图像边缘部分,经典的有以下几种算子:

Prewitt算子:由水平梯度和垂直梯度两个方向计算得到,分别用于检测垂直边缘和水平边缘。
[ − 1 0 1 − 1 0 1 − 1 0 1 ] [ − 1 − 1 − 1 0 0 0 1 1 1 ] \begin{bmatrix} -1 & 0 & 1 \\ -1 & 0 & 1 \\ -1 & 0 & 1 \end{bmatrix} \begin{bmatrix} -1 & -1 & -1 \\ 0 & 0 & 0 \\ 1 & 1 & 1 \end{bmatrix} 111000111101101101
Sobel算子:Sobel算子在Prewitt的基础上进行改进,增强中间位置的权重,检测垂直边缘和水平边缘分别为:
[ − 1 0 1 − 2 0 2 − 1 0 1 ] [ − 1 − 2 − 1 0 0 0 1 2 1 ] \begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{bmatrix} \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ 1 & 2 & 1 \end{bmatrix} 121000121101202101

def canny(src,dst,k=3,kernel='prewitt'):
    # kernel: prewitt, sobel
    img = np.array(Image.open(src).convert('L'),'f')
    heigh, width = img.shape
    new = np.zeros((heigh+2,width+2))
    if kernel == 'prewitt':
        kernel_x = np.array([[-1,0,1],[-1,0,1],[-1,0,1]])
        kernel_y = np.array([[-1,-1,-1],[0,0,0],[1,1,1]])
    elif kernel == 'sobel':
        kernel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
        kernel_y = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])
    else:
        raise ValueError('filter_type: prewitt, sobel')
    new[1:-1,1:-1] = img[:][:]
    for i in range(1,len(img)):
        for j in range(1,len(img[0])):
            tmp1 = new[i-k//2:i+k//2+1,j-k//2:j+k//2+1]*kernel_x
            tmp2 = new[i-k//2:i+k//2+1,j-k//2:j+k//2+1]*kernel_y
            img[i][j] = np.sum(tmp1+tmp2)
    img = Image.fromarray(img)
    img = img.convert('L')
    img.save(dst)

在这里插入图片描述 在这里插入图片描述

prewitt(左),sobel(右)

Laplacian算子:上面两个算子用于线的检测,通常用于边缘检测,而Laplacian算子是用于检测特殊点的。滤波器如下:
[ 0 1 0 1 − 4 1 0 1 0 ] o r [ 0 − 1 0 − 1 4 − 1 0 − 1 0 ] \begin{bmatrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{bmatrix} or \begin{bmatrix} 0 & -1 & 0 \\ -1 & 4 & -1 \\ 0 & -1 & 0 \end{bmatrix} 010141010or010141010对特殊点进行增强:
[ 1 1 1 1 − 8 1 1 1 1 ] o r [ − 1 − 1 − 1 − 1 8 − 1 − 1 − 1 − 1 ] \begin{bmatrix} 1 & 1 & 1 \\ 1 & -8 & 1 \\ 1 & 1 & 1 \end{bmatrix} or \begin{bmatrix} -1 & -1 & -1 \\ -1 & 8 & -1 \\ -1 & -1 & -1 \end{bmatrix} 111181111or111181111

def laplacian(src, dst, k=3,mode='high'):
    # mode: low, high
    img = np.array(Image.open(src).convert('L'), 'f')
    heigh, width = img.shape
    new = np.zeros((heigh + 2, width + 2))
    if mode=='low':
        kernel = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])
    elif mode == 'high':
        kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]])
    else:
        raise ValueError('')
    new[1:-1, 1:-1] = img[:][:]
    for i in range(1, len(img)):
        for j in range(1, len(img[0])):
            tmp = new[i - k // 2:i + k // 2 + 1, j - k // 2:j + k // 2 + 1] * kernel
            img[i][j] = np.sum(tmp)
    img = Image.fromarray(img)
    img = img.convert('L')
    img.save(dst)

在这里插入图片描述

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值