[390c2f]: / Cobiveco / @cobiveco / cobiveco.m

Download this file

268 lines (240 with data), 10.1 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
% Consistent Biventricular Coordinates
%
% Usage:
% c = cobiveco(config);
% c.computeAll;
% result = c.result;
%
% Input:
% - config: configuration struct with the following fields:
% config.inPrefix: prefix for input files, e.g. 'inputDir/namePrefix';
% a tetrahedral mesh has to be provided as VTU (inPrefix + '.vtu');
% boundary surfaces can either be providing as individual PLY files:
% inPrefix + '_base.ply', '_epi.ply', '_endo_lv.ply', '_endo_rv.ply'
% or as one VTP file (inPrefix + '.vtp') containing point data 'class' with the following values:
% 1: base, 2: epi, 3: endo_lv, 4: endo_rv
% config.outPrefix: prefix for output files, e.g. 'outputDir/' (trailing slash for directories!)
% config.exportLevel: integer defining what should be exported as files:
% 0: nothing
% 1: only the final result and the rotation matrix (default)
% 2: + intermediate PDE-solutions, all volume meshes and mmg command line outputs
% 3: + septal curves, heart axes, apicobasal streamlines and an axes-aligned version of the final result
% config.verbose: whether to print status messages (optional, default: true)
% config.mappingTol: tolerance for mapping of boundary surfaces, unit: mean edge length (optional, default: 0.5)
% config.tol: tolerance for iterative solvers (optional, default: 1e-8)
% config.maxit: maximum number of iterations for iterative solvers (optional, default: 3000)
% config.truncSeptSur: percentages of triangles to truncate from the septal surface on the anterior and posterior side, respectively;
% affects the left-right axis and the position of the apex point (optional, default: [20 10])
% config.abExtrapSmooth: smoothness for extrapolation of apicobasal coord from contour lines to mesh nodes
% (RMS deviation from the contour line values in percent), useful range: 0.1...1 (optional, default: 0.25)
% config.mmgSizingParam: a vector [hausd hmin hmax] defining sizing parameters for mmg (optional), unit: mean edge length
% hausd: maximal Hausdorff distance for approximation of boundaries (default: 0.1)
% hmin: minimal edge size (default: 0.9)
% hmax: maximal edge size (default: 1.1)
%
% Outputs:
% - c.result: VTK struct with the following point data fields:
% c.result.pointData.tv: transventricular coordinate
% c.result.pointData.tm: transmural coordinate
% c.result.pointData.ab: apicobasal coordinate
% c.result.pointData.rt: rotational coordinate
% c.result.pointData.rtSin: rotational sine coordinate
% c.result.pointData.rtCos: rotational cosine coordinate
% - c.R: rotation matrix for alignment of heart axes with x,y,z
% - c.cfg: complete configuration struct with all parameters
%
% Dependencies:
% - MMG3D by INP/Inria/U Bordeaux
% https://github.com/MmgTools/mmg
% Tested version: Release 5.4.3, 26 Feb 2020
% - gptoolbox by Alec Jacobson
% https://github.com/alecjacobson/gptoolbox
% Tested version: Commit bee2edb, 20 Aug 2020
% - vtkToolbox by Steffen Schuler
% https://github.com/KIT-IBT/vtkToolbox
% Run dependencies/install.sh to install them from source
%
% Written in 2020 by Steffen Schuler
% Institute of Biomedical Engineering, KIT
% www.ibt.kit.edu
classdef cobiveco < handle
properties (SetAccess = private)
% Configuration parameters
% (default values)
cfg = struct( ...
'inPrefix', '', ...
'outPrefix', '', ...
'exportLevel', 1, ...
'verbose', true, ...
'mappingTol', 0.5, ...
'tol', 1e-8, ...
'maxit', 3000, ...
'truncSeptSur', [20 10], ...
'abExtrapSmooth', 0.25, ...
'mmgSizingParam', [0.1 0.9 1.1], ...
'rvepi', false ...
);
% Mesh 0 (original mesh)
m0 = struct( ...
'vol', [], ...
'sur', [], ...
'surToVol', [], ...
'meanEdgLen', [], ...
'L', [], ...
'tv', [], ...
'tm', [], ...
'tmRVEpi', [], ...
'rtSin', [], ...
'rtCos', [], ...
'rt', [], ...
'ab', [], ...
'debug', [] ...
);
% Mesh 1 (isovalue discretization at boundary between LV and RV)
m1 = struct( ...
'vol', [], ...
'sur', [], ...
'surToVol', [], ...
'meanEdgLen', [], ...
'L', [], ...
'G', [], ...
'massMat', [], ...
'M', [], ...
'tm', [], ...
'tmRVEpi', [], ...
'ridgeLaplace', [], ...
'debug', [] ...
);
% Mesh 2 (isovalue discretization at ridge)
m2 = struct( ...
'vol', [], ...
'sur', [], ...
'surToVol', [], ...
'L', [], ...
'G', [], ...
'M', [], ...
'abLaplace', [], ...
'rtSin', [], ...
'rtCos', [], ...
'rt', [], ...
'debug', [] ...
);
% VTK struct with final coordinates as pointData
result
% Rotation matrix aligning heart axes with x,y,z
R
end
properties (Access = private)
septCurveAnt
septCurvePost
% used to keep track of what has already been computed
available = struct( ...
'mesh0', false, ...
'transventricular', false, ...
'mesh1', false, ...
'transmural', false, ...
'heartAxesAndApex', false, ...
'mesh2', false, ...
'rotational', false, ...
'apicobasal', false ...
);
end
methods
function o = cobiveco(config)
% c = cobiveco(config)
% overwrite default parameters, if provided by the user
fns = fieldnames(config);
for i = 1:numel(fns)
fn = fns{i};
if ~isfield(o.cfg,fn)
error('Unknown parameter ''%s'' found in config.', fn);
end
o.cfg.(fn) = config.(fn);
end
% check mandatory parameters
if ~isfield(config,'inPrefix')
error('Mandatory parameter ''inPrefix'' missing in config.');
end
if o.cfg.exportLevel > 0
if isempty(o.cfg.outPrefix)
error('Mandatory parameter ''outPrefix'' missing in config or empty.');
end
outPath = fileparts(o.cfg.outPrefix);
if ~isempty(outPath) && ~exist(outPath, 'dir')
mkdir(outPath);
end
testfile = sprintf('%stest', o.cfg.outPrefix);
[fid,errmsg] = fopen(testfile, 'w');
if fid==-1
error('Invalid parameter outPrefix. Could not create ''%s'' (%s).', testfile, errmsg);
else
fclose(fid);
delete(testfile);
end
end
% start timer
tic;
% set up paths
mpath = fileparts(mfilename('fullpath'));
addpath(['../functions']);
% if ~exist([mpath '/../dependencies/mmg/build/bin/mmg3d_O3'], 'file')
% error('Dependency ''mmg'' not found. Run dependencies/install_cobiveco.sh to install.');
% end
if ~exist('cotmatrix.m', 'file')
if ~exist(['../dependencies/gptoolbox'], 'dir')
error('Dependency ''gptoolbox'' not found. Run dependencies/install_cobiveco.sh to install.');
end
addpath(['../dependencies/gptoolbox/matrix']);
addpath(['../dependencies/gptoolbox/mesh']);
end
if ~exist('vtkRead.m', 'file')
if ~exist(['../dependencies/vtkToolbox'], 'dir')
error('Dependency ''vtkToolbox'' not found. Run dependencies/install_cobiveco.sh to install.');
end
addpath(['../dependencies/vtkToolbox/MATLAB']);
end
end
function computeAll(o)
% Compute all coordinates
t = toc;
o.prepareMesh0;
o.computeTransventricular;
o.prepareMesh1;
if o.cfg.rvepi
o.computeTransmuralRVEpi;
else
o.computeTransmural;
end
o.computeHeartAxesAndApex;
o.prepareMesh2;
o.computeRotational;
o.computeApicobasal;
o.exportResult;
o.printStatus('Total elapsed time:');
o.printStatus(sprintf('%.1f seconds\n', toc-t), true);
end
% declare methods defined in separate files
prepareMesh0(o)
computeTransventricular(o)
prepareMesh1(o)
computeTransmural(o)
computeTransmrualRVEpi(o)
computeHeartAxesAndApex(o)
prepareMesh2(o)
computeRotational(o)
computeApicobasal(o)
exportResult(o)
end
methods (Access = private)
function printStatus(o, msg, noPadding)
if o.cfg.verbose
if nargin > 2 && noPadding
fprintf('%s', msg);
else
pad = repmat(' ', 1, 41-numel(msg));
fprintf('%s%s', msg, pad);
end
end
end
end
end