3
dd                 @   sh  d Z ddlZddljZddlZddlZddl	m
Z
mZ ddlmZmZmZmZmZmZmZmZ ddlmZ ejdZG dd	 d	eZG d
d deZG dd deZG dd deZG dd deZG dd deZG dd deZG dd deZ G dd deZ!G dd deZ"G dd deZ#G dd deZ$G d d! d!eZ%G d"d# d#eZ&G d$d% d%eZ'dS )&z~
Image assessment algorithms. Typical overlap and error computation
measures to evaluate results from other processing units.
    N   )configlogging)SimpleInterfaceBaseInterfacetraitsTraitedSpecFileInputMultiPathBaseInterfaceInputSpec	isdefined)NipyBaseInterfaceznipype.interfacec            	   @   sL   e Zd ZeddddZeddddZejddddd	d
ddZedddZ	dS )DistanceInputSpecTz+Has to have the same dimensions as volume2.)exists	mandatorydescz+Has to have the same dimensions as volume1.eucl_mineucl_cog	eucl_mean
eucl_wmeaneucl_maxa  ""eucl_min": Euclidean distance between two closest points        "eucl_cog": mean Euclidian distance between the Center of Gravity        of volume1 and CoGs of volume2        "eucl_mean": mean Euclidian minimum distance of all volume2 voxels        to volume1        "eucl_wmean": mean Euclidian minimum distance of all volume2 voxels        to volume1 weighted by their values        "eucl_max": maximum over minimum Euclidian distances of all volume2        voxels to volume1 (also known as the Hausdorff distance))r   
usedefaultz(calculate overlap only within this mask.)r   r   N)
__name__
__module____qualname__r	   volume1volume2r   Enummethodmask_volume r    r    ;/tmp/pip-build-7vycvbft/nipype/nipype/algorithms/metrics.pyr      s   	r   c               @   s2   e Zd Zej ZejddZejddZe	 Z
dS )DistanceOutputSpec   )shapeN)r#   )r#   )r   r   r   r   FloatdistanceArraypoint1point2r	   	histogramr    r    r    r!   r"   :   s   r"   c               @   s^   e Zd ZdZeZeZdZdd Z	dd Z
dd Zd	d
 ZdddZdd Zdd Zdd ZdS )Distancez(Calculates distance between two volumes.zhist.pdfc             C   s*   ddl m} ||}tj|tj|}|S )Nr   )binary_erosion)Zscipy.ndimage.morphologyr,   nplogical_andZlogical_not)selfdatar,   ZerodedZborderr    r    r!   _find_borderI   s    zDistance._find_borderc             C   st   t |jdkr*|d d d d d d df }tjtj|}tj|tj|jd f}tj||}|d dd d f S )N   r      r#   )lenr$   r-   vstackZnonzeroonesdot)r/   r0   affineindicesZcoordinatesr    r    r!   _get_coordinatesP   s    zDistance._get_coordinatesc             C   s   ddl m}m} tj|jjt}| j|}tj|jjt}| j|}| j	||j
}	| j	||j
}
||	j|
j}tjtj||j\}}||	j|d d f |
j|d d f |	j|d d f |
j|d d f fS )Nr   )cdist	euclidean)scipy.spatial.distancer;   r<   r-   
asanyarraydataobjastypeboolr1   r:   r8   TZunravel_indexZargminr$   )r/   nii1nii2r;   r<   	origdata1border1	origdata2border2set1_coordinatesset2_coordinatesdist_matrixr(   r)   r    r    r!   	_eucl_minX   s    

$zDistance._eucl_minc             C   s6  ddl m} ddlm}m} tj|j}tj|dktj	| @ }tj
||jdd}tj|tj
dgf}tj|j|d dd d f }tj|j}	tj|	dktj	|	 @ }	||	\}
}tjd|f}x2t|D ]&}tj
||	|
|d |d d|f< qW tj|j|d dd d f }||j|j}tj|S )Nr   )r;   )center_of_masslabelr3   r#   r2   )r=   r;   Zscipy.ndimage.measurementsrM   rN   r-   r>   r?   rintisnanarrayreshaper5   r7   r8   r6   rangerB   mean)r/   rC   rD   r;   rM   rN   rE   Zcog_tZ
cog_t_coorrG   Zlabeled_dataZn_labelsZcogsiZ	cogs_coorrK   r    r    r!   	_eucl_cogm   s     &zDistance._eucl_cogFc             C   s   ddl m} tj|jjt}| j|}tj|jjt}| j||j	}| j||j	}	||j
|	j
}
tj|
dd}dd l}|jtjdd dd lj}|j  |j|dddd	 |j| j |j  |j  |rtj||j| jd
S tj|S d S )Nr   )r;   )axisZ	executionZmatplotlib_backend2   r3   Zgreen)ZnormedZ	facecolor)weights)r=   r;   r-   r>   r?   r@   rA   r1   r:   r8   rB   amin
matplotlibZuser   getZmatplotlib.pyplotZpyplotfigurehistZsavefig_hist_filenameZclfcloseaverageflatrU   )r/   rC   rD   weightedr;   rE   rF   rG   rI   rJ   rK   Zmin_dist_matrixr\   Zpltr    r    r!   
_eucl_mean   s&    

