From 18b6d25f7e4f0943b3592f3bb4f6ca5ed9c285d3 Mon Sep 17 00:00:00 2001
From: "Daniel M. Pelt" <D.M.Pelt@cwi.nl>
Date: Fri, 19 Jun 2015 22:28:06 +0200
Subject: Add support for Python algorithm plugins

---
 python/astra/__init__.py  |  1 +
 python/astra/plugin.py    | 95 +++++++++++++++++++++++++++++++++++++++++++++++
 python/astra/plugin_c.pyx | 59 +++++++++++++++++++++++++++++
 python/astra/utils.pyx    | 72 +++--------------------------------
 4 files changed, 160 insertions(+), 67 deletions(-)
 create mode 100644 python/astra/plugin.py
 create mode 100644 python/astra/plugin_c.pyx

(limited to 'python/astra')

diff --git a/python/astra/__init__.py b/python/astra/__init__.py
index 6c15d30..10ed74d 100644
--- a/python/astra/__init__.py
+++ b/python/astra/__init__.py
@@ -34,6 +34,7 @@ from . import algorithm
 from . import projector
 from . import projector3d
 from . import matrix
+from . import plugin
 from . import log
 from .optomo import OpTomo
 
diff --git a/python/astra/plugin.py b/python/astra/plugin.py
new file mode 100644
index 0000000..ccdb2cb
--- /dev/null
+++ b/python/astra/plugin.py
@@ -0,0 +1,95 @@
+#-----------------------------------------------------------------------
+#Copyright 2013 Centrum Wiskunde & Informatica, Amsterdam
+#
+#Author: Daniel M. Pelt
+#Contact: D.M.Pelt@cwi.nl
+#Website: http://dmpelt.github.io/pyastratoolbox/
+#
+#
+#This file is part of the Python interface to the
+#All Scale Tomographic Reconstruction Antwerp Toolbox ("ASTRA Toolbox").
+#
+#The Python interface to the ASTRA Toolbox is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+#
+#The Python interface to the ASTRA Toolbox is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with the Python interface to the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
+#
+#-----------------------------------------------------------------------
+
+from . import plugin_c as p
+from . import log
+
+class base(object):
+
+    def astra_init(self, cfg):
+        try:
+            try:
+                req = self.required_options
+            except AttributeError:
+                log.warn("Plugin '" + self.__class__.__name__ + "' does not specify required options")
+                req = {}
+
+            try:
+                opt = self.optional_options
+            except AttributeError:
+                log.warn("Plugin '" + self.__class__.__name__ + "' does not specify optional options")
+                opt = {}
+
+            try:
+                optDict = cfg['options']
+            except KeyError:
+                optDict = {}
+
+            cfgKeys = set(optDict.keys())
+            reqKeys = set(req)
+            optKeys = set(opt)
+
+            if not reqKeys.issubset(cfgKeys):
+                for key in reqKeys.difference(cfgKeys):
+                    log.error("Required option '" + key + "' for plugin '" + self.__class__.__name__ + "' not specified")
+                raise ValueError("Missing required options")
+
+            if not cfgKeys.issubset(reqKeys | optKeys):
+                log.warn(self.__class__.__name__ + ": unused configuration option: " + str(list(cfgKeys.difference(reqKeys | optKeys))))
+
+            self.initialize(cfg)
+        except Exception as e:
+            log.error(str(e))
+            raise
+
+def register(name, className):
+    """Register plugin with ASTRA.
+    
+    :param name: Plugin name to register
+    :type name: :class:`str`
+    :param className: Class name or class object to register
+    :type className: :class:`str` or :class:`class`
+    
+    """
+    p.register(name,className)
+
+def get_registered():
+    """Get dictionary of registered plugins.
+    
+    :returns: :class:`dict` -- Registered plugins.
+    
+    """
+    return p.get_registered()
+
+def get_help(name):
+    """Get help for registered plugin.
+    
+    :param name: Plugin name to get help for
+    :type name: :class:`str`
+    :returns: :class:`str` -- Help string (docstring).
+    
+    """
+    return p.get_help(name)
\ No newline at end of file
diff --git a/python/astra/plugin_c.pyx b/python/astra/plugin_c.pyx
new file mode 100644
index 0000000..91b3cd5
--- /dev/null
+++ b/python/astra/plugin_c.pyx
@@ -0,0 +1,59 @@
+#-----------------------------------------------------------------------
+#Copyright 2013 Centrum Wiskunde & Informatica, Amsterdam
+#
+#Author: Daniel M. Pelt
+#Contact: D.M.Pelt@cwi.nl
+#Website: http://dmpelt.github.io/pyastratoolbox/
+#
+#
+#This file is part of the Python interface to the
+#All Scale Tomographic Reconstruction Antwerp Toolbox ("ASTRA Toolbox").
+#
+#The Python interface to the ASTRA Toolbox is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+#
+#The Python interface to the ASTRA Toolbox is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with the Python interface to the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
+#
+#-----------------------------------------------------------------------
+# distutils: language = c++
+# distutils: libraries = astra
+
+import six
+import inspect
+
+from libcpp.string cimport string
+from libcpp cimport bool
+
+cdef CPluginAlgorithmFactory *fact = getSingletonPtr()
+
+from . import utils
+
+cdef extern from "astra/PluginAlgorithm.h" namespace "astra":
+    cdef cppclass CPluginAlgorithmFactory:
+        bool registerPlugin(string name, string className)
+        bool registerPluginClass(string name, object className)
+        object getRegistered()
+        string getHelp(string name)
+
+cdef extern from "astra/PluginAlgorithm.h" namespace "astra::CPluginAlgorithmFactory":
+    cdef CPluginAlgorithmFactory* getSingletonPtr()
+
+def register(name, className):
+    if inspect.isclass(className):
+        fact.registerPluginClass(six.b(name), className)
+    else:
+        fact.registerPlugin(six.b(name), six.b(className))
+
+def get_registered():
+    return fact.getRegistered()
+
+def get_help(name):
+    return utils.wrap_from_bytes(fact.getHelp(six.b(name)))
diff --git a/python/astra/utils.pyx b/python/astra/utils.pyx
index ddb37aa..3746b8e 100644
--- a/python/astra/utils.pyx
+++ b/python/astra/utils.pyx
@@ -30,7 +30,6 @@ cimport numpy as np
 import numpy as np
 import six
 from libcpp.string cimport string
