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

Siemens B1 map with dual TR #918

Open
ghammad opened this issue Feb 18, 2025 · 3 comments
Open

Siemens B1 map with dual TR #918

ghammad opened this issue Feb 18, 2025 · 3 comments

Comments

@ghammad
Copy link

ghammad commented Feb 18, 2025

Describe the bug

Similarly to #743 , our B1 mapping on a Siemens Terra uses a dual TR AFI protocol. When converting the DICOM images with dcm2niix, we end up with 2 images with "_e1.nii" and "_e2.nii" as suffixes. So far, so good. However, when looking into the associated .json files, we noticed that the "RepetitionTime" fields are identical between the 2 resulting images.

To reproduce

dcm2niix -f %p_%t_%s -i y -o dcm2niix/ ima/
grep RepetitionTime dcm2niix/kp_afib1_v1g_4mm_PA_20250217162937_2_e*.json
>> dcm2niix/kp_afib1_v1g_4mm_PA_20250217162937_2_e1.json:	"RepetitionTime": 0.025,
>> dcm2niix/kp_afib1_v1g_4mm_PA_20250217162937_2_e2.json:	"RepetitionTime": 0.025,

Expected behavior

The field "RepetitionTime" of the second image ("_e2.nii/json") should report the second TR.

For completeness, we also ran the DICOM to Nifti conversion from SPM12.6. The reported "RepetitionTime" are also identical between the two images:

grep RepetitionTime spm_convert/s00000000000000000000-0002-00001-0000*.json
>> spm_convert/s00000000000000000000-0002-00001-000048-01.json:			"RepetitionTime": 25,
>> spm_convert/s00000000000000000000-0002-00001-000096-02.json:			"RepetitionTime": 25,

However, the headers do contain some additional metadata, such as "alTR" that does contain the list of TR (although in a different unit):

grep alTR spm_convert/s00000000000000000000-0002-00001-0000*.json 
>> spm_convert/s00000000000000000000-0002-00001-000048-01.json:			"alTR": [25000,125000],
>> spm_convert/s00000000000000000000-0002-00001-000096-02.json:			"alTR": [25000,125000],

This SPM workflow is what we usually use in our lab but we would like to be able to use dcm2niix, precisely to avoid having to parse additional metadata based on the sequence type.
We fully understand the dcm2niix cannot accommodate all sequence configurations but we would grateful if you could suggest a way to extract the expected TR with dcm2niix.

Output log

Chris Rorden's dcm2niiX version v1.0.20241231  (JP2:OpenJPEG) (JP-LS:CharLS) GCC9.4.0 x86-64 (64-bit Linux)
Found 199 DICOM file(s)
Ignoring localizer (sequence '*qfl2d1') of series 1 ima/AFI_TEST.MR.PROTOCOLS_STUDIES.0001.0001.2025.02.18.10.41.43.88002.83659872.IMA
Ignoring localizer (sequence '*qfl2d1') of series 1 ima/AFI_TEST.MR.PROTOCOLS_STUDIES.0001.0002.2025.02.18.10.41.43.88002.83659928.IMA
Ignoring localizer (sequence '*qfl2d1') of series 1 ima/AFI_TEST.MR.PROTOCOLS_STUDIES.0001.0003.2025.02.18.10.41.43.88002.83659910.IMA
Slices not stacked: echo varies (TE 3.3, 3.3; echo 1, 2). Use 'merge 2D slices' option to force stacking
::autoBids:Siemens CSAseqFname:'%CustomerSeq%\kp_afib1_v1g' pulseSeq:'' seqName:'kpafi1g3d2'
Convert 48 DICOM as dcm2niix/00000000000000000000_kp_afib1_v1g_4mm_PA_trueform_20250217162937_3_e1 (56x64x48x1)
::autoBids:Siemens CSAseqFname:'%CustomerSeq%\kp_afib1_v1g' pulseSeq:'' seqName:'kpafi1g3d2'
Convert 48 DICOM as dcm2niix/00000000000000000000_kp_afib1_v1g_4mm_PA_trueform_20250217162937_3_e2 (56x64x48x1)
::autoBids:Siemens CSAseqFname:'%CustomerSeq%\kp_afib1_v1g' pulseSeq:'' seqName:'kpafi1g3d2'
Convert 48 DICOM as dcm2niix/00000000000000000000_kp_afib1_v1g_4mm_PA_20250217162937_2_e2 (56x64x48x1)
::autoBids:Siemens CSAseqFname:'%CustomerSeq%\kp_afib1_v1g' pulseSeq:'' seqName:'kpafi1g3d2'
Convert 48 DICOM as dcm2niix/00000000000000000000_kp_afib1_v1g_4mm_PA_20250217162937_2_e1 (56x64x48x1)
Conversion required 0.394387 seconds (0.242579 for core code).

