Mercurial > hg > orthanc-python
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); }