zDistance._eucl_meanc             C   s4  ddl m} tj|j}tj|dktj| @ }tj|j}tj|dktj| @ }t| jj	rtjt
j| jj	j}tj|dktj| @ }tj||}tj||}|j dks|j dkrtjS | j|}| j|}| j||j}	| j||j}
||	j|
j}tjtj|ddtj|ddf}tj|S )Nr   )r;   )rX   r3   )r=   r;   r-   r>   r?   rP   rQ   r   inputsr   nbloadr.   maxnanr1   r:   r8   rB   Zconcatenater[   )r/   rC   rD   r;   rE   rG   maskdatarF   rH   rI   rJ   Z	distancesZminsr    r    r!   	_eucl_max   s&    

"zDistance._eucl_maxc             C   s   t j| jjdd}t j| jjdd}| jjdkrJ| j||\| _| _| _	nr| jjdkrf| j
||| _nV| jjdkr| j||| _n:| jjdkr| j||dd| _n| jjd	kr| j||| _|S )
NF)Zmmapr   r   r   r   T)rd   r   )rg   rh   rf   r   r   r   rL   	_distance_point1_point2rW   re   rl   )r/   runtimerC   rD   r    r    r!   _run_interface   s    zDistance._run_interfacec             C   sZ   | j  j }| j|d< | jjdkr8| j|d< | j|d< n| jjdkrVtjj	| j
|d< |S )	Nr&   r   r(   r)   r   r   r*   )r   r   )_outputsr]   rm   rf   r   rn   ro   ospathabspathr`   )r/   outputsr    r    r!   _list_outputs   s    

zDistance._list_outputsN)F)r   r   r   __doc__r   
input_specr"   output_specr`   r1   r:   rL   rW   re   rl   rq   rw   r    r    r    r!   r+   A   s   
r+   c               @   sz   e Zd ZeddddZeddddZedddZejddddd	Z	ed
ddZ
ejddddddZejddddddZdS )OverlapInputSpecTz+Has to have the same dimensions as volume2.)r   r   r   z+Has to have the same dimensions as volume1.z(calculate overlap only within this mask.)r   r   Fzconsider zeros as a label)r   r   r   zdiff.nii)r   nonevolumesquared_volz'none': no class-overlap weighting is performed. 'volume': computed class-overlaps are weighted by class volume 'squared_vol': computed class-overlaps are weighted by the squared volume of the class)r   r   Zvoxelmmzunits for volumes)r   r   r   N)r   r   r   r	   r   r   r   r   ZBool
bg_overlapout_filer   	weighting	vol_unitsr    r    r    r!   r{      s    r{   c               @   s   e Zd ZejddZejddZejej ddZejej ddZ	ejddZ
ejej ddZejej ddZed	d
dZdS )OverlapOutputSpeczaveraged jaccard index)r   zaveraged dice indexzthe Jaccard index (JI) per ROIzthe Dice index (DI) per ROIzaveraged volume differencezvolume differences of ROIszdetected labelsTzerror map of differences)r   r   N)r   r   r   r   r%   jaccarddiceListroi_jiroi_divolume_differenceroi_voldiffZIntlabelsr	   	diff_filer    r    r    r!   r      s   r   c               @   s0   e Zd ZdZeZeZdd Zdd Z	dd Z
dS )	Overlapa1  
    Calculates Dice and Jaccard's overlap measures between two ROI maps.
    The interface is backwards compatible with the former version in
    which only binary files were accepted.

    The averaged values of overlap indices can be weighted. Volumes
    now can be reported in :math:`mm^3`, although they are given in voxels
    to keep backwards compatibility.

    Example
    -------

    >>> overlap = Overlap()
    >>> overlap.inputs.volume1 = 'cont1.nii'
    >>> overlap.inputs.volume2 = 'cont2.nii'
    >>> res = overlap.run() # doctest: +SKIP

    c             C   sH   ddl m}m} ||d}tj|p,tj|s2dS d|| |j|j S )Nr   )r   r   r3   )r=   r   r   r-   anyrc   )r/   Z	booldata1Z	booldata2r   r   r   methodsr    r    r!   _bool_vec_dissimilarity  s
    
zOverlap._bool_vec_dissimilarityc             C   s  t j| jj}t j| jj}d}| jjdkrDtj|jj	 d d }tj
|j}d|tj|dk tj|< t|j }|jtj|}tj
|jjtj|}d|tj|dk tj|< t| jjrtj
t j| jjj}tj|dktj| }d|| < d|| < g }	g }
g }tj||dk jdj }| jjrF|jdd x`|D ]X}|	j| j||k||kdd |
j|t|||k   |j|t|||k   qLW tg g d}tj|	|d< d	|d  |d d  |d
< tjt|
ftjd}| jj dkr&|tj|
 }| jj dkr&|d }|tj!| }tj"|j#}d||| dk< t j$t j%||j&|j| jj' || _(|| _)tj|
tj| tj|
 | _*t+tj!||d
  d| _,t+tj!||d  d| _-tj!|| j* | _.|S )Ng      ?r   r#   r   r3   r   )r   )r   r   g       @r   )dtyper|   r~   r      rO   )/rg   rh   rf   r   r   r   r-   prodheaderZ	get_zoomsr>   r?   
logical_orrQ   intri   r@   Zmin_scalar_typer   r   uniquerS   tolistr   insertappendr   r4   dictrR   r6   float32r   sumzerosr$   saveNifti1Imager8   r   _labels	_ove_rois	_vol_roisround_dice_jaccard_volume)r/   rp   rC   rD   ZscaleZdata1Zmax1Zdata2rk   resZvolumes1Zvolumes2r   lresultsrZ   Z	both_datar    r    r!   rq   '  s^    



  zOverlap._run_interfacec             C   s~   | j  j }| j|d< | j|d< | j|d< | j|d< | jd j |d< | jd j |d< | jj |d< t	j
j| jj|d< |S )	Nr   r   r   r   r   r   r   r   )rr   r]   r   r   r   r   r   r   r   rs   rt   ru   rf   r   )r/   rv   r    r    r!   rw   h  s    



zOverlap._list_outputsN)r   r   r   rx   r{   ry   r   rz   r   rq   rw   r    r    r    r!   r     s   Ar   c               @   sb   e Zd ZeedddddZeedddddZedddZej	dd	d
dddZ
eddddZdS )FuzzyOverlapInputSpecT)r   z8Reference image. Requires the same dimensions as in_tst.)r   r   z3Test image. Requires the same dimensions as in_ref.z"calculate overlap only within mask)r   r   r|   r}   r~   z'none': no class-overlap weighting is performed. 'volume': computed class-overlaps are weighted by class volume 'squared_vol': computed class-overlaps are weighted by the squared volume of the class)r   r   zdiff.niiz-alternative name for resulting difference-map)r   r   N)r   r   r   r
   r	   in_refin_tstin_maskr   r   r   r   r    r    r    r!   r   v  s&   r   c               @   sH   e Zd ZejddZejddZejej ddZejej ddZ	dS )FuzzyOverlapOutputSpecz*Fuzzy Jaccard Index (fJI), all the classes)r   z'Fuzzy Dice Index (fDI), all the classesz0Array containing the fJIs of each computed classz0Array containing the fDIs of each computed classN)
r   r   r   r   r%   r   r   r   	class_fji	class_fdir    r    r    r!   r     s   r   c               @   s    e Zd ZdZeZeZdd ZdS )FuzzyOverlapa  Calculates various overlap measures between two maps, using the fuzzy
    definition proposed in: Crum et al., Generalized Overlap Measures for
    Evaluation and Validation in Medical Image Analysis, IEEE Trans. Med.
    Ima. 25(11),pp 1451-1461, Nov. 2006.

    in_ref and in_tst are lists of 2/3D images, each element on the list
    containing one volume fraction map of a class in a fuzzy partition
    of the domain.

    Example
    -------

    >>> overlap = FuzzyOverlap()
    >>> overlap.inputs.in_ref = [ 'ref_class0.nii', 'ref_class1.nii' ]
    >>> overlap.inputs.in_tst = [ 'tst_class0.nii', 'tst_class1.nii' ]
    >>> overlap.inputs.weighting = 'volume'
    >>> res = overlap.run() # doctest: +SKIP
    c             C   sd  t j| jjj}t j| jjj}|j|jks@td|j|jf |jd }tj	|t
d}t| jjrtjt j| jjjdk}tj|dtjf |d}|j|jkst|| }|| }tj|dk rtjd tj|}tj|dk  rtjd tj|}tj|d	kr"tjd
 ||j  }tj|d	krHtjd ||j  }tjtj||jd|f}tjtj||jd|f}|jdd|jdd }tj	|td}	| jjdkrtj|| dkddjd|f}
d	|
 }	| jjdkr|	d }	|	tj|	 }	d| |d	  }t|	j|| jd< t|	j|| jd< dd |D | jd< dd |D | jd< |S )Nz3Size of "in_tst" %s must match that of "in_ref" %s.r3   )r   r   .g        zFNegative values encountered in "in_ref" input, taking absolute values.zFNegative values encountered in "in_tst" input, taking absolute values.g      ?z@Values greater than 1.0 found in "in_ref" input, scaling values.z@Values greater than 1.0 found in "in_tst" input, scaling values.)rX   r|   r~   r   g       @r   r   c             S   s   g | ]}t |qS r    )float).0vr    r    r!   
<listcomp>  s    z/FuzzyOverlap._run_interface.<locals>.<listcomp>r   c             S   s   g | ]}t |qS r    )r   )r   r   r    r    r!   r      s    r   rO   rO   rO   rO   rO   ) rg   Zconcat_imagesrf   r   r?   r   r$   RuntimeErrorr-   Z	ones_likerA   r   r   r>   rh   repeatZnewaxisAssertionErrorr   ifloggerwarningabsri   Z
atleast_2dZminimumrS   maximumr   r   r   r7   Z_results)r/   rp   ZrefdataZtstdataZncompmaskZ
numeratorsZdenominatorsZjaccardsrZ   ZvolumesZdicesr    r    r!   rq     s\    


 zFuzzyOverlap._run_interfaceN)	r   r   r   rx   r   ry   r   rz   rq   r    r    r    r!   r     s   r   c               @   sR   e Zd ZeddddZeddddZedddZejddd	ddd
Z	eddZ
dS )ErrorMapInputSpecTz8Reference image. Requires the same dimensions as in_tst.)r   r   r   z3Test image. Requires the same dimensions as in_ref.z(calculate overlap only within this mask.)r   r   sqeuclideanr<   z0error map metric (as implemented in scipy cdist))r   r   r   zName for the output file)r   N)r   r   r   r	   r   r   r   r   r   metricout_mapr    r    r    r!   r     s    r   c               @   s$   e Zd ZedddZejddZdS )ErrorMapOutputSpecTzresulting error map)r   r   z'Average distance between volume 1 and 2)r   N)r   r   r   r	   r   r   r%   r&   r    r    r    r!   r     s   r   c               @   s,   e Zd ZdZeZeZdZdd Z	dd Z
dS )ErrorMapa  Calculates the error (distance) map between two input volumes.

    Example
    -------

    >>> errormap = ErrorMap()
    >>> errormap.inputs.in_ref = 'cont1.nii'
    >>> errormap.inputs.in_tst = 'cont2.nii'
    >>> res = errormap.run() # doctest: +SKIP
     c             C   sJ  t j| jj}tj|j}tjt j| jjj}|j|jks@t	d}|j
}|jdkrl|j
d }|j
d d }t| jjrtjt j| jjj}||j
krtdt|j
t|f ntj|d}|jd}tj|dk}	|jd||	 jtj}
|jd||	 jtj}|
| }| jjdkrL|d }|dkr@tj|dd}n
tj|}n| jjdkrjtjj|dd}tj|tjd	}|||	< tj|| _|j|}|jj }|jtj d
|d< |j| t| jj st!j"t!j#| jj\}}|dkrt!j"|\}}|| }t!j$|d | | _%n
| jj | _%t j&|jtj|j'|j(| j% |S )Nr3   r2   z`Mask should match volume shape,                                    mask is %s and volumes are %s)r$   r   r   )rX   r<   )r      Z	data_typez.gzZ_errmaprO   rO   rO   rO   rO   ))rg   rh   rf   r   r-   Zsqueezer?   r   ndimr   r$   r   r   r>   r   listr6   rS   wherer@   r   r   r   ZlinalgZnormZ
zeros_likerb   rm   r   copyZset_data_dtypeZset_data_shaper   opsplitextbasenameru   	_out_filer   r8   to_filename)r/   rp   Znii_refZref_dataZtst_datacompsZmapshapeZmskZ	mskvectorZmsk_idxsZ	refvectorZ	tstvectorZ
diffvectorZ	errvectorZerrvectorexpZerrmaphdrfnameextZext2r    r    r!   rq   /  s^    









