DICOM affine matrix transformation from image space to patient space in Matlab

前端 未结 1 836
旧巷少年郎
旧巷少年郎 2020-12-29 00:10

From the nifti header its easy to get the affine matrix. However in the DICOM header there are lots of entries, but its unclear to me which entries describe the transformati

相关标签:
1条回答
  • 2020-12-29 00:48

    Ok so they were nested away in what might be a vendor specific entry of the struct. When loaded in Matlab, the name of the nest was inf.PerFrameFunctionalGroupsSequence.Item_X., then the framenumber, and then some more nesting which was more straightforward/self explanatory so I wont need to add it here. But search for the entries you need there. The slice spacing is called SpacingBetweenSlices (or slice thickness in the single slice case), the pixel spacing is called PixelSpacing and then there are ImagePositionPatient for the translation and ImageOrientationPatient for the rotation. Below is the code I wrote when following the steps from the nipy link below.

    What happens is you load the direction cosines in a rotation matrix to align the basis vectors and you load the the pixel spacing and slice spacing in a matrix to scale the basis vectors and you load the image position to translate the new coordinate system. Finding the directoin cosines for the z direction takes some calculations because dicom apparently was designed for 2d images. In the single slice case the z direction cosines is the unit vector orthogonal to the x and y direction cosines (the cross product between the two) and in the multi slice case you can calculate it from all the differences in the translations between the slcies. After this you still want to apply the transformation which is also not immediately straightforward.

    %load the header
    inf = dicominfo(filename, 'dictionary', yourvendorspecificdictfilehere);
    
    nSl = double(inf.MRSeriesNrOfSlices);
    nY = double(inf.Height);
    nX = double(inf.Width);
    T1 = double(inf.PerFrameFunctionalGroupsSequence.Item_1.PlanePositionSequence.Item_1.ImagePositionPatient);
    
    %load pixel spacing / scaling / resolution
    RowColSpacing = double(inf.PerFrameFunctionalGroupsSequence.Item_1.PixelMeasuresSequence.Item_1.PixelSpacing);
    %of inf.PerFrameFunctionalGroupsSequence.Item_1.PrivatePerFrameSq.Item_1.Pixel_Spacing;
    dx = double(RowColSpacing(1));
    dX = [1; 1; 1].*dx;%cols
    dy = double(RowColSpacing(2));
    dY = [1; 1; 1].*dy;%rows
    dz = double(inf.SpacingBetweenSlices);%inf.PerFrameFunctionalGroupsSequence.Item_1.PrivatePerFrameSq.Item_1.SliceThickness; %thickness of spacing?
    dZ = [1; 1; 1].*dz;
    
    %directional cosines per basis vector
    dircosXY = double(inf.PerFrameFunctionalGroupsSequence.Item_1.PlaneOrientationSequence.Item_1.ImageOrientationPatient);
    dircosX = dircosXY(1:3);
    dircosY = dircosXY(4:6);
    if nSl == 1;
        dircosZ = cross(dircosX,dircosY);%orthogonal to other two direction cosines!
    else
        N = nSl;%double(inf.NumberOfFrames);
        TN = double(-eval(['inf.PerFrameFunctionalGroupsSequence.Item_',sprintf('%d', N),'.PlanePositionSequence.Item_1.ImagePositionPatient']));
        dircosZ = ((T1-TN)./nSl)./dZ;
    end
    
    %all dircos together
    dimensionmixing = [dircosX dircosY dircosZ];
    
    %all spacing together
    dimensionscaling = [dX dY dZ];
    
    %mixing and spacing of dimensions together
    R = dimensionmixing.*dimensionscaling;%maps from image basis to patientbasis
    
    %offset and R together
    A = [[R T1];[0 0 0 1]];
    
    %you probably want to switch X and Y
    %(depending on how you load your dicom into a matlab array)
    Aold = A;
    A(:,1) = Aold(:,2);
    A(:,2) = Aold(:,1);
    

    This results in this affine formula:

    enter image description here

    So basically I followed this tutorial. What was the biggest struggle was getting the Z direction and the translation correct. Also finding identifying and converting the correct entries was not straightforward for me. I do think my answer adds something to that tutorial though, because it was pretty hard to find the entries they refer to and now I wrote some Matlab code getting the affine matrix from a DICOM header. Before using the found affine matrix you also might need to find the Z coordinates for all of your frames, which might not be trivial if your dataset has more than four dimensions (dicomread puts all higher dimensions in one big fourth dimension)

    -Edit- Corrected Z direction and translation of the transformation

    0 讨论(0)
提交回复
热议问题