Extracting the elements matching the filter

2019-06-14 18:57发布

I want to filter the indices whose footprint (3,3) consists of 1s.

import numpy as np
data = np.array([[1, 1 , 0 , 0 , 0 , 0 , 1 , 0],
                 [1, 1 , 1 , 0 , 0 , 1 , 1 , 0],
                 [1, 1 , 1 , 1 , 1 , 0 , 0 , 0],
                 [0, 0 , 1 , 1 , 1 , 0 , 0 , 0],
                 [0, 0 , 1 , 1 , 1 , 1 , 0 , 1],
                 [1, 1 , 0 , 0 , 0 , 1 , 1 , 1],
                 [1, 1 , 0 , 0 , 0 , 1 , 1 , 1]])

The expected answer is below, unwanted positions are set to 0s:

answer = np.array([[0, 0 , 0 , 0 , 0 , 0 , 0 , 0],
                 [0, 0 , 0 , 0 , 0 , 0 , 0 , 0],
                 [0, 0 , 1 , 1 , 1 , 0 , 0 , 0],
                 [0, 0 , 1 , 1 , 1 , 0 , 0 , 0],
                 [0, 0 , 1 , 1 , 1 , 0 , 0 , 0],
                 [0, 0 , 0 , 0 , 0 , 0 , 0 , 0],
                 [0, 0 , 0 , 0 , 0 , 0 , 0 , 0]])

1条回答
家丑人穷心不美
2楼-- · 2019-06-14 19:10

I'm basing this answer on a very similar answer I wrote just a few hours ago.

#from sklearn.feature_extraction.image import extract_patches  # similar to numpy's stride_tricks
from numpy.lib.stride_tricks import as_strided

data = np.array([[1, 1 , 0 , 0 , 0 , 0 , 1 , 0],
                 [1, 1 , 1 , 0 , 0 , 1 , 1 , 0],
                 [1, 1 , 1 , 1 , 1 , 0 , 0 , 0],
                 [0, 0 , 1 , 1 , 1 , 0 , 0 , 0],
                 [0, 0 , 1 , 1 , 1 , 1 , 0 , 1],
                 [1, 1 , 0 , 0 , 0 , 1 , 1 , 1],
                 [1, 1 , 0 , 0 , 0 , 1 , 1 , 1]])
patches = as_strided(data, shape=(data.shape[0]-2,data.shape[1]-2, 3, 3), strides=data.strides*2)
dual = patches.all(axis=-1).all(axis=-1)
patches[dual==False] = 0
patches[dual] = 1

The result (in data) is as you've shown.

The way this works is as follows: the function extract_patches generates a view into the array data, which means it is not a copy of the actual data but uses the strides to generate a seemingly different array. In this case, it will generate all possible 3x3 submatrices (which can be overlapping) of the array data. Then, by writing patches.all(axis=-1).all(axis=-1), you are first checking if the elements in the rows of the submatrices are all True (or equivalent to True in a boolean sense, so not 0, empty lists, empty dictionaries, empty tuples and a few other special cases), thereby collapsing one of the axes of this array and then with the second .all(axis=-1) the columns are being checked to see if they're all True.

Small example of this paragraph to clarify visually:

>>> A = np.array([
... [1, 1, 0],        # -> along this row, not all elements are 1: so `all` along the last axis will return False
... [1, 1, 1],        # -> along this row, all elements are 1: so `all` along the last axis (axis=-1) will return True
... [1, 1, 1]])
>>> A.all(axis=-1)
array([False,  True,  True], dtype=bool)  # so it is done along the last axis, along the rows
>>> A.all(axis=-1).all(axis=-1)
False

So this array dual now has a "1" (True actually) for every 3x3 submatrix that was full of ones. Those submatrices are overlapping however, so you first want to set the patches all to 0 whenevery any of these 3x3 submatrices was not all ones (that's the one-to-last line in the first code block: patches[dual==False] = 0) and then you can apply the ones again in each 3x3 submatrix that originally had all ones. The alternatives are to correlate with a kernel = np.ones((3,3)) or to take multiple bitwise operations into account (like in the other answer), but that last method becomes very difficult to write when the array's dimensions grow beyond simply (2,2).

查看更多
登录 后发表回答