xiangruili / dicm2nii

DICOM to NIfTI conversion, NIfTI tools
BSD 2-Clause "Simplified" License
89 stars 45 forks source link

Nifti to DICOM #56

Open loneblues opened 1 year ago

loneblues commented 1 year ago

Is it possible to convert a set of DICOM images into nifti, and do some processing, and the convert the derived nifti files back to DICOM with headers saved during the DICOM to nifti conversion using dicm2nii and provided tools?

Thanks in advance.

xiangruili commented 1 year ago

It is possible to do some processing on NIfTI, and then save into dicom. But with those header information stored in NIfTI header, the result dicom will be missing a lot of information. This is because, during dicom to NIfTI conversion, most of the dicom header info are dropped. If you have the original dicom, you could replace the dicom image with processed NIfTI image. Of course you will need to take care of the NIfTI image reorient, flip and/or transpose before you put it into dicom.

adityaapte commented 1 year ago

Is there code to obtain ImageOrientation and ImagePositionPatient from NIfTI header? Something similar to reverse of xform_mat function.

xiangruili commented 1 year ago

@adityaapte The following code will get ImageOrientation and ImagePosition in NIfTI space (typically axial slices, RAS+ rather than LPS+), but I think those in the NIfTI is more useful.

hdr = nii_tool('hdr', 'myFille.nii'); % read the NIfTI header
R = [hdr.srow_x; hdr.srow_y; hdr.srow_z]; % transformation matrix in NIfTI
ImagePosition = R(:,4); % 1st slice location (in mm) in NIfTI space
% ImagePosition = R(:,1:3)*[0 0 iSlice]' + R(:,4); % (iSlice+1)th slice location
ImageOrientation = R(:,1:3) ./ sqrt(sum(R(:,1:3) .^ 2)); % 3rd column for slice which is not in DICOM

If the NIfTI file stores the correct dim_info, it is possible to reverse Position and Orientation into DICOM space. This info is in

hdr.dim_info
adityaapte commented 1 year ago

Thanks. So we will need metadata to reverse the transforms that were applied while writing NIfTI. Any guidance on obtaining this metadata (like flp below) will be great.

Here are some of the transformations I found in the code-base to go from DICOM to NIfTI coordinates.

R(1:2,:) = -R(1:2,:); % dicom LPS to nifti RAS, xform matrix before reorient

In set_nii_hdr.m:

flp = R(ixyz+[0 3 6])<0; % flip an axis if true
d = det(R(:,1:3)) * prod(1-flp*2); % det after all 3 axis positive
if (d>0 && pf.lefthand) || (d<0 && ~pf.lefthand)
    flp(1) = ~flp(1); % left or right storage
end
rotM = diag([1-flp*2 1]); % 1 or -1 on diagnal
rotM(1:3, 4) = (dim-1) .* flp; % 0 or dim-1
R = R / rotM; % xform matrix after flip
adityaapte commented 1 year ago

For example, the following produced dicom orientation and position from NIfTI for a non-oblique dataset.

hdr = nii_tool('hdr', niiFile); % read the NIfTI header
R = [hdr.srow_x; hdr.srow_y; hdr.srow_z]; % transformation matrix in NIfTI
% R(1:2,:) = -R(1:2,:); % dicom LPS to nifti RAS, xform matrix before reorient (APA)
R(1:2,:) = -R(1:2,:); % dicom LPS to nifti RAS, xform matrix before reorient (APA)
ImagePosition = R(:,4); % 1st slice location (in mm) in NIfTI space
% ImagePosition = R(:,1:3)*[0 0 iSlice]' + R(:,4); % (iSlice+1)th slice location
ImageOrientation = R(:,1:3) ./ sqrt(sum(R(:,1:3) .^ 2)); % 3rd column for slice which is not in DICOM
imgSiz = hdr.dim(2:4);
flpV = [(ImageOrientation([1,5,9]) < 0) .* (imgSiz-1)]';
R = R / [[ImageOrientation,flpV];0,0,0,1];
ImageOrientationInDcm = R(:,1:3) ./ sqrt(sum(R(:,1:3) .^ 2));
ImagePositionInDcm = R(:,4);
xiangruili commented 1 year ago

