14
14
15
15
import operator
16
16
import warnings
17
+ from functools import cached_property
17
18
18
19
import numpy as np
19
20
20
21
from nibabel .optpkg import optional_package
21
22
22
- from ..onetime import auto_attr as one_time
23
23
from ..openers import ImageOpener
24
24
from . import csareader as csar
25
25
from .dwiparams import B2q , nearest_pos_semi_def , q2bg
@@ -140,15 +140,15 @@ def __init__(self, dcm_data):
140
140
"""
141
141
self .dcm_data = dcm_data
142
142
143
- @one_time
143
+ @cached_property
144
144
def image_shape (self ):
145
145
"""The array shape as it will be returned by ``get_data()``"""
146
146
shape = (self .get ('Rows' ), self .get ('Columns' ))
147
147
if None in shape :
148
148
return None
149
149
return shape
150
150
151
- @one_time
151
+ @cached_property
152
152
def image_orient_patient (self ):
153
153
"""Note that this is _not_ LR flipped"""
154
154
iop = self .get ('ImageOrientationPatient' )
@@ -158,15 +158,15 @@ def image_orient_patient(self):
158
158
iop = np .array (list (map (float , iop )))
159
159
return np .array (iop ).reshape (2 , 3 ).T
160
160
161
- @one_time
161
+ @cached_property
162
162
def slice_normal (self ):
163
163
iop = self .image_orient_patient
164
164
if iop is None :
165
165
return None
166
166
# iop[:, 0] is column index cosine, iop[:, 1] is row index cosine
167
167
return np .cross (iop [:, 1 ], iop [:, 0 ])
168
168
169
- @one_time
169
+ @cached_property
170
170
def rotation_matrix (self ):
171
171
"""Return rotation matrix between array indices and mm
172
172
@@ -193,7 +193,7 @@ def rotation_matrix(self):
193
193
raise WrapperPrecisionError ('Rotation matrix not nearly orthogonal' )
194
194
return R
195
195
196
- @one_time
196
+ @cached_property
197
197
def voxel_sizes (self ):
198
198
"""voxel sizes for array as returned by ``get_data()``"""
199
199
# pix space gives (row_spacing, column_spacing). That is, the
@@ -212,7 +212,7 @@ def voxel_sizes(self):
212
212
pix_space = list (map (float , pix_space ))
213
213
return tuple (pix_space + [zs ])
214
214
215
- @one_time
215
+ @cached_property
216
216
def image_position (self ):
217
217
"""Return position of first voxel in data block
218
218
@@ -231,7 +231,7 @@ def image_position(self):
231
231
# Values are python Decimals in pydicom 0.9.7
232
232
return np .array (list (map (float , ipp )))
233
233
234
- @one_time
234
+ @cached_property
235
235
def slice_indicator (self ):
236
236
"""A number that is higher for higher slices in Z
237
237
@@ -246,12 +246,12 @@ def slice_indicator(self):
246
246
return None
247
247
return np .inner (ipp , s_norm )
248
248
249
- @one_time
249
+ @cached_property
250
250
def instance_number (self ):
251
251
"""Just because we use this a lot for sorting"""
252
252
return self .get ('InstanceNumber' )
253
253
254
- @one_time
254
+ @cached_property
255
255
def series_signature (self ):
256
256
"""Signature for matching slices into series
257
257
@@ -390,15 +390,15 @@ def _apply_scale_offset(self, data, scale, offset):
390
390
return data + offset
391
391
return data
392
392
393
- @one_time
393
+ @cached_property
394
394
def b_value (self ):
395
395
"""Return b value for diffusion or None if not available"""
396
396
q_vec = self .q_vector
397
397
if q_vec is None :
398
398
return None
399
399
return q2bg (q_vec )[0 ]
400
400
401
- @one_time
401
+ @cached_property
402
402
def b_vector (self ):
403
403
"""Return b vector for diffusion or None if not available"""
404
404
q_vec = self .q_vector
@@ -469,7 +469,7 @@ def __init__(self, dcm_data):
469
469
raise WrapperError ('SharedFunctionalGroupsSequence is empty.' )
470
470
self ._shape = None
471
471
472
- @one_time
472
+ @cached_property
473
473
def image_shape (self ):
474
474
"""The array shape as it will be returned by ``get_data()``
475
475
@@ -573,7 +573,7 @@ def image_shape(self):
573
573
)
574
574
return tuple (shape )
575
575
576
- @one_time
576
+ @cached_property
577
577
def image_orient_patient (self ):
578
578
"""
579
579
Note that this is _not_ LR flipped
@@ -590,7 +590,7 @@ def image_orient_patient(self):
590
590
iop = np .array (list (map (float , iop )))
591
591
return np .array (iop ).reshape (2 , 3 ).T
592
592
593
- @one_time
593
+ @cached_property
594
594
def voxel_sizes (self ):
595
595
"""Get i, j, k voxel sizes"""
596
596
try :
@@ -610,7 +610,7 @@ def voxel_sizes(self):
610
610
# Ensure values are float rather than Decimal
611
611
return tuple (map (float , list (pix_space ) + [zs ]))
612
612
613
- @one_time
613
+ @cached_property
614
614
def image_position (self ):
615
615
try :
616
616
ipp = self .shared .PlanePositionSequence [0 ].ImagePositionPatient
@@ -623,7 +623,7 @@ def image_position(self):
623
623
return None
624
624
return np .array (list (map (float , ipp )))
625
625
626
- @one_time
626
+ @cached_property
627
627
def series_signature (self ):
628
628
signature = {}
629
629
eq = operator .eq
@@ -696,7 +696,7 @@ def __init__(self, dcm_data, csa_header=None):
696
696
csa_header = {}
697
697
self .csa_header = csa_header
698
698
699
- @one_time
699
+ @cached_property
700
700
def slice_normal (self ):
701
701
# The std_slice_normal comes from the cross product of the directions
702
702
# in the ImageOrientationPatient
@@ -720,7 +720,7 @@ def slice_normal(self):
720
720
else :
721
721
return std_slice_normal
722
722
723
- @one_time
723
+ @cached_property
724
724
def series_signature (self ):
725
725
"""Add ICE dims from CSA header to signature"""
726
726
signature = super ().series_signature
@@ -730,7 +730,7 @@ def series_signature(self):
730
730
signature ['ICE_Dims' ] = (ice , operator .eq )
731
731
return signature
732
732
733
- @one_time
733
+ @cached_property
734
734
def b_matrix (self ):
735
735
"""Get DWI B matrix referring to voxel space
736
736
@@ -767,7 +767,7 @@ def b_matrix(self):
767
767
# semi-definite.
768
768
return nearest_pos_semi_def (B_vox )
769
769
770
- @one_time
770
+ @cached_property
771
771
def q_vector (self ):
772
772
"""Get DWI q vector referring to voxel space
773
773
@@ -840,7 +840,7 @@ def __init__(self, dcm_data, csa_header=None, n_mosaic=None):
840
840
self .n_mosaic = n_mosaic
841
841
self .mosaic_size = int (np .ceil (np .sqrt (n_mosaic )))
842
842
843
- @one_time
843
+ @cached_property
844
844
def image_shape (self ):
845
845
"""Return image shape as returned by ``get_data()``"""
846
846
# reshape pixel slice array back from mosaic
@@ -850,7 +850,7 @@ def image_shape(self):
850
850
return None
851
851
return (rows // self .mosaic_size , cols // self .mosaic_size , self .n_mosaic )
852
852
853
- @one_time
853
+ @cached_property
854
854
def image_position (self ):
855
855
"""Return position of first voxel in data block
856
856
0 commit comments