TPCCLIB
Loading...
Searching...
No Matches
simimyoc.c
Go to the documentation of this file.
1
7/*****************************************************************************/
8#include "tpcclibConfig.h"
9/*****************************************************************************/
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <math.h>
14#include <time.h>
15/*****************************************************************************/
16#include "libtpcmisc.h"
17#include "libtpcmodel.h"
18#include "libtpccurveio.h"
19#include "libtpcimgio.h"
20#include "libtpcimgp.h"
21#include "libtpcmodext.h"
22#include "libtpcidi.h"
23/*****************************************************************************/
24#define SBFNR 12
25/*****************************************************************************/
26
27/*****************************************************************************/
28static char *info[] = {
29 "Simulate dynamic PET image of heart. Approach is very simplistic and",
30 "only applicable to software testing and basic simulations:",
31 "only one image plane is produced by default, containing a ring representing",
32 "myocardial muscle surrounding LV cavity.",
33 " ",
34 "Usage: @P [options] tacfile imgfile",
35 " ",
36 "TAC data file must contain the time-activity curves of myocardial muscle,",
37 "blood in LV cavity, and optionally background surrounding the heart.",
38 "Sample times in TAC file are used as time frames of the simulated image.",
39 " ",
40 "Options:",
41 " -dim=<Image x and y dimension in pixels>",
42 " Set image dimension; by default 128.",
43 " -3D",
44 " Simulation in 3D (note that computation may take time).",
45 " -pxlsize=<Voxel size (mm)>",
46 " Set image voxel size; by default 1 mm.",
47 " -fwhm=<FWHM (mm)>",
48 " Set image resolution; by default 8 mm.",
49 " -thickness=<Myocardial wall thickness (mm)>",
50 " Set (relaxed) myocardial wall thickness; by default 10 mm.",
51 " -diameter=<Max LV cavity diameter (mm)>",
52 " Set maximal LV cavity diameter; by default 52 mm.",
53 " -diamin=<Min LV cavity diameter (fraction)>",
54 " Set minimum LV cavity diameter as fraction of max diameter;",
55 " by default 0.62.",
56 " -bif=<Image filename>",
57 " Save heart beat phases separately as image planes;",
58 " not applicable with option -3D.",
59// " -hbr=<Heart beat rate (beats/min)",
60// " Set heart beat rate; by default 70 per min",
61 " -stdoptions", // List standard options like --help, -v, etc
62 " ",
63 "Example: @P tacs.dat myoc.v",
64 " ",
65 "See also: simiart, fvar4tac, imgfiltg, sim_3tcm, b2t_h2o, imgadd, simcirc",
66 " ",
67 "Keywords: simulation, image, software testing, myocardium, LV",
68 0};
69/*****************************************************************************/
70
71/*****************************************************************************/
72/* Turn on the globbing of the command line, since it is disabled by default in
73 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
74 In Unix&Linux wildcard command line processing is enabled by default. */
75/*
76#undef _CRT_glob
77#define _CRT_glob -1
78*/
79int _dowildcard = -1;
80/*****************************************************************************/
81
82/*****************************************************************************/
86int main(int argc, char **argv)
87{
88 int ai, help=0, version=0, verbose=1;
89 char tacfile[FILENAME_MAX], imgfile[FILENAME_MAX], biffile[FILENAME_MAX];
90 int dim=128, dimz=0;
91 double pxlsize=1.0, fwhm=8.0;
92 double thickness=10.0, diameter=52.0, diamin=0.62;
93 int scanner_type=SCANNER_HRPLUS;
94 float zoom=1.41421356;
95
96 char *cptr;
97 int ret;
98
99
100 /*
101 * Get arguments
102 */
103 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
104 tacfile[0]=imgfile[0]=biffile[0]=(char)0;
105
106 /* Options */
107 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
108 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
109 cptr=argv[ai]+1;
110 if(strncasecmp(cptr, "DIM=", 4)==0) {
111 dim=atoi(cptr+4); if(dim>4) continue;
112 } else if(strcasecmp(cptr, "3D")==0) {
113 dimz=1000; continue;
114 } else if(strncasecmp(cptr, "PXLSIZE=", 8)==0) {
115 if(!atof_with_check(cptr+8, &pxlsize) && pxlsize>0.0) continue;
116 } else if(strncasecmp(cptr, "DIAMETER=", 9)==0) {
117 if(!atof_with_check(cptr+9, &diameter) && diameter>0.0) continue;
118 } else if(strncasecmp(cptr, "DIAMIN=", 7)==0) {
119 if(!atof_with_check(cptr+7, &diamin) && diamin>0.0 && diamin<=1.0) continue;
120 } else if(strncasecmp(cptr, "FWHM=", 5)==0) {
121 if(!atof_with_check(cptr+5, &fwhm) && fwhm>=0.0) continue;
122 } else if(strncasecmp(cptr, "THICKNESS=", 10)==0) {
123 if(!atof_with_check(cptr+10, &thickness) && thickness>0.0) continue;
124 } else if(strncasecmp(cptr, "BIF=", 4)==0) {
125 strlcpy(biffile, cptr+4, FILENAME_MAX); if(strlen(biffile)>0) continue;
126 }
127 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
128 return(1);
129 } else break;
130
131 /* Print help or version? */
132 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
133 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
134 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
135
136 /* Process other arguments, starting from the first non-option */
137 if(ai<argc) {strlcpy(tacfile, argv[ai++], FILENAME_MAX);}
138 if(ai<argc) {strlcpy(imgfile, argv[ai++], FILENAME_MAX);}
139 if(ai<argc) {
140 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
141 return(1);
142 }
143 /* Is something missing? */
144 if(!imgfile[0]) {
145 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
146 return(1);
147 }
148 /* Set Z dimension to 1 or to dim */
149 if(dimz>0) dimz=dim; else dimz=1;
150 if(dimz>1 && biffile[0]) {
151 fprintf(stderr, "Warning: 3D beating image can not be saved; ignored.\n");
152 strcpy(biffile, "");
153 }
154
155 /* In verbose mode print arguments and options */
156 if(verbose>1) {
157 printf("tacfile := %s\n", tacfile);
158 printf("imgfile := %s\n", imgfile);
159 if(biffile[0]) printf("biffile := %s\n", biffile);
160 printf("dim := %d\n", dim);
161 printf("dimz := %d\n", dimz);
162 printf("pxlsize := %g\n", pxlsize);
163 printf("diameter := %g\n", diameter);
164 printf("diamin := %g\n", diamin);
165 printf("fwhm := %g\n", fwhm);
166// printf("hbr := %g\n", hbr);
167 printf("thickness := %g\n", thickness);
168 }
169
170
171 /*
172 * Read the TACs to be placed in image
173 */
174 DFT tac; dftInit(&tac);
175 if(verbose>1) printf("reading TACs\n");
176 if(dftRead(tacfile, &tac)) {
177 fprintf(stderr, "Error in reading '%s': %s\n", tacfile, dfterrmsg);
178 return(2);
179 }
180 if(tac.voiNr<2) {
181 fprintf(stderr, "Error: TAC file does not contain necessary TACs.\n");
182 dftEmpty(&tac); return(2);
183 }
184 if(tac.voiNr>3) {
185 fprintf(stderr, "Error: TAC file contains more than three TACs.\n");
186 dftEmpty(&tac); return(2);
187 }
188
189 /*
190 * Allocate memory for the image
191 */
192 if(verbose>1) printf("allocating memory for image data\n");
193 IMG img; imgInit(&img);
194 ret=imgAllocate(&img, dimz, dim, dim, tac.frameNr);
195 if(ret!=0) {
196 fprintf(stderr, "Error: cannot allocate memory for image data.\n");
197 dftEmpty(&tac); return(4);
198 }
199 /* Set header information */
200 if(verbose>1) printf("setting image headers\n");
202 imgSetUnit(&img, tac.unit);
204 img.zoom=zoom;
206 (void)imgSetScanner(&img, scanner_type);
208 for(int fi=0; fi<tac.frameNr; fi++) {
209 img.start[fi]=tac.x1[fi]; img.end[fi]=tac.x2[fi]; img.mid[fi]=tac.x[fi];
210 if(tac.timeunit==TUNIT_MIN) {
211 img.start[fi]*=60.; img.end[fi]*=60.; img.mid[fi]*=60.;
212 }
213 }
214 img.sizex=img.sizey=img.sizez=pxlsize;
215 for(int zi=0; zi<dimz; zi++) img.planeNumber[zi]=1+zi;
216
217
218 /* Allocate also memory for an image with heart beat phases as planes,
219 if necessary; not done if simulating 3D heart. */
220 IMG bimg; imgInit(&bimg);
221 if(biffile[0] && diamin<1.0) {
222 if(verbose>1) printf("allocating memory for heart beat phases image\n");
223 ret=imgAllocateWithHeader(&bimg, SBFNR, img.dimy, img.dimx, tac.frameNr, &img);
224 if(ret!=0) {
225 fprintf(stderr, "Error: cannot allocate memory for image data.\n");
226 dftEmpty(&tac); imgEmpty(&img); return(4);
227 }
228 for(int i=0; i<SBFNR; i++) bimg.planeNumber[i]=1+i;
229 } else biffile[0]=(char)0;
230
231 /* Allocate memory for a single image matrix for temp storage */
232 IMG simg; imgInit(&simg);
233 if(verbose>1) printf("allocating memory for temp image\n");
234 ret=imgAllocateWithHeader(&simg, img.dimz, img.dimy, img.dimx, 1, &img);
235 if(ret!=0) {
236 fprintf(stderr, "Error: cannot allocate memory for image data.\n");
237 dftEmpty(&tac); imgEmpty(&img); imgEmpty(&bimg); return(4);
238 }
239
240
241 /*
242 * Simulate image contents
243 */
244 if(verbose>0) printf("simulation...\n");
245
246 /* Myocardial ring is simulated with 12 different diameters in the range
247 [maxdiam,mindiam], assuming that during each PET frame each of these
248 radius is present the below specified fraction of time */
249 double rtfract[SBFNR];
250 if(diamin==1.0) {
251 rtfract[0]=1.0; for(int i=1; i<SBFNR; i++) rtfract[i]=0.0;
252 } else {
253 for(int i=0; i<SBFNR; i++) rtfract[i]=1.0;
254 }
255
256 /* Set the centre of heart in image */
257 double cx, cy, cz;
258 cx=0.5*(double)img.dimx*img.sizex;
259 cy=0.5*(double)img.dimy*img.sizey;
260 cz=0.5*(double)img.dimz*img.sizez;
261
262 /* 2D: Calculate muscle wall area in one image plane (excluding Pi) */
263 /* 3D: Calculate muscle wall volume (excluding Pi and division by 6) */
264 double a;
265 {
266 double r1inner, r1outer, d1, d2;
267 r1inner=0.5*diameter; r1outer=r1inner+thickness;
268 if(simg.dimz==1) {
269 a=r1outer*r1outer - r1inner*r1inner;
270 } else {
271 d1=2.0*r1outer; d2=2.0*r1inner;
272 a=d1*d1*d1 - d2*d2*d2;
273 }
274 }
275 if(verbose>2) printf("a := %g\n", a);
276
277 double dI, dO, c_cav, c_myo, c_bkg;
278 /* Process frame-by-frame */
279 ret=0;
280 for(int fi=0; fi<img.dimt; fi++) {
281 if(verbose>1) printf("Frame %d/%d\n", 1+fi, img.dimt);
282
283 c_myo=tac.voi[0].y[fi]; c_cav=tac.voi[1].y[fi];
284 if(tac.voiNr>2) c_bkg=tac.voi[2].y[fi]; else c_bkg=0.0;
285
286 /* Initial inner diameter */
287 dI=diameter;
288
289 /* Simulate image with one radius at a time and sum it to the frame image */
290 for(int ri=0; ri<SBFNR; ri++) if(rtfract[ri]>0.0) {
291
292 /* Calculate the outer muscle wall diameter, assuming that muscle
293 wall area (2D) or volume (3D) does not change when diameter changes */
294 double f=0.5*dI; // inner radius
295 // outer radius
296 double r;
297 if(simg.dimz==1) {
298 r=sqrt(a + f*f);
299 dO=2.0*r;
300 } else {
301 dO=cbrt(a + dI*dI*dI); // V=Pi*(Diameter^3)/6
302 r=0.5*dO;
303 }
304 if(verbose>2) {
305 printf("Radius %d/%d\n", 1+ri, SBFNR);
306 printf("inner diameter := %g\n", dI);
307 printf("outer diameter := %g\n", dO);
308 if(simg.dimz==1) printf("muscle area := %g\n", M_PI*(r*r-f*f));
309 else printf("muscle volume := %g\n", M_PI*(dO*dO*dO-dI*dI*dI)/6.0);
310 }
311
312 /* Simulate myocardial ring */
313 for(long long i=0; i<(long long)simg.dimx*simg.dimy*simg.dimz; i++) simg.pixel[i]=0.0;
314 if(simg.dimz==1)
315 ret=imgSimulateRing(&simg, 0, 0, cx, cy, f, r, c_myo, c_cav, c_bkg, verbose-3);
316 else
317 ret=imgSimulateSphere(&simg, 0, cx, cy, cz, f, r, c_myo, c_cav, c_bkg, verbose-3);
318 if(ret!=0) break;
319
320 /* Add weighted simulated image matrix to the image to be saved */
321 int zi, yi, xi;
322 for(zi=0; zi<img.dimz; zi++)
323 for(yi=0; yi<img.dimy; yi++)
324 for(xi=0; xi<img.dimx; xi++)
325 img.m[zi][yi][xi][fi]+=rtfract[ri]*simg.m[zi][yi][xi][0];
326
327 /* And without weighting to bimg if required */
328 if(biffile[0]) {
329 for(yi=0; yi<img.dimy; yi++)
330 for(xi=0; xi<img.dimx; xi++)
331 bimg.m[ri][yi][xi][fi]=simg.m[0][yi][xi][0];
332 }
333
334 /* Decrease the inner diameter */
335 dI-=diameter*(1.0-diamin)/(double)(SBFNR-1);
336 } // next diameter
337 if(ret!=0) break;
338 } // next frame
339 imgEmpty(&simg); dftEmpty(&tac);
340 /* Check that simulation was successful */
341 if(ret!=0) {
342 fprintf(stderr, "Error: cannot simulate image (%d).\n", ret);
343 imgEmpty(&img); imgEmpty(&bimg);
344 return(6);
345 }
346
347 /* Divide the image frames by the sum of time fractions to get the average during the frame */
348 if(verbose>1) printf("averaging heart beat phases\n");
349 /* Sum of fractional times */
350 double rtsum=0.0;
351 for(int i=0; i<SBFNR; i++) if(rtfract[i]>0.0) rtsum+=rtfract[i];
352 if(verbose>2) printf("rtsum := %g\n", rtsum);
353 ret=imgArithmConst(&img, (float)rtsum, ':', -1.0, verbose-10);
354 if(ret!=0) {
355 fprintf(stderr, "Error: cannot process simulated image (%d).\n", ret);
356 imgEmpty(&img); imgEmpty(&bimg);
357 return(7);
358 }
359
360 /*
361 * Simulate the image resolution effect
362 */
363 if(fwhm>0.0) {
364 if(verbose>0) printf("simulating resolution effect\n");
365 char tmp[256];
366 double s, d;
367 /* Calculate filter SD */
368 s=fwhm/2.354820; // 2*sqrt(2*ln(2))
369 /* Convert filter SD to pixels */
370 d=0.5*(img.sizex+img.sizey); // Z size is assumed to be the same
371 s/=d; // printf("filter_sd_in_pixels := %g\n", s);
372 /* Apply Gaussian filter */
373 if(img.dimz==1)
374 ret=imgGaussianFIRFilter(&img, s, s, 0.0, 1.0E-06, verbose-3);
375 else
376 ret=imgGaussianFIRFilter(&img, s, s, s, 1.0E-06, verbose-3);
377 if(ret!=0) {
378 fprintf(stderr, "Error: cannot filter simulated image (%d).\n", ret);
379 if(verbose>0) fprintf(stderr, " %s.\n", tmp);
380 imgEmpty(&img); imgEmpty(&bimg);
381 return(8);
382 }
383 /* Same for beat image, if necessary (only 2D) */
384 if(biffile[0]) {
385 ret=imgGaussianFIRFilter(&bimg, s, s, s, 1.0E-06, verbose-3);
386 if(ret!=0) {
387 fprintf(stderr, "Error: cannot filter simulated image (%d).\n", ret);
388 if(verbose>0) fprintf(stderr, " %s.\n", tmp);
389 imgEmpty(&img); imgEmpty(&bimg);
390 return(9);
391 }
392 }
393 }
394
395
396 /*
397 * Save image
398 */
399 if(verbose>1) printf("Writing image file %s ...\n", imgfile);
400 if(imgWrite(imgfile, &img)) {
401 fprintf(stderr, "Error: %s\n", img.statmsg);
402 imgEmpty(&img); imgEmpty(&bimg); return(11);
403 }
404 if(verbose>0) printf("%s written.\n", imgfile);
405 imgEmpty(&img);
406
407 /*
408 * Save beating image, if necessary
409 */
410 if(biffile[0]) {
411 if(verbose>1) printf("Writing image file %s ...\n", biffile);
412 if(imgWrite(biffile, &bimg)) {
413 fprintf(stderr, "Error: %s\n", bimg.statmsg);
414 imgEmpty(&bimg); return(13);
415 }
416 if(verbose>0) printf("%s written.\n", biffile);
417 imgEmpty(&bimg);
418 }
419
420 return(0);
421}
422/*****************************************************************************/
423
424/*****************************************************************************/
int atof_with_check(char *double_as_string, double *result_value)
Definition decpoint.c:107
void dftInit(DFT *data)
Definition dft.c:38
char dfterrmsg[64]
Definition dft.c:6
void dftEmpty(DFT *data)
Definition dft.c:20
int dftRead(char *filename, DFT *data)
Definition dftio.c:22
int imgSimulateSphere(IMG *img, int fi, double cx, double cy, double cz, double r1, double r2, double vr, double vi, double vo, int verbose)
Definition heart.c:154
int imgSimulateRing(IMG *img, int fi, int zi, double cx, double cy, double r1, double r2, double vr, double vi, double vo, int verbose)
Definition heart.c:17
int imgAllocate(IMG *image, int planes, int rows, int columns, int frames)
Definition img.c:194
int imgAllocateWithHeader(IMG *image, int planes, int rows, int columns, int frames, IMG *image_from)
Definition img.c:279
void imgEmpty(IMG *image)
Definition img.c:121
void imgInit(IMG *image)
Definition img.c:60
int imgArithmConst(IMG *img, float operand, char operation, float ulimit, int verbose)
Definition imgarithm.c:100
int imgWrite(const char *fname, IMG *img)
Definition imgfile.c:136
int imgGaussianFIRFilter(IMG *img, float xsd, float ysd, float zsd, double tolerance, int verbose)
Definition imgfilter.c:18
int imgSetScanner(IMG *img, int scanner_type)
Definition imgscanner.c:14
int imgSetUnit(IMG *img, char *unit)
Definition imgunits.c:328
Header file for libtpccurveio.
Header file for libtpcidi.
Header file for libtpcimgio.
#define IMG_UNKNOWN
#define IMG_DC_CORRECTED
#define SCANNER_HRPLUS
#define IMG_TYPE_IMAGE
Header file for libtpcimgp.
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:40
size_t strlcpy(char *dst, const char *src, size_t dstsize)
Definition strext.c:245
#define MAX_STUDYNR_LEN
Definition libtpcmisc.h:163
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:213
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:383
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:158
Header file for libtpcmodel.
Header file for libtpcmodext.
Voi * voi
int timeunit
char studynr[MAX_STUDYNR_LEN+1]
double * x1
int voiNr
double * x2
int frameNr
double * x
char unit[MAX_UNITS_LEN+1]
float * pixel
float sizex
unsigned short int dimx
char type
float **** m
char decayCorrection
int _fileFormat
unsigned short int dimt
int * planeNumber
float sizey
float * start
unsigned short int dimz
unsigned short int dimy
float * end
float zoom
const char * statmsg
char studyNr[MAX_STUDYNR_LEN+1]
float * mid
float sizez
double * y