-from libcpp.list cimport list
 from libcpp.vector cimport vector
 from cython.operator cimport dereference as deref, preincrement as inc
 from cpython.version cimport PY_MAJOR_VERSION
@@ -40,6 +39,9 @@ from .PyXMLDocument cimport XMLDocument
 from .PyXMLDocument cimport XMLNode
 from .PyIncludes cimport *
 
+cdef extern from "astra/PluginAlgorithm.h" namespace "astra":
+    object XMLNode2dict(XMLNode)
+
 
 cdef Config * dictToConfig(string rootname, dc):
     cdef Config * cfg = new Config()
@@ -91,6 +93,8 @@ cdef void readDict(XMLNode root, _dc):
     dc = convert_item(_dc)
     for item in dc:
         val = dc[item]
+        if isinstance(val, list):
+            val = np.array(val,dtype=np.float64)
         if isinstance(val, np.ndarray):
             if val.size == 0:
                 break
@@ -142,69 +146,3 @@ cdef void readOptions(XMLNode node, dc):
 cdef configToDict(Config *cfg):
     return XMLNode2dict(cfg.self)
 
-def castString3(input):
-    return input.decode('utf-8')
-
-def castString2(input):
-    return input
-
-if six.PY3:
-    castString = castString3
-else:
-    castString = castString2
-
-def stringToPythonValue(inputIn):
-    input = castString(inputIn)
-    # matrix
-    if ';' in input:
-        row_strings = input.split(';')
-        col_strings = row_strings[0].split(',')
-        nRows = len(row_strings)
-        nCols = len(col_strings)
-
-        out = np.empty((nRows,nCols))
-        for ridx, row in enumerate(row_strings):
-            col_strings = row.split(',')
-            for cidx, col in enumerate(col_strings):
-                out[ridx,cidx] = float(col)
-        return out
-
-    # vector
-    if ',' in input:
-        items = input.split(',')
-        out = np.empty(len(items))
-        for idx,item in enumerate(items):
-            out[idx] = float(item)
-        return out
-
-    try:
-        # integer
-        return int(input)
-    except ValueError:
-        try:
-            #float
-            return float(input)
-        except ValueError:
-            # string
-            return str(input)
-
-
-cdef XMLNode2dict(XMLNode node):
-    cdef XMLNode subnode
-    cdef list[XMLNode] nodes
-    cdef list[XMLNode].iterator it
-    dct = {}
-    opts = {}
-    if node.hasAttribute(six.b('type')):
-        dct['type'] = castString(node.getAttribute(six.b('type')))
-    nodes = node.getNodes()
-    it = nodes.begin()
-    while it != nodes.end():
-        subnode = deref(it)
-        if castString(subnode.getName())=="Option":
-            opts[castString(subnode.getAttribute('key'))] = stringToPythonValue(subnode.getAttribute('value'))
-        else:
-            dct[castString(subnode.getName())] = stringToPythonValue(subnode.getContent())
-        inc(it)
-    if len(opts)>0: dct['options'] = opts
-    return dct
-- 
cgit v1.2.3