Test Failed
Push — master ( 6971d7...651feb )
by
unknown
01:28 queued 16s
created

MinAndMax.process_frames()   B

Complexity

Conditions 6

Size

Total Lines 16
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 15
nop 2
dl 0
loc 16
rs 8.6666
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
.. module:: min_and_max
16
   :platform: Unix
17
   :synopsis: A plugin to calculate the min and max of each frame
18
.. moduleauthor:: Nicola Wadeson <[email protected]>
19
"""
20
import logging
21
import numpy as np
22
23
from scipy.ndimage import gaussian_filter
24
from savu.plugins.plugin import Plugin
25
from savu.plugins.utils import register_plugin
26
from savu.plugins.driver.cpu_plugin import CpuPlugin
27
import savu.core.utils as cu
28
29
30
@register_plugin
31
class MinAndMax(Plugin, CpuPlugin):
32
    """
33
    A plugin to calculate the min and max values of each slice (as determined \
34
    by the pattern parameter)
35
36
    :u*param pattern: How to slice the data. Default: 'VOLUME_XZ'.
37
    :param smoothing: Apply a smoothing filter or not. Default: True.
38
    :u*param masking: Apply a circular mask or not. Default: True.
39
    :u*param ratio: Used to calculate the circular mask. If not provided, \
40
        it is calculated using the center of rotation. Default: None.
41
    :u*param method: Method to find the global min and the global max. \
42
        Available options: 'extrema', 'percentile'. Default: 'percentile'.
43
    :u*param p_range: Percentage range if use the 'percentile' method. \
44
        Default: [0.0, 100.0].
45
    :param out_datasets: The default names. Default: ['the_min','the_max'].
46
    """
47
48
    def __init__(self):
49
        super(MinAndMax, self).__init__("MinAndMax")
50
51
    def circle_mask(self, width, ratio):
52
        # Create a circle mask.
53
        mask = np.zeros((width, width), dtype=np.float32)
54
        center = width // 2
55
        radius = ratio * center
56
        y, x = np.ogrid[-center:width - center, -center:width - center]
57
        mask_check = x * x + y * y <= radius * radius
58
        mask[mask_check] = 1.0
59
        return mask
60
61
    def pre_process(self):
62
        in_pData = self.get_plugin_in_datasets()[0]
63
        in_meta_data = self.get_in_meta_data()[0]
64
        data = self.get_in_datasets()[0]
65
        data_shape = data.get_shape()
66
        width = data_shape[0]
67
        self.use_mask = self.parameters['masking']
68
        self.data_pattern = self.parameters['pattern']
69
        self.mask = np.ones((width, width), dtype=np.float32)
70
        if self.use_mask is True:
71
            ratio = self.parameters['ratio']
72
            if ratio is None:
73
                try:
74
                    cor = np.min(in_meta_data.get('centre_of_rotation'))
75
                    ratio = (min(cor, abs(width - cor))) / (width * 0.5)
76
                except KeyError:
77
                    ratio = 1.0
78
            self.mask = self.circle_mask(width, ratio)
79
        self.method = self.parameters['method']
80
        if not (self.method == 'percentile' or self.method == 'extrema'):
81
            msg = "\n***********************************************\n"\
82
                "!!! ERROR !!! -> Wrong method. Please use only one of "\
83
                "the provided options \n"\
84
                "***********************************************\n"
85
            logging.warning(msg)
86
            cu.user_message(msg)
87
            raise ValueError(msg)
88
        self.p_min, self.p_max = np.sort(np.clip(np.asarray(
89
            self.parameters['p_range'], dtype=np.float32), 0.0, 100.0))
90
91
    def process_frames(self, data):
92
        use_filter = self.parameters['smoothing']
93
        frame = np.nan_to_num(data[0])
94
        if use_filter is True:
95
            frame = gaussian_filter(frame, (3, 3))
96
        if (self.use_mask is True) and (self.data_pattern == 'VOLUME_XZ')\
97
                and (self.mask.shape == frame.shape):
98
            frame = frame * self.mask
99
        if self.method == 'percentile':
100
            list_out = [np.array(
101
                [np.percentile(frame, self.p_min)], dtype=np.float32),
102
                np.array([np.percentile(frame, self.p_max)], dtype=np.float32)]
103
        else:
104
            list_out = [np.array([np.min(frame)], dtype=np.float32),
105
                        np.array([np.max(frame)], dtype=np.float32)]
106
        return list_out
107
108
    def post_process(self):
109
        in_datasets, out_datasets = self.get_datasets()
110
        the_min = np.squeeze(out_datasets[0].data[...])
111
        the_max = np.squeeze(out_datasets[1].data[...])
112
        pattern = self._get_pattern()
113
        in_datasets[0].meta_data.set(['stats', 'min', pattern], the_min)
114
        in_datasets[0].meta_data.set(['stats', 'max', pattern], the_max)
115
116
    def setup(self):
117
        in_dataset, out_datasets = self.get_datasets()
118
        in_pData, out_pData = self.get_plugin_datasets()
119
        try:
120
            in_pData[0].plugin_data_setup(self._get_pattern(), 'single')
121
        except:
122
            msg = "\n***************************************************"\
123
            "**********\n"\
124
            "Can't find the data pattern: {}.\nThe pattern parameter of " \
125
            "this plugin must be relevant to its \nprevious plugin" \
126
            "\n*************************************************************"\
127
            "\n".format(self._get_pattern())
128
            logging.warning(msg)
129
            cu.user_message(msg)
130
            raise ValueError(msg)
131
132
        slice_dirs = list(in_dataset[0].get_slice_dimensions())
133
        orig_shape = in_dataset[0].get_shape()
134
        new_shape = (np.prod(np.array(orig_shape)[slice_dirs]), 1)
135
136
        labels = ['x.pixels', 'y.pixels']
137
        for i in range(len(out_datasets)):
138
            out_datasets[i].create_dataset(shape=new_shape, axis_labels=labels,
139
                                           remove=True, transport='hdf5')
140
            out_datasets[i].add_pattern(
141
                "METADATA", core_dims=(1,), slice_dims=(0,))
142
            out_pData[i].plugin_data_setup('METADATA', 'single')
143
144
    def _get_pattern(self):
145
        return self.parameters['pattern']
146
147
    def nOutput_datasets(self):
148
        return 2
149