summaryrefslogtreecommitdiffstats
path: root/Wrappers/Python
diff options
context:
space:
mode:
authorEdoardo Pasca <edo.paskino@gmail.com>2018-01-10 14:57:58 +0000
committerEdoardo Pasca <edo.paskino@gmail.com>2018-01-10 14:57:58 +0000
commitaf5450ea4c3a47fca0e9917c6739f248bf3a79df (patch)
tree4d5da1be487f63f1f54464e29350940ecd13a36e /Wrappers/Python
parent2844f34f4eba1910017c683358ea755d2d007caa (diff)
downloadframework-af5450ea4c3a47fca0e9917c6739f248bf3a79df.tar.gz
framework-af5450ea4c3a47fca0e9917c6739f248bf3a79df.tar.bz2
framework-af5450ea4c3a47fca0e9917c6739f248bf3a79df.tar.xz
framework-af5450ea4c3a47fca0e9917c6739f248bf3a79df.zip
Added conda recipe
Added Methods to DataSet. Added SliceDataSet and VolumeDataSet (TODO make them useful) Added DataSetProcessor and some examples
Diffstat (limited to 'Wrappers/Python')
-rw-r--r--Wrappers/Python/ccpi/__init__.py18
-rw-r--r--Wrappers/Python/ccpi/common.py231
-rw-r--r--Wrappers/Python/conda-recipe/bld.bat9
-rw-r--r--Wrappers/Python/conda-recipe/build.sh9
-rw-r--r--Wrappers/Python/conda-recipe/meta.yaml26
-rw-r--r--Wrappers/Python/setup.py107
6 files changed, 330 insertions, 70 deletions
diff --git a/Wrappers/Python/ccpi/__init__.py b/Wrappers/Python/ccpi/__init__.py
new file mode 100644
index 0000000..cf2d93d
--- /dev/null
+++ b/Wrappers/Python/ccpi/__init__.py
@@ -0,0 +1,18 @@
+# -*- 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. \ No newline at end of file
diff --git a/Wrappers/Python/ccpi/common.py b/Wrappers/Python/ccpi/common.py
index 9d7780e..e2816db 100644
--- a/Wrappers/Python/ccpi/common.py
+++ b/Wrappers/Python/ccpi/common.py
@@ -21,6 +21,7 @@ import numpy
import os
import sys
import time
+import vtk
if sys.version_info[0] >= 3 and sys.version_info[1] >= 4:
ABC = abc.ABC
@@ -31,11 +32,18 @@ def find_key(dic, val):
"""return the key of dictionary dic given the value"""
return [k for k, v in dic.items() if v == val][0]
-class CCPiBaseClass(object):
+class CCPiBaseClass(ABC):
def __init__(self, **kwargs):
self.acceptedInputKeywords = []
self.pars = {}
self.debug = True
+ # add keyworded arguments as accepted input keywords and add to the
+ # parameters
+ for key, value in kwargs.items():
+ self.acceptedInputKeywords.append(key)
+ #print ("key {0}".format(key))
+ #self.setParameter(key.__name__=value)
+ self.setParameter(**{key:value})
def setParameter(self, **kwargs):
'''set named parameter for the reconstructor engine
@@ -70,8 +78,8 @@ class CCPiBaseClass(object):
if self.debug:
print ("{0}: {1}".format(self.__class__.__name__, msg))
-class DataSet(ABC):
- '''Abstract class to hold data'''
+class DataSet():
+ '''Generic class to hold data'''
def __init__ (self, array, deep_copy=True, dimension_labels=None,
**kwargs):
@@ -88,11 +96,14 @@ class DataSet(ABC):
else:
for i in range(self.number_of_dimensions):
self.dimension_labels[i] = 'dimension_{0:02}'.format(i)
-
- if deep_copy:
- self.array = array[:]
+
+ if type(array) == numpy.ndarray:
+ if deep_copy:
+ self.array = array[:]
+ else:
+ self.array = array
else:
- self.array = array
+ raise TypeError('Array must be NumpyArray')
def as_array(self, dimensions=None):
'''Returns the DataSet as Numpy Array
@@ -163,11 +174,181 @@ class DataSet(ABC):
cleaned = numpy.transpose(cleaned, axes).copy()
return DataSet(cleaned , True, dimensions)
+
+ def fill(self, array):
+ '''fills the internal numpy array with the one provided'''
+ if numpy.shape(array) != numpy.shape(self.array):
+ raise ValueError('Cannot fill with the provided array.' + \
+ 'Expecting {0} got {1}'.format(
+ numpy.shape(self.array),
+ numpy.shape(array)))
+ self.array = array[:]
+
+
+class SliceData(DataSet):
+ '''DataSet for holding 2D images'''
+ def __init__(self, array, deep_copy=True, dimension_labels=None,
+ **kwargs):
+
+ if type(array) == DataSet:
+ # if the array is a DataSet get the info from there
+ if array.number_of_dimensions != 2:
+ raise ValueError('Number of dimensions are != 2: {0}'\
+ .format(array.number_of_dimensions))
+
+ DataSet.__init__(self, array.as_array(), deep_copy,
+ array.dimension_labels, **kwargs)
+ elif type(array) == numpy.ndarray:
+ if dimension_labels is None:
+ dimension_labels = ['horizontal_x' , 'horizontal_y' , 'vertical']
+ shape = numpy.shape(array)
+ ndims = len(shape)
+ if ndims != 3:
+ raise ValueError('Number of dimensions are != 2: {0}'.format(ndims))
+
+ DataSet.__init__(self, array, deep_copy, dimension_labels, **kwargs)
+
+ # Metadata
+ self.origin = [0,0]
+ self.spacing = [1,1]
+
+ # load metadata from kwargs if present
+ for key, value in kwargs.items():
+ if key == 'origin' :
+ if type(value) == list and len (value) == 2:
+ self.origin = value
+ if key == 'spacing' :
+ if type(value) == list and len (value) == 2:
+ self.spacing = value
+ def rotate(self, center_of_rotation, angle):
+ pass
+
+
+class VolumeData(DataSet):
+ '''DataSet for holding 3D images'''
+ def __init__(self, array, deep_copy=True, dimension_labels=None,
+ **kwargs):
+ if type(array) == DataSet:
+ # if the array is a DataSet get the info from there
+ if array.number_of_dimensions != 3:
+ raise ValueError('Number of dimensions are != 3: {0}'\
+ .format(array.number_of_dimensions))
+ DataSet.__init__(self, array.as_array(), deep_copy,
+ array.dimension_labels, **kwargs)
+ elif type(array) == numpy.ndarray:
+ if dimension_labels is None:
+ dimension_labels = ['horizontal_x' , 'horizontal_y' , 'vertical']
+ shape = numpy.shape(array)
+ ndims = len(shape)
+ if ndims != 3:
+ raise ValueError('Number of dimensions are != 3: {0}'.format(ndims))
+ DataSet.__init__(self, array, deep_copy, dimension_labels, **kwargs)
+
+ # Metadata
+ self.origin = [0,0,0]
+ self.spacing = [1,1,1]
+
+ # load metadata from kwargs if present
+ for key, value in kwargs.items():
+ if key == 'origin' :
+ if type(value) == list and len (value) == 3:
+ self.origin = value
+ if key == 'spacing' :
+ if type(value) == list and len (value) == 3:
+ self.spacing = value
+
+class DataSetProcessor(CCPiBaseClass):
+ '''Abstract class for a DataSetProcessor'''
+
+ def __init__(self, number_of_inputs, number_of_outputs, **kwargs):
+ kwargs['number_of_inputs'] = number_of_inputs
+ kwargs['number_of_outputs'] = number_of_outputs
+
+ CCPiBaseClass.__init__(self, **kwargs)
+
+
+
+ def setInput(self, **inData):
+ '''set the input data for the Processor
+
+ this calls the setParameter method'''
+ self.setParameter(**inData)
+
+ def getOutput(self):
+ raise NotImplementedError('The getOutput method is not implemented!')
+
+ def apply(self):
+ raise NotImplementedError('The apply method is not implemented!')
+
+
+
+class AX(DataSetProcessor):
+ '''Example DataSetProcessor
+ The AXPY routines perform a vector multiplication operation defined as
+
+ y := a*x
+ where:
+
+ a is a scalar
+
+ x a DataSet.
+ '''
+
+ def __init__(self, scalar, input_dataset):
+ kwargs = {'scalar':scalar,
+ 'input_dataset':input_dataset,
+ 'output_dataset': None}
+ DataSetProcessor.__init__(self, 2, 1, **kwargs)
+
+
+
+ def apply(self):
+ a, x = self.getParameter(['scalar' , 'input_dataset' ])
+
+ y = DataSet( a * x.as_array() , True,
+ dimension_labels=x.dimension_labels )
+ self.setParameter(output_dataset=y)
+
+ def getOutput(self):
+ return self.getParameter( 'output_dataset' )
+
+
+class PixelByPixelDataSetProcessor(DataSetProcessor):
+ '''Example DataSetProcessor
+
+ This processor applies a python function to each pixel of the DataSet
+
+ f is a python function
+
+ x a DataSet.
+ '''
+
+ def __init__(self, pyfunc, input_dataset):
+ kwargs = {'pyfunc':pyfunc,
+ 'input_dataset':input_dataset,
+ 'output_dataset': None}
+ DataSetProcessor.__init__(self, 2, 1, **kwargs)
+
+
+
+ def apply(self):
+ pyfunc, x = self.getParameter(['pyfunc' , 'input_dataset' ])
+
+ eval_func = numpy.frompyfunc(pyfunc,1,1)
+
+
+ y = DataSet( eval_func( x.as_array() ) , True,
+ dimension_labels=x.dimension_labels )
+ self.setParameter(output_dataset=y)
+
+ def getOutput(self):
+ return self.getParameter( 'output_dataset' )
+
if __name__ == '__main__':
shape = (2,3,4,5)
size = shape[0]
@@ -181,10 +362,42 @@ if __name__ == '__main__':
b = ds.subset( subset )
print ("b label {0} shape {1}".format(b.dimension_labels,
numpy.shape(b.as_array())))
- c = ds.as_array(['Z','W'])
+ c = ds.subset(['Z','W','X'])
+ # Create a VolumeData sharing the array with c
+ volume0 = VolumeData(c.as_array(), False, dimensions = c.dimension_labels)
+ volume1 = VolumeData(c, False)
+ print ("volume0 {0} volume1 {1}".format(id(volume0.array),
+ id(volume1.array)))
+
+ # Create a VolumeData copying the array from c
+ volume2 = VolumeData(c.as_array(), dimensions = c.dimension_labels)
+ volume3 = VolumeData(c)
+
+ print ("volume2 {0} volume3 {1}".format(id(volume2.array),
+ id(volume3.array)))
-
+ # single number DataSet
+ sn = DataSet(numpy.asarray([1]))
+
+ ax = AX(scalar = 2 , input_dataset=c)
+ ax.apply()
+ print ("ax in {0} out {1}".format(c.as_array().flatten(),
+ ax.getOutput().as_array().flatten()))
+ axm = AX(scalar = 0.5 , input_dataset=ax.getOutput())
+ axm.apply()
+ print ("axm in {0} out {1}".format(c.as_array(), axm.getOutput().as_array()))
+
+ # create a PixelByPixelDataSetProcessor
+
+ #define a python function which will take only one input (the pixel value)
+ pyfunc = lambda x: -x if x > 20 else x
+ clip = PixelByPixelDataSetProcessor(pyfunc,c)
+ clip.apply()
+
+ print ("clip in {0} out {1}".format(c.as_array(), clip.getOutput().as_array()))
+
+
\ No newline at end of file
diff --git a/Wrappers/Python/conda-recipe/bld.bat b/Wrappers/Python/conda-recipe/bld.bat
new file mode 100644
index 0000000..2f090eb
--- /dev/null
+++ b/Wrappers/Python/conda-recipe/bld.bat
@@ -0,0 +1,9 @@
+IF NOT DEFINED CIL_VERSION (
+ECHO CIL_VERSION Not Defined.
+exit 1
+)
+
+ROBOCOPY /E "%RECIPE_DIR%\.." "%SRC_DIR%"
+
+%PYTHON% setup.py build_py
+if errorlevel 1 exit 1
diff --git a/Wrappers/Python/conda-recipe/build.sh b/Wrappers/Python/conda-recipe/build.sh
new file mode 100644
index 0000000..0c6ad30
--- /dev/null
+++ b/Wrappers/Python/conda-recipe/build.sh
@@ -0,0 +1,9 @@
+if [ -z "$CIL_VERSION" ]; then
+ echo "Need to set CIL_VERSION"
+ exit 1
+fi
+mkdir ${SRC_DIR}/ccpi
+cp -r "${RECIPE_DIR}/../../../" ${SRC_DIR}/ccpi
+
+cd ${SRC_DIR}/ccpi/Wrappers/python
+$PYTHON setup.py install
diff --git a/Wrappers/Python/conda-recipe/meta.yaml b/Wrappers/Python/conda-recipe/meta.yaml
new file mode 100644
index 0000000..fc5f09d
--- /dev/null
+++ b/Wrappers/Python/conda-recipe/meta.yaml
@@ -0,0 +1,26 @@
+package:
+ name: ccpi-common
+ version: {{ environ['CIL_VERSION'] }}
+
+
+build:
+ preserve_egg_dir: False
+ script_env:
+ - CIL_VERSION
+# number: 0
+
+requirements:
+ build:
+ - python
+ - numpy
+ - setuptools
+
+ run:
+ - python
+ - numpy
+ - scipy
+
+about:
+ home: http://www.ccpi.ac.uk
+ license: BSD license
+ summary: 'CCPi Toolbox'
diff --git a/Wrappers/Python/setup.py b/Wrappers/Python/setup.py
index 31fe195..8ee6b58 100644
--- a/Wrappers/Python/setup.py
+++ b/Wrappers/Python/setup.py
@@ -1,13 +1,25 @@
#!/usr/bin/env python
+# -*- 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
-import setuptools
-from distutils.core import setup
-from distutils.extension import Extension
-from Cython.Distutils import build_ext
+# 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 distutils.core import setup
import os
-import numpy
-import platform
import sys
cil_version=os.environ['CIL_VERSION']
@@ -15,62 +27,35 @@ if cil_version == '':
print("Please set the environmental variable CIL_VERSION")
sys.exit(1)
-library_include_path = []
-library_lib_path = []
-try:
- library_include_path = [ os.environ['LIBRARY_INC'] ]
- library_lib_path = [ os.environ['LIBRARY_LIB'] ]
-except:
- if platform.system() == 'Windows':
- pass
- else:
- try:
- library_include_path = [ os.environ['PREFIX']+'/include' ]
- library_lib_path = [ os.environ['PREFiX']+'/lib' ]
- except:
- pass
- pass
-extra_include_dirs = [numpy.get_include()]
-extra_library_dirs = []
-extra_compile_args = []
-extra_link_args = []
-extra_libraries = []
+cil_version=os.environ['CIL_VERSION']
+if cil_version == '':
+ print("Please set the environmental variable CIL_VERSION")
+ sys.exit(1)
+
+setup(
+ name="ccpi-common",
+ version=cil_version,
+ packages=['ccpi'],
-if platform.system() == 'Windows':
- extra_compile_args += ['/DWIN32','/EHsc','/DBOOST_ALL_NO_LIB',
- '/openmp','/DHAS_TIFF','/DCCPiReconstructionIterative_EXPORTS']
- extra_include_dirs += ["..\\..\\Core\\src\\","..\\..\\Core\\src\\Algorithms","..\\..\\Core\\src\\Readers", "."]
- extra_include_dirs += library_include_path
- extra_library_dirs += library_lib_path
- extra_libraries += ['tiff' , 'cilrec']
- if sys.version_info.major == 3 :
- extra_libraries += ['boost_python3-vc140-mt-1_64', 'boost_numpy3-vc140-mt-1_64']
- else:
- extra_libraries += ['boost_python-vc90-mt-1_64', 'boost_numpy-vc90-mt-1_64']
-else:
- extra_include_dirs += ["../../Core/src/","../../Core/src/Algorithms","../../Core/src/Readers", "."]
- extra_include_dirs += library_include_path
- extra_compile_args += ['-fopenmp','-O2', '-funsigned-char', '-Wall','-Wl,--no-undefined','-DHAS_TIFF','-DCCPiReconstructionIterative_EXPORTS']
- extra_libraries += ['tiff' , 'cilrec']
- if sys.version_info.major == 3 :
- extra_libraries += ['boost_python3', 'boost_numpy3','gomp']
- else:
- extra_libraries += ['boost_python', 'boost_numpy','gomp']
+ # Project uses reStructuredText, so ensure that the docutils get
+ # installed or upgraded on the target machine
+ #install_requires=['docutils>=0.3'],
+# package_data={
+# # If any package contains *.txt or *.rst files, include them:
+# '': ['*.txt', '*.rst'],
+# # And include any *.msg files found in the 'hello' package, too:
+# 'hello': ['*.msg'],
+# },
+ # zip_safe = False,
-setup(
- name='ccpi-reconstruction',
- description='This is a CCPi Core Imaging Library package for Iterative Reconstruction codes',
- version=cil_version,
- cmdclass = {'build_ext': build_ext},
- ext_modules = [Extension("ccpi.reconstruction.parallelbeam",
- sources=[ "src/diamond_module.cpp",
- "src/diamond_wrapper.cpp"],
- include_dirs=extra_include_dirs, library_dirs=extra_library_dirs, extra_compile_args=extra_compile_args, libraries=extra_libraries, extra_link_args=extra_link_args ),
- Extension("ccpi.reconstruction.conebeam",
- sources=[ "src/conebeam_module.cpp",
- "src/conebeam_wrapper.cpp"],
- include_dirs=extra_include_dirs, library_dirs=extra_library_dirs, extra_compile_args=extra_compile_args, libraries=extra_libraries ) ],
- zip_safe = False,
- packages = {'ccpi','ccpi.reconstruction'}
+ # metadata for upload to PyPI
+ author="Edoardo Pasca",
+ author_email="edoardo.pasca@stfc.ac.uk",
+ description='CCPi Core Imaging Library - Python Framework Module',
+ license="Apache v2.0",
+ keywords="Python Framework",
+ url="http://www.ccpi.ac.uk", # project home page, if any
+
+ # could also include long_description, download_url, classifiers, etc.
)