a b/tool/Code/utilities/conform.py
1
# Copyright 2019 Population Health Sciences and Image Analysis, German Center for Neurodegenerative Diseases(DZNE)
2
#
3
#    Licensed under the Apache License, Version 2.0 (the "License");
4
#    you may not use this file except in compliance with the License.
5
#    You may obtain a copy of the License at
6
#
7
#        http://www.apache.org/licenses/LICENSE-2.0
8
#
9
#    Unless required by applicable law or agreed to in writing, software
10
#    distributed under the License is distributed on an "AS IS" BASIS,
11
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
#    See the License for the specific language governing permissions and
13
#    limitations under the License.
14
15
import numpy as np
16
import nibabel as nib
17
import scipy.ndimage
18
import os
19
20
21
def calculated_new_ornt(iornt,base_ornt):
22
23
    new_iornt=iornt[:]
24
25
    for axno, direction in np.asarray(base_ornt):
26
        idx=np.where(iornt[:,0] == axno)
27
        idirection=iornt[int(idx[0][0]),1]
28
        if direction == idirection:
29
            new_iornt[int(idx[0][0]), 1] = 1.0
30
        else:
31
            new_iornt[int(idx[0][0]), 1] = -1.0
32
33
    return new_iornt
34
35
def check_orientation(img,base_ornt=np.array([[0,-1],[1,1],[2,1]])):
36
37
    iornt=nib.io_orientation(img.affine)
38
39
    if not np.array_equal(iornt,base_ornt):
40
        img = img.as_reoriented(calculated_new_ornt(iornt,base_ornt))
41
42
    return img
43
44
45
def resample(image, spacing, new_spacing=[1, 1, 1],order=1,prefilter=True):
46
    # Determine current pixel spacing
47
48
    resize_factor = spacing / new_spacing
49
    new_real_shape = image.shape * resize_factor
50
    new_shape = np.round(new_real_shape)
51
    real_resize_factor = new_shape / image.shape
52
    new_spacing = spacing / real_resize_factor
53
54
    image = scipy.ndimage.interpolation.zoom(image,real_resize_factor,order=order,prefilter=prefilter)
55
56
    return image, new_spacing
57
58
59
def define_size(mov_dim,ref_dim):
60
    new_dim=np.zeros(len(mov_dim),dtype=np.int)
61
    borders=np.zeros((len(mov_dim),2),dtype=int)
62
63
    padd = [int(mov_dim[0] // 2), int(mov_dim[1] // 2), int(mov_dim[2] // 2)]
64
65
    for i in range(len(mov_dim)):
66
        new_dim[i]=int(max(2*mov_dim[i],2*ref_dim[i]))
67
        borders[i,0]= int(new_dim[i] // 2) -padd [i]
68
        borders[i,1]= borders[i,0] +mov_dim[i]
69
70
    return list(new_dim),borders
71
72
def map_size(arr,base_shape,axial):
73
74
    if axial:
75
        base_shape[2]=arr.shape[2]
76
77
    print('Volume will be resize from %s to %s ' % (arr.shape, base_shape))
78
79
    new_shape,borders=define_size(np.array(arr.shape),np.array(base_shape))
80
    new_arr=np.zeros(new_shape)
81
    final_arr=np.zeros(base_shape)
82
83
    new_arr[borders[0,0]:borders[0,1],borders[1,0]:borders[1,1],borders[2,0]:borders[2,1]]= arr[:]
84
85
    middle_point = [int(new_arr.shape[0] // 2), int(new_arr.shape[1] // 2), int(new_arr.shape[2] // 2)]
86
    padd = [int(base_shape[0]/2), int(base_shape[1]/2), int(base_shape[2]/2)]
87
88
    low_border=np.array((np.array(middle_point)-np.array(padd)),dtype=int)
89
    high_border=np.array(np.array(low_border)+np.array(base_shape),dtype=int)
90
91
    final_arr[:,:,:]= new_arr[low_border[0]:high_border[0],
92
                   low_border[1]:high_border[1],
93
                   low_border[2]:high_border[2]]
94
95
    return final_arr
96
97
98
def map_image(img_arr,base_zoom,izoom,order,axial):
99
    if axial:
100
        base_zoom[2] = izoom[2]
101
102
    print('Volume will be sample from %s to %s ' % (izoom, base_zoom))
103
    resample_arr, izoom= resample(img_arr, spacing=np.array(izoom),
104
                                         new_spacing=np.array(base_zoom), order=order)
105
106
    resample_arr[resample_arr < 0] = 0
107
108
    return resample_arr,izoom
109
110
def conform(img,flags,order,save_path,mod,axial=False):
111
    """
112
    Args:
113
        img: nibabel img: Loaded source image
114
        flags: dict : Dictionary containing the image size, spacing and orientation
115
        order: int : interpolation order (0=nearest,1=linear(default),2=quadratic,3=cubic)
116
    Returns:
117
        new_img: nibabel img : conformed nibabel image
118
    """
119
    save=False
120
    # check orientation LAS
121
    img=check_orientation(img,base_ornt=flags['base_ornt'])
122
123
    img_arr=img.get_data()
124
    img_header = img.header
125
126
    # check voxel sizer
127
    i_zoom=img.header.get_zooms()
128
129
    #check the spacing idx for interpolation
130
    if axial:
131
        idx=2
132
    else:
133
        idx=3
134
    if not np.allclose(np.array(i_zoom)[:idx],np.array(flags['spacing'])[:idx],rtol=0.3):
135
        img_arr,i_zoom= map_image(img_arr,flags['spacing'],i_zoom,order,axial)
136
        save=True
137
138
    ishape = img_arr.shape
139
140
    # check dimensions
141
    if int(ishape[0]) != int(flags['imgSize'][0]) or int(ishape[1]) != int(flags['imgSize'][1]) or int(ishape[2]) != int(flags['imgSize'][2]):
142
        img_arr=map_size(img_arr,flags['imgSize'],axial)
143
        save = True
144
145
    img_header.set_data_shape(img_arr.shape)
146
    img_header.set_zooms(i_zoom)
147
148
    affine = img_header.get_qform()
149
    affine[0][3] += ((flags['imgSize'][0] - ishape[0]) / 2 * i_zoom[0])
150
    affine[1][3] -= ((flags['imgSize'][1] - ishape[1]) / 2 * i_zoom[1])
151
    affine[2][3] -= ((flags['imgSize'][2] - ishape[2]) / 2 * i_zoom[2])
152
    img_header.set_qform(affine)
153
154
    new_img = nib.Nifti1Image(img_arr, affine, img_header)
155
    #save images if modified
156
157
    if save:
158
159
        if not os.path.isdir(os.path.join(save_path, 'MRI')):
160
            os.mkdir(os.path.join(save_path, 'MRI'))
161
162
        mri_path = os.path.join(save_path, 'MRI')
163
164
        if mod == 'fat':
165
            new_img_path = os.path.join(mri_path, 'FatImaging_F.nii.gz')
166
        else:
167
            new_img_path = os.path.join(mri_path, 'FatImaging_W.nii.gz')
168
169
        nib.save(new_img, new_img_path)
170
171
    return new_img