2

I am trying to obtain an ndarray of points along the boundary of a shape denoted by 1's in the array. I tried using scipy.spatial.ConvexHull but the boundary created by the convex hull did not account for the hole in the middle of the shape (I need a boundary around the hole too). This is the kind of boundary I am trying to create from the array. How can I account for the hole in the shape?

enter image description here

The colored in area is the area that the boundary points should be calculated from. enter image description here

import numpy as np
from skimage.measure import label, regionprops
import matplotlib.pyplot as plt
from scipy.spatial import Voronoi, voronoi_plot_2d, ConvexHull


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

coords = []
for x in range(arr.shape[0]):
    for y in range(arr.shape[1]):
        if arr[x][y] > 0:
            tile = [x,y]
            coords.append(tile)
            # print("tile", tile)
coords = np.array(coords)
# print(coords)

hull = ConvexHull(coords)
plt.plot(coords[:,0], coords[:,1], 'o')
for simplex in hull.simplices:
    plt.plot(coords[simplex, 0], coords[simplex, 1], 'k-')

plt.plot(coords[hull.vertices,0], coords[hull.vertices,1], 'r--', lw=2)
plt.plot(coords[hull.vertices[0],0], coords[hull.vertices[0],1], 'ro')
Evan Kim
  • 769
  • 2
  • 8
  • 26
  • The image is not clear to me, for example, why is `(3, 2)` part of the frontier but `(1, 3)` is not? – vlizana Apr 15 '19 at 21:03
  • @vlizana updated the image to show image I am trying to make a boundary from – Evan Kim Apr 15 '19 at 21:12
  • 2
    thanks, but I still can't tell if you're going for a v4 or v8 frontier. For example in the picture you just uploaded, using a v8 neighborhood every point (1) would be part of the frontier, where as in the v4 kind there are a few who are interior points. – vlizana Apr 15 '19 at 21:17
  • In the picture I drew out a v4 frontier. I presume the method would be the same for v4 as it is for v8 too – Evan Kim Apr 15 '19 at 21:55

1 Answers1

1

This is a bit hacky, but if you convolve the zeros with the right kernel (v4 or v8) you get the outer part plus the frontier, so if you do an and kind of operation with the inner part you get only the frontier. Here's an example:

import numpy as np
from scipy.signal import convolve2d

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

# v4 example, 
kernel = np.array([
    [0,1,0],
    [1,0,1],
    [0,1,0],
])

# you have to zero pad first in order to get the edges
padded = np.pad(arr, ((1, 1), (1, 1)), 'constant', constant_values=0)

# the `astype(bool)` is for normalization
# the `and` operation in this case is the elementwise product
frontier = convolve2d(1-padded, kernel, mode='valid').astype(bool) * arr
vlizana
  • 2,962
  • 1
  • 16
  • 26