Test Failed
Pull Request — master (#728)
by
unknown
04:13
created

ImageSaver.executive_summary()   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nop 1
dl 0
loc 6
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:: image_saver
17
   :platform: Unix
18
   :synopsis: A class to save output as images
19
20
.. moduleauthor:: Dan Nixon, Nghia Vo <[email protected]>
21
22
"""
23
24
import logging
25
import skimage.exposure
26
import skimage.io
27
import numpy as np
28
from PIL import Image
29
30
from savu.plugins.savers.base_image_saver import BaseImageSaver
31
from savu.plugins.utils import register_plugin
32
from savu.plugins.driver.cpu_plugin import CpuPlugin
33
import savu.core.utils as cu
34
35
36
@register_plugin
37
class ImageSaver(BaseImageSaver, CpuPlugin):
38
    """
39
    A class to save tomography data to image files.  Run the MaxAndMin plugin\
40
    before this to rescale the data.
41
42
    :param pattern: How to slice the data. Default: 'VOLUME_XZ'.
43
    :u*param format: Image format. Default: 'tif'.
44
    :u*param num_bit: Bit depth of the tiff format (8, 16 or 32). Default: 16.
45
    :param max: Global max for tiff scaling. Default: None.
46
    :param min: Global min for tiff scaling. Default: None.
47
    :param jpeg_quality: JPEG encoding quality (1 is worst, 100 is best). Default: 75.
48
    :param prefix: Override the default output jpg file prefix. Default: None.
49
50
    :config_warn: Do not use this plugin if the raw data is greater than \
51
    100 GB.
52
    """
53
54
    def __init__(self, name='ImageSaver'):
55
        super(ImageSaver, self).__init__(name)
56
57
    def setup(self):
58
        data_pattern = self.parameters['pattern']
59
        in_pData, _ = self.get_plugin_datasets()
60
        try:
61
            in_pData[0].plugin_data_setup(data_pattern, 'single')
62
        except:
63
            msg = "\n***************************************************"\
64
            "**********\n"\
65
            "Can't find the data pattern: {}.\nThe pattern parameter of " \
66
            "this plugin must be relevant to its \nprevious plugin" \
67
            "\n*************************************************************"\
68
            "\n".format(data_pattern)
69
            logging.warning(msg)
70
            cu.user_message(msg)
71
            raise ValueError(msg)
72
73
    def pre_process(self):
74
        super(ImageSaver, self).pre_process()
75
        self.pData = self.get_plugin_in_datasets()[0]
76
        self.file_format = self.parameters['format']
77
        num_bit = self.parameters['num_bit']
78
        if not (num_bit == 8 or num_bit == 16 or num_bit == 32):
79
            self.num_bit = 32
80
            msg = "\n***********************************************\n"\
81
                "This option %s is not available. Reset to 32 \n"\
82
                % str(num_bit)
83
            cu.user_message(msg)
84
        else:
85
            self.num_bit = num_bit
86
        self._data_range = self._get_min_and_max()
87
88
    def process_frames(self, data):
89
        frame = self.pData.get_current_frame_idx()[0]
90
        filename = '%s%05i.%s' % (self.filename, frame, self.file_format)
91
        frame = np.nan_to_num(data[0])
92
        if (self.file_format == "tiff") or (self.file_format == "tif"):
93
            global_min = self.parameters['min']
94
            global_max = self.parameters['max']
95
            if self.num_bit == 32:
96
                rescaled_image = frame
97
            else:
98
                if global_min is None:
99
                    if self.the_min is not None:
100
                        global_min = self.the_min
101
                    else:
102
                        global_min = np.min(frame)
103
                if global_max is None:
104
                    if self.the_max is not None:
105
                        global_max = self.the_max
106
                    else:
107
                        global_max = np.max(frame)
108
                rescaled_image = np.clip(frame, global_min, global_max)
109
                rescaled_image = (rescaled_image - global_min) \
110
                    / (global_max - global_min)
111
                if self.num_bit == 16:
112
                    rescaled_image = np.clip(
113
                        np.uint16(rescaled_image * 65535), 0, 65535)
114
                else:
115
                    rescaled_image = np.clip(
116
                        np.uint8(rescaled_image * 255), 0, 255)
117
            img = Image.fromarray(rescaled_image)
118
            img.save(filename)
119
        else:
120
            # Rescale image to (0.0, 1.0) range
121
            rescaled_image = skimage.exposure.rescale_intensity(
122
                frame, in_range=self._data_range, out_range=(0.0, 1.0))
123
            # Save image
124
            skimage.io.imsave(
125
                filename, rescaled_image, quality=self.parameters['jpeg_quality'])
126
127
    def _get_min_and_max(self):
128
        data = self.get_in_datasets()[0]
129
        pattern = self.parameters['pattern']
130
        try:
131
            self.the_min = np.min(
132
                data.meta_data.get(['stats', 'min', pattern]))
133
            self.the_max = np.max(
134
                data.meta_data.get(['stats', 'max', pattern]))
135
            self._data_range = (self.the_min, self.the_max)
136
        except KeyError:
137
            self._data_range = 'image'
138
            if (self.file_format == "tiff") or (self.file_format == "tif"):
139
                self.the_min = None
140
                self.the_max = None
141
                msg = "\n***********************************************\n"\
142
                    "!!!Warning!!!-> No global maximum and global minimum found\n"\
143
                    "in the metadata. Please run the MaxAndMin plugin before\n" \
144
                    "ImageSaver or input manually. Otherwise, local minimum\n" \
145
                    "and local maximum will be used for rescaling. This may\n"\
146
                    "result the fluctuation of brightness between slices.\n"\
147
                    "***********************************************\n"
148
                if (self.num_bit == 8) or (self.num_bit == 16):
149
                    cu.user_message(msg)
150
        return self._data_range
151
152
    def executive_summary(self):
153
        if self._data_range == 'image':
154
            return ["To rescale and normalise the data between global max and "
155
                    "min values, please run MaxAndMin plugin before "
156
                    "ImageSaver."]
157
        return ["Nothing to Report"]
158