|
a |
|
b/functions/ac_seg.m |
|
|
1 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
|
|
2 |
% I : input image |
|
|
3 |
% init_mask: initial mask |
|
|
4 |
% max_its : maximum iterations |
|
|
5 |
% lengthEweight : weight of the length energy |
|
|
6 |
% shapeEweight : weight of the shape energy |
|
|
7 |
% display : display |
|
|
8 |
|
|
|
9 |
% This code was edited for LV segmentation in MRI from the original code by Shawn Lankton |
|
|
10 |
|
|
|
11 |
function [seg,phi] = ac_seg(I,init_mask,max_its,lengthEweight,shapeEweight,display) |
|
|
12 |
|
|
|
13 |
%-- default behavior is to display intermediate outputs |
|
|
14 |
if(~exist('display','var')) |
|
|
15 |
display = true; |
|
|
16 |
end |
|
|
17 |
|
|
|
18 |
%-- ensures image is 2D double matrix |
|
|
19 |
I = im2graydouble(I); |
|
|
20 |
|
|
|
21 |
%-- Create a signed distance map (SDF) from mask |
|
|
22 |
phi = mask2phi(init_mask); |
|
|
23 |
|
|
|
24 |
phi_prior=phi; |
|
|
25 |
|
|
|
26 |
%--main loop |
|
|
27 |
for its = 1:max_its % Note: no automatic convergence test |
|
|
28 |
|
|
|
29 |
idx = find(phi <= 1.2 & phi >= -1.2); %get the curve's narrow band |
|
|
30 |
|
|
|
31 |
%-- find interior and exterior mean |
|
|
32 |
upts = find(phi<=0); % interior points |
|
|
33 |
vpts = find(phi>0); % exterior points |
|
|
34 |
u = sum(I(upts))/(length(upts)+eps); % interior mean |
|
|
35 |
v = sum(I(vpts))/(length(vpts)+eps); % exterior mean |
|
|
36 |
|
|
|
37 |
|
|
|
38 |
F = (I(idx)-u).^2-(I(idx)-v).^2; % region-based force from image information |
|
|
39 |
curvature = get_curvature(phi,idx); % force from curvature penalty |
|
|
40 |
|
|
|
41 |
% derivite of energy function for prior shape |
|
|
42 |
dEdl= -(phi(idx)-phi_prior(idx)); |
|
|
43 |
|
|
|
44 |
% note that d phi/dt= - d E/d phi |
|
|
45 |
dphidt = F./max(abs(F)) + lengthEweight*curvature+shapeEweight*dEdl; % gradient descent to minimize energy |
|
|
46 |
|
|
|
47 |
%-- maintain the CFL condition |
|
|
48 |
dt = .45/(max(dphidt)+eps); |
|
|
49 |
|
|
|
50 |
%-- evolve the curve |
|
|
51 |
phi(idx) = phi(idx) + dt*dphidt; |
|
|
52 |
|
|
|
53 |
indicator=0; |
|
|
54 |
|
|
|
55 |
%-- Keep SDF smooth |
|
|
56 |
phi = sussman(phi, .5); |
|
|
57 |
|
|
|
58 |
%-- intermediate output |
|
|
59 |
if((display>0)&&(mod(its,20) == 0)) |
|
|
60 |
showCurveAndPhi(I,phi,its); |
|
|
61 |
end |
|
|
62 |
if indicator==1 |
|
|
63 |
disp(['stopped at:',num2str(its)]) |
|
|
64 |
break |
|
|
65 |
end |
|
|
66 |
end |
|
|
67 |
|
|
|
68 |
%-- final output |
|
|
69 |
if(display) |
|
|
70 |
showCurveAndPhi(I,phi,its); |
|
|
71 |
end |
|
|
72 |
|
|
|
73 |
%-- make mask from SDF |
|
|
74 |
seg = phi<=0; %-- Get mask from levelset |
|
|
75 |
|
|
|
76 |
|
|
|
77 |
%--------------------------------------------------------------------- |
|
|
78 |
%--------------------------------------------------------------------- |
|
|
79 |
%-- AUXILIARY FUNCTIONS ---------------------------------------------- |
|
|
80 |
%--------------------------------------------------------------------- |
|
|
81 |
%--------------------------------------------------------------------- |
|
|
82 |
|
|
|
83 |
|
|
|
84 |
%-- Displays the image with curve superimposed |
|
|
85 |
function showCurveAndPhi(I, phi, i) |
|
|
86 |
max_range=min(200,max(I(:))); |
|
|
87 |
imshow(I,'initialmagnification',200,'displayrange',[0 max_range]); hold on; |
|
|
88 |
contour(phi, [0 0], 'g','LineWidth',4); |
|
|
89 |
contour(phi, [0 0], 'k','LineWidth',2); |
|
|
90 |
hold off; title([num2str(i) ' Iterations']); drawnow; |
|
|
91 |
|
|
|
92 |
%-- converts a mask to a SDF |
|
|
93 |
function phi = mask2phi(init_a) |
|
|
94 |
phi=bwdist(init_a)-bwdist(1-init_a)+im2double(init_a)-.5; |
|
|
95 |
|
|
|
96 |
%-- compute curvature along SDF |
|
|
97 |
function curvature = get_curvature(phi,idx) |
|
|
98 |
[dimy, dimx] = size(phi); |
|
|
99 |
[y x] = ind2sub([dimy,dimx],idx); % get subscripts |
|
|
100 |
|
|
|
101 |
%-- get subscripts of neighbors |
|
|
102 |
ym1 = y-1; xm1 = x-1; yp1 = y+1; xp1 = x+1; |
|
|
103 |
|
|
|
104 |
%-- bounds checking |
|
|
105 |
ym1(ym1<1) = 1; xm1(xm1<1) = 1; |
|
|
106 |
yp1(yp1>dimy)=dimy; xp1(xp1>dimx) = dimx; |
|
|
107 |
|
|
|
108 |
%-- get indexes for 8 neighbors |
|
|
109 |
idup = sub2ind(size(phi),yp1,x); |
|
|
110 |
iddn = sub2ind(size(phi),ym1,x); |
|
|
111 |
idlt = sub2ind(size(phi),y,xm1); |
|
|
112 |
idrt = sub2ind(size(phi),y,xp1); |
|
|
113 |
idul = sub2ind(size(phi),yp1,xm1); |
|
|
114 |
idur = sub2ind(size(phi),yp1,xp1); |
|
|
115 |
iddl = sub2ind(size(phi),ym1,xm1); |
|
|
116 |
iddr = sub2ind(size(phi),ym1,xp1); |
|
|
117 |
|
|
|
118 |
%-- get central derivatives of SDF at x,y |
|
|
119 |
phi_x = -phi(idlt)+phi(idrt); |
|
|
120 |
phi_y = -phi(iddn)+phi(idup); |
|
|
121 |
phi_xx = phi(idlt)-2*phi(idx)+phi(idrt); |
|
|
122 |
phi_yy = phi(iddn)-2*phi(idx)+phi(idup); |
|
|
123 |
phi_xy = -0.25*phi(iddl)-0.25*phi(idur)... |
|
|
124 |
+0.25*phi(iddr)+0.25*phi(idul); |
|
|
125 |
phi_x2 = phi_x.^2; |
|
|
126 |
phi_y2 = phi_y.^2; |
|
|
127 |
|
|
|
128 |
%-- compute curvature (Kappa) |
|
|
129 |
curvature = ((phi_x2.*phi_yy + phi_y2.*phi_xx - 2*phi_x.*phi_y.*phi_xy)./... |
|
|
130 |
(phi_x2 + phi_y2 +eps).^(3/2)).*(phi_x2 + phi_y2).^(1/2); |
|
|
131 |
|
|
|
132 |
%-- Converts image to one channel (grayscale) double |
|
|
133 |
function img = im2graydouble(img) |
|
|
134 |
[dimy, dimx, c] = size(img); |
|
|
135 |
if(isfloat(img)) % image is a double |
|
|
136 |
if(c==3) |
|
|
137 |
img = rgb2gray(uint8(img)); |
|
|
138 |
end |
|
|
139 |
else % image is a int |
|
|
140 |
if(c==3) |
|
|
141 |
img = rgb2gray(img); |
|
|
142 |
end |
|
|
143 |
img = double(img); |
|
|
144 |
end |
|
|
145 |
|
|
|
146 |
%-- level set re-initialization by the sussman method |
|
|
147 |
function D = sussman(D, dt) |
|
|
148 |
% forward/backward differences |
|
|
149 |
a = D - shiftR(D); % backward |
|
|
150 |
b = shiftL(D) - D; % forward |
|
|
151 |
c = D - shiftD(D); % backward |
|
|
152 |
d = shiftU(D) - D; % forward |
|
|
153 |
|
|
|
154 |
a_p = a; a_n = a; % a+ and a- |
|
|
155 |
b_p = b; b_n = b; |
|
|
156 |
c_p = c; c_n = c; |
|
|
157 |
d_p = d; d_n = d; |
|
|
158 |
|
|
|
159 |
a_p(a < 0) = 0; |
|
|
160 |
a_n(a > 0) = 0; |
|
|
161 |
b_p(b < 0) = 0; |
|
|
162 |
b_n(b > 0) = 0; |
|
|
163 |
c_p(c < 0) = 0; |
|
|
164 |
c_n(c > 0) = 0; |
|
|
165 |
d_p(d < 0) = 0; |
|
|
166 |
d_n(d > 0) = 0; |
|
|
167 |
|
|
|
168 |
dD = zeros(size(D)); |
|
|
169 |
D_neg_ind = find(D < 0); |
|
|
170 |
D_pos_ind = find(D > 0); |
|
|
171 |
dD(D_pos_ind) = sqrt(max(a_p(D_pos_ind).^2, b_n(D_pos_ind).^2) ... |
|
|
172 |
+ max(c_p(D_pos_ind).^2, d_n(D_pos_ind).^2)) - 1; |
|
|
173 |
dD(D_neg_ind) = sqrt(max(a_n(D_neg_ind).^2, b_p(D_neg_ind).^2) ... |
|
|
174 |
+ max(c_n(D_neg_ind).^2, d_p(D_neg_ind).^2)) - 1; |
|
|
175 |
|
|
|
176 |
D = D - dt .* sussman_sign(D) .* dD; |
|
|
177 |
|
|
|
178 |
%-- whole matrix derivatives |
|
|
179 |
function shift = shiftD(M) |
|
|
180 |
shift = shiftR(M')'; |
|
|
181 |
|
|
|
182 |
function shift = shiftL(M) |
|
|
183 |
shift = [ M(:,2:size(M,2)) M(:,size(M,2)) ]; |
|
|
184 |
|
|
|
185 |
function shift = shiftR(M) |
|
|
186 |
shift = [ M(:,1) M(:,1:size(M,2)-1) ]; |
|
|
187 |
|
|
|
188 |
function shift = shiftU(M) |
|
|
189 |
shift = shiftL(M')'; |
|
|
190 |
|
|
|
191 |
function S = sussman_sign(D) |
|
|
192 |
S = D ./ sqrt(D.^2 + 1); |
|
|
193 |
|
|
|
194 |
|
|
|
195 |
|
|
|
196 |
|
|
|
197 |
|
|
|
198 |
|