Mercurial > hg > orthanc-book
annotate Sphinx/source/plugins/python.rst @ 348:d8359cecdc89
pillow sample
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 27 Mar 2020 12:20:40 +0100 |
parents | 04fae9d4b65f |
children | 60080d792f25 |
rev | line source |
---|---|
343
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
1 .. _python-plugin: |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
2 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
3 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
4 Python plugin for Orthanc |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
5 ========================= |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
6 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
7 .. contents:: |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
8 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
9 Work-in-progress. |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
10 |
345 | 11 The Python API is automatically generated from the `Orthanc plugin SDK |
12 in C | |
13 <https://hg.orthanc-server.com/orthanc/file/Orthanc-1.5.7/Plugins/Include/orthanc/OrthancCPlugin.h>`__ | |
14 using the `Clang <https://en.wikipedia.org/wiki/Clang>`__ compiler | |
15 front-end. The coverage of the C SDK is about 75% (105 functions are | |
16 automatically wrapped in Python out of a total of 139 functions in C). | |
343
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
17 |
348 | 18 This provides much more flexibility than the Lua scripts. |
19 | |
343
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
20 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
21 Samples |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
22 ------- |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
23 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
24 Extending the REST API |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
25 ...................... |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
26 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
27 .. highlight:: python |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
28 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
29 Here is a basic Python script that registers two new routes in the |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
30 REST API:: |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
31 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
32 import orthanc |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
33 import pprint |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
34 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
35 def OnRest(output, uri, **request): |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
36 pprint.pprint(request) |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
37 print('Accessing uri: %s' % uri) |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
38 output.AnswerBuffer('ok\n', 'text/plain') |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
39 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
40 orthanc.RegisterRestCallback('/(to)(t)o', OnRest) |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
41 orthanc.RegisterRestCallback('/tata', OnRest) |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
42 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
43 .. highlight:: json |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
44 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
45 Here is the associated minimal configuration file for Orthanc |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
46 (provided the Python script is saved as ``rest.py``):: |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
47 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
48 { |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
49 "Plugins" : [ "." ], |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
50 "PythonScript" : "rest.py", |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
51 "PythonVerbose" : false |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
52 } |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
53 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
54 .. highlight:: bash |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
55 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
56 The route can then be accessed as:: |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
57 |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
58 $ curl http://localhost:8042/toto |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
59 ok |
fff45618262d
creating the documentation of the Python plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
diff
changeset
|
60 |
345 | 61 |
62 Listening to changes | |
63 .................... | |
64 | |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
65 .. highlight:: python |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
66 |
345 | 67 This sample uploads a DICOM file as soon as Orthanc is started:: |
68 | |
69 import orthanc | |
70 | |
71 def OnChange(changeType, level, resource): | |
72 if changeType == orthanc.ChangeType.ORTHANC_STARTED: | |
73 print('Started') | |
74 | |
75 with open('/tmp/sample.dcm', 'rb') as f: | |
76 orthanc.RestApiPost('/instances', f.read()) | |
77 | |
78 elif changeType == orthanc.ChangeType.ORTHANC_STOPPED: | |
79 print('Stopped') | |
80 | |
81 elif changeType == orthanc.ChangeType.NEW_INSTANCE: | |
82 print('A new instance was uploaded: %s' % resource) | |
83 | |
84 orthanc.RegisterOnChangeCallback(OnChange) | |
85 | |
86 | |
87 Accessing the content of a new instance | |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
88 ....................................... |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
89 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
90 .. highlight:: python |
345 | 91 |
92 :: | |
93 | |
94 import orthanc | |
95 import json | |
96 import pprint | |
97 | |
98 def OnStoredInstance(dicom, instanceId): | |
99 print('Received instance %s of size %d (transfer syntax %s, SOP class UID %s)' % ( | |
100 instanceId, dicom.GetInstanceSize(), | |
101 dicom.GetInstanceMetadata('TransferSyntax'), | |
102 dicom.GetInstanceMetadata('SopClassUid'))) | |
103 | |
104 # Print the origin information | |
105 if dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.DICOM_PROTOCOL: | |
106 print('This instance was received through the DICOM protocol') | |
107 elif dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.REST_API: | |
108 print('This instance was received through the REST API') | |
109 | |
110 # Print the DICOM tags | |
111 pprint.pprint(json.loads(dicom.GetInstanceSimplifiedJson())) | |
112 | |
113 orthanc.RegisterOnStoredInstanceCallback(OnStoredInstance) | |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
114 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
115 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
116 Calling pydicom |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
117 ............... |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
118 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
119 .. highlight:: python |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
120 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
121 Here is a sample Python plugin that registers a REST callback to dump |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
122 the content of the dataset of one given DICOM instance stored in |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
123 Orthanc, using `pydicom <https://pydicom.github.io/>`__:: |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
124 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
125 import io |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
126 import orthanc |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
127 import pydicom |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
128 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
129 def DecodeInstance(output, uri, **request): |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
130 if request['method'] == 'GET': |
347 | 131 # Retrieve the instance ID from the regular expression (*) |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
132 instanceId = request['groups'][0] |
347 | 133 # Get the content of the DICOM file |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
134 f = orthanc.GetDicomForInstance(instanceId) |
347 | 135 # Parse it using pydicom |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
136 dicom = pydicom.dcmread(io.BytesIO(f)) |
347 | 137 # Return a string representation the dataset to the caller |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
138 output.AnswerBuffer(str(dicom), 'text/plain') |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
139 else: |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
140 output.SendMethodNotAllowed('GET') |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
141 |
347 | 142 orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance) # (*) |
346
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
143 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
144 .. highlight:: bash |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
145 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
146 This can be called as follows:: |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
147 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
148 $ curl http://localhost:8042/pydicom/19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5 |
bdf8757449e3
more python samples
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
345
diff
changeset
|
149 |
347 | 150 |
151 Auto-routing studies | |
152 .................... | |
153 | |
154 .. highlight:: python | |
155 | |
156 Here is a sample Python plugin that routes any :ref:`stable study | |
157 <lua-callbacks>` to a modality named ``samples`` (as declared in the | |
158 ``DicomModalities`` configuration option):: | |
159 | |
160 import orthanc | |
161 | |
162 def OnChange(changeType, level, resourceId): | |
163 if changeType == orthanc.ChangeType.STABLE_STUDY: | |
164 print('Stable study: %s' % resourceId) | |
165 orthanc.RestApiPost('/modalities/sample/store', resourceId) | |
166 | |
167 orthanc.RegisterOnChangeCallback(OnChange) | |
348 | 168 |
169 | |
170 Render a thumbnail using PIL/Pillow | |
171 ................................... | |
172 | |
173 .. highlight:: python | |
174 | |
175 :: | |
176 | |
177 from PIL import Image | |
178 import io | |
179 import orthanc | |
180 | |
181 def DecodeInstance(output, uri, **request): | |
182 if request['method'] == 'GET': | |
183 # Retrieve the instance ID from the regular expression (*) | |
184 instanceId = request['groups'][0] | |
185 | |
186 # Render the instance, then open it in Python using PIL/Pillow | |
187 png = orthanc.RestApiGet('/instances/%s/rendered' % instanceId) | |
188 image = Image.open(io.BytesIO(png)) | |
189 | |
190 # Downsize the image as a 64x64 thumbnail | |
191 image.thumbnail((64, 64), Image.ANTIALIAS) | |
192 | |
193 # Save the thumbnail as JPEG, then send the buffer to the caller | |
194 jpeg = io.BytesIO() | |
195 image.save(jpeg, format = "JPEG", quality = 80) | |
196 jpeg.seek(0) | |
197 output.AnswerBuffer(jpeg.read(), 'text/plain') | |
198 | |
199 else: | |
200 output.SendMethodNotAllowed('GET') | |
201 | |
202 orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance) # (*) |