image.py 8.52 KB
Newer Older
Chris Smith's avatar
Chris Smith committed
1
2
3
4
5
6
7
8
9
"""
Created on Feb 9, 2016

@author: Chris Smith
"""

import os
import numpy as np
from skimage.measure import block_reduce
10
from ..io_image import read_image
Chris Smith's avatar
Chris Smith committed
11
from .translator import Translator
12
13
from .utils import generate_dummy_main_parms, build_ind_val_dsets
from ..hdf_utils import getH5DsetRefs, calc_chunks, link_as_main
Chris Smith's avatar
Chris Smith committed
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from ..io_hdf5 import ioHDF5
from ..microdata import MicroDataGroup, MicroDataset


class ImageTranslator(Translator):
    """
    Translate Pytchography data from a set of images to an HDF5 file
    """

    def __init__(self, *args, **kwargs):
        super(ImageTranslator, self).__init__(*args, **kwargs)

        self.rebin = False
        self.bin_factor = 1
        self.hdf = None
        self.binning_func = self.__no_bin
        self.bin_func = None
        self.image_path = None
        self.h5_path = None

Chris Smith's avatar
Chris Smith committed
34
    def translate(self, image_path, h5_path=None, bin_factor=None, bin_func=np.mean, normalize=False, **image_args):
Chris Smith's avatar
Chris Smith committed
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
        """
        Basic method that adds Ptychography data to existing hdf5 thisfile
        You must have already done the basic translation with BEodfTranslator

        Parameters
        ----------------
        image_path : str
            Absolute path to folder holding the image files
        h5_path : str, optional
            Absolute path to where the HDF5 file should be located.
            Default is None
        bin_factor : array_like of uint, optional
            Downsampling factor for each dimension.  Default is None.
        bin_func : callable, optional
            Function which will be called to calculate the return value
            of each block.  Function must implement an axis parameter,
            i.e. numpy.mean.  Ignored if bin_factor is None.  Default is
            numpy.mean.
Chris Smith's avatar
Chris Smith committed
53
54
55
        normalize : boolean, optional
            Should the raw image be normalized when read in
            Default False
Chris Smith's avatar
Chris Smith committed
56
57
58
59
60
61
62
63
64
        image_args : dict
            Arguments to be passed to read_image.  Arguments depend on the type of image.

        Returns
        ----------
        h5_main : h5py.Dataset
            HDF5 Dataset object that contains the flattened images

        """
65
        image_path, h5_path = self._parse_file_path(image_path)
Chris Smith's avatar
Chris Smith committed
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

        image, image_parms = read_image(image_path, **image_args)
        usize, vsize = image.shape[:2]

        self.image_path = image_path
        self.h5_path = h5_path

        '''
        Check if a bin_factor is given.  Set up binning objects if it is.
        '''
        if bin_factor is not None:
            self.rebin = True
            if isinstance(bin_factor, int):
                self.bin_factor = (bin_factor, bin_factor)
            elif len(bin_factor) == 2:
                self.bin_factor = tuple(bin_factor)
            else:
                raise ValueError('Input parameter `bin_factor` must be a length 2 array_like or an integer.\n' +
                                 '{} was given.'.format(bin_factor))
            usize = int(usize / self.bin_factor[0])
            vsize = int(vsize / self.bin_factor[1])
            self.binning_func = block_reduce
            self.bin_func = bin_func

        image = self.binning_func(image, self.bin_factor, self.bin_func)

Chris Smith's avatar
Chris Smith committed
92
93
94
        image_parms['normalized'] = normalize
        image_parms['image_min'] = np.min(image)
        image_parms['image_max'] = np.max(image)
95
96
97
        '''
        Normalize Raw Image
        '''
Chris Smith's avatar
Chris Smith committed
98
99
100
101
        if normalize:
            image -= np.min(image)
            image = image/np.float32(np.max(image))

102

103
        h5_main = self._setup_h5(usize, vsize, image.dtype.type, image_parms)
Chris Smith's avatar
Chris Smith committed
104
105
106
107
108

        h5_main = self._read_data(image, h5_main)

        return h5_main

109
    def _setup_h5(self, usize, vsize, data_type, image_parms):
