Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] AFNI NIDM export using the minimal JSON API #3

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions bin/nidm-results_fsl → bin/nidmafni
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ specification. The path to feat directory must be passed as first argument.

import sys
import os
from nidmfsl.fsl_exporter.fsl_exporter import FSLtoNIDMExporter
from afni_exporter.afni_exporter import AFNItoNIDMExporter

if __name__ == "__main__":
# Remove first argument (script name)
num_args = len(sys.argv)-1
sys.argv.pop(0)
args = sys.argv

usage = "Usage: python nidm-results_fsl.py path/to/feat/dir"
usage = "Usage: nidmafni path/to/feat/dir"

if num_args != 1:
raise Exception(usage)
Expand Down
369 changes: 369 additions & 0 deletions bin/nidmjsonafni
Original file line number Diff line number Diff line change
@@ -0,0 +1,369 @@
#!/usr/bin/python
"""
Export neuroimaging results created with FSL feat following NIDM-Results

DC code convergence
"""
import json
import os
import sys
from subprocess import check_call, check_output
from collections import OrderedDict
import argparse
import itertools
from nidmresults.graph import *

# nidmjsonafni ttest++_result+tlrc.BRIK

if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('-inset')
parser.add_argument('-mask')
parser.add_argument('-mask_from_hdr')
parser.add_argument('-out_mask')
parser.add_argument('-ithr')
parser.add_argument('-idat')
parser.add_argument('-1sided', nargs=2, dest='onesided')
parser.add_argument('-2sided')
parser.add_argument('-bisided')
parser.add_argument('-within_range')
parser.add_argument('-NN')
parser.add_argument('-clust_nvox')
parser.add_argument('-clust_vol')
parser.add_argument('-pref_map')
parser.add_argument('-pref_dat')
parser.add_argument('-1Dformat')
parser.add_argument('-no_1Dformat')
parser.add_argument('-summarize')
parser.add_argument('-nosum')
parser.add_argument('-quiet')
parser.add_argument('-outvol_if_no_clust')
parser.add_argument('-orient')
parser.add_argument('-noabs')
parser.add_argument('-binary')

args = parser.parse_args()

resbrik = args.inset

nidm = OrderedDict()
nidm['NIDMResults_version'] = '1.3.0'
nidm['NIDMResultsExporter_type'] = 'nidmresults-afni'
nidm['NIDMResultsExporter_softwareVersion'] = '0.0.1'

nidm['NeuroimagingAnalysisSoftware_type'] = "AFNI"
nidm['ImagingInstrument_type'] = 'nlx_MagneticResonanceImagingScanner'

ver = check_output([
# afni --version
'afni', '--version'])
nidm['NeuroimagingAnalysisSoftware_softwareVersion'] = str(ver)

# TODO rethink in NIDM
nidm['Data_grandMeanScaling'] = True
nidm['Data_targetIntensity'] = 100

# TODO design matrix
nidm['DesignMatrix_atLocation'] = "DesignMatrix.csv"
nidm['DesignMatrix_regressorNames'] = ["group"]

check_call([
# 3dAFNItoNIFTI -prefix <NAME> <RES_BRIK>[0]
'3dAFNItoNIFTI', '-prefix', 'ParameterEstimate.nii.gz', resbrik + '[0]'])

nidm['ParameterEstimateMaps'] = ["ParameterEstimate.nii.gz"]

nidm['ErrorModel_hasErrorDistribution'] = "obo_NormalDistribution"
nidm['ErrorModel_errorVarianceHomogeneous'] = True
# TODO: check
nidm['ErrorModel_varianceMapWiseDependence'] = "nidm_IndependentParameter"
nidm['ErrorModel_hasErrorDependence'] = "nidm_IndependentParameter"
nidm['ErrorModel_dependenceMapWiseDependence'] = "nidm_IndependentParameter"

nidm['ModelParameterEstimation_withEstimationMethod'] = "obo_OrdinaryLeastSquaresEstimation"

# TODO residualmeansquares --> for this 3dttest++ must be run with the -resid option
nidm['ResidualMeanSquaresMap_atLocation'] = args.mask # "TODO"

# TODO GrandMeanMap --> not needed --> 3dClac -expression 100
nidm['GrandMeanMap_atLocation'] = args.mask # "TODO"
nidm['MaskMap_atLocation'] = args.mask

# TODO CoordinateSpace_inWorldCoordinateSystem
nidm['CoordinateSpace_inWorldCoordinateSystem'] = "MNICoordinateSystem"

