changeset 65:4da5ce3468b4

new wrapped functions: CreateImageFromBuffer(), DicomInstance.GetInstanceData() and Image.GetImageBuffer()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 11 Jun 2021 12:47:18 +0200
parents 091fb1903bfc
children 6fc445793796
files CodeAnalysis/ParseOrthancSDK.py NEWS Sources/Autogenerated/sdk_OrthancPluginDicomInstance.impl.h Sources/Autogenerated/sdk_OrthancPluginImage.impl.h Sources/Plugin.cpp
diffstat 5 files changed, 176 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/CodeAnalysis/ParseOrthancSDK.py	Fri Jun 11 09:03:43 2021 +0200
+++ b/CodeAnalysis/ParseOrthancSDK.py	Fri Jun 11 12:47:18 2021 +0200
@@ -35,11 +35,11 @@
 ## implemented (not autogenerated)
 ##
 
-CUSTOM_FUNCTIONS = {    
+CUSTOM_FUNCTIONS = set([
     'OrthancPluginCreateDicom',
+    'OrthancPluginCreateImageAccessor',                # Replaced by "orthanc.CreateImageFromBuffer()"
     'OrthancPluginFreeMemoryBuffer',
     'OrthancPluginFreeString',
-    'OrthancPluginGetFindQueryTag',
     'OrthancPluginRegisterFindCallback',
     'OrthancPluginRegisterIncomingHttpRequestFilter',  # Implemented through v2
     'OrthancPluginRegisterIncomingHttpRequestFilter2',
@@ -49,8 +49,7 @@
     'OrthancPluginRegisterRestCallback',               # Implemented using OrthancPlugins::RegisterRestCallback
     'OrthancPluginRegisterRestCallbackNoLock',         # Implemented using OrthancPlugins::RegisterRestCallback
     'OrthancPluginRegisterWorklistCallback',
-    'OrthancPluginWorklistAddAnswer',
-}
+])
 
 CUSTOM_METHODS = [
     {
@@ -71,8 +70,23 @@
         'implementation' : 'WorklistAddAnswer',
         'sdk_function' : 'OrthancPluginWorklistAddAnswer',
     },
+    {
+        'class_name' : 'OrthancPluginDicomInstance',
+        'method_name' : 'GetInstanceData',
+        'implementation' : 'GetInstanceData',
+        'sdk_function' : 'OrthancPluginGetInstanceData',
+    },    
+    {
+        'class_name' : 'OrthancPluginImage',
+        'method_name' : 'GetImageBuffer',
+        'implementation' : 'GetImageBuffer',
+        'sdk_function' : 'OrthancPluginGetImageBuffer',
+    },    
 ]
 
+for method in CUSTOM_METHODS:
+    CUSTOM_FUNCTIONS.add(method['sdk_function'])
+
 
 ##
 ## Parse the command-line arguments
@@ -393,7 +407,11 @@
             countAllFunctions += 1
             args = args[1:]
 
-            if not IsSupportedTargetType(node.result_type):
+            if node.spelling in CUSTOM_FUNCTIONS:
+                print('Ignoring custom function that is manually implemented: %s()' % node.spelling)
+                countSupportedFunctions += 1                
+
+            elif not IsSupportedTargetType(node.result_type):
                 print('*** UNSUPPORTED OUTPUT: %s' % node.spelling)
             
             elif (len(args) == 1 and
@@ -457,10 +475,6 @@
                 classes[className]['methods'].append(method)
                 countSupportedFunctions += 1
             
-            elif node.spelling in CUSTOM_FUNCTIONS:
-                print('Ignoring custom function that is manually implemented: %s()' % node.spelling)
-                countSupportedFunctions += 1                
-
             else:
                 print('*** UNSUPPORTED INPUT: %s' % node.spelling)
             
--- a/NEWS	Fri Jun 11 09:03:43 2021 +0200
+++ b/NEWS	Fri Jun 11 12:47:18 2021 +0200
@@ -3,8 +3,11 @@
 
 * New functions from the SDK wrapped in Python:
   - orthanc.CreateDicom()
+  - orthanc.CreateImageFromBuffer()
+  - orthanc.DicomInstance.GetInstanceData()
   - orthanc.FindQuery.GetFindQueryTagElement()
   - orthanc.FindQuery.GetFindQueryTagGroup()
+  - orthanc.Image.GetImageBuffer()
   - orthanc.RegisterFindCallback()
   - orthanc.RegisterMoveCallback()
   - orthanc.RegisterWorklistCallback()
--- a/Sources/Autogenerated/sdk_OrthancPluginDicomInstance.impl.h	Fri Jun 11 09:03:43 2021 +0200
+++ b/Sources/Autogenerated/sdk_OrthancPluginDicomInstance.impl.h	Fri Jun 11 12:47:18 2021 +0200
@@ -50,6 +50,8 @@
 
 
 // Forward declaration of the custom methods
+extern PyObject *GetInstanceData(
+  sdk_OrthancPluginDicomInstance_Object* self, PyObject *args);
 // End of forward declarations
 
 
@@ -96,6 +98,9 @@
   { "GetInstanceAdvancedJson",
     (PyCFunction) sdk_OrthancPluginDicomInstance_OrthancPluginGetInstanceAdvancedJson, METH_VARARGS,
     "Generated from C function OrthancPluginGetInstanceAdvancedJson()" },
+  { "GetInstanceData",
+    (PyCFunction) GetInstanceData, METH_VARARGS,
+    "Generated from C function OrthancPluginGetInstanceData()" },
   { NULL }  /* Sentinel */
 };
 