Chris Smith's avatar
Chris Smith committed
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
        """
        Setup the HDF5 file in which to store the data including creating
        the Position and Spectroscopic datasets

        Parameters
        ----------
        usize : int
            Number of pixel columns in the images
        vsize : int
            Number of pixel rows in the images
        data_type : type
            Data type to save image as
        image_parms : dict
            Image parameters to be stored as attributes of the measurement
            group

        Returns
        -------
        h5_main : h5py.Dataset
            HDF5 Dataset that the images will be written into
        """
        num_pixels = usize * vsize

133
        root_parms = generate_dummy_main_parms()
Chris Smith's avatar
Chris Smith committed
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
        root_parms['data_type'] = 'ImageData'

        root_parms.update(image_parms)

        main_parms = {'image_size_u': usize,
                      'image_size_v': vsize,
                      'num_pixels': num_pixels,
                      'translator': 'Image'}

        # Create the hdf5 data Group
        root_grp = MicroDataGroup('/')
        root_grp.attrs = root_parms
        meas_grp = MicroDataGroup('Measurement_000')
        meas_grp.attrs = main_parms
        chan_grp = MicroDataGroup('Channel_000')
        # Get the Position and Spectroscopic Datasets
        #     ds_spec_ind, ds_spec_vals = self._buildspectroscopicdatasets(usize, vsize, num_pixels)
151
152
153
154
155
156
157
        ds_spec_ind, ds_spec_vals = build_ind_val_dsets([1],
                                                        is_spectral=True,
                                                        labels=['Image'])
        ds_pos_ind, ds_pos_val = build_ind_val_dsets((usize, vsize),
                                                     is_spectral=False,
                                                     labels=['X', 'Y'],
                                                     units=['pixel', 'pixel'])
Chris Smith's avatar
Chris Smith committed
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

        ds_chunking = calc_chunks([num_pixels, 1],
                                  data_type(0).itemsize,
                                  unit_chunks=[num_pixels, 1])

        # Allocate space for Main_Data and Pixel averaged Data
        ds_main_data = MicroDataset('Raw_Data', data=[], maxshape=(num_pixels, 1),
                                    chunking=ds_chunking, dtype=data_type, compression='gzip')
        # Add datasets as children of Measurement_000 data group
        chan_grp.addChildren([ds_main_data, ds_spec_ind, ds_spec_vals, ds_pos_ind,
                              ds_pos_val])
        meas_grp.addChildren([chan_grp])

        root_grp.addChildren([meas_grp])
        # print('Writing following tree to this file:')
        # root_grp.showTree()

        # Open the hdf5 file and delete any contents
        try:
            hdf = ioHDF5(self.h5_path)
            hdf.clear()
        except:
            raise

        self.hdf = hdf

        h5_refs = self.hdf.writeData(root_grp)
        h5_main = getH5DsetRefs(['Raw_Data'], h5_refs)[0]
        aux_ds_names = ['Position_Indices',
                        'Position_Values',
                        'Spectroscopic_Indices',
                        'Spectroscopic_Values']

191
        link_as_main(h5_main, *getH5DsetRefs(aux_ds_names, h5_refs))
Chris Smith's avatar
Chris Smith committed
192
193
194
195
196
197

        self.hdf.flush()

        return h5_main

    @staticmethod
198
    def _parse_file_path(image_path):
Chris Smith's avatar
Chris Smith committed
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
        """
        Returns a list of all files in the directory given by path

        Parameters
        ---------------
        image_path : string / unicode
            absolute path to directory containing files

        Returns
        ----------
        image_path : str
            Absolute file path to the image
        image_ext : str
            File extension of image
        """
        if not os.path.exists(os.path.abspath(image_path)):
            raise ValueError('Specified image does not exist.')
        else:
            image_path = os.path.abspath(image_path)

        base_name, _ = os.path.splitext(image_path)

        h5_name = base_name + '.h5'
        h5_path = os.path.join(image_path, h5_name)

        return image_path, h5_path

    @staticmethod
    def __no_bin(image, *args, **kwargs):
        """
        Does absolutely nothing to the image.  Exists so that we can have
        a bin function to call whether we actually rebin the image or not.

        Parameters
        ----------
        image : ndarray
            Image
        args:
            Argument list
        kwargs:
            Keyword argument list

        Returns
        -------
        image : ndarray
            The input image
        """
        return image

    @staticmethod
    def _read_data(image, h5_main):
        """
        Read the image into the dataset

        image : numpy.array
            Numpy array containing the image
        h5_main : h5py.Dataset
            HDF5 Dataset that will hold the image
        """

        h5_main[:] = image.reshape(h5_main.shape)

        h5_main.file.flush()

        return h5_main