diff --git a/bin/nidm-results_fsl b/bin/nidmafni similarity index 85% rename from bin/nidm-results_fsl rename to bin/nidmafni index 590141b..1ee68d2 100644 --- a/bin/nidm-results_fsl +++ b/bin/nidmafni @@ -9,7 +9,7 @@ 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) @@ -17,7 +17,7 @@ if __name__ == "__main__": 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) diff --git a/bin/nidmjsonafni b/bin/nidmjsonafni new file mode 100644 index 0000000..eacd7c5 --- /dev/null +++ b/bin/nidmjsonafni @@ -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 [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 [0] + '3dAFNItoNIFTI', '-prefix', 'Contrast.nii.gz', resbrik + '[0]']) + check_call([ + # 3dAFNItoNIFTI -prefix [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 [0] + '3dAFNItoNIFTI', '-prefix', 'ExcursionSetMap.nii.gz', args.pref_dat + '+tlrc[0]']) + check_call([ + # 3dAFNItoNIFTI -prefix [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("----") \ No newline at end of file diff --git a/setup.py b/setup.py index 7fef986..2b8c3ac 100644 --- a/setup.py +++ b/setup.py @@ -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={