Find the 1s pixels that immediately and completely enclose an area of 0s
Following the approach suggested by Jerome and Mark:
- Pad the matrix with a 1px border of zeros
- Flood the matrix and keep just the islands of central 0s
- Expand those islands with dilate (after inverting them) to expand the contours -> B
- bitwise AND it with A to get back just the contours and remove the initial padding
from collections import deque as queue
from scipy import ndimage
import numpy as np
from skimage.segmentation import flood_fill
A = np.array([[1,1,1,1,1,1],
[1,1,0,0,1,1],
[1,1,0,1,1,1],
[1,1,1,1,1,1],
[0,0,1,1,0,0]])
A = np.pad(A, pad_width=1, mode='constant', constant_values=0)
print("A after padding")
print(A)
A = flood_fill(A, (0, 0), 1)
print("A after flooding")
print(A)
# you can also use cv2.dilate if you want to avoid ndimage
struct2 = ndimage.generate_binary_structure(2, 2)
B = ndimage.binary_dilation(1-A, structure=struct2).astype(A.dtype)
print("B")
print(B)
print("result")
res = B & A
print(res[1:-1, 1:-1]) # remove padding
Output:
A after padding
[[0 0 0 0 0 0 0 0]
[0 1 1 1 1 1 1 0]
[0 1 1 0 0 1 1 0]
[0 1 1 0 1 1 1 0]
[0 1 1 1 1 1 1 0]
[0 0 0 1 1 0 0 0]
[0 0 0 0 0 0 0 0]]
A after BFS
[[1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1]
[1 1 1 0 0 1 1 1]
[1 1 1 0 1 1 1 1]
[1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1]]
B
[[0 0 0 0 0 0 0 0]
[0 0 1 1 1 1 0 0]
[0 0 1 1 1 1 0 0]
[0 0 1 1 1 1 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]]
result
[[0 1 1 1 1 0]
[0 1 0 0 1 0]
[0 1 0 1 1 0]
[0 1 1 1 0 0]
[0 0 0 0 0 0]]