1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
Python API
==========
.. highlight:: python
The Python API tries to mimic the :doc:`C++ API <../cpp/index>` as much as possible: most classes and other constructs keep the same name and semantics. However, when C++ and Python differ too much, new Python functions or classes are defined.
Basic operations
----------------
The following example show basic manipulations of a data set. This code snippet, and all the others in this section assume that for Python 2, ``from __future__ import print_function`` was used. Note that the Python version of ``odil.DataSet`` adds a few member functions to the C++ version (e.g. ``items``, ``__len__``, ``__iter__``) to make it more similar to Python containers.
::
import sys
import odil
# Reading a data set
with odil.open(sys.argv[1]) as stream:
header, data_set = odil.Reader.read_file(stream)
# Data set size, using C++ API
print(
"The header {} empty and has {} elements".format(
"is" if header.empty() else "is not", header.size()))
# Data set size, using Python API
print(
"The data set {} empty and has {} elements".format(
"is" if not data_set else "is not", len(data_set)))
# Element presence, C++ and Python API
print("Patient's Name {} in header".format(
"is" if header.has(odil.registry.PatientName) else "is not"))
print("Patient's Name {} in data set".format(
"is" if "PatientName" in data_set else "is not"))
# Element access, assuming PatientName is in data set
data_set.as_string("PatientName")[0] = "Somebody^Else"
print("Patient's Name: {}".format(list(data_set.as_string("PatientName"))))
# Iteration, sequence-like
for tag in header:
element = header[tag]
print(tag.get_name(), element.vr)
# Iteration, dict-like
for tag, element in data_set.items():
try:
name = tag.get_name()
except odil.Exception as e:
name = str(tag)
print(name, element.vr)
# Writing a data set
with odil.open(sys.argv[2], "w") as stream:
odil.Writer.write_file(data_set, stream)
DICOM services -- client side
-----------------------------
The behavior of C++ SCUs w is kept as is in Python: services which return data sets (C-FIND, C-GET, C-MOVE) either return all data sets to the caller or call a function for each of them. The following example adapts the C++ examples for C-ECHO, C-FIND and C-GET in Python.
::
import sys
import odil
transfer_syntaxes = [
getattr(odil.registry, "{}VRLittleEndian".format(x))
for x in ["Implicit", "Explicit"]]
# Create the association
association = odil.Association()
association.set_peer_host("www.dicomserver.co.uk")
association.set_peer_port(11112)
association.update_parameters()\
.set_calling_ae_title("WORKSTATION")\
.set_called_ae_title("SERVER")\
.set_presentation_contexts([
odil.AssociationParameters.PresentationContext(
odil.registry.Verification,
transfer_syntaxes,
odil.AssociationParameters.PresentationContext.Role.SCU
),
odil.AssociationParameters.PresentationContext(
odil.registry.StudyRootQueryRetrieveInformationModelFind,
transfer_syntaxes,
odil.AssociationParameters.PresentationContext.Role.SCU
)
])
association.associate()
# Check DICOM connectivity
echo_scu = odil.EchoSCU(association)
try:
echo_scu.echo()
except odil.Exception as e:
print("DICOM connection to remote server failed: {}".format(e))
sys.exit(1)
# Find the matching studies
query = odil.DataSet()
query.add("QueryRetrieveLevel", [ "STUDY" ])
query.add("PatientName", ["Doe"])
query.add("StudyInstanceUID")
query.add("SOPClassesInStudy")
find_scu = odil.FindSCU(association)
find_scu.set_affected_sop_class(odil.registry.StudyRootQueryRetrieveInformationModelFind)
study = find_scu.find(query)[0]
# Fetch the first study
association.release()
association = odil.Association()
association.set_peer_host("www.dicomserver.co.uk")
association.set_peer_port(11112)
association.update_parameters()\
.set_calling_ae_title("WORKSTATION")\
.set_called_ae_title("SERVER")\
.set_presentation_contexts([
odil.AssociationParameters.PresentationContext(
odil.registry.StudyRootQueryRetrieveInformationModelGet,
transfer_syntaxes,
odil.AssociationParameters.PresentationContext.Role.SCU
)
]
+[
odil.AssociationParameters.PresentationContext(
x, [odil.registry.ExplicitVRLittleEndian],
odil.AssociationParameters.PresentationContext.Role.SCU)
for x in study.as_string("SOPClassesInStudy")
])
association.associate()
query = odil.DataSet()
query.add("QueryRetrieveLevel", [ "STUDY" ])
query.add("StudyInstanceUID", study.as_string("StudyInstanceUID"))
query.add("SOPClassesInStudy")
get_scu = odil.GetSCU(association)
get_scu.set_affected_sop_class(odil.registry.StudyRootQueryRetrieveInformationModelGet)
def data_set_received(data_set):
print("Got data set {}".format(data_set.as_string("SOPInstanceUID")[0]))
get_scu.get(query, data_set_received)
DICOM services -- server side
-----------------------------
Similar to C++ SCPs, the Python SCPs work with generators, inherited from their base classes (e.g. ``odil.FindSCP.DataSetGenerator``). The following example shows the implementation of a dummy C-FIND SCP.
::
import odil
# Create the association
association = odil.Association()
association.receive_association("v4", 11112)
# Create the generator
class Generator(odil.FindSCP.DataSetGenerator):
def __init__(self):
odil.FindSCP.DataSetGenerator.__init__(self)
self._responses = []
self._response_index = None
def initialize(self, message):
data_set = odil.DataSet()
data_set.add("PatientName", ["Hello^World"])
data_set.add("PatientID", ["1234"])
self._responses.append(data_set)
self._response_index = 0
def done(self):
return (self._response_index == len(self._responses))
def next(self):
self._response_index += 1
def get(self):
return self._responses[self._response_index]
find_scp = odil.FindSCP(association)
generator = Generator()
find_scp.set_generator(generator)
# Receive and handle a message
message = association.receive_message()
find_scp(message)
# Check if we have more
termination_ok = False
try:
association.receive_message()
except odil.AssociationReleased:
print("Association released")
except odil.AssociationAborted:
print("Association aborted")
|