5
define_types,SysUtils,part,StatThds,statcr,StatThdsUtil,Brunner,
6
DISTR,nifti_img, hdr,filename,Messages, Classes, Graphics,
7
Controls, Forms, Dialogs,StdCtrls,ComCtrls,ExtCtrls,Menus, overlap,
8
ReadInt,lesion_pattern,stats,LesionStatThds,nifti_hdr,
9
upower,firthThds,firth,IniFiles,cpucount,userdir,math,
10
{$IFDEF FPC} LResources,gzio2,
11
{$ELSE} gziod,associate,{$ENDIF} //must be in search path, e.g. C:\pas\mricron\npm\math
12
{$IFNDEF UNIX} Windows, {$ENDIF}
15
function AnacomLesionNPMAnalyze (var lImages: TStrings; var lMaskHdr: TMRIcroHdr; lnCrit,lRun,lnControl: integer; var lSymptomRA,lControlSymptomRA: SingleP;var lFactname,lOutName: string; lttestIn,lBMIn: boolean): boolean;
17
function readTxt (lFilename: string; var lnObservations : integer; var ldataRA1: singlep): boolean;
25
function AnacomLesionNPMAnalyze (var lImages: TStrings; var lMaskHdr: TMRIcroHdr; lnCrit,lRun,lnControl: integer; var lSymptomRA,lControlSymptomRA: SingleP;var lFactname,lOutName: string; lttestIn,lBMIn: boolean): boolean;
31
lOutImgSum,lOutImgBM,lOutImgT,
32
lPermuteMaxT, lPermuteMinT,lPermuteMaxBM, lPermuteMinBM,lCombinedSymptomRA: singleP;
33
lPos,lPlank,lThread,lnControlsPlusPatients: integer;
34
lVolVox,lMinMask,lMaxMask,lTotalMemory,lnPlanks,lVoxPerPlank,
35
lThreadStart,lThreadEnd,lThreadInc,lnLesion,lnPermute,
36
lPos2,lPos2Offset,lStartVox,lEndVox,lPlankImgPos,lnTests,lnVoxTested,lPosPct: int64;
37
lT,lBMz, lSum,lThresh,lThreshBonf,lThreshPermute,lThreshNULP :double;
44
lSave,lBM,lttest,lLtest: boolean;
45
lnControlNeg: integer;
48
lmedianFX,lmeanFX,lsummean,lsummedian: double;
49
lmediancount: integer;
53
lnControlNeg := lnControl; //negative for binomial test
56
if (not (lttest)) and (not (lbm)) then begin
59
lnControlNeg := -lnControl;
61
//lttest:= ttestmenu.checked;
62
//lBM := BMmenu.checked;
63
if lnControl < 1 then begin
64
MainForm.NPMmsg('AnaCOM aborted - need data from at least 1 control individual');
67
lnPermute := 0;//MainForm.ReadPermute;
68
MainForm.NPMmsg('Permutations = ' +IntToStr(lnPermute));
69
MainForm.NPMmsg('Analysis began = ' +TimeToStr(Now));
71
lVolVox := lMaskHdr.NIFTIhdr.dim[1]*lMaskHdr.NIFTIhdr.dim[2]* lMaskHdr.NIFTIhdr.dim[3];
72
if (lVolVox < 1) then goto 667;
75
lVoxPerPlank := kPlankSz div lImages.Count div sizeof(byte) ;
76
if (lVoxPerPlank = 0) then goto 667; //no data
77
lTotalMemory := ((lMaxMask+1)-lMinMask) * lImages.Count;
78
if (lTotalMemory = 0) then goto 667; //no data
79
lnPlanks := trunc(lTotalMemory/(lVoxPerPlank*lImages.Count) ) + 1;
80
MainForm.NPMmsg('Memory planks = ' +Floattostr(lTotalMemory/(lVoxPerPlank*lImages.Count)));
81
MainForm.NPMmsg('Max voxels per Plank = ' +Floattostr(lVoxPerPlank));
82
if (lnPlanks = 1) then
83
getmem(lPlankImg,lTotalMemory) //assumes 1bpp
85
getmem(lPlankImg,kPlankSz);
86
lStartVox := lMinMask;
87
lEndVox := lMinMask-1;
93
for lPos := 1 to lImages.Count do
94
if gScaleRA[lPos] = 0 then
96
lnControlsPlusPatients := lImages.Count+lnControl;
97
createArray64(lObsp,lObs,lnControlsPlusPatients);
98
getmem(lOutImgSum,lVolVox* sizeof(single));
99
getmem(lOutImgBM,lVolVox* sizeof(single));
100
getmem(lOutImgT,lVolVox* sizeof(single));
101
MainForm.InitPermute (lImages.Count, lnPermute, lPermuteMaxT, lPermuteMinT,lPermuteMaxBM, lPermuteMinBM, lRanOrderp, lRanOrder);
102
for lPos := 1 to lVolVox do begin
103
lOutImgSum^[lPos] := 0;
104
lOutImgBM^[lPos] := 0;
105
lOutImgT^[lPos] := 0;
107
//sumptom array for lesions AND controls
108
for lPos := 1 to lImages.Count do
109
lObs^[lPos-1] := lSymptomRA^[lPos];
110
for lPos := 1 to lnControl do
111
lObs^[lPos-1+lImages.Count] := lControlSymptomRA^[lPos];
112
getmem(lCombinedSymptomRA,lnControlsPlusPatients* sizeof(single));
113
for lPos := 1 to lnControlsPlusPatients do
114
lCombinedSymptomRA^[lPos] := lObs^[lPos-1];
115
//next create permuted BM bounds
117
MainForm.NPMmsg('Generating BM permutation thresholds');
119
//for lPos := 1 to lImages.Count do
120
// lObs^[lPos-1] := lSymptomRA^[lPos];
121
genBMsim (lnControlsPlusPatients, lObs);
123
ClearThreadData(gnCPUThreads,lnPermute) ;
124
for lPlank := 1 to lnPlanks do begin
125
MainForm.NPMmsg('Computing plank = ' +Inttostr(lPlank));
127
Application.processmessages;
128
lEndVox := lEndVox + lVoxPerPlank;
129
if lEndVox > lMaxMask then begin
130
lVoxPerPlank := lVoxPerPlank - (lEndVox-lMaxMask);
134
for lPos := 1 to lImages.Count do begin
135
if not LoadImg8(lImages[lPos-1], lPlankImg, lStartVox, lEndVox,round(gOffsetRA[lPos]),lPlankImgPos,gDataTypeRA[lPos],lVolVox) then
137
lPlankImgPos := lPlankImgPos + lVoxPerPlank;
141
lThreadInc := lVoxPerPlank div gnCPUThreads;
142
lThreadEnd := lThreadInc;
143
Application.processmessages;
144
for lThread := 1 to gnCPUThreads do begin
145
if lThread = gnCPUThreads then
146
lThreadEnd := lVoxPerPlank; //avoid integer rounding error
148
with TLesionContinuous.Create (MainForm.ProgressBar1,lttest,lBM,lnCrit, lnPermute,lThread,lThreadStart,lThreadEnd,lStartVox,lVoxPerPlank,lImages.Count,lnControlNeg,lPlankImg,lOutImgSum,lOutImgBM,lOutImgT,nil,lCombinedSymptomRA) do
149
{$IFDEF FPC} OnTerminate := @MainForm.ThreadDone; {$ELSE}OnTerminate := MainForm.ThreadDone;{$ENDIF}
150
inc(gThreadsRunning);
151
lThreadStart := lThreadEnd + 1;
152
lThreadEnd :=lThreadEnd + lThreadInc;
153
end; //for each thread
155
Application.processmessages;
156
until gThreadsRunning = 0;
157
Application.processmessages;
159
lStartVox := lEndVox + 1;
162
lnVoxTested := SumThreadData(gnCPUThreads,lnPermute,lPermuteMaxT, lPermuteMinT,lPermuteMaxBM, lPermuteMinBM);
163
//next report findings
164
if lnVoxTested < 1 then begin
165
MainForm.NPMmsg('**Error: no voxels tested: no regions lesioned in at least '+inttostr(lnCrit)+' patients**');
169
MainForm.NPMmsg('Voxels tested = ' +Inttostr(lnVoxTested));
171
MainForm.NPMmsg('Average MEAN effect size = ' +realtostr((lsummean/lmediancount),3));
172
MainForm.NPMmsg('Average MEDIAN effect size = ' +realtostr((lsummedian/lmediancount),3));
174
MainForm.NPMmsg('Only tested voxels with more than '+inttostr(lnCrit)+' lesions');
175
//Next: save results from permutation thresholding....
176
//Next: save results from permutation thresholding....
177
lThreshBonf := MainForm.reportBonferroni('Std',lnVoxTested);
179
if lRun > 0 then //terrible place to do this - RAM problems, but need value to threshold maps
180
lThreshNULP := MainForm.reportBonferroni('Unique overlap',CountOverlap2 (lImages, lnCrit,lnVoxTested,lPlankImg));
182
//lThreshNULP := MainForm.reportBonferroni('Unique overlap',CountOverlap (lImages, lnCrit));
184
MakeHdr (lMaskHdr.NIFTIhdr,lStatHdr);
186
lOutNameMod := ChangeFilePostfixExt(lOutName,'Sum'+lFactName,'.hdr');
187
if (lSave) and (lRun < 1) then
188
NIFTIhdr_SaveHdrImg(lOutNameMod,lStatHdr,true,not IsNifTiMagic(lMaskHdr.NIFTIhdr),true,lOutImgSum,1);
189
//create new header - subsequent images will use Z-scores
190
MakeStatHdr (lMaskHdr.NIFTIhdr,lStatHdr,-6, 6,1{df},0,lnVoxTested,kNIFTI_INTENT_ZSCORE,inttostr(lnVoxTested) );
191
if (lSave) and (lRun < 1) and (Sum2PowerCont(lOutImgSum,lVolVox,lImages.Count)) then begin
192
lOutNameMod := ChangeFilePostfixExt(lOutName,'Power'+lFactName,'.hdr');
193
NIFTIhdr_SaveHdrImg(lOutNameMod,lStatHdr,true,not IsNifTiMagic(lMaskHdr.NIFTIhdr),true,lOutImgSum,1);
196
//MakeStatHdr (lMaskHdr.NIFTIhdr,lStatHdr,-6, 6,1{df},0,lnVoxTested,kNIFTI_INTENT_ZSCORE,inttostr(lnVoxTested) );
197
if lttest then begin //save Ttest
198
//next: convert t-scores to z scores
200
if lnControl < 1 then //do not convert t-scores for anaCOM - numbers vary from voxel to voxel...
201
for lPos := 1 to lVolVox do
202
lOutImgT^[lPos] := TtoZ (lOutImgT^[lPos],lImages.Count-2);
203
for lPos := 1 to lnPermute do begin
204
lPermuteMaxT^[lPos] := TtoZ (lPermuteMaxT^[lPos],lImages.Count-2);
205
lPermuteMinT^[lPos] := TtoZ (lPermuteMinT^[lPos],lImages.Count-2);
207
lThresh := MainForm.reportFDR ('ttest', lVolVox, lnVoxTested, lOutImgT);
208
lThreshPermute := MainForm.reportPermute('attest',lnPermute,lPermuteMaxT, lPermuteMinT);
209
lOutNameMod := ChangeFilePostfixExt(lOutName,'attest'+lFactName,'.hdr');
211
MainForm.NPMmsgAppend('AnaComthreshtt,'+inttostr(lRun)+','+inttostr(MainForm.ThreshMap(lThreshNULP,lVolVox,lOutImgT))+','+realtostr(lThreshNULP,3)+','+realtostr(lThreshPermute,3)+','+realtostr(lThreshBonf,3));
213
NIFTIhdr_SaveHdrImg(lOutNameMod,lStatHdr,true,not IsNifTiMagic(lMaskHdr.NIFTIhdr),true,lOutImgT,1);
216
if lBM then begin //save Mann Whitney
217
lThresh := MainForm.reportFDR ('BM', lVolVox, lnVoxTested, lOutImgBM);
218
lThreshPermute := MainForm.reportPermute('aBM',lnPermute,lPermuteMaxBM, lPermuteMinBM);
219
lOutNameMod := ChangeFilePostfixExt(lOutName,'aBM'+lFactName,'.hdr');
221
MainForm.NPMmsgAppend('AnaCOMthreshbm,'+inttostr(lRun)+','+inttostr(MainForm.ThreshMap(lThreshNULP,lVolVox,lOutImgBM))+','+realtostr(lThreshNULP,3)+','+realtostr(lThreshPermute,3)+','+realtostr(lThreshBonf,3));
223
NIFTIhdr_SaveHdrImg(lOutNameMod,lStatHdr,true,not IsNifTiMagic(lMaskHdr.NIFTIhdr),true,lOutImgBM,1);
225
//next: free dynamic memory
227
MainForm.FreePermute (lnPermute,lPermuteMaxT, lPermuteMinT,lPermuteMaxBM, lPermuteMinBM, lRanOrderp);
233
freemem(lCombinedSymptomRA);
234
MainForm.NPMmsg('Analysis finished = ' +TimeToStr(Now));
235
lOutNameMod := ChangeFilePostfixExt(lOutName,'Notes'+lFactName,'.txt');
237
MainForm.MsgSave(lOutNameMod);
238
MainForm.ProgressBar1.Position := 0;
240
667: //you only get here if you aborted ... free memory and report error
241
if lTotalMemory > 1 then freemem(lPlankImg);
242
MainForm.NPMmsg('Unable to complete analysis.');
243
MainForm.ProgressBar1.Position := 0;
244
end; //LesionNPMAnalyze
248
(*function readCSV2 (lFilename: string; lCol1,lCol2: integer; var lnObservations : integer; var ldataRA1,ldataRA2: singlep): boolean;
257
lnFactors,MaxC,R,C:integer;
263
if not fileexists(lFilename) then begin
264
showmessage('Can not find '+lFilename);
267
AssignFile(F, lFilename);
268
FileMode := 0; //Set file access to read only
269
//First pass: determine column height/width
274
while not Eof(F) do begin
279
while not (lCh in [#10,#13]) do
281
else if not (lCh in [#10,#13,#9,',']) then begin
282
lNumStr := lNumStr + lCh;
283
end else if lNumStr <> '' then begin
288
if (lCh in [#10,#13]) then begin
293
end; //if lNumStr <> '' and not tab
295
if lNumStr <> '' then //july06- read data immediately prior to EOF
298
if (R <= (kHdrRow+1)) or (MaxC < (kHdrCol+lCol1)) or (MaxC < (kHdrCol+lCol2)) then begin
299
showmessage('problems reading CSV - not enough columns/rows '+inttostr(lCol1)+' '+inttostr(lCol2));
303
lnObservations := R -kHdrRow ; //-1: first row is header....
304
lnFactors := MaxC-1;// -1: first column is Y values
305
//fx(lnObservations,lnFactors);
308
getmem(ldataRA1,lnObservations*sizeof(single));
309
getmem(ldataRA2,lnObservations*sizeof(single));
318
while not Eof(F) do begin
322
while not (lCh in [#10,#13]) do
324
else if not (lCh in [#10,#13,#9,',']) then begin
325
lNumStr := lNumStr + lCh;
326
end else if lNumStr <> '' then begin
327
if (R > kHdrRow) and (C > kHdrCol) then begin
328
if ((C-kHdrCol) = lCol1) or ((C-kHdrCol) = lCol2) then begin
329
if lNumStr = '-' then begin
331
end else begin //number
333
lTempFloat := strtofloat(lNumStr);
335
on EConvertError do begin
337
showmessage('Empty cells? Error reading CSV file row:'+inttostr(R)+' col:'+inttostr(C)+' - Unable to convert the string '+lNumStr+' to a number');
342
//showmessage(lNumStr);
343
if (C-kHdrCol) = lCol1 then
344
ldataRA1^[R-kHdrRow] := lTempFloat
345
else if (C-kHdrCol) = lCol2 then
346
ldataRA2^[R-kHdrRow] := lTempFloat;
355
if (lCh in [#10,#13]) then begin
359
end; //if lNumStr <> '' and not tab
361
if (lNumStr <> '') and (C = lnFactors) then begin //unterminated string
363
lTempFloat := strtofloat(lNumStr);
365
on EConvertError do begin
367
showmessage('Empty cells? Error reading CSV file row:'+inttostr(R)+' col:'+inttostr(C)+' - Unable to convert the string '+lNumStr+' to a number');
372
ldataRA2^[R-1] := lTempFloat;
373
end;//unterminated string
376
FileMode := 2; //Set file access to read/write
380
function readTxt (lFilename: string; var lnObservations : integer; var ldataRA1: singlep): boolean;
390
lnFactors,MaxC,R,C:integer;
396
if not fileexists(lFilename) then begin
397
showmessage('Can not find '+lFilename);
400
AssignFile(F, lFilename);
401
FileMode := 0; //Set file access to read only
402
//First pass: determine column height/width
407
while not Eof(F) do begin
412
while not (lCh in [#10,#13]) do
414
else if not (lCh in [#10,#13,#9,',']) then begin
415
lNumStr := lNumStr + lCh;
416
end else if lNumStr <> '' then begin
421
if (lCh in [#10,#13]) then begin
426
end; //if lNumStr <> '' and not tab
428
if lNumStr <> '' then //july06- read data immediately prior to EOF
431
if (R <= (kHdrRow+1)) or (MaxC < (kHdrCol+lCol1)) then begin
432
showmessage('problems reading CSV - not enough columns/rows ');
436
lnObservations := R -kHdrRow ; //-1: first row is header....
437
lnFactors := kHdrCol+lCol1;// -1: first column is Y values
438
//fx(lnObservations,lnFactors);
441
getmem(ldataRA1,lnObservations*sizeof(single));
450
while not Eof(F) do begin
454
while not (lCh in [#10,#13]) do
456
else if not (lCh in [#10,#13,#9,',']) then begin
457
lNumStr := lNumStr + lCh;
458
end else if lNumStr <> '' then begin
459
if (R > kHdrRow) and (C > kHdrCol) then begin
460
if ((C-kHdrCol) = lCol1) {or ((C-kHdrCol) = lCol2)} then begin
461
if lNumStr = '-' then begin
463
end else begin //number
465
lTempFloat := strtofloat(lNumStr);
467
on EConvertError do begin
469
showmessage('Empty cells? Error reading CSV file row:'+inttostr(R)+' col:'+inttostr(C)+' - Unable to convert the string '+lNumStr+' to a number');
474
//showmessage(lNumStr);
475
if (C-kHdrCol) = lCol1 then begin
476
//showmessage(lNumStr);
477
ldataRA1^[R-kHdrRow] := lTempFloat;
479
{else if (C-kHdrCol) = lCol2 then
480
ldataRA2^[R-kHdrRow] := lTempFloat;}
489
if (lCh in [#10,#13]) then begin
493
end; //if lNumStr <> '' and not tab
495
//showmessage(lNumStr+' '+inttostr(lnFactors)+' '+inttostr(C));
496
if (lNumStr <> '') and (C = lnFactors) then begin //unterminated string
499
lTempFloat := strtofloat(lNumStr);
501
on EConvertError do begin
503
showmessage('Empty cells? Error reading CSV file row:'+inttostr(R)+' col:'+inttostr(C)+' - Unable to convert the string '+lNumStr+' to a number');
508
//showmessage(inttostr(R)+' '+floattostr(lTempFLoat));
509
ldataRA1^[R] := lTempFloat;
510
end;//unterminated string
513
FileMode := 2; //Set file access to read/write
514
result := not lError;
521
lControlFilename: string;
522
lI, lnControlObservations : integer;
523
lControldata: singlep;
524
//lBinomial: boolean;
525
lFact,lnFactors,lSubj,lnSubj,lnSubjAll,lMaskVoxels,lnCrit: integer;
526
lImageNames,lImageNamesAll: TStrings;
527
lPredictorList: TStringList;
528
lTemp4D,lMaskname,lOutName,lFactname: string;
529
lMaskHdr: TMRIcroHdr;
530
lMultiSymptomRA,lSymptomRA: singleP;
532
npmform.MainForm.memo1.lines.clear;
533
npmform.MainForm.memo1.lines.add('AnaCOM analysis requires TXT/CSV format text file.');
534
npmform.MainForm.memo1.lines.add('One row per control participant.');
535
npmform.MainForm.memo1.lines.add('First column is performance of that participant.');
536
npmform.MainForm.memo1.lines.add('Example file:');
537
npmform.MainForm.memo1.lines.add('11');
538
npmform.MainForm.memo1.lines.add('19');
539
npmform.MainForm.memo1.lines.add('2');
540
npmform.MainForm.memo1.lines.add('22');
541
npmform.MainForm.memo1.lines.add('19');
542
npmform.MainForm.memo1.lines.add('6');
543
if not MainForm.OpenDialogExecute('Select text file',false,false,'Text file (*.txt)|*.txt;*.csv') then begin
544
showmessage('AnaCOM aborted: Control data file selection failed.');
546
end; //if not selected
547
lControlFilename := MainForm.OpenHdrDlg.Filename;
548
if (not readTxt (lControlFilename, lnControlObservations,lControldata)) or (lnControlObservations < 1) then begin
549
showmessage('Error reading file '+lControlFilename);
552
npmform.MainForm.memo1.lines.add('Control (n='+inttostr(lnControlObservations)+')performance ['+lControlFilename+']');
553
for lI := 1 to lnControlObservations do
554
npmform.MainForm.memo1.lines.add(inttostr(lI)+' '+floattostr(lControldata^[lI]));
556
lImageNamesAll:= TStringList.Create; //not sure why TStrings.Create does not work???
557
lImageNames:= TStringList.Create; //not sure why TStrings.Create does not work???
558
//next, get 1st group
559
if not MainForm.GetValX(lnSubjAll,lnFactors,lMultiSymptomRA,lImageNamesAll,lnCrit,{,binom}lPredictorList) then begin
560
showmessage('Error with VAL file');
563
lTemp4D := CreateDecompressed4D(lImageNamesAll);
564
if (lnSubjAll < 1) or (lnFactors < 1) then begin
565
Showmessage('AnaCOM error: not enough patients ('+inttostr(lnSubjAll)+') or factors ('+inttostr(lnFactors)+').');
568
lMaskname := lImageNamesAll[0];
569
if not NIFTIhdr_LoadHdr(lMaskname,lMaskHdr) then begin
570
showmessage('Error reading 1st file: '+lMaskName);
573
lMaskVoxels := ComputeImageDataBytes8bpp(lMaskHdr);
574
if (lMaskVoxels < 2) or (not CheckVoxels(lMaskname,lMaskVoxels,0)){make sure there is uncompressed .img file} then begin
575
showmessage('Mask file size too small.');
578
lOutName := ExtractFileDirWithPathDelim(lMaskName)+'results';
579
MainForm.SaveHdrDlg.Filename := loutname;
580
lOutName := lOutName+'.nii.gz';
581
if not MainForm.SaveHdrName ('Base Statistical Map', lOutName) then exit;
582
for lFact := 1 to lnFactors do begin
584
for lSubj := 1 to lnSubjAll do
585
{$IFNDEF FPC}if lMultiSymptomRA^[lSubj+((lFact-1)*lnSubjAll)] <> NaN then {$ENDIF}
586
lImageNames.Add(lImageNamesAll[lSubj-1]);
587
lnSubj := lImageNames.Count;
588
if lnSubj > 1 then begin
589
getmem(lSymptomRA,lnSubj * sizeof(single));
591
for lSubj := 1 to lnSubjAll do
592
{$IFNDEF FPC}if lMultiSymptomRA^[lSubj+((lFact-1)*lnSubjAll)] <> NaN then begin
593
{$ELSE} begin{$ENDIF}
595
lSymptomRA^[lnSubj] := lMultiSymptomRA^[lSubj+((lFact-1)*lnSubjAll)];
597
MainForm.NPMmsgClear;
598
MainForm.NPMMsg(MainForm.GetKVers);
599
MainForm.NPMMsg('Threads: '+inttostr(gnCPUThreads));
600
npmform.MainForm.memo1.lines.add('Control (n='+inttostr(lnControlObservations)+')performance ['+lControlFilename+']');
601
for lI := 1 to lnControlObservations do
602
npmform.MainForm.memo1.lines.add(inttostr(lI)+' '+floattostr(lControldata^[lI]));
603
lFactName := lPredictorList.Strings[lFact-1];
604
lFactName := LegitFilename(lFactName,lFact);
605
MainForm.NPMMsg('Patient performance, (n= '+inttostr(lnSubj)+') Factor = '+lFactname);
606
For lSubj := 1 to lnSubj do
607
MainForm.NPMMsg (lImageNames.Strings[lSubj-1] + ' = '+realtostr(lSymptomRA^[lSubj],2) );
608
MainForm.NPMMsg('Total voxels = '+inttostr(lMaskVoxels));
609
MainForm.NPMMsg('Only testing voxels damaged in at least '+inttostr(lnCrit)+' individual[s]');
610
MainForm.NPMMsg('Number of Lesion maps = '+inttostr(lnSubj));
611
if not CheckVoxelsGroup(lImageNames,lMaskVoxels) then begin
612
showmessage('File dimensions differ from mask.');
615
MainForm.ReportDescriptives(lSymptomRA,lnSubj);
616
AnacomLesionNPMAnalyze(lImageNames,lMaskHdr,lnCrit,-1,lnControlObservations,lSymptomRA,lControldata,lFactName,lOutname,true {ttest},false{BM});
619
end; //for each factor
620
if lnSubjAll > 0 then
621
Freemem(lMultiSymptomRA);
626
DeleteDecompressed4D(lTemp4D);
627
freemem(lControldata);