zErrorMap._run_interfacec             C   s$   | j  j }| j|d< | j|d< |S )Nr   r&   )rz   r]   r   rm   )r/   rv   r    r    r!   rw   x  s    

zErrorMap._list_outputsN)r   r   r   rx   r   ry   r   rz   r   rq   rw   r    r    r    r!   r     s   
Ir   c               @   sf   e Zd ZeddddZeddddZedddZedddZej	ej
dddd	d
dej dddZdS )SimilarityInputSpecTz3D/4D volume)r   r   r   z	3D volume)r   r   cccrZcrl1miZnmiZslra  str or callable
Cost-function for assessing image similarity. If a string,
one of 'cc': correlation coefficient, 'cr': correlation
ratio, 'crl1': L1-norm based correlation ratio, 'mi': mutual
information, 'nmi': normalized mutual information, 'slr':
supervised log-likelihood ratio. If a callable, it should
take a two-dimensional array representing the image joint
histogram as an input and return a float.)r   r   N)r   r   r   r	   r   r   mask1mask2r   ZEitherr   Callabler   r    r    r    r!   r     s   r   c               @   s   e Zd ZejejddZdS )SimilarityOutputSpecz1Similarity between volume 1 and 2, frame by frame)r   N)r   r   r   r   r   r%   
similarityr    r    r    r!   r     s   r   c               @   s(   e Zd ZdZeZeZdd Zdd Z	dS )
Similaritya  Calculates similarity between two 3D or 4D volumes. Both volumes have to be in
    the same coordinate system, same space within that coordinate system and
    with the same voxel dimensions.

    .. note:: This interface is an extension of
              :py:class:`nipype.interfaces.nipy.utils.Similarity` to support 4D files.
              Requires :py:mod:`nipy`

    Example
    -------
    >>> from nipype.algorithms.metrics import Similarity
    >>> similarity = Similarity()
    >>> similarity.inputs.volume1 = 'rc1s1.nii'
    >>> similarity.inputs.volume2 = 'rc1s2.nii'
    >>> similarity.inputs.mask1 = 'mask.nii'
    >>> similarity.inputs.mask2 = 'mask.nii'
    >>> similarity.inputs.metric = 'cr'
    >>> res = similarity.run() # doctest: +SKIP
    c             C   s6  ddl m} ddlm} tj| jj}tj| jj}t	|j
}|dksN|dkrZ|g}|g}|dkrvtj|}tj|}|dk s|dkrtd| t| jjrtjtj| jjjdk}	nd }	t| jjrtjtj| jjjdk}
nd }
g | _x@t||D ]2\}}|||| jj|	|
d	}| jj|j|  qW |S )
Nr   )HistogramRegistration)Affiner#   r   r2   z2Image dimensions not supported (detected %dD file)r3   )Zfrom_imgZto_imgr   Z	from_maskZto_mask)Z3nipy.algorithms.registration.histogram_registrationr   Z#nipy.algorithms.registration.affiner   rg   rh   rf   r   r   r4   r$   Zfour_to_threer   r   r   r-   r>   r?   r   _similarityzipr   r   eval)r/   rp   r   r   Zvol1_niiZvol2_niiZdimsZvols1Zvols2r   r   Zts1Zts2Zhistregr    r    r!   rq     s<    



zSimilarity._run_interfacec             C   s   | j  j }| j|d< |S )Nr   )rr   r]   r   )r/   rv   r    r    r!   rw     s    
zSimilarity._list_outputsN)
r   r   r   rx   r   ry   r   rz   rq   rw   r    r    r    r!   r     s
   /r   )(rx   rs   os.pathrt   r   Znibabelrg   numpyr-   r   r   r   Zinterfaces.baser   r   r   r   r	   r
   r   r   Zinterfaces.nipy.baser   	getLoggerr   r   r"   r+   r{   r   r   r   r   r   r   r   r   r   r   r   r    r    r    r!   <module>   s0   
(

 n!b`