2017-07-31 78 views
0

我发现了一个使用scipy制作循环过滤器的代码片段,我想了解它是如何工作的。我知道skimage中有一个更好的,但我对这一场比赛感兴趣。在Python中编码循环过滤器

from scipy.ndimage.filters import generic_filter as gf 

# Define physical shape of filter mask 
def circular_filter(image_data, radius): 
    kernel = np.zeros((2*radius+1, 2*radius+1)) 
    y, x = np.ogrid[-radius:radius+1, -radius:radius+1] 
    mask = x**2 + y**2 <= radius**2 
    kernel[mask] = 1     
    filtered_image = gf(image_data, np.median, footprint = kernel) 
    return filtered_image 

但我不确定我完全理解发生了什么事。特别是,究竟什么行

 y, x = np.ogrid[-radius:radius+1, -radius:radius+1] 
    mask = x**2 + y**2 <= radius**2 
    kernel[mask] = 1 

呢?

我发布了这个答案作为我之前的一个问题的答案,但没有回复,所以我把它作为一个新问题发布。

回答

1

寻找详细:

kernel = np.zeros((2*radius+1, 2*radius+1)) 
y, x = np.ogrid[-radius:radius+1, -radius:radius+1] 
mask = x**2 + y**2 <= radius**2 
kernel[mask] = 1     

第一行:

kernel = np.zeros((2*radius+1, 2*radius+1)) 

创建零的2-d阵列,其中的中心点和“半径”点在任一侧。对于半径= 2,你会得到:

# __r__ +1 __r__ 
[ 0, 0, 0, 0, 0, ] #\ 
[ 0, 0, 0, 0, 0, ] #_} r 
[ 0, 0, 0, 0, 0, ] # +1 
[ 0, 0, 0, 0, 0, ] #\ 
[ 0, 0, 0, 0, 0, ] #_} r 

接下来,你从numpy.ogrid创建的开放网格 2个阵列。网格网格在numpy中是一个“技巧”,它涉及存储一个“并行”数组或矩阵,该数组或矩阵将特定单元格的x或y坐标保存在该单元格的位置。

例如,y -mesh电网可能是这样的:

[ 0, 0, 0, 0, 0, ] 
[ 1, 1, 1, 1, 1, ] 
[ 2, 2, 2, 2, 2, ] 
[ 3, 3, 3, 3, 3, ] 
[ 4, 4, 4, 4, 4, ] 

而一个x -mesh电网可能是这样的:

[ 0, 1, 2, 3, 4, ] 
[ 0, 1, 2, 3, 4, ] 
[ 0, 1, 2, 3, 4, ] 
[ 0, 1, 2, 3, 4, ] 
[ 0, 1, 2, 3, 4, ] 

如果你看看他们,你”我会意识到Y_grid[x][y] == yX_grid[x][y] == x这是经常使用,它有多个numpy函数来支持它。 ;-)

开放式网格与封闭式网格相似,只不过它只有“一维”。也就是说,不是一对(例如)5x5阵列,而是一个1x5阵列和一个5x1阵列。这就是ogrid所做的 - 它返回两个打开的网格。这些值是从-radius到半径+ 1,根据蟒规则(意味着半径+ 1被省略了):

y, x = np.ogrid[-radius:radius+1, -radius:radius+1] 

所以y是numpy的阵列从例如存储,-2..2 (含),x是-2..2的数组。下一步是构建一个布尔型掩码 - 即一个布满值的数组。如你所知,当你在一个numpy数组上操作时,你会得到另一个numpy数组。因此,在以恒定的表达涉及两个阵列产生另一个数组:

mask = x**2 + y**2 <= radius**2 

掩模的值将是2色的位图,其中一个颜色是“真”,而另一颜色是“假。 “位图将描述实心圆或磁盘。 (由于<=关系。请记住,x和y包含-2..2,而不是0..4。)

最后,从类型布尔转换通过使用掩蔽阵列作为kernel阵列(的零)上的覆盖为int时,零设置到那些每当掩码为“真”:

kernel[mask] = 1 

在这一点上,内核看起来像:

# __r__ +1 __r__ 
[ 0, 0, 1, 0, 0, ] #\ 
[ 0, 1, 1, 1, 0, ] #_} r 
[ 1, 1, 1, 1, 1, ] # +1 
[ 0, 1, 1, 1, 0, ] #\ 
[ 0, 0, 1, 0, 0, ] #_} r 
0

我对SciPy并不熟悉,但我会尝试解释一下基本概念。

这整个功能的目的是通过应用一个过滤器来改变原始图像。该过滤器可以做很多事情,从改变图像的对比度,或添加特殊效果等

让我们通过不同的线路:

 kernel = np.zeros((2*radius+1, 2*radius+1)) 

在这一行,的副本正在创建图像数据,但所有数据均为零(因此正在使用零功能)。这样就可以将面罩稍后应用到它上面。

 y, x = np.ogrid[-radius:radius+1, -radius:radius+1] 

这是创建所谓的“meshgrid”或多维网格。这是创建圆形“面具”。就像图中的x轴和y轴具有均匀间隔的缩放比例一样,这里在网格中也是相同的。 在这种情况下,x和y变量存储均匀间隔的值,用作轴的缩放比例。

 mask = x**2 + y**2 <= radius**2 

这里,创建了一个“掩码”。掩码将用作要保护的图像中的区域以免过滤器,以便不更改任何原始数据。请注意,在毕达哥拉斯式不等式中(这很重要,它不仅仅是一个圆而是一个圆盘)在这里使用x和y变量,就像它们在数学意义上的方式一样。这将创建一个具有给定半径的磁盘,现在将其视为掩码。掩码变量现在包含原始数据值不应更改的所有坐标(x,y)。

 kernel[mask] = 1 

这是掩码现在应用于之前创建的图像副本的位置。现在,图像有一个完美的副本(即相同的尺寸),但是具有“保护”原始数据不被改变的盘状“掩膜”。这就是为什么磁盘覆盖的所有点都设置为1.另请注意,kernelmask的尺寸是如何匹配的。两者都是多维的。图像副本中的其余值仍然设置为零,如在第一行中所做的那样。

 filtered_image = gf(image_data, np.median, footprint = kernel) 

这是所有东西拼合在一起的最后部分。原始数据存储在image_data中,并且存在kernel,它是在其上应用了掩码的图像副本,指示不应更改数据的位置。它们都作为参数传递到实际滤波函数gf(代表通用滤波器),并且输出是新的滤波图像。

这是图像过滤的核心概念,如果您想了解更多信息,我建议您先学习基本的信号处理概念。信号处理课程涵盖了这些概念如何工作的数学,但通常在真正抽象的数学中解释,因为这个概念可以应用于许多不同的例子。在您的代码

+0

你说,掩码标识图像中不会被改变的区域。但是,这不是过滤器改变图像的重点吗?或者你是说这是图像的副本,而不是原始图像本身,它是由滤镜更改的? – Jim421616

+0

遮罩确实识别图像中不会被改变的区域。就像你脸上的面具来保护它,面具“保护”原始数据不被改变。这会使图像的其他部分暴露于过滤。图像的蒙版部分将通过坐标为1的'内核'meshgrid指示。曝光部分的坐标为0. –

+0

噢,我向后隐藏了0和1!我的错。谢谢 :) – Jim421616