1
|
|
|
import unittest |
2
|
|
|
import numpy |
3
|
|
|
from mock import Mock, sentinel |
4
|
|
|
from datetime import datetime, timedelta |
5
|
|
|
from tests.test_basefile import BaseFileTests |
6
|
|
|
|
7
|
|
|
|
8
|
|
|
class ParrecTests(BaseFileTests): |
9
|
|
|
|
10
|
|
|
def setUp(self): |
11
|
|
|
super(ParrecTests, self).setUp() |
12
|
|
|
self.libs = Mock() |
13
|
|
|
self.dependencies.getLibraries.return_value = self.libs |
14
|
|
|
self.setupNibabel() |
15
|
|
|
from niprov.parrec import ParrecFile |
16
|
|
|
self.constructor = ParrecFile |
17
|
|
|
self.file = ParrecFile(self.path, dependencies=self.dependencies) |
18
|
|
|
|
19
|
|
|
def test_Gets_basic_info_from_nibabel_and_returns_it(self): |
20
|
|
|
out = self.file.inspect() |
21
|
|
|
self.assertEqual(out['subject'], 'John Doeish') |
22
|
|
|
self.assertEqual(out['protocol'], 'T1 SENSE') |
23
|
|
|
self.assertEqual(out['acquired'], datetime(2014, 8, 5, 11, 27, 34)) |
24
|
|
|
|
25
|
|
|
def test_Gets_dimensions(self): |
26
|
|
|
out = self.file.inspect() |
27
|
|
|
self.assertEqual(out['dimensions'], [80,80,10]) |
28
|
|
|
|
29
|
|
|
def test_Gets_advanced_fields(self): |
30
|
|
|
out = self.file.inspect() |
31
|
|
|
self.assertEqual(out['technique'], 'T1TFE') |
32
|
|
|
self.assertEqual(out['repetition-time'], 4.364) |
33
|
|
|
self.assertEqual(out['field-of-view'], [130., 100., 154.375]) |
34
|
|
|
self.assertEqual(out['epi-factor'], 1) |
35
|
|
|
self.assertEqual(out['magnetization-transfer-contrast'], False) |
36
|
|
|
self.assertEqual(out['diffusion'], False) |
37
|
|
|
self.assertEqual(out['duration'], timedelta(seconds=65)) |
38
|
|
|
self.assertEqual(out['subject-position'], 'Head First Supine') |
39
|
|
|
self.assertEqual(out['water-fat-shift'], 1.117) |
40
|
|
|
# per-image |
41
|
|
|
self.assertEqual(out['slice-thickness'], 10.0) |
42
|
|
|
self.assertEqual(out['slice-orientation'], 1) |
43
|
|
|
self.assertEqual(out['echo-time'], 2.0800000000000001) |
44
|
|
|
self.assertEqual(out['flip-angle'], 8.0) |
45
|
|
|
self.assertEqual(out['inversion-time'], 0.0) |
46
|
|
|
|
47
|
|
|
def test_getProtocolFields(self): |
48
|
|
|
protocol = self.file.getProtocolFields() |
49
|
|
|
self.assertIn('repetition-time', protocol) |
50
|
|
|
self.assertIn('echo-time', protocol) |
51
|
|
|
|
52
|
|
|
def test_multiple_TRs(self): |
53
|
|
|
img = self.libs.nibabel.load.return_value |
54
|
|
|
img.header.general_info['repetition_time'] = numpy.array([130, 450]) |
55
|
|
|
self.libs.nibabel.load.return_value = img |
56
|
|
|
out = self.file.inspect() |
57
|
|
|
self.assertEqual(out['repetition-time'], [130, 450]) |
58
|
|
|
|
59
|
|
|
def test_Tells_camera_to_save_snapshot_to_cache(self): |
60
|
|
|
img = self.libs.nibabel.load.return_value |
61
|
|
|
data = sentinel.imagedata |
62
|
|
|
img.get_data.return_value = data |
63
|
|
|
out = self.file.inspect() |
64
|
|
|
self.camera.saveSnapshot.assert_called_with(data, for_=self.file) |
65
|
|
|
|
66
|
|
|
def test_Determines_modality(self): |
67
|
|
|
out = self.file.inspect() |
68
|
|
|
self.assertEqual(out['modality'], 'MRI') |
69
|
|
|
|
70
|
|
|
def test_Determines_modality_for_diffusion(self): |
71
|
|
|
img = self.libs.nibabel.load.return_value |
72
|
|
|
img.header.general_info['diffusion'] = 1 |
73
|
|
|
self.libs.nibabel.load.return_value = img |
74
|
|
|
out = self.file.inspect() |
75
|
|
|
self.assertEqual(out['modality'], 'DWI') |
76
|
|
|
|
77
|
|
|
def test_Preserves_modality_if_inherited(self): |
78
|
|
|
pass # Doesn't have to preserve |
79
|
|
|
|
80
|
|
|
def setupNibabel(self): |
81
|
|
|
import numpy |
82
|
|
|
img = Mock() |
83
|
|
|
img.header.general_info = { |
84
|
|
|
'acq_nr': 6, |
85
|
|
|
'angulation': numpy.array([-1.979, 0.546, 0.019]), |
86
|
|
|
'diffusion': 0, |
87
|
|
|
'diffusion_echo_time': 0.0, |
88
|
|
|
'dyn_scan': 0, |
89
|
|
|
'epi_factor': 1, |
90
|
|
|
'exam_date': '2014.08.05 / 11:27:34', |
91
|
|
|
'exam_name': 'test', |
92
|
|
|
'flow_compensation': 0, |
93
|
|
|
'fov': numpy.array([ 130. , 100. , 154.375]), |
94
|
|
|
'max_cardiac_phases': 1, |
95
|
|
|
'max_diffusion_values': 1, |
96
|
|
|
'max_dynamics': 1, |
97
|
|
|
'max_echoes': 1, |
98
|
|
|
'max_gradient_orient': 1, |
99
|
|
|
'max_mixes': 1, |
100
|
|
|
'max_slices': 10, |
101
|
|
|
'mtc': 0, |
102
|
|
|
'nr_label_types': 0, |
103
|
|
|
'off_center': numpy.array([-18.805, 22.157, -17.977]), |
104
|
|
|
'patient_name': 'John Doeish', |
105
|
|
|
'patient_position': 'Head First Supine', |
106
|
|
|
'phase_enc_velocity': numpy.array([ 0., 0., 0.]), |
107
|
|
|
'prep_direction': 'Right-Left', |
108
|
|
|
'presaturation': 0, |
109
|
|
|
'protocol_name': 'T1 SENSE', |
110
|
|
|
'recon_nr': 1, |
111
|
|
|
'repetition_time': 4.364, |
112
|
|
|
'scan_duration': 65.0, |
113
|
|
|
'scan_mode': '3D', |
114
|
|
|
'scan_resolution': numpy.array([76, 62]), |
115
|
|
|
'series_type': 'Image MRSERIES', |
116
|
|
|
'spir': 0, |
117
|
|
|
'tech': 'T1TFE', |
118
|
|
|
'water_fat_shift': 1.117} |
119
|
|
|
img.header.image_defs = numpy.array([ (1, 1, 1, 1, 0, 2, 0, 16, 81, [80, 80], 0.0, 1.26032, 2.84925e-05, 133, 231, [-1.98, 0.55, 0.02], [-18.79, -22.82, -16.42], 10.0, 0.0, 0, 1, 0, 2, [1.912, 1.912], 2.08, 0.0, 0.0, 0.0, 1, 8.0, 0, 0, 0, 7, 0.0, 1, 1, '7', '0', [0.0, 0.0, 0.0], 1), |
120
|
|
|
(2, 1, 1, 1, 0, 2, 1, 16, 81, [80, 80], 0.0, 1.26032, 2.84925e-05, 294, 512, [-1.98, 0.55, 0.02], [-18.79, -12.82, -16.77], 10.0, 0.0, 0, 1, 0, 2, [1.912, 1.912], 2.08, 0.0, 0.0, 0.0, 1, 8.0, 0, 0, 0, 7, 0.0, 1, 1, '7', '0', [0.0, 0.0, 0.0], 1), |
121
|
|
|
(3, 1, 1, 1, 0, 2, 2, 16, 81, [80, 80], 0.0, 1.26032, 2.84925e-05, 427, 742, [-1.98, 0.55, 0.02], [-18.8, -2.83, -17.11], 10.0, 0.0, 0, 1, 0, 2, [1.912, 1.912], 2.08, 0.0, 0.0, 0.0, 1, 8.0, 0, 0, 0, 7, 0.0, 1, 1, '7', '0', [0.0, 0.0, 0.0], 1)], |
122
|
|
|
dtype=[('slice number', '<i8'), ('echo number', '<i8'), ('dynamic scan number', '<i8'), ('cardiac phase number', '<i8'), ('image_type_mr', '<i8'), ('scanning sequence', '<i8'), ('index in REC file', '<i8'), ('image pixel size', '<i8'), ('scan percentage', '<i8'), ('recon resolution', '<i8', (2,)), ('rescale intercept', '<f8'), ('rescale slope', '<f8'), ('scale slope', '<f8'), ('window center', '<i8'), ('window width', '<i8'), ('image angulation', '<f8', (3,)), ('image offcentre', '<f8', (3,)), ('slice thickness', '<f8'), ('slice gap', '<f8'), ('image_display_orientation', '<i8'), ('slice orientation', '<i8'), ('fmri_status_indication', '<i8'), ('image_type_ed_es', '<i8'), ('pixel spacing', '<f8', (2,)), ('echo_time', '<f8'), ('dyn_scan_begin_time', '<f8'), ('trigger_time', '<f8'), ('diffusion_b_factor', '<f8'), ('number of averages', '<i8'), ('image_flip_angle', '<f8'), ('cardiac frequency', '<i8'), ('minimum RR-interval', '<i8'), ('maximum RR-interval', '<i8'), ('TURBO factor', '<i8'), ('Inversion delay', '<f8'), ('diffusion b value number', '<i8'), ('gradient orientation number', '<i8'), ('contrast type', 'S30'), ('diffusion anisotropy type', 'S30'), ('diffusion', '<f8', (3,)), ('label type', '<i8')]) |
123
|
|
|
img.shape = (80,80,10) |
124
|
|
|
self.libs.nibabel.load.return_value = img |
125
|
|
|
self.libs.hasDependency.return_value = True |
126
|
|
|
|
127
|
|
|
|
128
|
|
|
|
129
|
|
|
|
130
|
|
|
|