Diff of /cmac/aux_strain.py [000000] .. [4cda31]

Switch to unified view

a b/cmac/aux_strain.py
1
2
from scipy.ndimage.measurements import center_of_mass
3
from skimage.measure import find_contours
4
import numpy as np
5
6
7
def label_to_tags(label_3d, label_id=2):
8
    Ck = []
9
    for z in range(label_3d.shape[-1]):
10
        try:
11
            ck = tag_contour((label_3d[:,:,z].T==label_id)*1. )
12
            ck = np.concatenate((ck,np.zeros((24,3,1))+z),-1)
13
            Ck += [ck]
14
        except:
15
            continue
16
    return np.stack(Ck)
17
18
def label_to_tags2(contours_dict, K=24, R=5, centroid=None):
19
    Ck = []
20
    for z in contours_dict.keys():
21
        ck = tag_contour2(contours_dict[z], K=K, R=R, centroid=centroid)
22
        ck = np.concatenate((ck,np.zeros((K,R,1))+z),-1)
23
        Ck += [ck]
24
    return np.stack(Ck)
25
26
def tag_contour(myocardial_mask_2d, K=24, R=3):
27
    """Generate polar grid using myocardial mass mask.
28
    """    
29
    Cx, Cy = center_of_mass(myocardial_mask_2d)
30
31
    contours_endo, contours_epi  = find_contours(myocardial_mask_2d,0.8)
32
33
    # cartesian coordinates (centered) of endo/epi contours
34
    x_endo, y_endo = contours_endo[:,0], contours_endo[:,1]
35
    x_epi, y_epi   = contours_epi[:,0], contours_epi[:,1]
36
37
    # polar coordinates of endo/epi contours
38
    phi_endo = np.rad2deg(np.arctan2(y_endo-Cy, x_endo-Cx))+180
39
    phi_epi  = np.rad2deg(np.arctan2(y_epi-Cy, x_epi-Cx))+180
40
    rho_endo = ((x_endo-Cx)**2 + (y_endo-Cy)**2)**0.5
41
    rho_epi  = ((x_epi-Cx)**2 + (y_epi-Cy)**2)**0.5
42
43
    IDX = [np.array_split(np.argsort(phi_endo),K)[k][0] for k in range(K)]
44
45
    ck = []
46
    for i in IDX:
47
        d = (x_endo[i]-x_epi)**2+(y_endo[i]-y_epi)**2
48
        dmin_i = np.argmin(d)
49
50
        rhos = np.linspace(rho_endo[i],rho_epi[dmin_i],R)
51
        xk   = [rhos[k] * np.cos(np.deg2rad(phi_endo[i]-180)) for k in range(R)] + Cx
52
        yk   = [rhos[k] * np.sin(np.deg2rad(phi_endo[i]-180)) for k in range(R)] + Cy
53
        ck  += [np.stack((yk,xk),-1)]
54
55
    ck = np.stack(ck,0)
56
    
57
    return ck
58
59
def tag_contour2(contours, phi_regionIDs, K=24, R=3, centroid=None):
60
    """Generate polar grid using endocardial and epicardial contours.
61
    """
62
    get_cx_cy = lambda contour : (contour.max(axis=0) + contour.min(axis=0)) / 2
63
    
64
    contours_endo, contours_epi  = contours['endocardium'], contours['epicardium']
65
    
66
    if centroid is None:
67
        Cx, Cy = get_cx_cy(contours_endo)
68
    else: 
69
        Cx, Cy = centroid
70
    
71
    # cartesian coordinates (centered) of endo/epi contours
72
    x_endo, y_endo = contours_endo[:,0], contours_endo[:,1]
73
    x_epi, y_epi   = contours_epi[:,0], contours_epi[:,1]
74
    
75
    # polar coordinates of endo/epi contours
76
    phi_endo = np.rad2deg(np.arctan2(y_endo-Cy, x_endo-Cx))+180
77
    phi_epi  = np.rad2deg(np.arctan2(y_epi-Cy, x_epi-Cx))+180
78
    rho_endo = ((x_endo-Cx)**2 + (y_endo-Cy)**2)**0.5
79
    rho_epi  = ((x_epi-Cx)**2 + (y_epi-Cy)**2)**0.5
80
81
    IDX = [np.array_split(np.argsort(phi_endo),K)[k][0] for k in range(K)]
82
    
83
    
84
    ck = []
85
    ck_regionIDs = []
