Switch to unified view

a b/CLI/MusculoskeletalAnalysisCLITools/width.py
1
"""Calculates the width of a 2d shape using rotating calipers method."""
2
3
4
def crossProduct(a,b,c):
5
    return ((b[0]-a[0])*(c[1]-a[1]) - (b[1]-a[1])*(c[0]-a[0]))
6
7
8
def convexHull(pts):
9
    """Finds the points on the convex hull.
10
11
    Prereq: The points must be sorted left to right and top to bottom
12
    """
13
14
    hull = []
15
    n = len(pts)
16
    # Add points from left to right, remove any that make a clockwise turn
17
    for p in pts:
18
        while len(hull) >= 2 and crossProduct(hull[-2], hull[-1], p) <= 0:
19
            hull.pop()
20
        hull.append(p)
21
    # Repeat from right to left to fill out the bottom of the hull
22
    for p in pts[n-2:-1:-1]:
23
        while len(hull) >= 2 and crossProduct(hull[-2], hull[-1], p) <= 0:
24
            hull.pop()
25
        hull.append(p)
26
    # Exclude the last point because it is same as the first
27
    return hull[:-1]
28
29
30
def rotatingCaliper(pts):
31
    import math
32
33
    hull = convexHull(pts)
34
    n = len(hull)
35
36
    if n <= 1:
37
        return 0
38
    if n == 2:
39
        return math.sqrt((hull[0][0]-hull[1][0])**2+(hull[1][0]-hull[1][1])**2)
40
    k = 1
41
    # Find the furthest point
42
    while crossProduct(hull[n - 1], hull[0], hull[(k + 1) % n]) > crossProduct(hull[n - 1], hull[0], hull[k]):
43
        k += 1
44
45
    res = 0
46
47
    # For each point between 0 and k find the opposite point j
48
    for i in range(k + 1):
49
        j = (i + 1) % n
50
        while crossProduct(hull[i], hull[(i + 1) % n], hull[(j + 1) % n]) > crossProduct(hull[i], hull[(i + 1) % n], hull[j]):
51
            j = (j + 1) % n
52
            res = max(res, math.sqrt((hull[i][0]-hull[j][0])**2+(hull[i][0]-hull[j][1])**2))
53
54
    return res
55
56
57
def edge(img):
58
    import numpy as np
59
    from scipy.ndimage import convolve
60
61
    filt = [[0,-1,0],
62
              [-1,4,-1],
63
              [0,-1,0]]
64
    return np.argwhere(convolve(img ,filt, mode='constant') >= 1)
65
66
67
def width(mask):
68
    """Calculates the width of a 2d shape using rotating calipers method."""
69
    n = mask.shape[2]
70
    maxw = 0
71
    for i in range(n):
72
        pts = edge(mask[:,:,i])
73
        w = rotatingCaliper(pts)
74
        maxw = max(maxw, w)
75
    return maxw