forked from pyushkevich/cmrep
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFit.cxx
353 lines (304 loc) · 9.6 KB
/
Fit.cxx
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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
#include "ScriptInterface.h"
#include "BasisFunctions2D.h"
#include "MedialAtom.h"
#include "CartesianMedialModel.h"
#include "OptimizationTerms.h"
#include "CoefficientMapping.h"
#include "MedialAtomGrid.h"
#include "PrincipalComponents.h"
#include "System.h"
#include "TestSolver.h"
#include "ITKImageWrapper.h"
#include <itksys/SystemTools.hxx>
#include "itk_to_nifti_xform.h"
// ITK includes
#include <itkOrientedRASImage.h>
#include <itkVTKImageExport.h>
// VTK includes
#include <vtkImageData.h>
#include <vtkImageImport.h>
#include <vtkMarchingCubes.h>
#include <vtkPolyDataWriter.h>
#include <vtkTransformPolyDataFilter.h>
#include <vtkTransform.h>
#include <vtkMatrix4x4.h>
int usage()
{
cout << "cmrep_fit: fit cm-rep to image" << endl;
cout << "usage: " << endl;
cout << " cmrep_fit [options] param.txt template.cmrep target.img output_dir" << endl;
cout << "options: " << endl;
cout << " -s N : Only run fitting stage N (first stage is stage 0)" << endl;
cout << " -g FN : Supply 'gray' image filename for direct-to-image fitting" << endl;
cout << " -t : Test gradient computation for each of the terms (debug)" << endl;
cout << " -d : Dump out mesh with gradient vectors at each iteration (debug)" << endl;
cout << "parameter file specification: " << endl;
cout << " http://alliance.seas.upenn.edu/~pauly2/wiki/index.php?n=Main.CM-RepFittingToolCmrFit" << endl;
cout << endl;
return -1;
}
// Generate contour and save to file
void GenerateContour(FloatImage *image, string file)
{
// Get the contour
vtkSmartPointer<vtkPolyData> mesh = GenerateContour(image);
// Create a writer
vtkSmartPointer<vtkPolyDataWriter> m_Writer = vtkSmartPointer<vtkPolyDataWriter>::New();
m_Writer->SetFileName(file.c_str());
m_Writer->SetInputData(mesh);
m_Writer->Update();
}
// Data fitting modes
enum FitMode {ALIGN_MOMENTS, FIT_TO_BINARY, NONE};
struct SubdivisionRefinement
{
size_t sub_atoms;
size_t sub_control;
};
struct Refinement
{
SubdivisionRefinement sub_ref;
};
struct StageInfo
{
string name;
FitMode mode;
double blur;
size_t max_iter;
Registry param;
Refinement ref;
};
int main(int argc, char *argv[])
{
// Report errors
SetupSignalHandlers();
// Check the number of input parameters
if(argc < 5)
return usage();
// Check optional parameters
bool flag_one_stage = false;
size_t selected_stage = 0;
// Optimization flags
OptimizationFlags flags_opt;
size_t argoptmax = argc-4;
string fn_gray = "";
for(size_t i = 1; i < argoptmax; i++)
{
string arg = argv[i];
if(arg == "-s" && i < argoptmax-1)
{
flag_one_stage = true;
selected_stage = atoi(argv[i+1]);
i++;
}
else if(arg == "-g" && i < argoptmax-1)
{
fn_gray = argv[i+1];
i++;
}
else if(arg == "-t")
{
flags_opt.flagTestGradient = true;
}
else if(arg == "-d")
{
flags_opt.flagDumpGradientMesh = true;
}
else
{
cerr << "Unknown option " << arg << endl;
return -1;
}
}
// Read the registry
Registry r;
try
{
r.ReadFromFile(argv[argc-4]);
}
catch(...)
{
cerr << "Unable to read parameters from " << argv[argc-4] << endl;
cerr << "Call without parameters for proper usage" << endl;
return -1;
}
// Registry-enum thingy
RegistryEnumMap<FitMode> xModeEnumMap;
xModeEnumMap.AddPair(ALIGN_MOMENTS, "AlignMoments");
xModeEnumMap.AddPair(FIT_TO_BINARY, "FitToBinary");
xModeEnumMap.AddPair(NONE, "NONE");
try
{
// Read the number of stages
size_t n_stages = r["Stage.ArraySize"][0];
if(n_stages == 0)
throw string("No optimization stages found in parameter file");
// Create the vector of stages
vector<StageInfo> stages;
// Read the default parameters
Registry pdef = r.Folder("DefaultParameters");
// For each stage read the interesting information
for(size_t i = 0; i < n_stages; i++)
{
stages.push_back(StageInfo());
// Read the stage folder
string key = r.Key("Stage.Element[%d]",i);
if(!r.IsFolder(key))
throw string("Missing folder ") + key;
Registry rs = r.Folder(key);
// Get the name
ostringstream oss;
oss << "stage " << i;
stages[i].name = rs["Name"][oss.str()];
// Get the mode
stages[i].mode = rs["Mode"].GetEnum(xModeEnumMap, NONE);
if(stages[i].mode == NONE)
throw string("Missing 'Mode' for stage ") + stages[i].name;
// Get the number of iterations
stages[i].max_iter = rs["MaxIterations"][1000];
stages[i].blur = rs["Blur"][1.2];
// Get the refinement info (depends on model type)
stages[i].ref.sub_ref.sub_atoms = rs["Refinement.Subdivision.Atoms"][0];
stages[i].ref.sub_ref.sub_control = rs["Refinement.Subdivision.Controls"][0];
// Read the overriding parameters
stages[i].param = pdef;
Registry rp = rs.Folder("Parameters");
// Get the list of all overriding parameters
Registry::StringListType klist;
rp.CollectKeys(klist);
for(Registry::StringListType::iterator it = klist.begin(); it != klist.end(); it++)
{
stages[i].param[*it] << rp.Entry(*it).GetInternalString();
}
}
// Read the input template
string fn_temp = argv[argc-3];
try
{
MedialPDE cmr_template(fn_temp.c_str());
}
catch(...)
{
throw string("Unable to read template file ") + fn_temp;
}
// Read the input image
string fn_image = argv[argc-2];
BinaryImage img;
try
{
img.LoadFromFile(fn_image.c_str());
}
catch(...)
{
throw string("Unable to read image ") + fn_image;
}
// Float image for future use
FloatImage imgfloat;
// Grayscale image (if supplied)
FloatImage imggray;
if(fn_gray.length())
{
imggray.LoadFromFile(fn_gray.c_str());
}
// Before starting the loop, we need a filename for the current model
string fn_current = fn_temp;
// Get the output directory
string dir_out = argv[argc-1];
// Create all the output directories
itksys::SystemTools::MakeDirectory((dir_out + "/cmrep").c_str());
itksys::SystemTools::MakeDirectory((dir_out + "/mesh").c_str());
itksys::SystemTools::MakeDirectory((dir_out + "/image").c_str());
// Process each of the stages in turn
for(size_t i = 0; i < n_stages; i++)
{
// If we are processing only one stage, we skip the rest
if(flag_one_stage)
{
if(selected_stage != i) continue;
if(i > 0)
{
// Load the starting point for this stage
fn_current = dir_out + "/cmrep/" + stages[i-1].name + ".cmrep";
}
}
cout << "### STAGE " << i << " ###" << endl;
// Blur the image at appropriate blur level
if(i == 0 || flag_one_stage || stages[i].blur != stages[i-1].blur)
{
if(stages[i].blur == 0.0)
{
cout << "Treating input image as smooth speed image (bkg -1, fore: +1)" << endl;
imgfloat.LoadFromFile(fn_image.c_str());
imgfloat.SetOutsideValue(-1.0);
}
else
{
cout << "Treating input image as binary (background 0, foreground 1)" << endl;
cout << "Gaussian smoothing with sigma = " << stages[i].blur << endl;
try
{
imgfloat.SetToBlurredBinary(&img, stages[i].blur);
imgfloat.SetOutsideValue(-1.0);
}
catch(exception &exc)
{
cerr << exc.what() << endl;
return -1;
}
cout << "Done blurring" << endl;
}
}
// Subdivide the model if necessary
if(stages[i].ref.sub_ref.sub_atoms > 0 || stages[i].ref.sub_ref.sub_control > 0)
{
// Read the current model
SubdivisionMPDE smod(fn_current.c_str());
smod.SubdivideMeshes(stages[i].ref.sub_ref.sub_control, stages[i].ref.sub_ref.sub_atoms);
// Create a filename for the output model
string fn_init = dir_out + "/cmrep/" + stages[i].name + "_init.cmrep";
smod.SaveToParameterFile(fn_init.c_str());
fn_current = fn_init;
}
// Create a filename for the output model
string fn_result = dir_out + "/cmrep/" + stages[i].name + ".cmrep";
// Load the model
MedialPDE mrep(fn_current.c_str());
// Use the appropriate optimization approach depending on mode
if(stages[i].mode == ALIGN_MOMENTS)
{
mrep.MatchImageByMoments(&imgfloat, 10);
}
else if(stages[i].mode == FIT_TO_BINARY)
{
mrep.RunOptimization(&imgfloat, stages[i].max_iter, stages[i].param, flags_opt, &imggray);
}
else throw string("Unknown optimization mode");
// Save the model
mrep.SaveToParameterFile(fn_result.c_str());
fn_current = fn_result;
// Write mesh file(s)
string fn_mesh_med = dir_out + "/mesh/" + stages[i].name + ".med.vtk";
string fn_mesh_bnd = dir_out + "/mesh/" + stages[i].name + ".bnd.vtk";
// This is so that the float image is sampled
mrep.SaveVTKMesh(fn_mesh_med.c_str(), fn_mesh_bnd.c_str());
// Write the target image
string fn_target_mesh = dir_out + "/mesh/" + stages[i].name + "_target.vtk";
GenerateContour(&imgfloat, fn_target_mesh);
}
}
catch(MedialModelException &exc)
{
cerr << "MedialModelException: " << exc.what() << endl;
return -1;
}
catch(string &err)
{
cerr << "Exception: " << err << endl;
return -1;
}
catch(...)
{
cerr << "Unknown exception" << endl;
return -1;
}
}