1
|
|
|
# Copyright 2014 Diamond Light Source Ltd. |
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
|
|
|
""" |
16
|
|
|
.. module:: temp_loader |
17
|
|
|
:platform: Unix |
18
|
|
|
:synopsis: A class for loading standard tomography data in a variety of |
19
|
|
|
formats. |
20
|
|
|
|
21
|
|
|
.. moduleauthor:: Nicola Wadeson <[email protected]> |
22
|
|
|
|
23
|
|
|
""" |
24
|
|
|
|
25
|
|
|
import os |
26
|
|
|
import tempfile |
27
|
|
|
import warnings |
28
|
|
|
|
29
|
|
|
import h5py |
30
|
|
|
import numpy as np |
31
|
|
|
|
32
|
|
|
from savu.data.data_structures.data_types.data_plus_darks_and_flats \ |
33
|
|
|
import NoImageKey |
34
|
|
|
from savu.data.data_structures.data_types.image_data import ImageData |
35
|
|
|
from savu.plugins.loaders.base_loader import BaseLoader |
36
|
|
|
from savu.plugins.utils import register_plugin |
37
|
|
|
|
38
|
|
|
|
39
|
|
|
@register_plugin |
40
|
|
|
class ImageLoader(BaseLoader): |
41
|
|
|
""" |
42
|
|
|
Load any FabIO compatible formats (e.g. tiffs) |
43
|
|
|
|
44
|
|
|
:param dataset_name: The name assigned to the dataset. Default: 'tomo'. |
45
|
|
|
:param angles: A python statement to be evaluated \ |
46
|
|
|
(e.g np.linspace(0, 180, nAngles)) or a file. Default: None. |
47
|
|
|
:param frame_dim: Which dimension requires stitching? Default: 0. |
48
|
|
|
:param data_prefix: A file prefix for the data file. Default: None. |
49
|
|
|
:param dark_prefix: A file prefix for the dark field files, including the\ |
50
|
|
|
folder path if different from the data. Default: None. |
51
|
|
|
:param flat_prefix: A file prefix for the flat field files, including the\ |
52
|
|
|
folder path if different from the data. Default: None. |
53
|
|
|
""" |
54
|
|
|
|
55
|
|
|
def __init__(self, name='ImageLoader'): |
56
|
|
|
super(ImageLoader, self).__init__(name) |
57
|
|
|
|
58
|
|
|
def setup(self): |
59
|
|
|
exp = self.exp |
60
|
|
|
data_obj = exp.create_data_object('in_data', |
61
|
|
|
self.parameters['dataset_name']) |
62
|
|
|
|
63
|
|
|
rot = 0 |
64
|
|
|
detY = 1 |
65
|
|
|
detX = 2 |
66
|
|
|
data_obj.set_axis_labels('rotation_angle.degrees', |
67
|
|
|
'detector_y.pixel', |
68
|
|
|
'detector_x.pixel') |
69
|
|
|
|
70
|
|
|
data_obj.add_pattern('PROJECTION', core_dims=(detX, detY), |
71
|
|
|
slice_dims=(rot,)) |
72
|
|
|
data_obj.add_pattern('SINOGRAM', core_dims=(detX, rot), |
73
|
|
|
slice_dims=(detY,)) |
74
|
|
|
|
75
|
|
|
path = os.path.abspath(exp.meta_data.get("data_file")) |
76
|
|
|
data_obj.data = self._get_data_type(data_obj, path) |
77
|
|
|
|
78
|
|
|
self.set_rotation_angles(data_obj) |
79
|
|
|
|
80
|
|
|
# dummy file |
81
|
|
|
filename = path.split(os.sep)[-1] + '.h5' |
82
|
|
|
data_obj.backing_file = \ |
83
|
|
|
h5py.File(os.path.join(tempfile.mkdtemp(), filename), 'a') |
84
|
|
|
|
85
|
|
|
data_obj.set_shape(data_obj.data.get_shape()) |
86
|
|
|
self.set_data_reduction_params(data_obj) |
87
|
|
|
self._set_darks_and_flats(data_obj, path) |
88
|
|
|
|
89
|
|
|
def _set_darks_and_flats(self, dObj, path): |
90
|
|
|
if not self.parameters['flat_prefix']: |
91
|
|
|
return |
92
|
|
|
|
93
|
|
|
dObj.data = NoImageKey(dObj, None, 0) |
94
|
|
|
fdim = self.parameters['frame_dim'] |
95
|
|
|
|
96
|
|
|
# read dark and flat images |
97
|
|
|
fpath, ffix = self._get_path(self.parameters['flat_prefix'], path) |
98
|
|
|
flat = ImageData(fpath, dObj, [fdim], None, ffix) |
99
|
|
|
|
100
|
|
|
if self.parameters['dark_prefix']: |
101
|
|
|
dpath, dfix = self._get_path(self.parameters['dark_prefix'], path) |
102
|
|
|
dark = ImageData(dpath, dObj, [fdim], None, dfix) |
103
|
|
|
else: |
104
|
|
|
shape = dObj.get_shape() |
105
|
|
|
dark = np.zeros([1] + [shape[i] for i in [1, 2]], dtype=flat.dtype) |
106
|
|
|
|
107
|
|
|
dObj.data._set_dark_path(dark) |
108
|
|
|
dObj.data._set_flat_path(flat) |
109
|
|
|
dObj.data._set_dark_and_flat() |
110
|
|
|
|
111
|
|
|
def _get_path(self, param, data): |
112
|
|
|
ppath, pfix = os.path.split(param) |
113
|
|
|
ppath = \ |
114
|
|
|
os.path.join(data, ppath) if not os.path.isabs(ppath) else ppath |
115
|
|
|
return ppath, pfix |
116
|
|
|
|
117
|
|
|
def _get_data_type(self, obj, path): |
118
|
|
|
prefix = self.parameters['data_prefix'] |
119
|
|
|
return ImageData(path, obj, [self.parameters['frame_dim']], None, prefix) |
120
|
|
|
|
121
|
|
|
def set_rotation_angles(self, data_obj): |
122
|
|
|
angles = self.parameters['angles'] |
123
|
|
|
|
124
|
|
|
if angles is None: |
125
|
|
|
angles = np.linspace(0, 180, data_obj.data.get_shape()[0]) |
126
|
|
|
else: |
127
|
|
|
try: |
128
|
|
|
ldict = {} |
129
|
|
|
exec("angles = " + angles,globals(),ldict) |
130
|
|
|
angles = ldict['angles'] |
131
|
|
|
except Exception as e: |
132
|
|
|
warnings.warn("Could not execute statement: {}".format(e)) |
133
|
|
|
try: |
134
|
|
|
angles = np.loadtxt(angles) |
135
|
|
|
except Exception as e: |
136
|
|
|
raise Exception('Cannot set angles in loader. Error: {}'.format(e)) |
137
|
|
|
|
138
|
|
|
n_angles = len(angles) |
139
|
|
|
data_angles = data_obj.data.get_shape()[self.parameters['frame_dim']] |
140
|
|
|
if data_angles != n_angles: |
141
|
|
|
raise Exception("The number of angles %s does not match the data " |
142
|
|
|
"dimension length %s", n_angles, data_angles) |
143
|
|
|
data_obj.meta_data.set("rotation_angle", angles) |
144
|
|
|
|