Version

dcm2niiX version v1.0.20241231 (JP2:OpenJPEG) (JP-LS:CharLS) GCC9.4.0 x86-64 (64-bit Linux)

This build was created with the code under the development branch, commit c26502a6f2820ed7cc122ab8803579ffd5b88512 (2025/01/20)

Troubleshooting

We also tried with the latest release (version 11-December-2024) before using the source code in the development branch. The outcome was identical.

Thank you for your help.

@ghammad
Copy link
Author

ghammad commented Feb 18, 2025

We just sent you a link to our institutional file storage system (Dox, University of Liège) where you can find AFI B1 test data made with a phantom.

Best regards,

Grégory

@ghammad
Copy link
Author

ghammad commented Mar 6, 2025

The link to the shared data might expire at some point. May I ask you to confirm that you received the test data?

Thank you.

Best regards,

Grégory

@neurolabusc
Copy link
Collaborator

neurolabusc commented Mar 6, 2025

dcm2niix is correctly following the recipe of the DICOM images. You can see that yourself by running this Python script. The series claims to have two echoes, but only a single TR. All images in the series claim identical TR and TE times.

python dcm_values.py ./issue918
2	1	1	2.13	25
2	2	1	2.13	25
2	3	1	2.13	25
2	4	1	2.13	25
2	5	1	2.13	25
2	6	1	2.13	25
2	7	1	2.13	25
2	8	1	2.13	25
2	9	1	2.13	25
...
2	47	1	2.13	25
2	48	1	2.13	25
2	49	2	2.13	25
2	50	2	2.13	25
...
2	94	2	2.13	25
2	95	2	2.13	25
2	96	2	2.13	25

dcm2niix and dicm2nii have the same interpretation (albeit dicm2nii concatenates each echo in a single 4D file, while dcm2niix follows the BIDS specification to save each echo as a separate file). You can merge 3D NIfTI files as a 4D file with fslmerge. dcm2niix (and dicm2nii and SPM) assume the DICOM files are truthful, which does not appear to be the case here. I see the data was acquired on a developmental version (syngo MR E12) of Siemens software, so an upgrade may resolve this. I would work with the Siemens Research Collaboration Manager associated with your center to resolve this.

import os
import sys
import pydicom

def extract_dicom_info(filepath):
    try:
        ds = pydicom.dcmread(filepath, stop_before_pixels=True)

        series_number = getattr(ds, 'SeriesNumber', 0)
        instance_number = getattr(ds, 'InstanceNumber', 0)
        echo_number = getattr(ds, 'EchoNumbers', 0)
        echo_time = getattr(ds, 'EchoTime', 0)
        repetition_time = getattr(ds, 'RepetitionTime', 0)

        return series_number, instance_number, echo_number, echo_time, repetition_time
    except Exception as e:
        print(f"Warning: Failed to read {filepath}: {e}", file=sys.stderr)
        return None

def main():
    if len(sys.argv) != 2:
        print(f"Usage: {sys.argv[0]} <folder>")
        sys.exit(1)

    folder = sys.argv[1]

    if not os.path.isdir(folder):
        print(f"Error: '{folder}' is not a valid directory.")
        sys.exit(1)

    dicom_files = []
    for filename in os.listdir(folder):
        if filename.lower().endswith('.dcm'):
            filepath = os.path.join(folder, filename)
            info = extract_dicom_info(filepath)
            if info:
                dicom_files.append(info)

    # Sort by series number, then instance number
    dicom_files.sort(key=lambda x: (x[0], x[1]))

    # Output header (optional, remove if not needed)
    # print("SeriesNumber\tInstanceNumber\tEchoNumber\tEchoTime\tRepetitionTime")

    for series_number, instance_number, echo_number, echo_time, repetition_time in dicom_files:
        print(f"{series_number}\t{instance_number}\t{echo_number}\t{echo_time}\t{repetition_time}")

if __name__ == "__main__":
    main()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants