2
// questions re: fiez et al.
4
// fig 3a shows sizes of 16 and 19 and calculates the percent
5
// difference as 16%. the calculation given in the article (diff
6
// divided by mean size) gives 17%. which is right?
8
// fig 3b shows surface distances. the algorithm isn't clear, and not
9
// spelled out in the refs, so here's my guess. accumulate all the
10
// surface voxels for each volume. surface-surface matches are 0's.
11
// other surface voxels that don't overlap with non-surface voxels are
12
// given the distance to the nearest surface voxel (positive for A-B,
13
// negative for B-A). calculate euclidean distances, but round.
19
// mask comparison utility for doing inter-rater reliability
20
// Copyright (c) 2005-2009 by The VoxBo Development Team
22
// VoxBo is free software: you can redistribute it and/or modify it
23
// under the terms of the GNU General Public License as published by
24
// the Free Software Foundation, either version 3 of the License, or
25
// (at your option) any later version.
27
// VoxBo is distributed in the hope that it will be useful, but
28
// WITHOUT ANY WARRANTY; without even the implied warranty of
29
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30
// General Public License for more details.
32
// You should have received a copy of the GNU General Public License
33
// along with VoxBo. If not, see <http://www.gnu.org/licenses/>.
35
// For general information on VoxBo, including the latest complete
36
// source code and binary distributions, manual, and associated files,
37
// see the VoxBo home page at: http://www.voxbo.org/
39
// original version written by Dan Kimberg
40
// based on Fiez et al.
45
#include "vbversion.h"
46
#include "vbmaskcompare.hlp.h"
51
int32 xmin,xmax,ymin,ymax,zmin,zmax;
52
void include(int x,int y,int z);
56
subrect::include(int x,int y,int z)
64
if (x<xmin) xmin=x; else if (x>xmax) xmax=x;
65
if (y<ymin) ymin=y; else if (y>ymax) ymax=y;
66
if (z<zmin) zmin=z; else if (z>zmax) zmax=z;
72
int m_union,m_intersection,m_just1,m_just2,m_neither;
73
int m_discrepant1,m_discrepant2;
74
VBRegion surface1,surface2; // surfaces for the two volumes
75
VBRegion unique1,unique2; // unique voxels for the two volumes
76
VBRegion discrepants1,discrepants2,nondiscrepants;
77
VBRegion allvoxels1,allvoxels2;
78
vector<int> distances;
82
// double and integer versions of the maximum allowable non-discrepant distance
86
int load_masks(string m1,string m2);
88
void check_volumes2();
89
void check_surfaces();
90
void check_discrepants();
91
void write_discrepants();
95
void vbmaskcompare_help();
96
void vbmaskcompare_version();
99
main(int argc,char *argv[])
102
vbmaskcompare_help();
108
vector<string> filelist;
109
args.Transfer(argc-1,argv+1);
110
for (int i=0; i<args.size(); i++) {
113
else if (args[i]=="-d") // OBSOLETED
115
else if (args[i]=="-dd" && i<args.size()-1) {
116
cmp.f_ddist=strtod(args[i+1]);
117
cmp.f_idist=strtol(args[i+1]);
120
else if (args[i]=="-o" && i<args.size()-1)
122
else if (args[i]=="-h") {
123
vbmaskcompare_help();
126
else if (args[i]=="-v") {
127
vbmaskcompare_version();
131
filelist.push_back(args[i]);
134
if (filelist.size()!=2) {
135
vbmaskcompare_help();
139
if (cmp.load_masks(filelist[0],filelist[1])) {
140
printf("[E] vbmaskcompare: couldn't load masks (or discrepant sizes)\n");
143
// printf("[I] vbmaskcompare: checking volumes\n");
145
// cmp.check_volumes2(); // little extra work to establish bounding box
146
if (cmp.surfaceflag) {
147
printf("[I] vbmaskcompare: checking surfaces\n");
148
cmp.check_surfaces();
150
cmp.check_discrepants();
152
if (cmp.stem.size()) {
153
cmp.write_discrepants();
170
VBComp::load_masks(string m1,string m2)
178
if (mask1.dimx!=mask2.dimx ||
179
mask1.dimy!=mask2.dimy ||
180
mask1.dimz!=mask2.dimz)
186
VBComp::check_volumes2()
188
// bool found1=0,found2=0;
192
for (int k=0; k<mask1.dimz; k++) {
193
for (int j=0; j<mask1.dimy; j++) {
194
for (int i=0; i<mask1.dimx; i++) {
195
v1=mask1.testValue(i,j,k);
196
v2=mask2.testValue(i,j,k);
197
if (v1) r1.include(i,j,k);
198
if (v2) r2.include(i,j,k);
206
VBComp::check_volumes()
209
for (int k=0; k<mask1.dimz; k++) {
210
for (int j=0; j<mask1.dimy; j++) {
211
for (int i=0; i<mask1.dimx; i++) {
212
v1=(int)mask1.testValue(i,j,k);
213
v2=(int)mask2.testValue(i,j,k);
216
nondiscrepants.add(i,j,k,1);
219
unique1.add(i,j,k,0.0);
223
unique2.add(i,j,k,0.0);
229
allvoxels1.add(i,j,k,0.0);
230
if (mask1.is_surface(i,j,k))
231
surface1.add(i,j,k,0.0);
234
allvoxels2.add(i,j,k,0.0);
235
if (mask2.is_surface(i,j,k))
236
surface2.add(i,j,k,0.0);
241
m_union=m_intersection+m_just1+m_just2;
244
// here's my understanding of the algorithm. matching surface voxels
245
// count as zeros. for unmatched surface voxels, that don't overlap
246
// with the other mask, find the nearest surface voxel on the other
250
VBComp::check_surfaces()
253
// distances for first surface (including matching surface voxels)
254
// for (int i=0; i<(int)surface1.size(); i++) {
255
for (VI vox1=surface1.begin(); vox1!=surface1.end(); vox1++) {
256
mindist=mask1.dimx+mask1.dimy+mask1.dimz; // suitably large value
257
for (VI vox2=surface2.begin();vox2!=surface2.end(); vox2++) {
258
int dist=(int)round(voxeldistance(vox1->second,vox2->second));
263
distances.push_back(0);
264
if (mindist>0 && !(allvoxels2.contains(vox1->second.x,vox1->second.y,vox1->second.z)))
265
distances.push_back(mindist);
267
// distances for second surface (excluding matching surface voxels)
268
// for (int i=0; i<(int)surface2.size(); i++) {
269
for (VI vox2=surface2.begin(); vox2!=surface2.end(); vox2++) {
270
mindist=mask1.dimx+mask1.dimy+mask1.dimz; // suitably large value
271
for (VI vox1=surface1.begin(); vox1!=surface1.end(); vox1++) {
272
int dist=(int)round(voxeldistance(vox1->second,vox2->second));
276
if (mindist!=0 && !(allvoxels1.contains(vox2->second.x,vox2->second.y,vox2->second.z)))
277
distances.push_back(0-mindist);
280
for (size_t i=0; i<distances.size(); i++)
281
meandist+=distances[i];
282
meandist/=distances.size();
283
printf("[I] vbmaskcompare: mean inter-surface distance: %g\n",meandist);
288
VBComp::check_discrepants()
290
m_discrepant1=m_discrepant2=0;
291
discrepants1.clear();
292
discrepants2.clear();
294
for (VI vox1=unique1.begin(); vox1!=unique1.end(); vox1++) {
296
int x1=vox1->second.x-f_idist;
297
int x2=vox1->second.x+f_idist;
298
int y1=vox1->second.y-f_idist;
299
int y2=vox1->second.y+f_idist;
300
int z1=vox1->second.z-f_idist;
301
int z2=vox1->second.z+f_idist;
302
for (int i=x1; i<=x2; i++) {
303
for (int j=y1; j<=y2; j++) {
304
for (int k=z1; k<=z2; k++) {
305
if (!(mask2.testValue(i,j,k))) continue;
306
dist=voxeldistance(i,j,k,vox1->second.x,vox1->second.y,vox1->second.z);
314
if (mindist-f_ddist>DBL_MIN) {
316
discrepants1.add(vox1->second);
319
nondiscrepants.add(vox1->second);
321
for (VI vox2=unique2.begin(); vox2!=unique2.end(); vox2++) {
323
int x1=vox2->second.x-f_idist;
324
int x2=vox2->second.x+f_idist;
325
int y1=vox2->second.y-f_idist;
326
int y2=vox2->second.y+f_idist;
327
int z1=vox2->second.z-f_idist;
328
int z2=vox2->second.z+f_idist;
329
for (int i=x1; i<=x2; i++) {
330
for (int j=y1; j<=y2; j++) {
331
for (int k=z1; k<=z2; k++) {
332
if (!(mask1.testValue(i,j,k))) continue;
333
dist=voxeldistance(i,j,k,vox2->second.x,vox2->second.y,vox2->second.z);
341
if (mindist-f_ddist>DBL_MIN) {
343
discrepants2.add(vox2->second);
346
nondiscrepants.add(vox2->second);
351
VBComp::write_discrepants()
355
cb.SetVolume(mask1.dimx,mask1.dimy,mask1.dimz,vb_byte);
357
for (VI vox=discrepants1.begin(); vox!=discrepants1.end(); vox++)
358
cb.setValue<char>(vox->second.x,vox->second.y,vox->second.z,1);
359
if (discrepants1.size()==0)
360
cout << format("[W] vbmaskcompare: no discrepant voxels in first mask\n");
362
replace_string(fname,"XXX","d1");
363
if (cb.WriteFile(fname))
364
cout << format("[E] vbmaskcompare: couldn't write output file %s\n") % fname;
366
cout << format("[I] vbmaskcompare: wrote output file %s\n") % fname;
369
for (VI vox=discrepants2.begin(); vox!=discrepants2.end(); vox++)
370
cb.setValue<char>(vox->second.x,vox->second.y,vox->second.z,1);
371
if (discrepants2.size()==0)
372
cout << format("[W] vbmaskcompare: no discrepant voxels in second mask\n");
374
replace_string(fname,"XXX","d2");
375
if (cb.WriteFile(fname))
376
cout << format("[E] vbmaskcompare: couldn't write output file %s\n") % fname;
378
cout << format("[I] vbmaskcompare: wrote output file %s\n") % fname;
381
for (VI vox=nondiscrepants.begin(); vox!=nondiscrepants.end(); vox++)
382
cb.setValue<char>(vox->second.x,vox->second.y,vox->second.z,1);
383
if (nondiscrepants.size()==0)
384
cout << format("[W] vbmaskcompare: no nondiscrepant voxels anywhere\n");
386
replace_string(fname,"XXX","nond");
387
if (cb.WriteFile(fname))
388
cout << format("[E] vbmaskcompare: couldn't write output file %s\n") % fname;
390
cout << format("[I] vbmaskcompare: wrote output file %s\n") % fname;
397
printf("[E] vbmaskcompare: no voxels found in either mask\n");
400
printf("[I] vbmaskcompare: total voxels: %d\n",m_union+m_neither);
401
printf("[I] vbmaskcompare: %s: %d total, %d unique\n",
402
mask1.GetFileName().c_str(),m_intersection+m_just1,m_just1);
403
printf("[I] vbmaskcompare: %s: %d total, %d unique\n",
404
mask2.GetFileName().c_str(),m_intersection+m_just2,m_just2);
405
printf("[I] vbmaskcompare: total mask voxels (mask union): %d\n",m_union);
406
printf("[I] vbmaskcompare: common (mask intersection): %d\n",m_intersection);
407
float dice=(2.0*m_intersection)/(m_union+m_intersection);
408
printf("[I] vbmaskcompare: dice overlap: %.4f\n",dice);
409
float disc=(float)abs(m_union-m_intersection)/(float)m_union;
410
printf("[I] vbmaskcompare: percent non-overlapping: %.2f%%\n",100.0*disc);
411
printf("[I] vbmaskcompare: discrepant voxels in %s: %d\n",
412
mask1.GetFileName().c_str(),m_discrepant1);
413
printf("[I] vbmaskcompare: discrepant voxels in %s: %d\n",
414
mask2.GetFileName().c_str(),m_discrepant2);
415
printf("[I] vbmaskcompare: total discrepant voxels: %d (%.2f%%)\n",m_discrepant1+m_discrepant2,
416
(float)(m_discrepant1+m_discrepant2)/(float)m_union);
422
cout << boost::format(myhelp) % vbversion;
426
vbmaskcompare_version()
428
printf("VoxBo vbmaskcompare (v%s)\n",vbversion.c_str());