comparison Sources/Plugin.cpp @ 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
comparison
equal deleted inserted replaced
64:091fb1903bfc 65:4da5ce3468b4
37 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" 37 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
38 38
39 #include <boost/algorithm/string/predicate.hpp> 39 #include <boost/algorithm/string/predicate.hpp>
40 #include <boost/filesystem.hpp> 40 #include <boost/filesystem.hpp>
41 41
42
43 // The "dl_iterate_phdr()" function (to walk through shared libraries) 42 // The "dl_iterate_phdr()" function (to walk through shared libraries)
44 // is not available on Microsoft Windows and Apple OS X 43 // is not available on Microsoft Windows and Apple OS X
45 #if defined(_WIN32) 44 #if defined(_WIN32)
46 # define HAS_DL_ITERATE 0 45 # define HAS_DL_ITERATE 0
47 #elif defined(__APPLE__) && defined(__MACH__) 46 #elif defined(__APPLE__) && defined(__MACH__)
76 { 75 {
77 image = reinterpret_cast<sdk_OrthancPluginImage_Object*>(pixelData)->object_; 76 image = reinterpret_cast<sdk_OrthancPluginImage_Object*>(pixelData)->object_;
78 } 77 }
79 else 78 else
80 { 79 {
81 PyErr_SetString(PyExc_TypeError, "Second parameter is not a valid orthanc.Image"); 80 PyErr_SetString(PyExc_TypeError, "Second parameter is not a valid orthanc.Image object");
82 return NULL; 81 return NULL;
83 } 82 }
84 83
85 OrthancPlugins::MemoryBuffer buffer; 84 OrthancPlugins::MemoryBuffer buffer;
86 OrthancPluginErrorCode code = OrthancPluginCreateDicom(OrthancPlugins::GetGlobalContext(), *buffer, json, image, 85 OrthancPluginErrorCode code = OrthancPluginCreateDicom(OrthancPlugins::GetGlobalContext(), *buffer, json, image,
97 } 96 }
98 } 97 }
99 } 98 }
100 99
101 100
101 PyObject* GetInstanceData(sdk_OrthancPluginDicomInstance_Object* self, PyObject *args)
102 {
103 // The GIL is locked at this point (no need to create "PythonLock")
104
105 if (self->object_ == NULL)
106 {
107 PyErr_SetString(PyExc_ValueError, "Invalid object");
108 return NULL;
109 }
110 else
111 {
112 OrthancPluginDicomInstance* instance = reinterpret_cast<sdk_OrthancPluginDicomInstance_Object*>(self)->object_;
113
114 const void* data = OrthancPluginGetInstanceData(OrthancPlugins::GetGlobalContext(), instance);
115 size_t size = OrthancPluginGetInstanceSize(OrthancPlugins::GetGlobalContext(), instance);
116
117 if (data == NULL &&
118 size != 0)
119 {
120 PyErr_SetString(PyExc_ValueError, "Accessing an invalid orthanc.DicomInstance object");
121 return NULL;
122 }
123 else
124 {
125 return PyBytes_FromStringAndSize(reinterpret_cast<const char*>(data), size);
126 }
127 }
128 }
129
130
131 /**
132 * Contrarily to "OrthancPluginGetImageBuffer()" that provides a
133 * read-write pointer, the method "orthanc.Image.GetImageBuffer()"
134 * returns a copy of the image buffer. Use "CreateImageFromBuffer()"
135 * to create an image from a Python buffer.
136 **/
137 PyObject* GetImageBuffer(sdk_OrthancPluginImage_Object* self, PyObject *args)
138 {
139 // The GIL is locked at this point (no need to create "PythonLock")
140
141 if (self->object_ == NULL)
142 {
143 PyErr_SetString(PyExc_ValueError, "Invalid object");
144 return NULL;
145 }
146 else
147 {
148 OrthancPluginImage* image = reinterpret_cast<sdk_OrthancPluginImage_Object*>(self)->object_;
149
150 const void* buffer = OrthancPluginGetImageBuffer(OrthancPlugins::GetGlobalContext(), image);
151 size_t size = (OrthancPluginGetImagePitch(OrthancPlugins::GetGlobalContext(), image) *
152 OrthancPluginGetImageHeight(OrthancPlugins::GetGlobalContext(), image));
153
154 if (buffer == NULL &&
155 size != 0)
156 {
157 PyErr_SetString(PyExc_ValueError, "Accessing an invalid orthanc.Image object");
158 return NULL;
159 }
160 else
161 {
162 return PyBytes_FromStringAndSize(reinterpret_cast<const char*>(buffer), size);
163 }
164 }
165 }
166
167
168 /**
169 * This function is the Python alternative for function
170 * "OrthancPluginCreateImageAccessor()". Indeed, it is not possible to
171 * share a memory buffer between Orthanc and Python, so we have to
172 * create a copy of the image.
173 **/
174 PyObject* CreateImageFromBuffer(PyObject* module, PyObject* args)
175 {
176 // The GIL is locked at this point (no need to create "PythonLock")
177
178 unsigned long format, width, height, sourcePitch;
179 Py_buffer buffer;
180
181 if (!PyArg_ParseTuple(args, "kkkks*", &format, &width, &height, &sourcePitch, &buffer))
182 {
183 PyErr_SetString(PyExc_TypeError, "5 arguments are needed: image.PixelFormat, width, height, pitch and memory buffer");
184 return NULL;
185 }
186 else if (static_cast<Py_ssize_t>(sourcePitch * height) != buffer.len)
187 {
188 PyBuffer_Release(&buffer);
189
190 PyErr_SetString(PyExc_TypeError, "The size of the memory buffer must match the product of height by pitch");
191 return NULL;
192 }
193 else
194 {
195 OrthancPluginImage* image = OrthancPluginCreateImage(
196 OrthancPlugins::GetGlobalContext(), static_cast<OrthancPluginPixelFormat>(format), width, height);
197
198 if (image == NULL)
199 {
200 PyBuffer_Release(&buffer);
201
202 PyErr_SetString(PyExc_ValueError, "Cannot create the image");
203 return NULL;
204 }
205 else
206 {
207 // Copy the image line by line
208 unsigned long targetPitch = OrthancPluginGetImagePitch(OrthancPlugins::GetGlobalContext(), image);
209
210 const uint8_t* sourcePixels = reinterpret_cast<const uint8_t*>(buffer.buf);
211 uint8_t* targetPixels = reinterpret_cast<uint8_t*>(OrthancPluginGetImageBuffer(OrthancPlugins::GetGlobalContext(), image));
212
213 unsigned long rowSize = std::min(sourcePitch, targetPitch);
214
215 for (unsigned int y = 0; y < height; y++)
216 {
217 memcpy(targetPixels, sourcePixels, rowSize);
218 targetPixels += targetPitch;
219 sourcePixels += sourcePitch;
220 }
221
222 PyBuffer_Release(&buffer);
223
224 {
225 PyObject *argList = Py_BuildValue("Lb", image, false /* not borrowed */);
226 PyObject *python = PyObject_CallObject((PyObject *) GetOrthancPluginImageType(), argList);
227 Py_DECREF(argList);
228 return python;
229 }
230 }
231 }
232 }
233
234
102 235
103 static bool pythonEnabled_ = false; 236 static bool pythonEnabled_ = false;
104 static std::string userScriptName_; 237 static std::string userScriptName_;
105 static std::vector<PyMethodDef> globalFunctions_; 238 static std::vector<PyMethodDef> globalFunctions_;
106 239
155 * New in release 3.2 288 * New in release 3.2
156 **/ 289 **/
157 290
158 { 291 {
159 PyMethodDef f = { "CreateDicom", CreateDicom, METH_VARARGS, "" }; 292 PyMethodDef f = { "CreateDicom", CreateDicom, METH_VARARGS, "" };
293 functions.push_back(f);
294 }
295
296 {
297 PyMethodDef f = { "CreateImageFromBuffer", CreateImageFromBuffer, METH_VARARGS, "" };
160 functions.push_back(f); 298 functions.push_back(f);
161 } 299 }
162 300
163 { 301 {
164 PyMethodDef f = { "RegisterFindCallback", RegisterFindCallback, METH_VARARGS, "" }; 302 PyMethodDef f = { "RegisterFindCallback", RegisterFindCallback, METH_VARARGS, "" };