nidm['Contrasts'] = []
check_call([
# 3dAFNItoNIFTI -prefix <NAME> <RES_BRIK>[0]
'3dAFNItoNIFTI', '-prefix', 'Contrast.nii.gz', resbrik + '[0]'])
check_call([
# 3dAFNItoNIFTI -prefix <NAME> <RES_BRIK>[1]
'3dAFNItoNIFTI', '-prefix', 'TStatistic.nii.gz', resbrik + '[1]'])


# degrees of freedom
stat_args = check_output([
# 3dAttribute BRICK_STATAUX ttest++_result+tlrc
'3dAttribute', 'BRICK_STATAUX', resbrik])
dof = int(stat_args.split()[-1])

nidm['Contrasts'].append({
# TODO: do we want specific names?
"StatisticMap_contrastName": "group",
"ContrastWeightMatrix_value": [1],
"StatisticMap_statisticType": "obo_TStatistic",
# degrees of freedom
"StatisticMap_errorDegreesOfFreedom": dof,
"StatisticMap_atLocation": "TStatistic.nii.gz",
# TODO inworldcoordsyst
"StatisticMap_inWorldCoordinateSystem": "MNICoordinateSystem", # TODO
"ContrastMap_atLocation": "Contrast.nii.gz",
# TODO inworldcoordsyst
"ContrastMap_inWorldCoordinateSystem": "MNICoordinateSystem", #TODO
# TODO ContrastStandardErrorMap_atLocation
"ContrastStandardErrorMap_atLocation": args.mask
})

CONN_DICT = {
'1': 6,
'2': 18,
'3': 26
}

nidm['ClusterDefinitionCriteria_hasConnectivityCriterion'] = \
CONN_DICT[args.NN]
nidm['PeakDefinitionCriteria_minDistanceBetweenPeaks'] = 0
nidm['PeakDefinitionCriteria_maxNumberOfPeaksPerCluster'] = 1

# TODO: we might want to retreive info from 3dClustSim rather than setting
# thresholds to stat values.
nidm['Inferences'] = list()
nidm['Inferences'].append(dict())
nidm['Inferences'][0]['HeightThreshold_type'] = 'obo_Statistic'
nidm['Inferences'][0]['HeightThreshold_value'] = args.onesided[1]
nidm['Inferences'][0]['ExtentThreshold_type'] = 'obo_Statistic'
nidm['Inferences'][0]['ExtentThreshold_clusterSizeInVoxels'] = args.clust_nvox
# TODO: deal with two-tailed
nidm['Inferences'][0]["Inference_hasAlternativeHypothesis"] = "nidm_OneTailedTest"
nidm['Inferences'][0]["StatisticMap_contrastName"] = "group"
nidm['Inferences'][0]["SearchSpaceMaskMap_atLocation"] = args.mask
nidm['Inferences'][0]["ExcursionSetMap_atLocation"] = args.pref_dat + '.nii.gz'
nidm['Inferences'][0]["ClusterLabelsMap_atLocation"] = args.pref_map + '.nii.gz'

# CM -> center of mass
# min/max is a bounding box on the cluster

nidm['Inferences'][0]["Clusters"] = list()
nidm['Inferences'][0]["Clusters"].append(dict())
nidm['Inferences'][0]["Clusters"][0] = {
"SupraThresholdCluster_clusterSizeInVoxels": 37,
"SupraThresholdCluster_clusterSizeInResels": 0.2783889246953182,
"SupraThresholdCluster_pValueUncorrected": 0.004979532475540043,
"SupraThresholdCluster_pValueFWER": 0.0002553840091309434,
"SupraThresholdCluster_qValueFDR": 0.008299220792566739,
}

nidm['Inferences'][0]["Clusters"][0]['Peaks'] = dict()
nidm['Inferences'][0]["Clusters"][0]['Peaks'] = {
"Peak_value": 6.557459354400635,
"Coordinate_coordinateVector": [36,-28,-13],
"Peak_pValueUncorrected": 2.10478867668229e-09,
"Peak_equivalentZStatistic": 5.875740336992659,
"Peak_pValueFWER": 9.175743025868766e-05,
"Peak_qValueFDR": 0.002576053966466678
}

# # Harcoded for now
# {


# },
# {
# "SupraThresholdCluster_clusterSizeInVoxels": 29,
# "SupraThresholdCluster_clusterSizeInResels": 0.2181967247611954,
# "SupraThresholdCluster_pValueUncorrected": 0.01102570321047734,
# "SupraThresholdCluster_pValueFWER": 0.0005653847503775955,
# "SupraThresholdCluster_qValueFDR": 0.01378212901309668,
# "Peaks": {
# "Peak_value": 6.195584774017334,
# "Coordinate_coordinateVector": [-33,-31,-16],
# "Peak_pValueUncorrected": 1.032591323557597e-08,
# "Peak_equivalentZStatistic": 5.606450280165436,
# "Peak_pValueFWER": 0.0003824539073036259,
# "Peak_qValueFDR": 0.009491545229817806
# }
# },
# {
# "SupraThresholdCluster_clusterSizeInVoxels": 12,
# "SupraThresholdCluster_clusterSizeInResels": 0.09028829990118428,
# "SupraThresholdCluster_pValueUncorrected": 0.08183931845143072,
# "SupraThresholdCluster_pValueFWER": 0.004189009772489038,
# "SupraThresholdCluster_qValueFDR": 0.08183931845143072,
# "Peaks": {
# "Peak_value": 5.273201942443848,
# "Coordinate_coordinateVector": [45,-40,32],
# "Peak_pValueUncorrected": 5.123862998335227e-07,
# "Peak_equivalentZStatistic": 4.886820854904772,
# "Peak_pValueFWER": 0.0119099090973821,
# "Peak_qValueFDR": 0.2515542547177581
# }
# }


nidm['Inferences'][0]["SearchSpaceMaskMap_searchVolumeInVoxels"] = 69306
nidm['Inferences'][0]["SearchSpaceMaskMap_searchVolumeInUnits"] = 1871262
# "SearchSpaceMaskMap_reselSizeInVoxels": 132.907586178202,
# "SearchSpaceMaskMap_searchVolumeInResels": 467.0764234388099,
# "SearchSpaceMaskMap_searchVolumeReselsGeometry": [7,42.96312274763005,269.40914815306,467.0764234388099],
# "SearchSpaceMaskMap_noiseFWHMInVoxels": [5.412789859106939,5.436389572402861,4.516666588774812],
# "SearchSpaceMaskMap_noiseFWHMInUnits": [16.23836957732082,16.30916871720859,13.54999976632444],
# "SearchSpaceMaskMap_randomFieldStationarity": true,


# "ExcursionSetMap_numberOfSupraThresholdClusters": 5,
# "ExcursionSetMap_pValue": 2.835106815979316e-09,
# "ExcursionSetMap_hasMaximumIntensityProjection": "./oct-p80GGE/MaximumIntensityProjection.png",


# "Clusters": [
# {
# "SupraThresholdCluster_clusterSizeInVoxels": 839,
# "SupraThresholdCluster_clusterSizeInResels": 6.312656968091135,
# "SupraThresholdCluster_pValueUncorrected": 3.558968244804772e-19,
# "SupraThresholdCluster_pValueFWER": 0,
# "SupraThresholdCluster_qValueFDR": 1.779484122402386e-18,
# "Peaks": [
# {
# "Peak_value": 17.5207633972168,
# "Coordinate_coordinateVector": [-60,-25,11],
# "Peak_pValueUncorrected": 4.440892098500626e-16,
# "Peak_equivalentZStatistic": null,
# "Peak_pValueFWER": 0,
# "Peak_qValueFDR": 1.191565917138381e-11
# },
# ]
# },
# {
# "SupraThresholdCluster_clusterSizeInVoxels": 695,
# "SupraThresholdCluster_clusterSizeInResels": 5.229197369276923,
# "SupraThresholdCluster_pValueUncorrected": 5.342802826320728e-17,
# "SupraThresholdCluster_pValueFWER": 0,
# "SupraThresholdCluster_qValueFDR": 1.335700706580182e-16,
# "Peaks": [
# {
# "Peak_value": 13.54255771636963,
# "Coordinate_coordinateVector": [63,-13,-4],
# "Peak_pValueUncorrected": 4.440892098500626e-16,
# "Peak_equivalentZStatistic": null,
# "Peak_pValueFWER": 0,
# "Peak_qValueFDR": 1.191565917138381e-11
# },
# {
# "Peak_value": 12.47287178039551,
# "Coordinate_coordinateVector": [60,-22,11],
# "Peak_pValueUncorrected": 4.440892098500626e-16,
# "Peak_equivalentZStatistic": null,
# "Peak_pValueFWER": 0,
# "Peak_qValueFDR": 1.191565917138381e-11
# },
# {
# "Peak_value": 9.721034049987793,
# "Coordinate_coordinateVector": [57,-40,5],
# "Peak_pValueUncorrected": 1.221245327087672e-15,
# "Peak_equivalentZStatistic": null,
# "Peak_pValueFWER": 6.925060525020399e-11,
# "Peak_qValueFDR": 6.521696930243519e-09
# }
# ]
# },
# {
# "SupraThresholdCluster_clusterSizeInVoxels": 37,
# "SupraThresholdCluster_clusterSizeInResels": 0.2783889246953182,
# "SupraThresholdCluster_pValueUncorrected": 0.004979532475540043,
# "SupraThresholdCluster_pValueFWER": 0.0002553840091309434,
# "SupraThresholdCluster_qValueFDR": 0.008299220792566739,
# "Peaks": {
# "Peak_value": 6.557459354400635,
# "Coordinate_coordinateVector": [36,-28,-13],
# "Peak_pValueUncorrected": 2.10478867668229e-09,
# "Peak_equivalentZStatistic": 5.875740336992659,
# "Peak_pValueFWER": 9.175743025868766e-05,
# "Peak_qValueFDR": 0.002576053966466678
# }
# },
# {
# "SupraThresholdCluster_clusterSizeInVoxels": 29,
# "SupraThresholdCluster_clusterSizeInResels": 0.2181967247611954,
# "SupraThresholdCluster_pValueUncorrected": 0.01102570321047734,
# "SupraThresholdCluster_pValueFWER": 0.0005653847503775955,
# "SupraThresholdCluster_qValueFDR": 0.01378212901309668,
# "Peaks": {
# "Peak_value": 6.195584774017334,
# "Coordinate_coordinateVector": [-33,-31,-16],
# "Peak_pValueUncorrected": 1.032591323557597e-08,
# "Peak_equivalentZStatistic": 5.606450280165436,
# "Peak_pValueFWER": 0.0003824539073036259,
# "Peak_qValueFDR": 0.009491545229817806
# }
# },
# {
# "SupraThresholdCluster_clusterSizeInVoxels": 12,
# "SupraThresholdCluster_clusterSizeInResels": 0.09028829990118428,
# "SupraThresholdCluster_pValueUncorrected": 0.08183931845143072,
# "SupraThresholdCluster_pValueFWER": 0.004189009772489038,
# "SupraThresholdCluster_qValueFDR": 0.08183931845143072,
# "Peaks": {
# "Peak_value": 5.273201942443848,
# "Coordinate_coordinateVector": [45,-40,32],
# "Peak_pValueUncorrected": 5.123862998335227e-07,
# "Peak_equivalentZStatistic": 4.886820854904772,
# "Peak_pValueFWER": 0.0119099090973821,
# "Peak_qValueFDR": 0.2515542547177581
# }
# }
# ]
# }

# with file.read('w') as fid:
# fid.write(json.dumps(nidm, indent=2))


# Keep only arguments that were defined
defined_args = {'-' + k: v for k, v in args._get_kwargs() if v is not None}
# Transform into flat list with all defined key value pairs
defined_args = list(itertools.chain(*defined_args.items()))
# Flatten any list (multi value arguments)
# TODO: could this be done with simpler syntax
dargs = list()
for aa in defined_args:
if isinstance(aa, list):
dargs.extend(aa)
else:
dargs.append(aa)

dargs = [w.replace('onesided', '1sided') for w in dargs]

print(['3dClusterize'] + dargs + ['>', 'clusters'])
out = check_output(['3dClusterize'] + dargs + ['>', 'clusters'])
print('----------------------')
print(out)


check_call([
# 3dAFNItoNIFTI -prefix <NAME> <RES_BRIK>[0]
'3dAFNItoNIFTI', '-prefix', 'ExcursionSetMap.nii.gz', args.pref_dat + '+tlrc[0]'])
check_call([
# 3dAFNItoNIFTI -prefix <NAME> <RES_BRIK>[0]
'3dAFNItoNIFTI', '-prefix', 'ClusterLabelsMap.nii.gz', args.pref_map + '+tlrc[0]'])

json_file = 'nidm_from_afni.json'
with open(json_file, "w") as fid:
fid.write(json.dumps(nidm, indent=4))

nidmresjson = NIDMResults(json_file=json_file, prepend_path=os.getcwd())

# Rewrite the NIDM pack
new_name = os.path.join('afni_study')
nidmresjson.serialize(new_name)
print('Serialised to ' + new_name)
print("----")
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
" as specified at http://nidm.nidash.org/specs/nidm-results.html."),
license = "BSD",
keywords = "Prov, NIDM, Provenance",
scripts=['bin/nidm-results_afni'],
scripts=['bin/nidmafni', 'bin/nidmjsonafni'],
# packages=['nidmfsl', 'test'],
packages=find_packages(),
package_dir={
Expand Down