Test Failed
Pull Request — master (#734)
by Daniil
03:20
created

savu.plugins.simulation.tomo_phantom.TomoPhantom.nInput_datasets()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
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:: A wrapper for TomoPhantom software
17
   :platform: Unix
18
   :synopsis: TomoPhantom package provides an access to simulated phantom libraries and projection data 2D/3D
19
20
.. moduleauthor:: Daniil Kazantsev <[email protected]>
21
"""
22
23
import savu.plugins.utils as pu
24
from savu.plugins.plugin import Plugin
25
from savu.plugins.driver.cpu_plugin import CpuPlugin
26
from savu.plugins.utils import register_plugin
27
28
import tomophantom
29
from tomophantom import TomoP2D, TomoP3D
30
from tomophantom.supp.artifacts import _Artifacts_
31
import os
32
from savu.data.plugin_list import CitationInformation
33
import numpy as np
34
35
@register_plugin
36
class TomoPhantom(Plugin, CpuPlugin):
37
    """
38
    A plugin for TomoPhantom software which generates synthetic phantoms and \
39
    projection data (2D from Phantom2DLibrary.dat and 3D from Phantom3DLibrary.dat)
40
41
    :param geom_model: Select a model (integer) from the library (see TomoPhantom files). Default: 1.
42
    :param geom_model_size: Set the size of the phantom. Default: 256.
43
    :param geom_projections_total: The total number of projections. Default: 360.
44
    :param geom_detectors_horiz: The size of _horizontal_ detectors. Default: 300.
45
    :param artifacts_noise_type: Set the noise type, Poisson or Gaussian. Default: 'Poisson'.
46
    :param artifacts_noise_sigma: Define noise amplitude. Default: 5000.
47
    :param artifacts_misalignment_maxamplitude: Incorporate misalignment into projections (in pixels). Default: None.
48
    :param artifacts_zingers_percentage: add broken pixels to projections, e.g. 0.25. Default: None.
49
    :param artifacts_stripes_percentage: the amount of stripes in the data, e.g. 1.0. Default: None.
50
    :param artifacts_stripes_maxthickness: defines the maximal thickness of a stripe. Default: 3.0.
51
    :param artifacts_stripes_intensity: to incorporate the change of intensity in the stripe. Default: 0.3.
52
    :param artifacts_stripes_type: set the stripe type between full and partial. Default: 'full'.
53
    :param artifacts_stripes_variability: the intensity variability of a stripe. Default:  0.007.
54
    :param out_datasets: Default out dataset names. Default: ['tomo', 'model']
55
    """
56
57
    def __init__(self):
58
        super(TomoPhantom, self).__init__('TomoPhantom')
59
60
    def get_max_frames(self):
61
        return 'single'
62
63
    def nInput_datasets(self):
64
        return 1
65
66
    def nOutput_datasets(self):
67
        return 2
68
69
    def map_volume_dimensions(self, data):
70
        data._finalise_patterns()
71
        dim_rotAngle = data.get_data_patterns()['PROJECTION']['main_dir']
72
        sinogram = data.get_data_patterns()['SINOGRAM']
73
        dim_detY = sinogram['main_dir']
74
75
        core_dirs = sinogram['core_dims']
76
        dim_detX = list(set(core_dirs).difference(set((dim_rotAngle,))))[0]
77
78
        dim_volX = dim_rotAngle
79
        dim_volY = dim_detY
80
        dim_volZ = dim_detX
81
        return dim_volX, dim_volY, dim_volZ
82
83
    def setup(self):
84
        in_dataset, self.out_dataset = self.get_datasets()
85
        in_pData, self.out_pData = self.get_plugin_datasets()
86
        in_pData[0].plugin_data_setup('SINOGRAM', self.get_max_frames())
87
88
        [self.out_shape_sino, out_shape_phantom] = self.new_shape(in_dataset[0].get_shape(), in_dataset[0])
89
        self.out_dataset[0].create_dataset(patterns=in_dataset[0],
90
                                           axis_labels=in_dataset[0],
91
                                           shape=self.out_shape_sino)
92
        self.out_pData[0].plugin_data_setup('SINOGRAM',self.get_max_frames())
93
94
        dim_volX, dim_volY, dim_volZ = \
95
            self.map_volume_dimensions(in_dataset[0])
96
97
        axis_labels = [0]*3
98
        axis_labels = {in_dataset[0]:
99
                       [str(dim_volX) + '.voxel_x.voxels',
100
                        str(dim_volY) + '.voxel_y.voxels',
101
                        str(dim_volZ) + '.voxel_z.voxels']}
102
103
        self.out_dataset[1].create_dataset(axis_labels=axis_labels,
104
                                           shape=out_shape_phantom)
105
        self.out_dataset[1].add_volume_patterns(dim_volX, dim_volY, dim_volZ)
106
107
        self.out_pData[1].plugin_data_setup('VOLUME_XZ', self.get_max_frames())
108
109
        self.angles = np.linspace(0.0,179.999,self.parameters['geom_projections_total'],dtype='float32')
110
        self.out_dataset[0].meta_data.set('rotation_angle', self.angles)
111
112
    def new_shape(self, full_shape, data):
113
        # calculate a new output data shape based on the input data shape
114
        core_dirs = data.get_core_dimensions()
115
        new_shape_sino = list(full_shape)
116
        new_shape_phantom = list(full_shape)
117
        new_shape_sino= (self.parameters['geom_projections_total'], new_shape_sino[1], self.parameters['geom_detectors_horiz'])
118
        for dim in core_dirs:
119
            new_shape_phantom[dim] = self.parameters['geom_model_size']
120
        return [tuple(new_shape_sino), tuple(new_shape_phantom)]
121
122
    def pre_process(self):
123
        # set parameters for TomoPhantom:
124
        self.model = self.parameters['geom_model']
125
        self.dims = self.parameters['geom_model_size']
126
        self.proj_num = self.parameters['geom_projections_total']
127
        self.detectors_num = self.parameters['geom_detectors_horiz']
128
        path = os.path.dirname(tomophantom.__file__)
129
        self.path_library2D = os.path.join(path, "Phantom2DLibrary.dat")
130
        self.path_library3D = os.path.join(path, "Phantom3DLibrary.dat")
131
        #print "The full data shape is", self.get_in_datasets()[0].get_shape()
132
133
    def process_frames(self, data):
134
        # print "The input data shape is", data[0].shape
135
        if (self.out_shape_sino[1] == 1):
136
            # create a 2D phantom
137
            model = TomoP2D.Model(self.model, self.dims, self.path_library2D)
138
            # create a 2D sinogram
139
            projdata_clean = TomoP2D.ModelSino(self.model, self.dims, self.detectors_num, self.angles, self.path_library2D)
140
            # forming dictionaries with different artifact types and noise
141
            _noise_ =  {'noise_type' : self.parameters['artifacts_noise_type'],
142
                        'noise_sigma' : self.parameters['artifacts_noise_sigma'],
143
                        'noise_seed' : 0}
144
            # misalignment dictionary
145
            _sinoshifts_ = {}
146
            if self.parameters['artifacts_misalignment_maxamplitude'] is not None:
147
                _sinoshifts_ = {'sinoshifts_maxamplitude' : self.parameters['artifacts_misalignment_maxamplitude']}
148
149
            # adding zingers and stripes
150
            _zingers_ = {}
151
            if self.parameters['artifacts_zingers_percentage'] is not None:
152
                _zingers_ = {'zingers_percentage' : self.parameters['artifacts_zingers_percentage'],
153
                             'zingers_modulus' : 10}
154
            _stripes_ = {}
155
            if self.parameters['artifacts_stripes_percentage'] is not None:
156
                _stripes_ = {'stripes_percentage' : self.parameters['artifacts_stripes_percentage'],
157
                             'stripes_maxthickness' : self.parameters['artifacts_stripes_maxthickness'],
158
                             'stripes_intensity' : self.parameters['artifacts_stripes_intensity'],
159
                             'stripes_type' : self.parameters['artifacts_stripes_type'],
160
                             'stripes_variability' : self.parameters['artifacts_stripes_variability']}
161
162
            if self.parameters['artifacts_misalignment_maxamplitude'] is not None:
163
                [projdata, shifts] = _Artifacts_(projdata_clean, **_noise_, **_zingers_, **_stripes_, **_sinoshifts_)
164
            else:
165
                projdata = _Artifacts_(projdata_clean, **_noise_, **_zingers_, **_stripes_, **_sinoshifts_)
166
        else:
167
            # create a 3D phantom
168
            frame_idx = self.out_pData[0].get_current_frame_idx()[0]
169
            model = TomoP3D.ModelSub(self.model, self.dims, (frame_idx, frame_idx+1), self.path_library3D)
170
            model = np.swapaxes(model,0,1)
171
            model = np.flipud(model[:,0,:])
172
            # create a 3D projection data
173
            projdata_clean = TomoP3D.ModelSinoSub(self.model, self.dims, self.detectors_num, self.dims, (frame_idx, frame_idx+1), self.angles, self.path_library3D)
174
            # forming dictionaries with different artifact types and noise
175
            _noise_ =  {'noise_type' : self.parameters['artifacts_noise_type'],
176
                        'noise_sigma' : self.parameters['artifacts_noise_sigma'],
177
                        'noise_seed' : 0}
178
            # misalignment dictionary
179
            _sinoshifts_ = {}
180
            if self.parameters['artifacts_misalignment_maxamplitude'] is not None:
181
                _sinoshifts_ = {'sinoshifts_maxamplitude' : self.parameters['artifacts_misalignment_maxamplitude']}
182
183
            # adding zingers and stripes
184
            _zingers_ = {}
185
            if self.parameters['artifacts_zingers_percentage'] is not None:
186
                _zingers_ = {'zingers_percentage' : self.parameters['artifacts_zingers_percentage',
187
                             'zingers_modulus' : 10}
0 ignored issues
show
introduced by
invalid syntax (<unknown>, line 187)
Loading history...
188
            _stripes_ = {}
189
            if self.parameters['artifacts_stripes_percentage'] is not None:
190
                _stripes_ = {'stripes_percentage' : self.parameters['artifacts_stripes_percentage'],
191
                             'stripes_maxthickness' : self.parameters['artifacts_stripes_maxthickness'],
192
                             'stripes_intensity' : self.parameters['artifacts_stripes_intensity'],
193
                             'stripes_type' : self.parameters['artifacts_stripes_type'],
194
                             'stripes_variability' : self.parameters['artifacts_stripes_variability']}
195
196
            if self.parameters['artifacts_misalignment_maxamplitude'] is not None:
197
                [projdata, shifts] = _Artifacts_(projdata_clean, **_noise_, **_zingers_, **_stripes_, **_sinoshifts_)
198
            else:
199
                projdata = _Artifacts_(projdata_clean, **_noise_, **_zingers_, **_stripes_, **_sinoshifts_)
200
            projdata = np.swapaxes(projdata,0,1)
201
        return [projdata, model]
202
203
    def get_citation_information(self):
204
        cite_info1 = CitationInformation()
205
        cite_info1.name = 'citation1'
206
        cite_info1.description = \
207
            ("TomoPhantom is a software package to generate 2D-4D analytical phantoms and their Radon transforms for various testing purposes.")
208
        cite_info1.bibtex = \
209
            ("@article{kazantsevTP2018,\n" +
210
             "title={TomoPhantom, a software package to generate 2D-4D analytical phantoms for CT image reconstruction algorithm benchmarks},\n" +
211
             "author={Daniil and Kazantsev, Valery and Pickalov, Srikanth and Nagella, Edoardo and Pasca, Philip and Withers},\n" +
212
             "journal={Software X},\n" +
213
             "volume={7},\n" +
214
             "number={--},\n" +
215
             "pages={150--155},\n" +
216
             "year={2018},\n" +
217
             "publisher={Elsevier}\n" +
218
             "}")
219
        cite_info1.endnote = \
220
            ("%0 Journal Article\n" +
221
             "%T TomoPhantom, a software package to generate 2D-4D analytical phantoms for CT image reconstruction algorithm benchmarks\n" +
222
             "%A Kazantsev, Daniil\n" +
223
             "%A Pickalov, Valery\n" +
224
             "%A Nagella, Srikanth\n" +
225
             "%A Pasca, Edoardo\n" +
226
             "%A Withers, Philip\n" +
227
             "%J Software X\n" +
228
             "%V 7\n" +
229
             "%N --\n" +
230
             "%P 150--155\n" +
231
             "%@ --\n" +
232
             "%D 2018\n" +
233
             "%I Elsevier\n")
234
        cite_info1.doi = "doi:10.1016/j.softx.2018.05.003"
235
        return [cite_info1]
236