--- a/Sources/Autogenerated/sdk_OrthancPluginImage.impl.h	Fri Jun 11 09:03:43 2021 +0200
+++ b/Sources/Autogenerated/sdk_OrthancPluginImage.impl.h	Fri Jun 11 12:47:18 2021 +0200
@@ -34,6 +34,8 @@
 
 
 // Forward declaration of the custom methods
+extern PyObject *GetImageBuffer(
+  sdk_OrthancPluginImage_Object* self, PyObject *args);
 // End of forward declarations
 
 
@@ -56,6 +58,9 @@
   { "DrawText",
     (PyCFunction) sdk_OrthancPluginImage_OrthancPluginDrawText, METH_VARARGS,
     "Generated from C function OrthancPluginDrawText()" },
+  { "GetImageBuffer",
+    (PyCFunction) GetImageBuffer, METH_VARARGS,
+    "Generated from C function OrthancPluginGetImageBuffer()" },
   { NULL }  /* Sentinel */
 };
 
--- a/Sources/Plugin.cpp	Fri Jun 11 09:03:43 2021 +0200
+++ b/Sources/Plugin.cpp	Fri Jun 11 12:47:18 2021 +0200
@@ -39,7 +39,6 @@
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/filesystem.hpp>
 
-
 // The "dl_iterate_phdr()" function (to walk through shared libraries)
 // is not available on Microsoft Windows and Apple OS X
 #if defined(_WIN32)
@@ -78,7 +77,7 @@
     }
     else
     {
-      PyErr_SetString(PyExc_TypeError, "Second parameter is not a valid orthanc.Image");
+      PyErr_SetString(PyExc_TypeError, "Second parameter is not a valid orthanc.Image object");
       return NULL;
     }
 
@@ -99,6 +98,140 @@
 }
 
 