It seems you do need Position and Orientation in DICOM? This is relatively easy for axial acquisition (hdr.dim_info=[1 2 3]), but for sagittal and coronal acquisition, it will be complicated, and you also need valid hdr.dim_info stored, and this is not always the case for some DICOM to NIfTI converters.

If you use my dicm2nii.m, you won't bother for this, since a file, named dcmHeaders.mat, is always saved together with the NIfTI files, and you can get ALL dicom headers for each NIfTI.

I am curious about the purpose of going back to DICOM.

adityaapte commented 1 year ago

The purpose of going back to DICOM is to (1) archive analysis results (e.g. segmentation, deformation vector field, derived images etc) and (2) maintain compatibility with patient data that is already available in DICOM format. For example, we might have CT, RTDOSE, RTSTRUCT, PR etc modalities for a patient. We perform analysis using NIfTI format and obtain a resulting NIfTI file for deformation vector field. We would like to convert it to DICOM format so that all objects associated with the patient are in the same format.

In me experience, using the nii file exported from dicm2nii, the coordinates match between the imported DICOM series and and nii file in 3DSlicer. This is true for axial, sag, cor or oblique scans. So it seems that the NIfTI file has the metadata to reproduce DICOM coordinates without hdr.dim_info.

xiangruili commented 1 year ago

If you perform some processing to the images and like to save them into dicom format, the best way is to preserve the original dicom header, and modify some of them and then replace the images with processed images.

The only confusion may happen when NIfTI converter reorient and/or flip axis for the DICOM image. Besides left/right storage (you can turn off the left-handed storage for the converter), there is a fixed pattern for dealing with each of TRA/SAG/COR acquisition.

During dicm2nii conversion, only a TINY bit of the dicom info are save in nii. So without original dicom header, most information are lost.

xiangruili commented 1 year ago

@adityaapte I was thinking a way to make nii2dcm easier for you. For the package released on 2023/02/23, a new preference, to reorient NIfTI or not, is added. You can run dicm2nii without input to activate the GUI, and you will see "Reorient NIfTI" option. If you uncheck it, and then do the DICOM to NIfTI conversion, the nii files won't be reoriented.

You can do your process on these nii files, and it will be much easier to store the processed images back into DICOM. There is no need to flip images or swap axis, and you can simply keep the original DICOM header, including ImageOrientaionPatient and ImagePositionPatien.

Reorient is not necessory according to NIfTI standard. Converters tend to do it for better compatibility with some packages, like FSL. If you can process the nii without issue, this option should avoid all the confusions

Let me know if you see any issues.

prlabu commented 9 months ago

Thanks for you work on this @xiangruili. I am looking to convert a nii to dicom. I was looking for a 'nii2dicm' function in this repo but don't see one. Is that function available? I was hopeful since in your comment above you mentioned nii2dcm ("I was thinking a way to make nii2dcm easier for you"). Right now we load into 3d slicer and export to dicom, but it would be really helpful to script this.

Thanks :)

xiangruili commented 9 months ago

@prlabu We don't have nii2dicm. As mentioned above, you can keep the your original dicom files, process your NIfTI, and put back the processed image into a copy of dicom, by replacing the image data. For example, following is psudo code:

copyfile('myOriginal.dcm', 'myNewFile.dcm');
s = dicm_hdr('myNewFile.dcm', 'Manufacturer'); % partial hdr to get PixelData.Start
nii = nii_tool('load', 'myProcessedFileWithoutReorient.nii');
fid = fopen( 'myNewFile.dcm', 'r+');
fseek(fid, s.PixelData.Start, 'bof');
fwrite(fid, nii.img(:,:,1), 'uint16'); % write first slice into the dicom
fclose(fid);
% dicm_val_rep % can be used to modify a dicom value if you wish