PET data conversion
History
The PET modality is an addition to BIDS with its introduction via BEP 009. If you're interested in seeing exactly what and how something gets added to BIDS see the pull request for BEP009 here. The results of that extension proposal can be read here in the BIDS standard.
PET image data file formats
Before we start to convert data we need to quickly mention that PET image data files come of the scanner in various different formats: some scanners provide DICOM files (.dcm) and others use proprietary formats for example ECAT format (.v). In order to facilitate easy testing of data conversion across different PET file formats the OpenNeuroPET project has compiled a bunch of phantom data from different scanner types and is distributing two examples here.
You can download them for testing purposes either manually or in the terminal by typing:
gdown https://drive.google.com/file/d/1O1hDmJwR1xm6LemIbhPrLf9YkSYHC6Mq/view?usp=sharing --fuzzy
You may need to install the python package gdown
for this to work:
pip install gdown
And then unzipping the downloaded file:
unzip OpenNeuroPET-Demo_raw.zip
You can now look at the file tree:
tree OpenNeuroPET-Demo_raw.
OpenNeuroPET-Demo_raw
│ .bidsignore
│ dataset_description.json
│ README
│
├───code
│ .python_conversions.sh.swp
│ matlab_conversions.m
│ python_conversions.sh
│ README.md
│
├───source
│ │
│ ├───SiemensBiographPETMR-NRU
│ │ X-CAL_7.PT.Kalibrering_xca.30003.1.2022.04.26.15.04.22.218.14689529.dcm
│ │ X-CAL_7.PT.Kalibrering_xca.30003.10.2022.04.26.15.04.22.218.14689628.dcm
│ │ X-CAL_7.PT.Kalibrering_xca.30003.100.2022.04.26.15.04.22.218.14690618.dcm
│ │ X-CAL_7.PT.Kalibrering_xca.30003.101.2022.04.26.15.04.22.218.14690629.dcm
│ │ ....
│ │
│ └───SiemensHRRT-NRU
│ XCal-Hrrt-2022.04.21.15.43.05_EM_3D.json
│ XCal-Hrrt-2022.04.21.15.43.05_EM_3D.v
│
├───sub-SiemensBiographNRU
│ └───pet
│ sub-SiemensBiographNRU_pet.json
│ sub-SiemensBiographNRU_pet.nii.gz
│
└───sub-SiemensHRRT
└───pet
sub-SiemensHRRT_pet.json
sub-SiemensHRRT_pet.nii.gz
Now you have an example dataset where you have source data (both for ECAT and DICOM PET image format) and the PET BIDS data sets constructed for it.
Also if you have access to another PET image file format, or data from a scanner not tested, please reach out to OpenNeuroPET project in order to add a phantom scan in your format.
Conversion
The OpenNeuroPET project has tried to develop tools for facilitating easy data conversion for PET. The main tool used for this is PET2BIDS freely available on the OpenNeuroPET GitHub repository along with other resources like altlases or pipelines.
It is available for both Python and MATLAB. Eventually, PET2BIDS will also be wrapped inside other BIDS conversion tools such as BIDScoin or ezBIDS, but this is work in progress at the moment.
Besides using PET2BIDS there is always the possibility to manually convert a data set to PET BIDS and an example will be shown below. In any case, for DICOM data format, one relies on dcm2niix.
Below we will show two ways of converting your PET data to BIDS:
- using PET2BIDS and
- manually.
Conversion of PET data using PET2BIDS
Detailed documentation for PET2BIDS can be found here or on the Github repo.
get dcm2niix
Download dcm2niix,
and for Mac and Linux users you can add it to your path,
for instance it would could downloaded and unzipped into /bin
.
export PATH="/home/$USER/bin:$PATH"
For Python
Open your terminal, either regular or anaconda terminal and type.
pip install pypet2bids
Now you already have the converter installed and can go ahead and convert your first dataset!
In this example, we are converting an image in DICOM format.
It should be noted that pypet2bids
contains several different tools
and is itself a part of the larger PET library PET2BIDS
,
the specific tool we will be using for the following DICOM conversion is dcm2niix4pet
.
You just need to point dcm2niix4pet
to:
- the folder where your data resides,
dcmfolder
, and - the folder where you want to output the PET BIDS formatted dataset,
mynewfolder
.
dcm2niix4pet dcmfolder -d mynewfolder
Note
dcm2niix4pet
will do its best to extract as much information
about radiological and blood data from the DICOM files in the dcmfolder
.
However, dcm2niix4pet
can't find information if it isn't there,
hence it will often be up to you the user
to provide some missing information at the time of conversion.
Additional information can be provided via the command line
with the --kwargs
argument in the form of key=pair values.
For an idea of what this looks like see below:
dcm2niix4pet /OpenNeuroPET-Demo_raw/source/SiemensBiographPETMR-NRU \
-d mynewfolder \
--kwargs TimeZero=ScanStart \
Manufacturer=Siemens \
ManufacturersModelName=Biograph \
InstitutionName="Rigshospitalet, NRU, DK" \
BodyPart=Phantom \
Units=Bq/mL \
TracerName=none \
TracerRadionuclide=F18 \
InjectedRadioactivity=81.24 \
SpecificRadioactivity=13019.23 \
ModeOfAdministration=infusion \
FrameTimesStart=0 \
AcquisitionMode="list mode" \
ImageDecayCorrected=true \
ImageDecayCorrectionTime=0 \
AttenuationCorrection=MR-corrected \
FrameDuration=300 \
FrameTimesStart=0
Now you have a dataset in PET BIDS format.
You will probably have gotten some warnings relating to the .json
sidecar file.
Carefully look at them, since they will help you to catch inconsistencies
and missing required fields that you need to add
in order for the dataset to pass the BIDS validator as well (see below how that's done).
You can always edit the .json
file, by opening it in a text editor and manually fixing errors.
Alternatively, adjust the meta structure you created above to correct the errors.
For MATLAB
Download/Clone the PET2BIDS GitHub repository.
Windows users must, in addition, indicate the full path of where is the dcm2niix.exe
in
dcm2niix4pet.m.
Then, just follow the MATLAB README. In the following it is just described how to convert DICOM files. It's very similar and described in the MATLAB README how to handle ECAT files.
The entire PET2BIDS repository or only the MATLAB subfolder (your choice) should be in your MATLAB path.
Defaults parameters can be set in (scannername).txt
files to generate metadata easily
(meaning to avoid passing all arguments in although this is also possible).
You can find templates of such parameter file under
/template_txt
:
- SiemensHRRTparameters.txt,
- SiemensBiographparameters.txt,
- GEAdvanceparameters.txt,
- PhilipsVereosparameters.txt.
Now in order to convert a PET dataset on disk to PET BIDS,
one creates a structure containing all the meta information (here passing on all arguments)
and then point the function to the folder where your data resides dcmfolder
and the folder where you want to output the PET BIDS formatted dataset mynewfolder
:
meta = get_pet_metadata('Scanner', 'SiemensBiograph', ...
'TimeZero', 'ScanStart', ...
'TracerName', 'CB36', ...
'TracerRadionuclide', 'C11', ...
'ModeOfAdministration', 'infusion', ...
'SpecificRadioactivity', 605.3220, ...
'InjectedMass', 1.5934, ...
'MolarActivity', 107.66, ...
'InstitutionName', 'Rigshospitalet, NRU, DK', ...
'AcquisitionMode', 'list mode', ...
'ImageDecayCorrected', 'true', ...
'ImageDecayCorrectionTime', 0, ...
'ReconMethodName', 'OP-OSEM', ...
'ReconMethodParameterLabels', {'subsets', 'iterations'}, ...
'ReconMethodParameterUnits', {'none', 'none'}, ...
'ReconMethodParameterValues', [21 3], ...
'ReconFilterType', 'XYZGAUSSIAN', ...
'ReconFilterSize', 2, ...
'AttenuationCorrection', 'CT-based attenuation correction');
dcm2niix4pet(dcmfolder, meta, 'o', mynewfolder);
Note
get_pet_metadata
can be called in a much simpler way
if you have a \*parameters.txt
seating on disk next to this function.
The call would then looks like:
% your SiemensBiographparameters.txt file is stored next to get_pet_metadata.m
meta = get_pet_metadata('Scanner', 'SiemensBiograph', ...
'TimeZero', 'ScanStart', ...
'TracerName', 'CB36', ...
'TracerRadionuclide', 'C11', ...
'ModeOfAdministration', 'infusion', ...
'SpecificRadioactivity', 605.3220, ...
'InjectedMass', 1.5934, ...
'MolarActivity', 107.66);
dcm2niix4pet(dcmfolder, meta, 'o', mynewfolder);
Now you have a dataset in PET BIDS format.
You will probably have gotten some warnings relating to the .json
sidecar file.
Carefully look at them, since they will help you to catch inconsistencies
and missing required fields that you need to add
in order for the dataset to pass the BIDS validator as well (see below how that's done).
You can always edit the .json
file, by opening it in a text editor and manually fixing errors.
Alternatively, adjust the meta structure you created above to correct the errors.
Manual conversion of PET data to PET BIDS
As stated above PET files come of the scanner in various formats. In the following manual data conversion is shown for an ECAT (.v) as well as a DICOM (.dcm) PET image dataset.
PET image data in ECAT format
Here is the structure of our starting dataset:
OldNotBidsPETDataSet/
├── MR_image
│ ├── 24310.cropped.hdr
│ ├── 24310.cropped.img
│ └── 24310_rois.mat
├── retest_scan_sub01
│ ├── sub01_8__PLASMA.txt
│ └── sub01_8_anon.v
└── test_scan_sub01
├── sub01_7_PLASMA.txt
└── sub01_7_anon.v
3 directories, 7 files
By the end of this document our starting dataset will be fully BIDS compliant and similar to the dataset seen here.
Setting the layout
This starter kit provides several template/example files that can be a great starting place to help get set up for the conversion process. PET specific text data files can be viewed at this location. and easily collected via this link or be cloned and extracted via git at the command line via:
git clone git@github.com:bids-standard/bids-starter-kit.git
The templates/
folder in bids-starter-kit/
contains a single subject
and examples of every BIDS modality text and .json
file for that subject.
Additionally, there exists a Short
and a Full
example for each text/json file.
The Short
files contain only the required BIDS fields for each modality
and the Full
example files contain every field included within the BIDS standard.
templates/
├── README
├── dataset_description.json
├── participants.json
├── participants.tsv
├── phenotype
│ ├── EpilepsyClassification.tsv
│ └── EpilepsyClassification2017.json
└── sub-01
└── ses-01
├── anat
│ ├── sub-01_ses-01_acq-FullExample_run-01_T1w.json
│ └── sub-01_ses-01_acq-ShortExample_run-01_T1w.json
├── eeg
│ ├── sub-01_ses-01_task-FilterExample_eeg.json
│ ├── sub-01_ses-01_task-FullExample_eeg.json
│ ├── sub-01_ses-01_task-MinimalExample_eeg.json
│ └── sub-01_ses-01_task-ReferenceExample_eeg.json
├── fmap
│ ├── sub-01_ses-01_task-Case1_run-01_phasediff.json
│ ├── sub-01_ses-01_task-Case2_run-01_phase1.json
│ ├── sub-01_ses-01_task-Case2_run-01_phase2.json
│ ├── sub-01_ses-01_task-Case3_run-01_fieldmap.json
│ └── sub-01_ses-01_task-Case4_dir-LR_run-01_epi.json
├── func
│ ├── sub-01_ses-01_task-FullExample_run-01_bold.json
│ ├── sub-01_ses-01_task-FullExample_run-01_events.json
│ ├── sub-01_ses-01_task-FullExample_run-01_events.tsv
│ └── sub-01_ses-01_task-ShortExample_run-01_bold.json
├── ieeg
│ ├── sub-01_ses-01_coordsystem.json
│ ├── sub-01_ses-01_electrodes.tsv
│ ├── sub-01_ses-01_task-LongExample_run-01_channels.tsv
│ └── sub-01_ses-01_task-LongExample_run-01_ieeg.json
├── meg
│ ├── sub-01_task-FullExample_acq-CTF_run-1_proc-sss_meg.json
│ └── sub-01_task-ShortExample_acq-CTF_run-1_proc-sss_meg.json
└── pet
├── sub-01_ses-01_recording-AutosamplerShortExample_blood.json
├── sub-01_ses-01_recording-AutosamplerShortExample_blood.tsv
├── sub-01_ses-01_recording-ManualFullExample_blood.json
├── sub-01_ses-01_recording-ManualFullExample_blood.tsv
├── sub-01_ses-01_recording-ManualShortExample_blood.json
├── sub-01_ses-01_recording-ManualShortExample_blood.tsv
├── sub-01_ses-01_task-FullExample_pet.json
└── sub-01_ses-01_task-ShortExample_pet.json
10 directories, 35 files
machine:bids-starter-kit user$
For the purposes of this exercise we will only be focusing on obtaining the minimum required fields, thus we collect and copy the following to our own BIDS folder.
# create the new BIDS folder
mkdir -p /NewBidsDataSet/sub-01/ses-01/pet
cp -r /path/to/bids-starter-kit/templates/sub-01/ses-01/pet/* /NewBidsDataSet/sub-01/pet/
Oops, we forgot to include our dataset specific files. Let's make it easy on ourselves and include the templates for those too.
cp /path/to/bids-starter-kit/templates/* /NewBidsDataSet/
Our skeleton of a data set should now look like this:
machine: Projects user$ tree NewBidsDataSet/
NewBidsDataSet/
├── README
├── dataset_description.json
├── participants.json
├── participants.tsv
└── sub-01
└── ses-01
└── pet
├── sub-01_ses-01_recording-AutosamplerShortExample_blood.json
├── sub-01_ses-01_recording-AutosamplerShortExample_blood.tsv
├── sub-01_ses-01_recording-ManualFullExample_blood.json
├── sub-01_ses-01_recording-ManualFullExample_blood.tsv
├── sub-01_ses-01_recording-ManualShortExample_blood.json
├── sub-01_ses-01_recording-ManualShortExample_blood.tsv
├── sub-01_ses-01_task-FullExample_pet.json
└── sub-01_ses-01_task-ShortExample_pet.json
3 directories, 12 files
machine:Projects user$
Now let's do the bare minimum and focus only on the short files:
machine:Projects user$ rm -rf NewBidsDataSet/sub-01/ses-01/pet/*Full*
machine:Projects user$ tree NewBidsDataSet
NewBidsDataSet/
├── README
├── dataset_description.json
├── participants.json
├── participants.tsv
└── sub-01
└── ses-01
└── pet
├── sub-01_ses-01_recording-AutosamplerShortExample_blood.json
├── sub-01_ses-01_recording-AutosamplerShortExample_blood.tsv
├── sub-01_ses-01_recording-ManualShortExample_blood.json
├── sub-01_ses-01_recording-ManualShortExample_blood.tsv
└── sub-01_ses-01_task-ShortExample_pet.json
3 directories, 9 files
machine:Projects user$
That certainly looks less daunting, now let's change the filenames of the templates
so that they make more sense for our data set (by removing ShortExample
from each filename).
Note
If you have multiple PET image files you can distinguish between them
by creating a session folder with a unique name
and then applying the ses-<label>
label to each file therein.
If there's a single pet scan you may omit the additional folder
and corresponding label(s) from the filename.
For more information on labeling see this link.
# for those of you in bash land
machine:Projects user$ cd NewBidsDataSet/sub-01/ses-01/pet
machine:pet user$ for i in *ShortExample*; do mv "$i" "`echo $i | sed 's/ShortExample//'`"; done
# Since we "don't" know if there are multiple PET image files
# we will omit the task label for our PET .json file for now.
machine:pet user$ mv sub-01_ses-01_task-_pet.json sub-01-ses-01_pet.json
After you've renamed your files your directory should look something like:
machine:Projects user$ tree NewBidsDataSet/
NewBidsDataSet/
├── README
├── dataset_description.json
├── participants.json
├── participants.tsv
└── sub-01
└── ses-01
└── pet
├── sub-01_ses-01_pet.json
├── sub-01_ses-01_recording-Autosampler_blood.json
├── sub-01_ses-01_recording-Autosampler_blood.tsv
├── sub-01_ses-01_recording-Manual_blood.json
└── sub-01_ses-01_recording-Manual_blood.tsv
3 directories, 9 files
machine:Projects user$
Ok great, but where do the imaging files go?
And what format should they be in?
And there's at least 2 .v
pet files shouldn't there be multiple sessions?
Well yes, yes, and yes. but before we add more sessions/directories we're going to make sure we have images to place in them.
Collecting and installing TPCCLIB
Since our raw imaging files are in ECAT format,
we'll be using the ecat2nii
tool it the Turku PET Centre C library (TPCCLIIB
)
as it's very handy at converting PET ECAT images into the more BIDS friendly nifti format.
If you're imaging files are in .IMG
you can use **<\insert converter>**
or if they're in DICOM dcm2niix
is an excellent tool to transform .dcm
files into .nii
.
Before we proceed we will need to collect and install the ecat2nii
tool from the TPCCLIIB
a container,
or a VM if you want to continue following along.
If you're on a non-posix based OS, we suggest you use WSL (windows subsystem for linux).
-
Download tpcclib via your download link of choice, ours is here
-
Once downloaded extract and place
ecat2nii
in an appropriate place
If you're using bash/posix step 3 will resemble something like the following:
machine:Downloads user$ unzip tpcclib-master.zip
machine:Downloads user$ mv tpcclib-master /some/directory/you/are/fond/of/ # could be /usr/bin if you so choose
Now you can choose to add tpcclib
to your path or not, in bash land we do the following:
machine:Downloads user$ echo "export PATH=$PATH:/some/directory/you/are/fond/of/tpcclib-master" >> ~/.bashrc
# reload bash shell and verify that library is available
machine:Downloads user$ source ~/.bashrc
machine:Downloads user$ ecat2nii
ecat2nii - tpcclib 0.7.6 (c) 2020 by Turku PET Centre
Converts PET images from ECAT 6.3 or 7 to NIfTI-1 format.
Conversion can also be done using ImageConverter (.NET application).
Image byte order is determined by the computer where the program is run.
NIfTI image format does not contain information on the frame times.
Frame times can be retrieved from SIF file, which can be created optionally.
SIF can also be created later using other software.
Usage: ecat2nii [Options] ecatfile(s)
Options:
-O=<output path>
Data directory for NIfTI files, if other than the current working path.
-dual
Save the image in dual file format (the header and voxel data in
separate files *.hdr and *.img); single file format (*.nii)
is the default.
-sif
SIF is saved with NIfTI; note that existing SIF will be overwritten.
-h, --help
Display usage information on standard output and exit.
-v, --version
Display version and compile information on standard output and exit.
-d[n], --debug[=n], --verbose[=n]
Set the level (n) of debugging messages and listings.
-q, --quiet
Suppress displaying normal results on standard output.
-s, --silent
Suppress displaying anything except errors.
Example:
ecat2nii *.v
See also: nii2ecat, nii_lhdr, ecat2ana, eframe, img2flat
Keywords: image, format conversion, ECAT, NIfTI
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it under
GNU General Public License. Source codes are available in
https://gitlab.utu.fi/vesoik/tpcclib.git
Congrats, you've installed ecat2nii
, you're one step closer to bidsifying your dataset
Now, let's convert one of our PET ECAT images
and move it to its final destination with ecat2nii
that we just installed.
# first create nifti's from the ecat images
machine:OldNotBidsPETDataSet user$ ecat2nii retest_scan_sub01/sub01_8_anon.v
retest_scan_sub01/sub01_8_anon.v :
processing retest_scan_sub01/sub01_8_anon.v
45 frame(s) processed.
machine:OldNotBidsPETDataSet user$ ecat2nii test_scan_sub01/sub01_7_anon.v
test_scan_sub01/sub01_7_anon.v :
processing test_scan_sub01/sub01_7_anon.v
45 frame(s) processed.
# great success! let's see what we got
machine:OldNotBidsPETDataSet user$ tree
.
├── MR_image
│ ├── 24310.cropped.hdr
│ ├── 24310.cropped.img
│ └── 24310_rois.mat
├── retest_scan_sub01
│ ├── sub01_8_PLASMA.txt
│ └── sub01_8_anon.v
├── sub01_7_anon.nii
├── sub01_8_anon.nii
└── test_scan_sub01
├── sub01_7_PLASMA.txt
└── sub01_7_anon.v
3 directories, 9 files
# not where we want those nifti's so let's move them
machine:OldNotBidsPETDataSet user$ mv *.nii ../NewBidsDataSet/sub-01/
machine:NewBidsDataSet user$ tree
.
├── README
├── dataset_description.json
├── participants.json
├── participants.tsv
└── sub-01
└── test_scan_sub01/sub01_7_anon.nii
└── test_scan_sub01/sub01_8_anon.nii
└── ses-01
└── pet
├── sub-01_ses-01_pet.json
├── sub-01_ses-01_recording-Autosampler_blood.json
├── sub-01_ses-01_recording-Autosampler_blood.tsv
├── sub-01_ses-01_recording-Manual_blood.json
├── sub-01_ses-01_recording-Manual_blood.tsv
3 directories, 11 files
Before we get too carried away, lets use populate our *_pet.json
files
with the relevant information from our ECAT and dataset.
This writer used a python tool specifically created to parse ECAT's for header and subheader info ecatdump. All it does is use Python libraries Nibabel and Argparse to collect and spit out the header information from an ECAT file.
You can use and install it via pip and use it on your command line like below:
pip install ecatdump
# use the --json argument to extract all metadata contained in the ecat including subheaders
ecatdump anon_sub01_8.v --json > sub01_8_anon.headerdata
# using it without the --json argument will produce this output
ecat dump anon_sub01_8.v
magic_number: MATRIX72v
original_filename: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
sw_version: 72
system_type: 962
file_type: 7
serial_number: hrplus
scan_start_time: 1111111111111
isotope_name: C-11
isotope_halflife: 1223.0
radiopharmaceutical: C-11 Flumazenil
gantry_tilt: 0.0
gantry_rotation: 0.0
bed_elevation: 82.6520004272461
intrinsic_tilt: 0.0
wobble_speed: 0
transm_source_type: 2
distance_scanned: 15.520000457763672
transaxial_fov: 58.29999923706055
angular_compression: 1
coin_samp_mode: 0
axial_samp_mode: 0
ecat_calibration_factor: 9698905.0
calibration_unitS: 1
calibration_units_type: 1
compression_code: 0
study_type: XXXXXXXXXXXX
patient_id: 0000000000000000
patient_name: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
patient_sex: F
patient_dexterity: U
patient_age: 0.0
patient_height: 0.0
patient_weight: 0.0
patient_birth_date: 0
physician_name:
operator_name:
study_description: C-11 Dynamic-1
acquisition_type: 4
patient_orientation: 3
facility_name: ECAT
num_planes: 63
num_frames: 20
num_gates: 1
num_bed_pos: 0
init_bed_position: 50.617000579833984
bed_position: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
plane_separation: 0.24250000715255737
lwr_sctr_thres: 0
lwr_true_thres: 350
upr_true_thres: 650
user_process_code:
acquisition_mode: 0
bin_size: 0.22499999403953552
branching_fraction: 1.0
dose_start_time: 111111111111
dosage: 0.0
well_counter_corr_factor: 1.0
data_units:
septa_state: 1
fill:
Using nibabel directly also works great:
>>> import nibabel
>>> ecat_file = nibabel.ecat.load('anon_sub01_8_43e5_1b831_de2_anon.v')
>>> print(ecat_file.get_header())
__main__:1: DeprecationWarning: get_header method is deprecated.
Please use the ``img.header`` property instead.
* deprecated from version: 2.1
* Will raise <class 'nibabel.deprecator.ExpiredDeprecationError'> as of version: 4.0
<class 'nibabel.ecat.EcatHeader'> object, endian='>'
magic_number : b'MATRIX72v'
original_filename : b'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
sw_version : 72
system_type : 962
file_type : 7
serial_number : b'hrplus'
scan_start_time : 1111111111
isotope_name : b'C-11'
isotope_halflife : 1223.0
radiopharmaceutical : b'C-11 Flumazenil'
gantry_tilt : 0.0
gantry_rotation : 0.0
bed_elevation : 82.652
intrinsic_tilt : 0.0
wobble_speed : 0
transm_source_type : 2
distance_scanned : 15.52
transaxial_fov : 58.3
angular_compression : 1
coin_samp_mode : 0
axial_samp_mode : 0
ecat_calibration_factor : 9698905.0
calibration_unitS : 1
calibration_units_type : 1
compression_code : 0
study_type : b'XXXXXXXXXXXX'
patient_id : b'0000000000000000'
patient_name : b'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
patient_sex : b'F'
patient_dexterity : b'U'
patient_age : 0.0
patient_height : 0.0
patient_weight : 0.0
patient_birth_date : 0
physician_name : b''
operator_name : b''
study_description : b'C-11 Dynamic-1'
acquisition_type : 4
patient_orientation : 3
facility_name : b'ECAT'
num_planes : 63
num_frames : 20
num_gates : 1
num_bed_pos : 0
init_bed_position : 50.617
bed_position : [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
plane_separation : 0.2425
lwr_sctr_thres : 0
lwr_true_thres : 350
upr_true_thres : 650
user_process_code : b''
acquisition_mode : 0
bin_size : 0.225
branching_fraction : 1.0
dose_start_time : 1111111111111
dosage : 0.0
well_counter_corr_factor : 1.0
data_units : b''
septa_state : 1
fill : b''
Using your wits, the available tools, and data you should be able
to produce a sidecar file like the following for your nifti image.
Repeat this process for each image file/nifti you're including
while reusing the filename for each image
and while substituting the extension to .json
instead of .nii
.
{
"Manufacturer": "Siemens",
"ManufacturersModelName": "HR+",
"Units": "Bq/mL",
"BodyPart": "Brain",
"TracerName": "DASB",
"TracerRadionuclide": "C11",
"TracerMolecularWeight": 282.39,
"TracerMolecularWeightUnits": "g/mol",
"InjectedRadioactivity": 629.74,
"InjectedRadioactivityUnits": "MBq",
"MolarActivity": 55.5,
"MolarActivityUnits": "MBq/nmol",
"SpecificRadioactivity": 196.53670455752683,
"SpecificRadioactivityUnits": "MBq/ug",
"Purity": 99,
"ModeOfAdministration": "bolus",
"InjectedMass": 3.2041852,
"InjectedMassUnits": "ug",
"AcquisitionMode": "list mode",
"ImageDecayCorrected": true,
"ImageDecayCorrectionTime": 0,
"TimeZero": "17:28:40",
"ScanStart": 0,
"InjectionStart": 0,
"FrameDuration": [
20, 20, 20, 60, 60, 60, 120, 120, 120, 300, 300.066, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600
],
"FrameTimesStart": [
0, 20, 40, 60, 120, 180, 240, 360, 480, 600, 900, 1200.066, 1800.066,
2400.066, 3000.066, 3600.066, 4200.066, 4800.066, 5400.066, 6000.066,
6600.066
],
"ReconMethodParameterLabels": [
"lower_threshold",
"upper_threshold",
"recon_zoom"
],
"ReconMethodParameterUnits": ["keV", "keV", "none"],
"ReconMethodParameterValues": [350, 650, 3],
"ScaleFactor": [
8.548972374455843e-8, 1.7544691388593492e-7, 1.3221580275057931e-7,
1.2703590357432404e-7, 1.1155360368775291e-7, 2.2050951997698576e-7,
2.184752503353593e-7, 1.7056818535365892e-7, 1.6606901453997125e-7,
1.5532630470715958e-7, 2.19175134930083e-7, 2.0248222654117853e-7,
2.277063231304055e-7, 2.425933018912474e-7, 2.3802238047210267e-7,
2.514642005735368e-7, 2.802861729378492e-7, 2.797820570776821e-7,
3.5299004252919985e-7, 4.6313422785715375e-7, 4.904185857412813e-7
],
"ScatterFraction": [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
],
"DecayCorrectionFactor": [
1.0056782960891724, 1.0171427726745605, 1.0287377834320068,
1.0522810220718384, 1.0886797904968262, 1.1263376474380493,
1.1851094961166382, 1.2685142755508423, 1.3577889204025269,
1.5278561115264893, 1.811025857925415, 2.328737735748291,
3.271937131881714, 4.597157001495361, 6.459125518798828,
9.075239181518555, 12.750947952270508, 17.915414810180664,
25.1716251373291, 35.36678695678711, 49.69125747680664
],
"PromptRate": [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
],
"RandomRate": [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
],
"SinglesRate": [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
],
"ReconMethodName": "Vendor",
"ReconFilterType": ["Shepp 0.5", "All-pass 0.4"],
"ReconFilterSize": [2.5, 2],
"AttenuationCorrection": "transmission scan"
}
Alright, now we're getting somewhere, let's rename these niftis so that it's easier to distinguish between them and so that they're both BIDS compliant.
# first lets divide the scans into their own sessions
machine:NewBidsDataSet user$ mv sub-01/ses-01/ sub-01/ses-testscan/
# and make a second session folder for the rescan
machine:NewBidsDataSet user$ cp -r sub-01/ses-testscan/ sub-01/ses-retestscan/
machine:NewBidsDataSet user$ mv sub-01/sub01_7_anon.nii sub-01/ses-testscan/pet/sub-01_ses-testscan_pet.nii
machine:NewBidsDataSet user$ mv sub-01/sub01_8_anon.nii sub-01/ses-retestscan/pet/sub-01_ses-retestscan_pet.nii
# some additional bash later and ......
machine:NewBidsDataSet user$ tree
.
├── README
├── dataset_description.json
├── participants.json
├── participants.tsv
└── sub-01
├── ses-retestscan
│ └── pet
│ ├── sub-01_ses-retestscan_pet.json
│ ├── sub-01_ses-retestscan_recording-Autosampler_blood.json
│ ├── sub-01_ses-retestscan_recording-Autosampler_blood.tsv
│ ├── sub-01_ses-retestscan_recording-Manual_blood.json
│ ├── sub-01_ses-retestscan_recording-Manual_blood.tsv
│ └── sub-01_ses-retestscan_pet.nii
└── ses-testscan
└── pet
├── sub-01_ses-testscan_pet.json
├── sub-01_ses-testscan_recording-Autosampler_blood.json
├── sub-01_ses-testscan_recording-Autosampler_blood.tsv
├── sub-01_ses-testscan_recording-Manual_blood.json
├── sub-01_ses-testscan_recording-Manual_blood.tsv
└── sub-01_ses-testscan_pet.nii
5 directories, 16 files
That looks better, but we need to do something about our tabular/text data. Since we're only dealing with manual blood samples we can omit the autosampler files from our data structure.
machine:NewBidsDataSet user$ rm -rf sub-01/ses-retestscan/pet/*Auto*
machine:NewBidsDataSet user$ rm -rf sub-01/ses-testscan/pet/*Auto*
machine:NewBidsDataSet user$ tree
.
├── README
├── dataset_description.json
├── participants.json
├── participants.tsv
└── sub-01
├── ses-retestscan
│ └── pet
│ ├── sub-01_ses-retestscan_pet.json
│ ├── sub-01_ses-retestscan_recording-Manual_blood.json
│ ├── sub-01_ses-retestscan_recording-Manual_blood.tsv
│ └── sub-01_ses-retestscan_pet.nii
└── ses-testscan
└── pet
├── sub-01_ses-testscan_pet.json
├── sub-01_ses-testscan_recording-Manual_blood.json
├── sub-01_ses-testscan_recording-Manual_blood.tsv
└── sub-01_ses-testscan_run-testscan_pet.nii
That looks better, now that we've got our nifti's sorted
let's do something about filling out our *_blood.tsv
files and our *_blood.json
files.
Here's are the contents of sub01_7_PLASMA.txt
:
Since our plasma data is available we need to indicate as such
in our *_blood.json
files so we do so now:
{
"PlasmaAvail": "true",
"MetaboliteAvail": "true",
"WholeBloodAvail": "false",
"DispersionCorrected": "false",
"MetaboliteMethod": "HPLC",
"MetaboliteRecoveryCorrectionApplied": "false",
"time": {
"Description": "Time in relation to time zero defined by the _pet.json",
"Units": "s"
},
"plasma_radioactivity": {
"Description": "Radioactivity in plasma samples",
"Units": "Bq/mL"
}
"metabolite_parent_fraction": {
"Description": "Parent fraction of the radiotracer.",
"Units": "unitless"
}
}
Additionally, we can open our *_blood.tsv
and add the plasma series of data to it from our base text file:
Whoops, these numbers aren't in SI units, so we quickly convert time from minutes to seconds and Microcurie to Becquerel to conform to the spec.
Note
If you're wondering what method was used to determine what units these unlabeled columns were in we can only tell you that prior knowledge, access to the associated publishing, or experience/domain knowledge coupled with a bit of guesswork are required to make that determination. One of the aims of adding PET to BIDS is to help to avoid confusion at this level of the process and instead allow researchers to reserve that confusion for use at some other point during the research process.
After a bit of conversion and the inclusion of the metabolite parent fraction (which if present is recommended to be included):
Now just repeat this process for each required field in the *_blood.json
file
and/or for each recommended field
that you wish to include from your non-BIDS dataset into this bidsified one.
The final results of our efforts leads us to having this final *blood_.json
:
{
"PlasmaAvail": true,
"PlasmaFreeFraction": 15.062331,
"PlasmaFreeFractionMethod": "Ultrafiltration",
"WholeBloodAvail": false,
"DispersionCorrected": false,
"MetaboliteAvail": true,
"MetaboliteMethod": "HPLC",
"MetaboliteRecoveryCorrectionApplied": false,
"time": {
"Description": "Time in relation to time zero defined by the _pet.json",
"Units": "s"
},
"plasma_radioactivity": {
"Description": "Radioactivity in plasma samples",
"Units": "Bq/mL"
},
"metabolite_parent_fraction": {
"Description": "Parent fraction of the radiotracer",
"Units": "arbitrary"
}
}
Last Pass
If you're thinking that we failed to document or include our T1 image from the start you would be correct.
There are many resources documenting that process here.
Once you've handled converting your MR images into BIDS your directory should look like the following:
machine:NewBidsDataSet user$ tree
.
├── README
├── dataset_description.json
├── participants.json
├── participants.tsv
└── sub-01
├── ses-retestscan
│ └── pet
│ ├── sub-01_ses-retestscan_pet.json
│ ├── sub-01_ses-retestscan_recording-Manual_blood.json
│ ├── sub-01_ses-retestscan_recording-Manual_blood.tsv
│ └── sub-01_ses-retestscan_pet.nii
└── ses-testscan
├── anat
│ ├── sub-01_ses-testscan_T1w.json
│ └── sub-01_ses-testscan_T1w.nii
└── pet
├── sub-01_ses-testscan_pet.json
├── sub-01_ses-testscan_recording-Manual_blood.json
├── sub-01_ses-testscan_recording-Manual_blood.tsv
└── sub-01_ses-testscan_pet.nii
Validating your converted PET BIDS data set
Installing the validator
The above structure is a valid BIDS dataset, however the bids-validator is a much more accurate and trustworthy method for making that determination. Follow the instructions here to collect and install the validator.
Validating your new dataset
Using the validator installed above in the previous step point it at your data and check to see if your conversion was successful:
machine:Projects user$ bids-validator NewBidsDataSet/
bids-validator@1.7.2
1: [ERR] Invalid JSON file. The file is not formatted according the schema. (code: 55 - JSON_SCHEMA_VALIDATION_ERROR)
./dataset_description.json
Evidence: .DatasetType should be equal to one of the allowed values
./dataset_description.json
Evidence: .EthicsApprovals should be array
Please visit https://neurostars.org/search?q=DATASET_DESCRIPTION_JSON_MISSING for existing conversations about this issue.
Summary: Available Tasks: Available Modalities:
13 Files, 4.55GB pet
1 - Subject blood
2 - Sessions anat
If you have any questions, please post on https://neurostars.org/tags/bids.
Good enough! But, in all seriousness the error messages and output from the bids-validator are invaluable during the conversion process. The error messages will guide you to the correct format; do not fear them. Running the validator during the conversion process will help to increase your familiarity with the specification and ensure that your data is properly formatted.
Tools and resources for PET conversion
Some useful tools and resources that have been used in this document are as follows:
-
BIDS Validator, which fully supports PET
-
PET2BIDS is a MATLAB and Python utility for converting your data to PET BIDS format
-
TPCCLIIB is a command line library containing (among many others) PET tools such as
ecat2nii
that will be used below to convert the imaging data from a PET dataset into nifti format. The Turku PET Centre site can be found here for additional information on anything PET. -
a BIDS PET Template from this starter kit to initially populate and translate text/tabulature/csv blood data into BIDS PET compliant
.tsv
and.json
files. -
dcm2niix can be used if your raw pet data happens to be in a DICOM format. Additionally this opens up the use of tools such as dcmdump from the dcmtk to help in your conversion.
Final thoughts
As of now, PET is still quite new to the BIDS standard, but at this very moment there are people working on stream-lining data collection and conversion to help move PET into this standard.
If you're reading this you've just become one of those people. Feel free to reach out to the OpenNeuroPET project