+PyObject* GetInstanceData(sdk_OrthancPluginDicomInstance_Object* self, PyObject *args)
+{
+  // The GIL is locked at this point (no need to create "PythonLock")
+
+  if (self->object_ == NULL)
+  {
+    PyErr_SetString(PyExc_ValueError, "Invalid object");
+    return NULL;
+  }
+  else
+  {
+    OrthancPluginDicomInstance* instance = reinterpret_cast<sdk_OrthancPluginDicomInstance_Object*>(self)->object_;
+
+    const void* data = OrthancPluginGetInstanceData(OrthancPlugins::GetGlobalContext(), instance);
+    size_t size = OrthancPluginGetInstanceSize(OrthancPlugins::GetGlobalContext(), instance);
+
+    if (data == NULL &&
+        size != 0)
+    {
+      PyErr_SetString(PyExc_ValueError, "Accessing an invalid orthanc.DicomInstance object");
+      return NULL;  
+    }
+    else
+    {
+      return PyBytes_FromStringAndSize(reinterpret_cast<const char*>(data), size);
+    }
+  }
+}
+
+
+/**
+ * Contrarily to "OrthancPluginGetImageBuffer()" that provides a
+ * read-write pointer, the method "orthanc.Image.GetImageBuffer()"
+ * returns a copy of the image buffer. Use "CreateImageFromBuffer()"
+ * to create an image from a Python buffer.
+ **/
+PyObject* GetImageBuffer(sdk_OrthancPluginImage_Object* self, PyObject *args)
+{
+  // The GIL is locked at this point (no need to create "PythonLock")
+  
+  if (self->object_ == NULL)
+  {
+    PyErr_SetString(PyExc_ValueError, "Invalid object");
+    return NULL;
+  }
+  else
+  {
+    OrthancPluginImage* image = reinterpret_cast<sdk_OrthancPluginImage_Object*>(self)->object_;
+
+    const void* buffer = OrthancPluginGetImageBuffer(OrthancPlugins::GetGlobalContext(), image);
+    size_t size = (OrthancPluginGetImagePitch(OrthancPlugins::GetGlobalContext(), image) *
+                   OrthancPluginGetImageHeight(OrthancPlugins::GetGlobalContext(), image));
+
+    if (buffer == NULL &&
+        size != 0)
+    {
+      PyErr_SetString(PyExc_ValueError, "Accessing an invalid orthanc.Image object");
+      return NULL;  
+    }
+    else
+    {
+      return PyBytes_FromStringAndSize(reinterpret_cast<const char*>(buffer), size);
+    }
+  }
+}
+
+
+/**
+ * This function is the Python alternative for function
+ * "OrthancPluginCreateImageAccessor()". Indeed, it is not possible to
+ * share a memory buffer between Orthanc and Python, so we have to
+ * create a copy of the image.
+ **/
+PyObject* CreateImageFromBuffer(PyObject* module, PyObject* args)
+{
+  // The GIL is locked at this point (no need to create "PythonLock")
+
+  unsigned long format, width, height, sourcePitch;
+  Py_buffer buffer;
+
+  if (!PyArg_ParseTuple(args, "kkkks*", &format, &width, &height, &sourcePitch, &buffer))
+  {
+    PyErr_SetString(PyExc_TypeError, "5 arguments are needed: image.PixelFormat, width, height, pitch and memory buffer");
+    return NULL;
+  }
+  else if (static_cast<Py_ssize_t>(sourcePitch * height) != buffer.len)
+  {
+    PyBuffer_Release(&buffer);
+
+    PyErr_SetString(PyExc_TypeError, "The size of the memory buffer must match the product of height by pitch");
+    return NULL;
+  }
+  else
+  {
+    OrthancPluginImage* image = OrthancPluginCreateImage(
+      OrthancPlugins::GetGlobalContext(), static_cast<OrthancPluginPixelFormat>(format), width, height);
+
+    if (image == NULL)
+    {
+      PyBuffer_Release(&buffer);
+
+      PyErr_SetString(PyExc_ValueError, "Cannot create the image");
+      return NULL;  
+    }
+    else
+    {
+      // Copy the image line by line
+      unsigned long targetPitch = OrthancPluginGetImagePitch(OrthancPlugins::GetGlobalContext(), image);
+
+      const uint8_t* sourcePixels = reinterpret_cast<const uint8_t*>(buffer.buf);
+      uint8_t* targetPixels = reinterpret_cast<uint8_t*>(OrthancPluginGetImageBuffer(OrthancPlugins::GetGlobalContext(), image));
+
+      unsigned long rowSize = std::min(sourcePitch, targetPitch);
+      
+      for (unsigned int y = 0; y < height; y++)
+      {
+        memcpy(targetPixels, sourcePixels, rowSize);
+        targetPixels += targetPitch;
+        sourcePixels += sourcePitch;
+      }
+             
+      PyBuffer_Release(&buffer);
+
+      {
+        PyObject *argList = Py_BuildValue("Lb", image, false /* not borrowed */);
+        PyObject *python = PyObject_CallObject((PyObject *) GetOrthancPluginImageType(), argList);
+        Py_DECREF(argList);
+        return python;
+      }
+    }
+  }
+}
+
+
 
 static bool pythonEnabled_ = false;
 static std::string userScriptName_;
@@ -161,6 +294,11 @@
   }
   
   {
+    PyMethodDef f = { "CreateImageFromBuffer", CreateImageFromBuffer, METH_VARARGS, "" };
+    functions.push_back(f);
+  }
+  
+  {
     PyMethodDef f = { "RegisterFindCallback", RegisterFindCallback, METH_VARARGS, "" };
     functions.push_back(f);
   }