forked from pzaffino/python-mha
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mha.py
executable file
·190 lines (151 loc) · 6.07 KB
/
mha.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
"""
This class reads and writes mha files (images or vector fields)
Author: Paolo Zaffino (p.zaffino@unicz.it)
Rev 19
NOT TESTED ON PYTHON 3
"""
import numpy as np
class new():
"""
PUBLIC PARAMETERS:
data=3D/4D matrix
size=3D/4D matrix size
spacing=voxel size
offset=spatial offset of data data
data_type='short', 'float' or 'uchar'
direction_cosines=direction cosines of the raw image/vf
CONSTRUCTOR OVERLOADING:
img=mha.new() # All the public parameters will be set to None
img=mha.new(input_file='img.mha')
img=mha.new(data=matrix, size=[512, 512, 80], spacing=[0.9, 0.9, 5], offset=[-240, -240, -160], data_type='short', direction_cosines=[1, 0, 0, 0, 1, 0, 0, 0, 1])
PUBLIC METHODS:
img.read_mha('file_name.mha')
img.write_mha('file_name.mha')
"""
data=None
size=None
spacing=None
offset=None
data_type=None
direction_cosines=None
######################## CONSTRUCTOR - START - #########################
def __init__ (self, input_file=None, data=None, size=None, spacing=None, offset=None, data_type=None, direction_cosines=None):
if input_file!=None and data==None and size==None and spacing==None and offset==None and data_type==None and direction_cosines==None:
self.read_mha(input_file)
elif input_file==None and data!=None and size!=None and spacing!=None and offset!=None and data_type!=None and direction_cosines!=None:
self.data=data
self.size=size
self.spacing=spacing
self.offset=offset
self.data_type=data_type
self.direction_cosines=direction_cosines
elif input_file==None and data==None and size==None and spacing==None and offset==None and data_type==None and direction_cosines==None:
pass
######################## CONSTRUCTOR - END - ###########################
######################## READ_MHA - START - ############################
def read_mha(self, fn):
"""
This method reads a mha file and assigns the data to the object parameters
INPUT PARAMETER:
fn=file name
"""
if fn.endswith('.mha'): ## Check if the file extension is ".mha"
f = open(fn,'rb')
data='img' ## On default the matrix is considered to be an image
## Read mha header
for r in range(20):
row=f.readline()
if row.startswith('TransformMatrix ='):
row=row.split('=')[1].strip()
self.direction_cosines=self._cast2int(map(float, row.split()))
elif row.startswith('Offset ='):
row=row.split('=')[1].strip()
self.offset=self._cast2int(map(float, row.split()))
elif row.startswith('ElementSpacing ='):
row=row.split('=')[1].strip()
self.spacing=self._cast2int(map(float, row.split()))
elif row.startswith('DimSize ='):
row=row.split('=')[1].strip()
self.size=map(int, row.split())
elif row.startswith('ElementNumberOfChannels = 3'):
data='vf' ## The matrix is a vf
self.size.append(3)
elif row.startswith('ElementType ='):
data_type=row.split('=')[1].strip()
elif row.startswith('ElementDataFile ='):
break
## Read raw data
self.data=''.join(f.readlines())
f.close()
## Raw data from string to array
if data_type == 'MET_SHORT':
self.data=np.fromstring(self.data, dtype=np.int16)
self.data_type = 'short'
elif data_type == 'MET_FLOAT':
self.data=np.fromstring(self.data, dtype=np.float32)
self.data_type = 'float'
elif data_type == 'MET_UCHAR':
self.data=np.fromstring(self.data, dtype=np.uint8)
self.data_type = 'uchar'
## Reshape array
if data == 'img':
self.data=self.data.reshape(self.size[2],self.size[1],self.size[0]).T
elif data == 'vf':
self.data=self.data.reshape(self.size[2],self.size[1],self.size[0],3)
self.data=self._shiftdim(self.data, 3).T
elif not fn.endswith('.mha'): ## Extension file is not ".mha". It returns all null values
raise NameError('The input file is not a mha file!')
######################### READ_MHA - END - #############################
######################## WRITE_MHA - START - ###########################
def write_mha (self,fn):
"""
This method writes the object parameters in a mha file
INPUT PARAMETER:
fn=file name
"""
if fn.endswith('.mha'): ## Check if the file extension is ".mha"
## Order the matrix in the proper way
self.data = np.array(self.data, order = "F")
## Check if the input matrix is an image or a vf
if self.data.ndim == 3:
data='img'
elif self.data.ndim == 4:
data='vf'
f=open(fn, 'wb')
## Write mha header
f.write('ObjectType = Image\n')
f.write('NDims = 3\n')
f.write('BinaryData = True\n')
f.write('BinaryDataByteOrderMSB = False\n')
f.write('CompressedData = False\n')
f.write('TransformMatrix = '+str(self.direction_cosines).strip('()[]').replace(',','')+'\n')
f.write('Offset = '+str(self.offset).strip('()[]').replace(',','')+'\n')
f.write('CenterOfRotation = 0 0 0\n')
f.write('AnatomicalOrientation = RAI\n')
f.write('ElementSpacing = '+str(self.spacing).strip('()[]').replace(',','')+'\n')
f.write('DimSize = '+str(self.size).strip('()[]').replace(',','')+'\n')
if data == 'vf':
f.write('ElementNumberOfChannels = 3\n')
self.data=self._shiftdim(self.data, 3) ## Shift dimensions if the input matrix is a vf
if self.data_type == 'short':
f.write('ElementType = MET_SHORT\n')
elif self.data_type == 'float':
f.write('ElementType = MET_FLOAT\n')
elif self.data_type == 'uchar':
f.write('ElementType = MET_UCHAR\n')
f.write('ElementDataFile = LOCAL\n')
## Write matrix
f.write(self.data)
f.close()
elif not fn.endswith('.mha'): ## File extension is not ".mha"
raise NameError('The input file name is not a mha file!')
######################## WRITE_MHA - END - #############################
############ UTILITY FUNCTIONS, NOT FOR PUBLIC USE - START - ###########
def _cast2int (self, l):
l_new=[]
for i in l:
if i.is_integer(): l_new.append(int(i))
else: l_new.append(i)
return l_new
_shiftdim = lambda self, x, n: x.transpose(np.roll(range(x.ndim), -n))
############# UTILITY FUNCTIONS, NOT FOR PUBLIC USE - END - ############