itktemplate.py
来自「DTMK软件开发包,此为开源软件,是一款很好的医学图像开发资源.」· Python 代码 · 共 407 行
PY
407 行
import types
import inspect
import sys
import os
import itkConfig
from itkTypes import itkCType
def registerNoTpl(name, cl):
"""Register a class without template
It can seem not useful to register classes without template (and it wasn't
useful until the SmartPointer template was generated), but those classes
can be used as template argument of classes with template.
"""
itkTemplate.__templates__[normalizeName(name)] = cl
def normalizeName(name):
"""Normalize the class name to remove ambiguity
This function removes the white spaces in the name, and also
remove the pointer declaration "*" (it have no sense in python) """
name = name.replace(" ","")
name = name.replace("*","")
return name
class itkTemplate(object):
"""This class manage access to avaible template arguments of C++ class
There is 2 ways to access types:
- with a dict interface. The user can manipulate template parameters nearly
as it do in c++, excepted that the available parameters sets are choosed at
build time. It is also possible, with the dict interface, to explore the
available parameters sets.
- with object attributes. The user can easily find the available parameters
sets by pressing tab in interperter like ipython
"""
__templates__ = {}
__class_to_template__ = {}
__named_templates__ = {}
__doxygen_root__ = itkConfig.doxygen_root
def __new__(cls, name):
# Singleton pattern: we only make a single instance of any Template of a
# given name. If we have already made the instance, just return it as-is.
if not cls.__named_templates__.has_key(name):
new_instance = object.__new__(cls)
new_instance.__name__ = name
new_instance.__template__ = {}
cls.__named_templates__[name] = new_instance
return cls.__named_templates__[name]
def __add__(self, paramSetString, cl):
"""add a new argument set and the resulting class to the template
paramSetString is the c++ string which define the parameters set
cl is the class which correspond to the couple template-argument set
"""
# recreate the full name and normalize it to avoid ambiguity
normalizedFullName = normalizeName(self.__name__+"<"+paramSetString+">")
# the full class should not be already registered. If it is, there is a problem
# somewhere so warn the user so he can fix the problem
if itkTemplate.__templates__.has_key( normalizedFullName ) :
print >>sys.stderr, "Warning: templated class already defined '%s'" % normalizedFullName
# register the class
itkTemplate.__templates__[normalizedFullName] = cl
# __find_param__ will parse the paramSetString and produce a list of the same
# parameters transformed in corresponding python classes.
# we transform this list in tuple to make it usable as key of the dict
param = tuple( self.__find_param__( paramSetString ) )
# once again, warn the user if the tuple of parameter is already defined
# so he can fix the problem
if self.__template__.has_key( param ) :
print >>sys.stderr,"Warning: template %s\n already defined as %s\n is redefined as %s" % (normalizedFullName, self.__template__[param], cl)
# and register the parameter tuple
self.__template__[param] = cl
# add in __class_to_template__ dictionary
itkTemplate.__class_to_template__[cl] = (self, param)
# now populate the template
# 2 cases:
# - the template is a SmartPointer. In that case, the attribute name will be the
# full real name of the class without the itk prefix and _Pointer suffix
# - the template is not a SmartPointer. In that case, we keep only the end of the
# real class name which is a short string discribing the template arguments
# (for example IUC2)
if cl.__name__.endswith("_Pointer") :
# it's a SmartPointer
attributeName = cl.__name__[len("itk"):-len("_Pointer")]
else :
# it's not a SmartPointer
# we need to now the size of the name to keep only the suffix
# short name does not contain :: and nested namespace
# itk::Numerics::Sample -> itkSample
import re
shortNameSize = len(re.sub(r':.*:', '', self.__name__))
attributeName = cl.__name__[shortNameSize:]
if attributeName.isdigit() :
# the attribute name can't be a number
# add a single undescore before it to build a valid name
attributeName = "_" + attributeName
# add the attribute to this object
self.__dict__[attributeName] = cl
# now replace New method by a custom one
if hasattr(cl, 'New') :
# the new method needs to call the old one, so keep it with another (hidden) name
cl.__New_orig__ = cl.New
cl.New = types.MethodType(New, cl)
def __find_param__(self, paramSetString):
"""find the parameters of the template
paramSetString is the c++ string which define the parameters set
__find_param__ returns a list of itk classes, itkCType, and/or numbers
which correspond to the parameters described in paramSetString.
The parameters MUST have been registered before calling this method,
or __find_param__ will return a string and not the wanted object, and
will display a warning. Registration order is important.
This method is not static only to be able to display the template name
in the warning
"""
# split the string in a list of parameters
paramStrings = []
inner = 0
part = paramSetString.split(",")
for elt in part :
if inner == 0 :
paramStrings.append( elt )
else:
paramStrings[-1] += "," + elt
inner += elt.count("<") - elt.count(">")
# convert all string parameters into classes (if possible)
parameters = []
for param in paramStrings:
# the parameter need to be normalized several time below
# do it once here
param = param.strip()
paramNorm = normalizeName(param)
if itkTemplate.__templates__.has_key( paramNorm ) :
# the parameter is registered.
# just get the really class form the dictionary
param = itkTemplate.__templates__[paramNorm]
elif itkCType.GetCType( param ) :
# the parameter is a c type
# just get the itkCtype instance
param = itkCType.GetCType( param )
elif paramNorm.isdigit() :
# the parameter is a number
# convert the string to a number !
param = int(param)
else :
# unable to convert the parameter
# use it without changes, but display a warning message, to incite
# developer to fix the problem
print >>sys.stderr,"Warning: Unknown parameter '%s' in template '%s'" % (param, self.__name__)
parameters.append( param )
return parameters
def __getitem__(self, parameters):
"""return the class which correspond to the given template parameters
parameters can be:
- a single parameter (Ex: itk.Index[2])
- a list of element (Ex: itk.Image[itk.UC, 2])
"""
if type(parameters) != types.TupleType and type(parameters) != types.ListType :
# parameters is a single element.
# include it in a list to manage the 2 cases in the same way
parameters = [parameters]
cleanParameters = []
for param in parameters:
# In the case of itk SmartPointer, get the pointed object class
try: param = param.GetPointer()
except:pass
# In the case where elt is a pointer (<className>Ptr), the real class
# can be found in the pointer class dictionary
try: param = param.__dict__[ param.__class__ ]
except: pass
# In the case of itk class instance, get the class
if not inspect.isclass( param ) and param.__class__.__name__[:3] == 'itk' and param.__class__.__name__!= "itkCType" :
param = param.__class__
# append the parameter to the list. If it's not a supported type, it is
# not in the dictionary and we will raise an exception below
cleanParameters.append( param )
try:
return(self.__template__[tuple(cleanParameters)])
except:
raise KeyError, 'itkTemplate : No template %s for the %s class' % (str(parameters), self.__name__)
def __repr__(self):
return '<itkTemplate %s>' % self.__name__
# support for reading doxygen man pages to produce __doc__ strings
def __getattribute__(self, attr):
if attr == '__doc__' and itkTemplate.__doxygen_root__ != "" and self.__name__.startswith('itk'):
try:
import commands
doxyname = self.__name__.replace("::", "_")
man_path = "%s/man3/%s.3" %(itkTemplate.__doxygen_root__, doxyname)
bzman_path = "%s/man3/%s.3.bz2" %(itkTemplate.__doxygen_root__, doxyname)
if os.path.exists(bzman_path):
return commands.getoutput("bunzip2 --stdout '"+bzman_path+"' | groff -mandoc -Tascii -c")
elif os.path.exists(man_path):
# Use groff here instead of man because man dies when it is passed paths with spaces (!)
# groff does not.
return commands.getoutput("groff -mandoc -Tascii -c '" + man_path +"'")
else:
return "Cannot find man page for %s in %s." %(self.__name__, itkTemplate.__doxygen_root__)
except Exception, e:
return "Cannot display man page for %s due to exception: %s." %(self.__name__, e)
else:
return object.__getattribute__(self, attr)
def keys(self):
return self.__template__.keys()
# everything after this comment is for dict interface
# and is a copy/paste from DictMixin
# only methods to edit dictionary are not there
def __iter__(self):
for k in self.keys():
yield k
def has_key(self,key):
try:
value=self[key]
except KeyError:
return False
return True
def __contains__(self,key):
return self.has_key(key)
# third level takes advantage of second level definitions
def iteritems(self):
for k in self:
yield (k,self[k])
def iterkeys(self):
return self.__iter__()
# fourth level uses definitions from lower levels
def itervalues(self):
for _,v in self.iteritems():
yield v
def values(self):
return [v for _,v in self.iteritems()]
def items(self):
return list(self.iteritems())
def get(self,key,default=None):
try:
return self[key]
except KeyError:
return default
def __len__(self):
return len(self.keys())
# create a new New function which accepts parameters
def New(self, *args, **kargs) :
import sys
newItkObject = self.__New_orig__()
# try to get the images from the filters in args
args = [image(arg) for arg in args]
# args without name are filter used to set input image
#
# count SetInput calls to call SetInput, SetInput2, SetInput3, ...
# usefull with filter which take 2 input (or more) like SubstractImageFiler
# Ex: substract image2.png to image1.png and save the result in result.png
# r1 = itk.ImageFileReader.US2.New(FileName='image1.png')
# r2 = itk.ImageFileReader.US2.New(FileName='image2.png')
# s = itk.SubtractImageFilter.US2US2US2.New(r1, r2)
# itk.ImageFileWriter.US2.New(s, FileName='result.png').Update()
try :
for setInputNb, arg in enumerate(args) :
methodName = 'SetInput%i' % (setInputNb+1)
if methodName in dir(newItkObject) :
# first try to use methods called SetInput1, SetInput2, ...
# those method should have more chances to work in case of multiple
# input types
getattr(newItkObject, methodName)(arg)
else :
# no method called SetInput?
# try with the standard SetInput(nb, input)
newItkObject.SetInput(setInputNb, arg)
except TypeError, e :
# the exception have (at least) to possible reasons:
# + the filter don't take the input number as first argument
# + arg is an object of wrong type
#
# if it's not the first input, re-raise the exception
if setInputNb != 0 :
raise e
# it's the first input, try to use the SetInput() method without input number
newItkObject.SetInput(args[0])
# but raise an exception if there is more than 1 argument
if len(args) > 1 :
raise TypeError('Object accept only 1 input.')
except AttributeError :
# There is no SetInput() method, try SetImage
# but before, check the number of inputs
if len(args) > 1 :
raise TypeError('Object accept only 1 input.')
methodList = ['SetImage', 'SetInputImage']
methodName = None
for m in methodList:
if m in dir(newItkObject):
methodName = m
if methodName :
getattr(newItkObject, methodName)(args[0])
else:
raise AttributeError('No method found to set the input.')
# named args : name is the function name, value is argument(s)
for attribName, value in kargs.iteritems() :
# use Set as prefix. It allow to use a shorter and more intuitive
# call (Ex: itk.ImageFileReader.UC2.New(FileName='image.png')) than with the
# full name (Ex: itk.ImageFileReader.UC2.New(SetFileName='image.png'))
if attribName != "auto_progress" :
attrib = getattr(newItkObject, 'Set' + attribName)
attrib(value)
# now, try to add observer to display progress
if "auto_progress" in kargs.keys() :
if kargs["auto_progress"] in [True, 1] :
import itk
callback = itk.terminal_progress_callback
elif kargs["auto_progress"] == 2 :
import itk
callback = itk.simple_progress_callback
else :
callback = None
elif itkConfig.ProgressCallback :
callback = itkConfig.ProgressCallback
else :
callback = None
if callback :
import itk
try :
def progress() :
# newItkObject and callback are kept referenced with a closure
callback(self.__name__, newItkObject.GetProgress())
command = itk.PyCommand.New()
command.SetCommandCallable(progress)
newItkObject.AddObserver(itk.ProgressEvent(), command.GetPointer())
except :
# it seems that something goes wrong...
# as this feature is designed for prototyping, it's not really a problem
# if an object don't have progress reporter, so adding reporter can silently fail
pass
if itkConfig.NotInPlace :
if "SetInPlace" in dir(newItkObject) :
newItkObject.SetInPlace( False )
return newItkObject
def image(input) :
try :
img = input.GetOutput()
except AttributeError :
img = input
try :
img = img.GetPointer()
except AttributeError :
pass
return img
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?