|
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 |