Test Failed
Pull Request — master (#728)
by
unknown
03:43
created

MtfDeconvolution.get_conf_path()   A

Complexity

Conditions 2

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nop 1
dl 0
loc 5
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:: Point-spread-function correction
17
   :platform: Unix
18
   :synopsis: A plugin for MTF (modulation transfer function) deconvolution\
19
    or PSF (point spread function) correction in the Fourier domain.
20
.. moduleauthor:: Nghia Vo <[email protected]>
21
22
"""
23
import os
24
import logging
25
import numpy as np
26
from PIL import Image
27
import pyfftw.interfaces.scipy_fftpack as fft
28
from savu.plugins.plugin import Plugin
29
from savu.plugins.driver.cpu_plugin import CpuPlugin
30
from savu.plugins.utils import register_plugin
31
from savu.data.plugin_list import CitationInformation
32
import savu.test.test_utils as tu
33
import savu.core.utils as cu
34
35
36
37
@register_plugin
38
class MtfDeconvolution(Plugin, CpuPlugin):
39
    """
40
    Method to correct the point-spread-function effect. \
41
    Working on raw projections and flats.
42
    :u*param file_path: Path to file containing a 2D array of a MTF function. \
43
        File formats: 'npy', or 'tif'. Default: None.
44
    :param pad_width: Pad the image before the deconvolution. Default: 128.
45
46
    """
47
48
    def __init__(self):
49
        super(MtfDeconvolution, self).__init__("MtfDeconvolution")
50
51
    def load_image(self, file_path):
52
        """
53
        Load data from an image.
54
        """
55
        mat = None
56
        try:
57
            mat = np.asarray(Image.open(file_path), dtype = np.float32)
58
        except IOError:
59
            raise ValueError("No such file or directory {}".format(file_path))
60
        if len(mat.shape) > 2:
61
            axis_m = np.argmin(mat.shape)
62
            mat = np.mean(mat, axis=axis_m)
63
        return mat
64
65
    def check_file_path(self, file_path):
66
        file_ext = ".tif"
67
        if file_path is None:
68
            msg = "!!! Please provide a file path to the MTF !!!"
69
            logging.warning(msg)
70
            cu.user_message(msg)
71
            raise ValueError(msg)
72
        else:
73
            if not os.path.isfile(file_path):
74
                msg = "!!! No such file: %s !!!"\
75
                        " Please check the file path" %str(file_path)
76
                logging.warning(msg)
77
                cu.user_message(msg)
78
                raise ValueError(msg)
79
            else:
80
                _, file_ext = os.path.splitext(file_path)
81
        return file_ext
82
83
    def psf_correction(self, mat, win, pad_width):
84
        (nrow, ncol) = mat.shape
85
        mat_pad = np.pad(mat, pad_width, mode = "reflect")
86
        win_pad = np.pad(win, pad_width, mode = "constant", constant_values=1.0)
87
        mat_dec = fft.ifft2(fft.fft2(mat_pad) / fft.ifftshift(win_pad))
88
        return np.abs(mat_dec)[pad_width:pad_width+nrow,pad_width:pad_width+ncol]
89
90
    def setup(self):
91
        in_dataset, out_dataset = self.get_datasets()
92
        out_dataset[0].create_dataset(in_dataset[0], raw=True)
93
        in_pData, out_pData = self.get_plugin_datasets()
94
        in_pData[0].plugin_data_setup('PROJECTION','single')
95
        out_pData[0].plugin_data_setup('PROJECTION','single')
96
97
    def pre_process(self):
98
        inData = self.get_in_datasets()[0]
99
        dark = inData.data.dark()
100
        flat = inData.data.flat()
101
        self.data_size = inData.get_shape()
102
        (self.depth, self.height, self.width) = flat.shape
103
        file_path = self.get_conf_path()
104
        file_ext = self.check_file_path(file_path)
105
        if file_ext==".npy":
106
            try:
107
                self.mtf_array = 1.0*np.load(file_path)
108
            except IOError:
109
                msg = "\n*****************************************\n"\
110
                    "!!! ERROR !!! -> Can't open this file: %s \n"\
111
                    "*****************************************\n\
112
                    " % str(file_path)
113
                logging.warning(msg)
114
                cu.user_message(msg)
115
                raise ValueError(msg)
116
        else:
117
            try:
118
                self.mtf_array = 1.0*np.float32(self.load_image(file_path))
119
            except IOError:
120
                msg = "\n*****************************************\n"\
121
                    "!!! ERROR !!! -> Can't open this file: %s \n"\
122
                    "*****************************************\n\
123
                    " % str(file_path)
124
                logging.warning(msg)
125
                cu.user_message(msg)
126
                raise ValueError(msg)
127
128
        self.mtf_array[self.mtf_array<=0.0] = 1.0
129
        self.mtf_array = self.mtf_array/np.max(self.mtf_array)
130
        (height_mtf, width_mtf) = self.mtf_array.shape
131
        if (self.height != height_mtf) or (self.width != width_mtf):
132
            msg = "\n*****************************************\n"\
133
            "!!! ERROR !!!-> Projection shape: ({0},{1}) is not the same as "\
134
            "the mtf shape: ({2},{3})".format(
135
                self.height, self.width, height_mtf, width_mtf)
136
            logging.warning(msg)
137
            cu.user_message(msg)
138
            raise ValueError(msg)
139
140
        self.pad_width = np.clip(int(self.parameters["pad_width"]), 0, None)
141
        if flat.size:
142
            flat_updated = np.ones_like(flat, dtype=np.float32)
143
            for i in np.arange(self.depth):
144
                flat_updated[i] = self.psf_correction(
145
                    flat[i], self.mtf_array, self.pad_width)
146
            inData.data.update_flat(flat_updated)
147
148
    def process_frames(self, data):
149
        return self.psf_correction(data[0], self.mtf_array, self.pad_width)
150
151
    def get_conf_path(self):
152
        path = self.parameters["file_path"]
153
        if path.split(os.sep)[0] == 'Savu':
154
            path = tu.get_test_data_path(path.split('/test_data/data')[1])
155
        return path
156
157
    def get_citation_information(self):
158
        cite_info = CitationInformation()
159
        cite_info.description = \
160
            ("The PSF correction used in this plugin is taken\
161
             from this work.")
162
        cite_info.bibtex = ("@inproceedings{10.1117/12.2530324,\n"\
163
            "author = {Nghia T. Vo and Robert C. Atwood "\
164
            "and Michael Drakopoulos},\n"\
165
            "title = {{Preprocessing techniques for removing artifacts in "\
166
            "synchrotron-based tomographic images}},\n"\
167
            "volume = {11113},\n"\
168
            "booktitle = {Developments in X-Ray Tomography XII},\n"\
169
            "editor = {Bert Muller and Ge Wang},\n"\
170
            "organization = {International Society for Optics and Photonics},\n"\
171
            "publisher = {SPIE},\n"\
172
            "pages = {309 -- 328},\n"\
173
            "year = {2019},\n"\
174
            "doi = {10.1117/12.2530324},\n"\
175
            "URL = {https://doi.org/10.1117/12.2530324}\n"\
176
            "}")
177
        cite_info.doi = "doi: DOI: 10.1117/12.2530324"
178
        return cite_info
179