86
    for i in IDX:
87
        
88
        d = (x_endo[i]-x_epi)**2+(y_endo[i]-y_epi)**2
89
        dmin_i = np.argmin(d)
90
91
        rhos = np.linspace(rho_endo[i],rho_epi[dmin_i],R)
92
        xk   = [rhos[k] * np.cos(np.deg2rad(phi_endo[i]-180)) for k in range(R)] + Cx
93
        yk   = [rhos[k] * np.sin(np.deg2rad(phi_endo[i]-180)) for k in range(R)] + Cy
94
        ck  += [np.stack((xk,yk),-1)]
95
        ck_regionIDs += [[phi_regionIDs[i]]*R]
96
        
97
    ck = np.stack(ck,0)
98
    ck_regionIDs = np.stack(ck_regionIDs,0)
99
    return ck, ck_regionIDs
100
101
102
103
def calculate_circumferential_strain(coords_batch, wall_index, use_linear_strain=False):
104
    # batch x time x 2 x 24
105
    midwall_points = coords_batch[:,:,:, wall_index::7]  # get point index 3 for every radial
106
    # print(midwall_points.shape)
107
108
    # we will have to calculate the strain between every points
109
110
    points_arr = np.split(midwall_points, 24, axis=3)
111
112
    # strain formula: ((l^2/L^2)-1) / 2  --> l^2 = x^2 + y^2
113
    # with x and y is the difference between x and y coords of 2 points
114
    ccs = []
115
    # the cc strain is circular, so we going through all of them and back to point 0
116
    for r in range(0,len(points_arr)):
117
        # for the last point, calculate between point_r and point_0
118
        if r+1 == len(points_arr):
119
            cc_diff = np.square(points_arr[r] - points_arr[0])
120
        else:
121
            cc_diff = np.square(points_arr[r] - points_arr[r+1])
122
123
        # do the sum: x^2 + y^2
124
        cc_sum = cc_diff[:,:,0] + cc_diff[:,:,1]
125
126
        if use_linear_strain:
127
            # use L instead of L^2
128
            cc_sum = np.sqrt(cc_sum)
129
130
        cc_sum_ed = cc_sum[:,0]
131
132
        # do the strain calculation
133
        partial_cc = cc_sum/cc_sum_ed[:, np.newaxis]
134
        if use_linear_strain:
135
            partial_cc = (partial_cc - 1)
136
        else:
137
            partial_cc = (partial_cc - 1) / 2
138
139
        # put the partial_cc in every time frame back together
140
        ccs.append(partial_cc)
141
    # stack the partial_cc for every links together
142
    stacked_ccs = np.stack(ccs, axis=2)
143
144
    # calculate the mean cc for every time frame
145
    mid_cc = np.mean(stacked_ccs, axis=2)
146
    # print(mid_cc.shape)
147
    # print(mid_cc[0][0:5])
148
    return mid_cc
149
150
def calculate_radial_strain(coords_batch, use_linear_strain=False):
151
    """
152
        Calculate rr strain for a batch of image sequences
153
        flattened_coords => [batch_size, nr_frames, 2, 168]
154
    """
155
    # point 0 is epi, point 6 is endo, do this for all the 'radials'
156
    endo_batch = coords_batch[:, :, :, ::7]
157
    epi_batch =  coords_batch[:, :, :, 6::7]
158
159
    # batch x time x 2 x 24 radials
160
    diff = (epi_batch - endo_batch) ** 2
161
    # print('diff', diff.shape)
162
163
    # batch x time x 24 sqrdiff
164
    summ = diff[:,:,0,:] + diff[:,:,1,:] # x^2 + y^2
165
    # print('summ', summ.shape)
166
167
    if use_linear_strain:
168
        # use L instead of L^2
169
        summ = np.sqrt(summ)
170
171
    # grab the frame 0 (ED) for all data, and 24 RR strains
172
    summ_ed = summ[:,0,:]
173
174
    # division through a certain column, without np.split
175
    # batch x time x 24 rr strains
176
    divv = summ/summ_ed[:,np.newaxis] # this is the trick, add new axis
177
178
    if use_linear_strain:
179
        rr_strains = divv - 1
180
    else:
181
        rr_strains = (divv - 1) / 2
182
183
    rr_strains = np.mean(rr_strains, axis=2)
184
185
    # batch x time x strain
186
    rr_strains = np.expand_dims(rr_strains, axis=2)
187
    return rr_strains