0

I could not find a fast and reliable concave hull and implemented my own (though a bit heterodox). It is fast and does the job (there is even an adjustment parameter) but it is a bit off. I got confuse with r,c vs x,y a couple of time maybe it has to do with that but I couldn't see it and any help is appreciated.

import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
from skimage.measure import label, find_contours
import matplotlib.cm as cm

# points = datapoints
def concaveHull(points,pixel_adjustment=3):
    mins = np.min(points, axis=0)
    maxes = np.max(points, axis=0) - mins

    #so if we make each point 10x10=100 pixels it should mostly fill the whole dummy image
    points = points - mins
    shape= np.array(10*maxes *np.sqrt(len(points))/ maxes.min(), dtype='int')+50
    factor= shape.max() /points.max()
    points_aligned = factor* (points - np.min(points,axis=0))
    points_aligned = points_aligned.astype('int')

    # plt.scatter(points_aligned[:,0],points_aligned[:,1])
    # plt.show()
    nmb_contours = 100
    threshold = 3
    hit=0
    while ((nmb_contours > 1 ) or (hit<2)):
        dummy_image = np.zeros(shape)
        for i in points_aligned:
            dummy_image[(i[0] - threshold):(i[0] + threshold), (i[1] - threshold):(i[1] + threshold)] = 1

        dummy_image_larger = np.zeros(shape + 2)
        dummy_image_larger[1:-1, 1:-1] = dummy_image
        img_fill_holes = ndimage.morphology.binary_fill_holes(dummy_image_larger)
        label_image = label(img_fill_holes)
        contours = find_contours(label_image, 0.5)
        nmb_contours = len(contours)
        if len(contours) == 1:
            contour = contours[0]
            threshold += pixel_adjustment
            if hit<1: print(f'threshold set to={threshold}')
            hit += 1

        else:
            threshold += 1
            continue

    final_contour =  mins+contour/factor
    return final_contour

datapoints =  np.random.rand(1000,2)
label_points = np.zeros((1000))

label_points[:] = 3
label_points[datapoints[:, 1] < 0.6]=2
label_points[datapoints[:, 1] < 0.3]=1

plt.scatter(datapoints[:,0],datapoints[:,1], c=label_points)
for subset_label in range(1,4):
    subset = datapoints[label_points == subset_label]
    outer_line=concaveHull(subset)
    plt.fill(outer_line[:,0], outer_line[:,1], facecolor='none', edgecolor=cm.Set1(subset_label),  lw=2)
plt.show()



label_points[:] = 3
label_points[datapoints[:, 1] < 0.6]=2
label_points[np.logical_and(datapoints[:, 1] < 0.9, datapoints[:, 0] < 0.15)]=1
label_points[np.logical_and(datapoints[:, 1] < 0.3, datapoints[:, 0] < 0.5)]=1
label_points[np.logical_and(datapoints[:, 1] < 0.3, datapoints[:, 0] > 0.7)]=4


fig, ax = plt.subplots()
ax.scatter(datapoints[:,0],datapoints[:,1], c=label_points)
for subset_label in range(1,5):
    subset = datapoints[label_points == subset_label]
    outer_line=concaveHull(subset)
    plt.fill(outer_line[:,0], outer_line[:,1], facecolor='none',edgecolor=cm.Set1(subset_label), lw=2)
plt.show()

Output1

Output2

Uğur Dinç
  • 303
  • 3
  • 16

0 Answers0