@@ -442,16 +442,18 @@ class FuzzyOverlapInputSpec(BaseInterfaceInputSpec):
442
442
desc = "Reference image. Requires the same dimensions as in_tst." )
443
443
in_tst = InputMultiPath ( File (exists = True ), mandatory = True ,
444
444
desc = "Test image. Requires the same dimensions as in_ref." )
445
- mask_volume = File ( exists = True , desc = "calculate overlap only within this mask." )
446
- weighting = traits . Enum ( "none" , "volume" , desc = '""none": no class-overlap weighting is performed \
447
- "volume ": computed class-overlaps are weighted by class volume' ,usedefault = True )
445
+ weighting = traits . Enum ( "none" , "volume" , "squared_vol" , desc = '""none": no class- overlap weighting is performed \
446
+ "volume": computed class-overlaps are weighted by class volume \
447
+ "squared_vol ": computed class-overlaps are weighted by the squared volume of the class ' ,usedefault = True )
448
448
out_file = File ("diff.nii" , usedefault = True )
449
449
450
450
451
451
class FuzzyOverlapOutputSpec (TraitedSpec ):
452
452
jaccard = traits .Float ()
453
453
dice = traits .Float ()
454
454
diff_file = File (exists = True )
455
+ class_ji = traits .List ( traits .Float () )
456
+ class_dsc = traits .List ( traits .Float () )
455
457
456
458
457
459
class FuzzyOverlap (BaseInterface ):
@@ -476,44 +478,59 @@ class FuzzyOverlap(BaseInterface):
476
478
input_spec = FuzzyOverlapInputSpec
477
479
output_spec = FuzzyOverlapOutputSpec
478
480
479
- def _bool_vec_dissimilarity (self , booldata1 , booldata2 , method ):
480
- methods = {"dice" : dice , "jaccard" : jaccard }
481
- if not (np .any (booldata1 ) or np .any (booldata2 )):
482
- return 0
483
- return 1 - methods [method ](booldata1 .flat , booldata2 .flat )
484
-
485
481
def _run_interface (self , runtime ):
486
482
ncomp = len (self .inputs .in_ref )
487
483
assert ( ncomp == len (self .inputs .in_tst ) )
488
484
weights = np .ones ( shape = ncomp )
489
485
490
- img_ref = np .array ( [ nib .load ( fname ).get_data () for fname in self .inputs .in_ref ] )
491
- img_tst = np .array ( [ nib .load ( fname ).get_data () for fname in self .inputs .in_tst ] )
486
+ img_ref = np .array ( [ nb .load ( fname ).get_data () for fname in self .inputs .in_ref ] )
487
+ img_tst = np .array ( [ nb .load ( fname ).get_data () for fname in self .inputs .in_tst ] )
488
+
489
+
490
+ msk = np .sum (img_ref , axis = 0 )
491
+ msk [msk > 0 ] = 1.0
492
+ tst_msk = np .sum (img_tst , axis = 0 )
493
+ tst_msk [tst_msk > 0 ] = 1.0
492
494
493
495
#check that volumes are normalized
494
- img_ref = img_ref / np .sum ( img_ref , axis = 0 )
495
- img_tst = img_tst / np .sum ( img_tst , axis = 0 )
496
+ # img_ref[:][msk>0] = img_ref[:][msk>0] / ( np.sum( img_ref, axis=0 ))[msk>0]
497
+ # img_tst[tst_msk>0] = img_tst[tst_msk>0] / np.sum( img_tst, axis=0 )[tst_msk>0]
496
498
497
- num = float ( np .minimum ( img_ref , img_test ) )
498
- ddr = float ( np .maximum ( img_ref , img_test ) )
499
- both_data = num / ddr
500
-
501
- jaccards = np .sum ( num , axis = 0 ) / np .sum ( ddr , axis = 0 )
502
- dices = 2.0 * jaccards / (1.0 + jaccards )
499
+ self ._jaccards = []
500
+ volumes = []
501
+
502
+ diff_im = np .zeros ( img_ref .shape )
503
+
504
+ for ref_comp , tst_comp , diff_comp in zip ( img_ref , img_tst , diff_im ):
505
+ num = np .minimum ( ref_comp , tst_comp )
506
+ ddr = np .maximum ( ref_comp , tst_comp )
507
+ diff_comp [ddr > 0 ]+= 1.0 - (num [ddr > 0 ]/ ddr [ddr > 0 ])
508
+ self ._jaccards .append ( np .sum ( num ) / np .sum ( ddr ) )
509
+ volumes .append ( np .sum ( ref_comp ) )
510
+
511
+ self ._dices = 2.0 * np .array (self ._jaccards ) / (np .array (self ._jaccards ) + 1.0 )
503
512
504
513
if self .inputs .weighting != "none" :
505
- weights = 1.0 / np .sum ( img_ref , axis = 0 )
514
+ weights = 1.0 / np .array (volumes )
515
+ if self .inputs .weighting == "squared_vol" :
516
+ weights = weights ** 2
517
+
518
+ weights = weights / np .sum ( weights )
506
519
507
- if self . inputs . weighting == "squared_vol" :
508
- weights = weights ** 2
520
+ setattr ( self , '_jaccard' , np . sum ( weights * self . _jaccards ) )
521
+ setattr ( self , '_dice' , np . sum ( weights * self . _dices ) )
509
522
510
- setattr ( self , '_jaccard' , np .sum ( weights * jaccards ) / np .sum ( weights ) )
511
- setattr ( self , '_dice' , np .sum ( weights * dices ) / np .sum ( weights ) )
523
+
524
+ diff = np .zeros ( diff_im [0 ].shape )
525
+
526
+ for w ,ch in zip (weights ,diff_im ):
527
+ ch [msk == 0 ] = 0
528
+ diff += w * ch
512
529
513
- # todo, this is a N+1 dimensional file, update header and affine.
514
- nb .save (nb .Nifti1Image (both_data , nii1 .get_affine (),
515
- nii1 .get_header ()), self .inputs .out_file )
530
+ nb .save (nb .Nifti1Image (diff , nb .load ( self .inputs .in_ref [0 ]).get_affine (),
531
+ nb .load ( self .inputs .in_ref [0 ]).get_header ()), self .inputs .out_file )
516
532
533
+
517
534
return runtime
518
535
519
536
def _list_outputs (self ):
@@ -522,6 +539,8 @@ def _list_outputs(self):
522
539
outputs [method ] = getattr (self , '_' + method )
523
540
#outputs['volume_difference'] = self._volume
524
541
outputs ['diff_file' ] = os .path .abspath (self .inputs .out_file )
542
+ outputs ['class_ji' ] = np .array (self ._jaccards ).astype (float ).tolist ();
543
+ outputs ['class_dsc' ]= self ._dices .astype (float ).tolist ();
525
544
return outputs
526
545
527
546
0 commit comments