diff options
author | Edoardo Pasca <edo.paskino@gmail.com> | 2019-06-12 14:21:07 +0100 |
---|---|---|
committer | Edoardo Pasca <edo.paskino@gmail.com> | 2019-06-12 14:21:07 +0100 |
commit | 25b16a2532d3fd470ce3cba58a40cdf7a3ea2a33 (patch) | |
tree | 1a05fd0468e19bf5dbe4a0531bdec4914856608a /Wrappers/Python | |
parent | 60adff8dc5eb28c64ca6786178bd38abecf355e0 (diff) | |
parent | d7fa15c50ee9155ac24ecfa88ca74b111690532e (diff) | |
download | framework-25b16a2532d3fd470ce3cba58a40cdf7a3ea2a33.tar.gz framework-25b16a2532d3fd470ce3cba58a40cdf7a3ea2a33.tar.bz2 framework-25b16a2532d3fd470ce3cba58a40cdf7a3ea2a33.tar.xz framework-25b16a2532d3fd470ce3cba58a40cdf7a3ea2a33.zip |
Merge branch 'composite_operator_datacontainer' into demos
Conflicts:
Wrappers/Python/ccpi/processors/__init__.py
Diffstat (limited to 'Wrappers/Python')
-rw-r--r-- | Wrappers/Python/ccpi/io/NEXUSDataReader.py | 143 | ||||
-rw-r--r-- | Wrappers/Python/ccpi/io/NEXUSDataWriter.py | 151 | ||||
-rw-r--r-- | Wrappers/Python/ccpi/io/NikonDataReader.py | 281 | ||||
-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 | 1 |
6 files changed, 843 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..cf67e27 --- /dev/null +++ b/Wrappers/Python/ccpi/io/NEXUSDataReader.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Wed Apr 3 10:30:25 2019 + +@author: evelina +""" + + +import numpy +import os +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) +''' diff --git a/Wrappers/Python/ccpi/io/NEXUSDataWriter.py b/Wrappers/Python/ccpi/io/NEXUSDataWriter.py new file mode 100644 index 0000000..f780f79 --- /dev/null +++ b/Wrappers/Python/ccpi/io/NEXUSDataWriter.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Thu May 2 10:11:20 2019 + +@author: evelina +""" + + +import numpy +import os +from ccpi.framework import AcquisitionData, AcquisitionGeometry, ImageData, ImageGeometry +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() +''' diff --git a/Wrappers/Python/ccpi/io/NikonDataReader.py b/Wrappers/Python/ccpi/io/NikonDataReader.py new file mode 100644 index 0000000..703b65b --- /dev/null +++ b/Wrappers/Python/ccpi/io/NikonDataReader.py @@ -0,0 +1,281 @@ +#!/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 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 index f8d050e..cba5897 100755 --- a/Wrappers/Python/ccpi/processors/__init__.py +++ b/Wrappers/Python/ccpi/processors/__init__.py @@ -7,3 +7,4 @@ Created on Tue Apr 30 13:51:09 2019 from .CenterOfRotationFinder import CenterOfRotationFinder
from .Normalizer import Normalizer
+from .Resizer import Resizer
|