diff options
| author | evelinaametova <47400194+evelinaametova@users.noreply.github.com> | 2019-06-12 11:24:32 +0100 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-06-12 11:24:32 +0100 | 
| commit | b7fa76940a2a86635d6b528009f1cce9f1fcdd5e (patch) | |
| tree | 80bc7cfef94226672ee97b79aab19a78f5cb8830 | |
| parent | 68d716ff14264bfaac8c9311d435530689382e7e (diff) | |
| parent | 78f962def0bdb188efe8f72aac7f26838f5c4b62 (diff) | |
| download | framework-b7fa76940a2a86635d6b528009f1cce9f1fcdd5e.tar.gz framework-b7fa76940a2a86635d6b528009f1cce9f1fcdd5e.tar.bz2 framework-b7fa76940a2a86635d6b528009f1cce9f1fcdd5e.tar.xz framework-b7fa76940a2a86635d6b528009f1cce9f1fcdd5e.zip | |
Merge pull request #307 from vais-ral/data_readers_merge
data readers, writer, resizer to composite_data_container branch
| -rw-r--r-- | Wrappers/Python/ccpi/io/NEXUSDataReader.py | 144 | ||||
| -rw-r--r-- | Wrappers/Python/ccpi/io/NEXUSDataWriter.py | 153 | ||||
| -rw-r--r-- | Wrappers/Python/ccpi/io/NikonDataReader.py | 282 | ||||
| -rw-r--r-- | Wrappers/Python/ccpi/io/__init__.py | 6 | ||||
| -rwxr-xr-x | Wrappers/Python/ccpi/processors/Resizer.py | 262 | ||||
| -rwxr-xr-x | Wrappers/Python/ccpi/processors/__init__.py | 10 | 
6 files changed, 856 insertions, 1 deletions
| diff --git a/Wrappers/Python/ccpi/io/NEXUSDataReader.py b/Wrappers/Python/ccpi/io/NEXUSDataReader.py new file mode 100644 index 0000000..a742dd5 --- /dev/null +++ b/Wrappers/Python/ccpi/io/NEXUSDataReader.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Wed Apr  3 10:30:25 2019 + +@author: evelina +""" + + +import numpy +import os +import matplotlib.pyplot as plt +from ccpi.framework import AcquisitionData, AcquisitionGeometry, ImageData, ImageGeometry + + +h5pyAvailable = True +try: +    import h5py  +except: +    h5pyAvailable = False + + +class NEXUSDataReader(object): +     +    def __init__(self, +                 **kwargs): +         +        ''' +        Constructor +         +        Input: +             +            nexus_file      full path to NEXUS file +        ''' +         +        self.nexus_file = kwargs.get('nexus_file', None) +         +        if self.nexus_file is not None: +            self.set_up(nexus_file = self.nexus_file, +                        roi = self.roi, +                        binning = self.binning) +             +    def set_up(self,  +               nexus_file = None): +         +        self.nexus_file = nexus_file +         +        # check that h5py library is installed +        if (h5pyAvailable == False): +            raise Exception('h5py is not available, cannot load NEXUS files.') +             +        if self.nexus_file == None: +            raise Exception('Path to nexus file is required.') +         +        # check if nexus file exists +        if not(os.path.isfile(self.nexus_file)): +            raise Exception('File\n {}\n does not exist.'.format(self.nexus_file))   +         +    def load_data(self): +         +        ''' +        Parse NEXUS file and returns either ImageData or Acquisition Data  +        depending on file content +        ''' +         +        try: +            with h5py.File(self.nexus_file,'r') as file: +                 +                if (file.attrs['creator'] != 'NEXUSDataWriter.py'): +                    raise Exception('We can parse only files created by NEXUSDataWriter.py') +                 +                ds_data = file['entry1/tomo_entry/data/data'] +                data = numpy.array(ds_data, dtype = 'float32') +                 +                dimension_labels = [] +                 +                for i in range(data.ndim): +                    dimension_labels.append(ds_data.attrs['dim{}'.format(i)]) +                 +                if ds_data.attrs['data_type'] == 'ImageData': +                    self._geometry = ImageGeometry(voxel_num_x = ds_data.attrs['voxel_num_x'], +                                                   voxel_num_y = ds_data.attrs['voxel_num_y'], +                                                   voxel_num_z = ds_data.attrs['voxel_num_z'], +                                                   voxel_size_x = ds_data.attrs['voxel_size_x'], +                                                   voxel_size_y = ds_data.attrs['voxel_size_y'], +                                                   voxel_size_z = ds_data.attrs['voxel_size_z'], +                                                   center_x = ds_data.attrs['center_x'], +                                                   center_y = ds_data.attrs['center_y'], +                                                   center_z = ds_data.attrs['center_z'], +                                                   channels = ds_data.attrs['channels']) +                     +                    return ImageData(array = data, +                                     deep_copy = False, +                                     geometry = self._geometry, +                                     dimension_labels = dimension_labels) +                     +                else:   # AcquisitionData +                    self._geometry = AcquisitionGeometry(geom_type = ds_data.attrs['geom_type'], +                                                         dimension = ds_data.attrs['dimension'], +                                                         dist_source_center = ds_data.attrs['dist_source_center'], +                                                         dist_center_detector = ds_data.attrs['dist_center_detector'], +                                                         pixel_num_h = ds_data.attrs['pixel_num_h'], +                                                         pixel_size_h = ds_data.attrs['pixel_size_h'], +                                                         pixel_num_v = ds_data.attrs['pixel_num_v'], +                                                         pixel_size_v = ds_data.attrs['pixel_size_v'], +                                                         channels = ds_data.attrs['channels'], +                                                         angles = numpy.array(file['entry1/tomo_entry/data/rotation_angle'], dtype = 'float32')) +                                                         #angle_unit = file['entry1/tomo_entry/data/rotation_angle'].attrs['units']) +                                              +                    return AcquisitionData(array = data, +                                           deep_copy = False, +                                           geometry = self._geometry, +                                           dimension_labels = dimension_labels) +                     +        except: +            print("Error reading nexus file") +            raise +                 +    def get_geometry(self): +         +        ''' +        Return either ImageGeometry or AcquisitionGeometry  +        depepnding on file content +        ''' +         +        return self._geometry + + +''' +# usage example +reader = NEXUSDataReader() +reader.set_up(nexus_file = '/home/evelina/test_nexus.nxs') +acquisition_data = reader.load_data() +print(acquisition_data) +ag = reader.get_geometry() +print(ag) + +reader = NEXUSDataReader() +reader.set_up(nexus_file = '/home/evelina/test_nexus_im.nxs') +image_data = reader.load_data() +print(image_data) +ig = reader.get_geometry() +print(ig) +'''
\ No newline at end of file diff --git a/Wrappers/Python/ccpi/io/NEXUSDataWriter.py b/Wrappers/Python/ccpi/io/NEXUSDataWriter.py new file mode 100644 index 0000000..d35e86b --- /dev/null +++ b/Wrappers/Python/ccpi/io/NEXUSDataWriter.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Thu May  2 10:11:20 2019 + +@author: evelina +""" + + +import numpy +import os +import matplotlib.pyplot as plt +from ccpi.framework import AcquisitionData, AcquisitionGeometry, ImageData, ImageGeometry +from NikonDataReader import NikonDataReader +import datetime + + +h5pyAvailable = True +try: +    import h5py  +except: +    h5pyAvailable = False + + +class NEXUSDataWriter(object): +     +    def __init__(self, +                 **kwargs): +         +        self.data_container = kwargs.get('data_container', None) +        self.file_name = kwargs.get('file_name', None) +         +        if ((self.data_container is not None) and (self.file_name is not None)): +            self.set_up(data_container = self.data_container, +                        file_name = self.file_name) +         +    def set_up(self, +               data_container = None, +               file_name = None): +         +        self.data_container = data_container +        self.file_name = file_name +         +        if not ((isinstance(self.data_container, ImageData)) or  +                (isinstance(self.data_container, AcquisitionData))): +            raise Exception('Writer supports only following data types:\n' + +                            ' - ImageData\n - AcquisitionData') +         +        # check that h5py library is installed +        if (h5pyAvailable == False): +            raise Exception('h5py is not available, cannot load NEXUS files.') +     +    def write_file(self): +         +        # if the folder does not exist, create the folder +        if not os.path.isdir(os.path.dirname(self.file_name)): +            os.mkdir(os.path.dirname(self.file_name)) +             +        # create the file +        with h5py.File(self.file_name, 'w') as f: +             +            # give the file some important attributes +            f.attrs['file_name'] = self.file_name +            f.attrs['file_time'] = str(datetime.datetime.utcnow()) +            f.attrs['creator'] = 'NEXUSDataWriter.py' +            f.attrs['NeXus_version'] = '4.3.0' +            f.attrs['HDF5_Version'] = h5py.version.hdf5_version +            f.attrs['h5py_version'] = h5py.version.version +             +            # create the NXentry group +            nxentry = f.create_group('entry1/tomo_entry') +            nxentry.attrs['NX_class'] = 'NXentry' +             +            # create dataset to store data +            ds_data = f.create_dataset('entry1/tomo_entry/data/data',  +                                       (self.data_container.as_array().shape),  +                                       dtype = 'float32',  +                                       data = self.data_container.as_array()) +             +            # set up dataset attributes +            if (isinstance(self.data_container, ImageData)): +                ds_data.attrs['data_type'] = 'ImageData' +            else: +                ds_data.attrs['data_type'] = 'AcquisitionData' +             +            for i in range(self.data_container.as_array().ndim): +                ds_data.attrs['dim{}'.format(i)] = self.data_container.dimension_labels[i] +             +            if (isinstance(self.data_container, AcquisitionData)):       +                ds_data.attrs['geom_type'] = self.data_container.geometry.geom_type +                ds_data.attrs['dimension'] = self.data_container.geometry.dimension +                ds_data.attrs['dist_source_center'] = self.data_container.geometry.dist_source_center +                ds_data.attrs['dist_center_detector'] = self.data_container.geometry.dist_center_detector +                ds_data.attrs['pixel_num_h'] = self.data_container.geometry.pixel_num_h +                ds_data.attrs['pixel_size_h'] = self.data_container.geometry.pixel_size_h +                ds_data.attrs['pixel_num_v'] = self.data_container.geometry.pixel_num_v +                ds_data.attrs['pixel_size_v'] = self.data_container.geometry.pixel_size_v +                ds_data.attrs['channels'] = self.data_container.geometry.channels +                ds_data.attrs['n_angles'] = self.data_container.geometry.angles.shape[0] +                 +                # create the dataset to store rotation angles +                ds_angles = f.create_dataset('entry1/tomo_entry/data/rotation_angle',  +                                             (self.data_container.geometry.angles.shape),  +                                             dtype = 'float32',  +                                             data = self.data_container.geometry.angles) +                 +                #ds_angles.attrs['units'] = self.data_container.geometry.angle_unit +             +            else:   # ImageData +                 +                ds_data.attrs['voxel_num_x'] = self.data_container.geometry.voxel_num_x +                ds_data.attrs['voxel_num_y'] = self.data_container.geometry.voxel_num_y +                ds_data.attrs['voxel_num_z'] = self.data_container.geometry.voxel_num_z +                ds_data.attrs['voxel_size_x'] = self.data_container.geometry.voxel_size_x +                ds_data.attrs['voxel_size_y'] = self.data_container.geometry.voxel_size_y +                ds_data.attrs['voxel_size_z'] = self.data_container.geometry.voxel_size_z +                ds_data.attrs['center_x'] = self.data_container.geometry.center_x +                ds_data.attrs['center_y'] = self.data_container.geometry.center_y +                ds_data.attrs['center_z'] = self.data_container.geometry.center_z +                ds_data.attrs['channels'] = self.data_container.geometry.channels + + +''' +# usage example +xtek_file = '/home/evelina/nikon_data/SophiaBeads_256_averaged.xtekct' +reader = NikonDataReader() +reader.set_up(xtek_file = xtek_file, +              binning = [3, 1], +              roi = [200, 500, 1500, 2000], +              normalize = True) + +data = reader.load_projections() +ag = reader.get_geometry() + +writer = NEXUSDataWriter() +writer.set_up(file_name = '/home/evelina/test_nexus.nxs', +              data_container = data) + +writer.write_file() + +ig = ImageGeometry(voxel_num_x = 100, +                   voxel_num_y = 100) + +im = ImageData(array = numpy.zeros((100, 100), dtype = 'float'), +               geometry = ig) + +im_writer = NEXUSDataWriter() + +writer.set_up(file_name = '/home/evelina/test_nexus_im.nxs', +              data_container = im) + +writer.write_file() +'''
\ No newline at end of file diff --git a/Wrappers/Python/ccpi/io/NikonDataReader.py b/Wrappers/Python/ccpi/io/NikonDataReader.py new file mode 100644 index 0000000..6f02aa0 --- /dev/null +++ b/Wrappers/Python/ccpi/io/NikonDataReader.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Wed Apr  3 10:30:25 2019 + +@author: evelina +""" + +from ccpi.framework import AcquisitionData, AcquisitionGeometry +import numpy +import matplotlib.pyplot as plt +import os + + +pilAvailable = True +try:     +    from PIL import Image +except: +    pilAvailable = False +     +         +class NikonDataReader(object): +     +    def __init__(self,  +                 **kwargs): +        ''' +        Constructor +         +        Input: +             +            xtek_file       full path to .xtexct file +             +            roi             region-of-interest to load. If roi = -1 (default),  +                            full projections will be loaded. Otherwise roi is  +                            given by [(row0, row1), (column0, column1)], where  +                            row0, column0 are coordinates of top left corner and  +                            row1, column1 are coordinates of bottom right corner. +                             +            binning         number of pixels to bin (combine) along 0 (column)  +                            and 1 (row) dimension. If binning = [1, 1] (default), +                            projections in original resolution are loaded. Note,  +                            if binning[0] != binning[1], then loaded projections +                            will have anisotropic pixels, which are currently not  +                            supported by the Framework +             +            normalize       normalize loaded projections by detector  +                            white level (I_0). Default value is False,  +                            i.e. no normalization. +                             +            flip            default = False, flip projections in the left-right direction +                     +        ''' +         +        self.xtek_file = kwargs.get('xtek_file', None) +        self.roi = kwargs.get('roi', -1) +        self.binning = kwargs.get('binning', [1, 1]) +        self.normalize = kwargs.get('normalize', False) +        self.flip = kwargs.get('flip', False) +         +        if self.xtek_file is not None: +            self.set_up(xtek_file = self.xtek_file, +                        roi = self.roi, +                        binning = self.binning, +                        normalize = self.normalize, +                        flip = self.flip) +             +    def set_up(self,  +               xtek_file = None,  +               roi = -1,  +               binning = [1, 1], +               normalize = False, +               flip = False): +         +        self.xtek_file = xtek_file +        self.roi = roi +        self.binning = binning +        self.normalize = normalize +        self.flip = flip +         +        if self.xtek_file == None: +            raise Exception('Path to xtek file is required.') +         +        # check if xtek file exists +        if not(os.path.isfile(self.xtek_file)): +            raise Exception('File\n {}\n does not exist.'.format(self.xtek_file))   +                 +        # check that PIL library is installed +        if (pilAvailable == False): +            raise Exception("PIL (pillow) is not available, cannot load TIFF files.") +                 +        # parse xtek file +        with open(self.xtek_file, 'r') as f: +            content = f.readlines()     +                 +        content = [x.strip() for x in content] +         +        for line in content: +            # filename of TIFF files +            if line.startswith("Name"): +                self._experiment_name = line.split('=')[1] +            # number of projections +            elif line.startswith("Projections"): +                num_projections = int(line.split('=')[1]) +            # white level - used for normalization +            elif line.startswith("WhiteLevel"): +                self._white_level = float(line.split('=')[1]) +            # number of pixels along Y axis +            elif line.startswith("DetectorPixelsY"): +                pixel_num_v_0 = int(line.split('=')[1]) +            # number of pixels along X axis +            elif line.startswith("DetectorPixelsX"): +                pixel_num_h_0 = int(line.split('=')[1]) +            # pixel size along X axis +            elif line.startswith("DetectorPixelSizeX"): +                pixel_size_h_0 = float(line.split('=')[1]) +            # pixel size along Y axis +            elif line.startswith("DetectorPixelSizeY"): +                pixel_size_v_0 = float(line.split('=')[1]) +            # source to center of rotation distance +            elif line.startswith("SrcToObject"): +                source_x = float(line.split('=')[1]) +            # source to detector distance +            elif line.startswith("SrcToDetector"): +                detector_x = float(line.split('=')[1]) +            # initial angular position of a rotation stage +            elif line.startswith("InitialAngle"): +                initial_angle = float(line.split('=')[1]) +            # angular increment (in degrees) +            elif line.startswith("AngularStep"): +                angular_step = float(line.split('=')[1]) +                 +        if self.roi == -1: +            self._roi_par = [(0, pixel_num_v_0), \ +                              (0, pixel_num_h_0)] +        else: +            self._roi_par = self.roi.copy() +            if self._roi_par[0] == -1: +                self._roi_par[0] = (0, pixel_num_v_0) +            if self._roi_par[1] == -1: +                self._roi_par[1] = (0, pixel_num_h_0) +                 +        # calculate number of pixels and pixel size +        if (self.binning == [1, 1]): +            pixel_num_v = self._roi_par[0][1] - self._roi_par[0][0] +            pixel_num_h = self._roi_par[1][1] - self._roi_par[1][0] +            pixel_size_v = pixel_size_v_0 +            pixel_size_h = pixel_size_h_0 +        else: +            pixel_num_v = (self._roi_par[0][1] - self._roi_par[0][0]) // self.binning[0] +            pixel_num_h = (self._roi_par[1][1] - self._roi_par[1][0]) // self.binning[1] +            pixel_size_v = pixel_size_v_0 * self.binning[0] +            pixel_size_h = pixel_size_h_0 * self.binning[1] +         +        ''' +        Parse the angles file .ang or _ctdata.txt file and returns the angles +        as an numpy array.  +        ''' +        input_path = os.path.dirname(self.xtek_file) +        angles_ctdata_file = os.path.join(input_path, '_ctdata.txt') +        angles_named_file = os.path.join(input_path, self._experiment_name+'.ang') +        angles = numpy.zeros(num_projections, dtype = 'float') +         +        # look for _ctdata.txt +        if os.path.exists(angles_ctdata_file): +            # read txt file with angles +            with open(angles_ctdata_file) as f: +                content = f.readlines() +            # skip firt three lines +            # read the middle value of 3 values in each line as angles in degrees +            index = 0 +            for line in content[3:]: +                angles[index] = float(line.split(' ')[1]) +                index += 1 +            angles = angles + initial_angle +         +        # look for ang file +        elif os.path.exists(angles_named_file): +            # read the angles file which is text with first line as header +            with open(angles_named_file) as f: +                content = f.readlines() +            # skip first line +            index = 0 +            for line in content[1:]: +                angles[index] = float(line.split(':')[1]) +                index += 1 +            angles = numpy.flipud(angles + initial_angle) # angles are in the reverse order +             +        else:   # calculate angles based on xtek file +            angles = initial_angle + angular_step * range(num_projections) +         +        # fill in metadata +        self._ag = AcquisitionGeometry(geom_type = 'cone',  +                                       dimension = '3D',  +                                       angles = angles,  +                                       pixel_num_h = pixel_num_h,  +                                       pixel_size_h = pixel_size_h,  +                                       pixel_num_v = pixel_num_v,  +                                       pixel_size_v = pixel_size_v,  +                                       dist_source_center = source_x,  +                                       dist_center_detector = detector_x - source_x,  +                                       channels = 1, +                                       angle_unit = 'degree') + +    def get_geometry(self): +         +        ''' +        Return AcquisitionGeometry object +        ''' +         +        return self._ag +         +    def load_projections(self): +         +        ''' +        Load projections and return AcquisitionData container +        ''' +             +        # get path to projections +        path_projection = os.path.dirname(self.xtek_file) +         +        # get number of projections +        num_projections = numpy.shape(self._ag.angles)[0] +         +        # allocate array to store projections     +        data = numpy.zeros((num_projections, self._ag.pixel_num_v, self._ag.pixel_num_h), dtype = float) +         +        for i in range(num_projections): +             +            filename = (path_projection + '/' + self._experiment_name + '_{:04d}.tif').format(i + 1) +             +            try: +                tmp = numpy.asarray(Image.open(filename), dtype = float) +            except: +                print('Error reading\n {}\n file.'.format(filename)) +                raise +                 +            if (self.binning == [1, 1]): +                data[i, :, :] = tmp[self._roi_par[0][0]:self._roi_par[0][1], self._roi_par[1][0]:self._roi_par[1][1]] +            else: +                shape = (self._ag.pixel_num_v, self.binning[0],  +                         self._ag.pixel_num_h, self.binning[1]) +                data[i, :, :] = tmp[self._roi_par[0][0]:(self._roi_par[0][0] + (((self._roi_par[0][1] - self._roi_par[0][0]) // self.binning[0]) * self.binning[0])), \ +                                    self._roi_par[1][0]:(self._roi_par[1][0] + (((self._roi_par[1][1] - self._roi_par[1][0]) // self.binning[1]) * self.binning[1]))].reshape(shape).mean(-1).mean(1) +         +        if (self.normalize): +            data /= self._white_level +            data[data > 1] = 1 +         +        if self.flip: +            return AcquisitionData(array = data[:, :, ::-1],  +                                   deep_copy = False, +                                   geometry = self._ag, +                                   dimension_labels = ['angle', \ +                                                       'vertical', \ +                                                       'horizontal']) +        else: +            return AcquisitionData(array = data,  +                                   deep_copy = False, +                                   geometry = self._ag, +                                   dimension_labels = ['angle', \ +                                                       'vertical', \ +                                                       'horizontal']) + + +''' +# usage example +xtek_file = '/home/evelina/nikon_data/SophiaBeads_256_averaged.xtekct' +reader = NikonDataReader() +reader.set_up(xtek_file = xtek_file, +              binning = [1, 1], +              roi = -1, +              normalize = True, +              flip = True) + +data = reader.load_projections() +print(data) +ag = reader.get_geometry() +print(ag) + +plt.imshow(data.as_array()[1, :, :]) +plt.show() +''' diff --git a/Wrappers/Python/ccpi/io/__init__.py b/Wrappers/Python/ccpi/io/__init__.py index 9233d7a..455faba 100644 --- a/Wrappers/Python/ccpi/io/__init__.py +++ b/Wrappers/Python/ccpi/io/__init__.py @@ -15,4 +15,8 @@  #   distributed under the License is distributed on an "AS IS" BASIS,
  #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  #   See the License for the specific language governing permissions and
 -#   limitations under the License.
\ No newline at end of file +#   limitations under the License.
 +
 +from .NEXUSDataReader import NEXUSDataReader
 +from .NEXUSDataWriter import NEXUSDataWriter
 +from .NikonDataReader import NikonDataReader
 diff --git a/Wrappers/Python/ccpi/processors/Resizer.py b/Wrappers/Python/ccpi/processors/Resizer.py new file mode 100755 index 0000000..7509a90 --- /dev/null +++ b/Wrappers/Python/ccpi/processors/Resizer.py @@ -0,0 +1,262 @@ +# -*- coding: utf-8 -*-
 +#   This work is part of the Core Imaging Library developed by
 +#   Visual Analytics and Imaging System Group of the Science Technology
 +#   Facilities Council, STFC
 +
 +#   Copyright 2018 Edoardo Pasca
 +
 +#   Licensed under the Apache License, Version 2.0 (the "License");
 +#   you may not use this file except in compliance with the License.
 +#   You may obtain a copy of the License at
 +
 +#       http://www.apache.org/licenses/LICENSE-2.0
 +
 +#   Unless required by applicable law or agreed to in writing, software
 +#   distributed under the License is distributed on an "AS IS" BASIS,
 +#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 +#   See the License for the specific language governing permissions and
 +#   limitations under the License
 +
 +from ccpi.framework import DataProcessor, AcquisitionData, ImageData
 +import warnings
 +
 +
 +class Resizer(DataProcessor):
 +
 +    def __init__(self,
 +                 roi = -1,
 +                 binning = 1):
 +        
 +        '''
 +        Constructor
 +        
 +        Input:
 +            
 +            roi             region-of-interest to crop. If roi = -1 (default), then no crop. 
 +                            Otherwise roi is given by a list with ndim elements, 
 +                            where each element is either -1 if no crop along this 
 +                            dimension or a tuple with beginning and end coodinates to crop to.
 +                            Example:
 +                                to crop 4D array along 2nd dimension:
 +                                roi = [-1, -1, (100, 900), -1]
 +                            
 +            binning         number of pixels to bin (combine) along each dimension.
 +                            If binning = 1, then projections in original resolution are loaded. 
 +                            Otherwise, binning is given by a list with ndim integers. 
 +                            Example:
 +                                to rebin 3D array along 1st direction:
 +                                binning = [1, 5, 1]
 +        '''
 +
 +        kwargs = {'roi': roi,
 +                  'binning': binning}
 +
 +        super(Resizer, self).__init__(**kwargs)
 +    
 +    def check_input(self, data):
 +        if not ((isinstance(data, ImageData)) or 
 +                (isinstance(data, AcquisitionData))):
 +            raise Exception('Processor supports only following data types:\n' +
 +                            ' - ImageData\n - AcquisitionData')
 +        elif (data.geometry == None):
 +            raise Exception('Geometry is not defined.')
 +        else:
 +            return True
 +    
 +    def process(self):
 +
 +        data = self.get_input()
 +        ndim = len(data.dimension_labels)
 +        
 +        geometry_0 = data.geometry
 +        geometry = geometry_0.clone()
 +        
 +        if (self.roi == -1):
 +            roi_par = [-1] * ndim
 +        else:
 +            roi_par = self.roi.copy()
 +            if (len(roi_par) != ndim):
 +                raise Exception('Number of dimensions and number of elements in roi parameter do not match')
 +
 +        if (self.binning == 1):
 +            binning = [1] * ndim
 +        else:
 +            binning = self.binning.copy()
 +            if (len(binning) != ndim):
 +                raise Exception('Number of dimensions and number of elements in binning parameter do not match')
 +                
 +        if (isinstance(data, ImageData)):
 +            if ((all(x == -1 for x in roi_par)) and (all(x == 1 for x in binning))):
 +                for key in data.dimension_labels:
 +                    if data.dimension_labels[key] == 'channel':
 +                        geometry.channels = geometry_0.channels
 +                        roi_par[key] = (0, geometry.channels)
 +                    elif data.dimension_labels[key] == 'horizontal_y':
 +                        geometry.voxel_size_y = geometry_0.voxel_size_y
 +                        geometry.voxel_num_y = geometry_0.voxel_num_y
 +                        roi_par[key] = (0, geometry.voxel_num_y)
 +                    elif data.dimension_labels[key] == 'vertical':
 +                        geometry.voxel_size_z = geometry_0.voxel_size_z
 +                        geometry.voxel_num_z = geometry_0.voxel_num_z
 +                        roi_par[key] = (0, geometry.voxel_num_z)
 +                    elif data.dimension_labels[key] == 'horizontal_x':
 +                        geometry.voxel_size_x = geometry_0.voxel_size_x
 +                        geometry.voxel_num_x = geometry_0.voxel_num_x
 +                        roi_par[key] = (0, geometry.voxel_num_x)
 +            else:
 +                for key in data.dimension_labels:
 +                    if data.dimension_labels[key] == 'channel':
 +                        if (roi_par[key] != -1):
 +                            geometry.channels = (roi_par[key][1] - roi_par[key][0]) // binning[key]
 +                            roi_par[key] = (roi_par[key][0], roi_par[key][0] + ((roi_par[key][1] - roi_par[key][0]) // binning[key]) * binning[key])
 +                        else:
 +                            geometry.channels = geometry_0.channels // binning[key]
 +                            roi_par[key] = (0, geometry.channels * binning[key])
 +                    elif data.dimension_labels[key] == 'horizontal_y':
 +                        if (roi_par[key] != -1):
 +                            geometry.voxel_num_y = (roi_par[key][1] - roi_par[key][0]) // binning[key]
 +                            geometry.voxel_size_y = geometry_0.voxel_size_y * binning[key]
 +                            roi_par[key] = (roi_par[key][0], roi_par[key][0] + ((roi_par[key][1] - roi_par[key][0]) // binning[key]) * binning[key])
 +                        else:
 +                            geometry.voxel_num_y = geometry_0.voxel_num_y // binning[key]
 +                            geometry.voxel_size_y = geometry_0.voxel_size_y * binning[key]
 +                            roi_par[key] = (0, geometry.voxel_num_y * binning[key])
 +                    elif data.dimension_labels[key] == 'vertical':
 +                        if (roi_par[key] != -1):
 +                            geometry.voxel_num_z = (roi_par[key][1] - roi_par[key][0]) // binning[key]
 +                            geometry.voxel_size_z = geometry_0.voxel_size_z * binning[key]
 +                            roi_par[key] = (roi_par[key][0], roi_par[key][0] + ((roi_par[key][1] - roi_par[key][0]) // binning[key]) * binning[key])
 +                        else:
 +                            geometry.voxel_num_z = geometry_0.voxel_num_z // binning[key]
 +                            geometry.voxel_size_z = geometry_0.voxel_size_z * binning[key]
 +                            roi_par[key] = (0, geometry.voxel_num_z * binning[key])
 +                    elif data.dimension_labels[key] == 'horizontal_x':
 +                        if (roi_par[key] != -1):
 +                            geometry.voxel_num_x = (roi_par[key][1] - roi_par[key][0]) // binning[key]
 +                            geometry.voxel_size_x = geometry_0.voxel_size_x * binning[key]
 +                            roi_par[key] = (roi_par[key][0], roi_par[key][0]+ ((roi_par[key][1] - roi_par[key][0]) // binning[key]) * binning[key])
 +                        else:
 +                            geometry.voxel_num_x = geometry_0.voxel_num_x // binning[key]
 +                            geometry.voxel_size_x = geometry_0.voxel_size_x * binning[key]
 +                            roi_par[key] = (0, geometry.voxel_num_x * binning[key])
 +            
 +        else: # AcquisitionData
 +            if ((all(x == -1 for x in roi_par)) and (all(x == 1 for x in binning))):
 +                for key in data.dimension_labels:
 +                    if data.dimension_labels[key] == 'channel':
 +                        geometry.channels = geometry_0.channels
 +                        roi_par[key] = (0, geometry.channels)
 +                    elif data.dimension_labels[key] == 'angle':
 +                        geometry.angles = geometry_0.angles
 +                        roi_par[key] = (0, len(geometry.angles))
 +                    elif data.dimension_labels[key] == 'vertical':
 +                        geometry.pixel_size_v = geometry_0.pixel_size_v
 +                        geometry.pixel_num_v = geometry_0.pixel_num_v
 +                        roi_par[key] = (0, geometry.pixel_num_v)
 +                    elif data.dimension_labels[key] == 'horizontal':
 +                        geometry.pixel_size_h = geometry_0.pixel_size_h
 +                        geometry.pixel_num_h = geometry_0.pixel_num_h
 +                        roi_par[key] = (0, geometry.pixel_num_h)
 +            else:
 +                for key in data.dimension_labels:
 +                    if data.dimension_labels[key] == 'channel':
 +                        if (roi_par[key] != -1):
 +                            geometry.channels = (roi_par[key][1] - roi_par[key][0]) // binning[key]
 +                            roi_par[key] = (roi_par[key][0], roi_par[key][0] + ((roi_par[key][1] - roi_par[key][0]) // binning[key]) * binning[key])
 +                        else:
 +                            geometry.channels = geometry_0.channels // binning[key]
 +                            roi_par[key] = (0, geometry.channels * binning[key])
 +                    elif data.dimension_labels[key] == 'angle':
 +                        if (roi_par[key] != -1):
 +                            geometry.angles = geometry_0.angles[roi_par[key][0]:roi_par[key][1]]
 +                        else:
 +                            geometry.angles = geometry_0.angles
 +                            roi_par[key] = (0, len(geometry.angles))
 +                        if (binning[key] != 1):
 +                            binning[key] = 1
 +                            warnings.warn('Rebinning in angular dimensions is not supported: \n binning[{}] is set to 1.'.format(key))
 +                    elif data.dimension_labels[key] == 'vertical':
 +                        if (roi_par[key] != -1):
 +                            geometry.pixel_num_v = (roi_par[key][1] - roi_par[key][0]) // binning[key]
 +                            geometry.pixel_size_v = geometry_0.pixel_size_v * binning[key]
 +                            roi_par[key] = (roi_par[key][0], roi_par[key][0] + ((roi_par[key][1] - roi_par[key][0]) // binning[key]) * binning[key])
 +                        else:
 +                            geometry.pixel_num_v = geometry_0.pixel_num_v // binning[key]
 +                            geometry.pixel_size_v = geometry_0.pixel_size_v * binning[key]
 +                            roi_par[key] = (0, geometry.pixel_num_v * binning[key])
 +                    elif data.dimension_labels[key] == 'horizontal':
 +                        if (roi_par[key] != -1):
 +                            geometry.pixel_num_h = (roi_par[key][1] - roi_par[key][0]) // binning[key]
 +                            geometry.pixel_size_h = geometry_0.pixel_size_h * binning[key]
 +                            roi_par[key] = (roi_par[key][0], roi_par[key][0] + ((roi_par[key][1] - roi_par[key][0]) // binning[key]) * binning[key])
 +                        else:
 +                            geometry.pixel_num_h = geometry_0.pixel_num_h // binning[key]
 +                            geometry.pixel_size_h = geometry_0.pixel_size_h * binning[key]
 +                            roi_par[key] = (0, geometry.pixel_num_h * binning[key])
 +                            
 +        if ndim == 2:
 +            n_pix_0 = (roi_par[0][1] - roi_par[0][0]) // binning[0]
 +            n_pix_1 = (roi_par[1][1] - roi_par[1][0]) // binning[1]
 +            shape = (n_pix_0, binning[0], 
 +                     n_pix_1, binning[1])
 +            data_resized = data.as_array()[roi_par[0][0]:(roi_par[0][0] + n_pix_0 * binning[0]), 
 +                                           roi_par[1][0]:(roi_par[1][0] + n_pix_1 * binning[1])].reshape(shape).mean(-1).mean(1)
 +        if ndim == 3:
 +            n_pix_0 = (roi_par[0][1] - roi_par[0][0]) // binning[0]
 +            n_pix_1 = (roi_par[1][1] - roi_par[1][0]) // binning[1]
 +            n_pix_2 = (roi_par[2][1] - roi_par[2][0]) // binning[2]
 +            shape = (n_pix_0, binning[0], 
 +                     n_pix_1, binning[1],
 +                     n_pix_2, binning[2])
 +            data_resized = data.as_array()[roi_par[0][0]:(roi_par[0][0] + n_pix_0 * binning[0]), 
 +                                           roi_par[1][0]:(roi_par[1][0] + n_pix_1 * binning[1]), 
 +                                           roi_par[2][0]:(roi_par[2][0] + n_pix_2 * binning[2])].reshape(shape).mean(-1).mean(1).mean(2)
 +        if ndim == 4:
 +            n_pix_0 = (roi_par[0][1] - roi_par[0][0]) // binning[0]
 +            n_pix_1 = (roi_par[1][1] - roi_par[1][0]) // binning[1]
 +            n_pix_2 = (roi_par[2][1] - roi_par[2][0]) // binning[2]
 +            n_pix_3 = (roi_par[3][1] - roi_par[3][0]) // binning[3]
 +            shape = (n_pix_0, binning[0], 
 +                     n_pix_1, binning[1],
 +                     n_pix_2, binning[2],
 +                     n_pix_3, binning[3])
 +            data_resized = data.as_array()[roi_par[0][0]:(roi_par[0][0] + n_pix_0 * binning[0]), 
 +                                           roi_par[1][0]:(roi_par[1][0] + n_pix_1 * binning[1]), 
 +                                           roi_par[2][0]:(roi_par[2][0] + n_pix_2 * binning[2]), 
 +                                           roi_par[3][0]:(roi_par[3][0] + n_pix_3 * binning[3])].reshape(shape).mean(-1).mean(1).mean(2).mean(3)
 +
 +        out = type(data)(array = data_resized, 
 +                         deep_copy = False,
 +                         dimension_labels = data.dimension_labels,
 +                         geometry = geometry)
 +        
 +        return out
 +
 +
 +'''
 +#usage exaample
 +ig = ImageGeometry(voxel_num_x = 200, 
 +                   voxel_num_y = 200, 
 +                   voxel_num_z = 200, 
 +                   voxel_size_x = 1, 
 +                   voxel_size_y = 1, 
 +                   voxel_size_z = 1, 
 +                   center_x = 0, 
 +                   center_y = 0, 
 +                   center_z = 0, 
 +                   channels = 200)
 +
 +im = ImageData(array = numpy.zeros((200, 200, 200, 200)),
 +               geometry = ig,
 +               deep_copy = False, 
 +               dimension_labels = ['channel',\
 +                                   'vertical',\
 +                                   'horizontal_y',\
 +                                   'horizontal_x'])
 +            
 +            
 +resizer = Resizer(binning = [1, 1, 7, 1], roi = -1)
 +resizer.input = im
 +data_resized = resizer.process()
 +print(data_resized)
 +'''
 diff --git a/Wrappers/Python/ccpi/processors/__init__.py b/Wrappers/Python/ccpi/processors/__init__.py new file mode 100755 index 0000000..cba5897 --- /dev/null +++ b/Wrappers/Python/ccpi/processors/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*-
 +"""
 +Created on Tue Apr 30 13:51:09 2019
 +
 +@author: ofn77899
 +"""
 +
 +from .CenterOfRotationFinder import CenterOfRotationFinder
 +from .Normalizer import Normalizer
 +from .Resizer import Resizer
 | 
