Package: pynetdicom
pynetdicomA Python implementation of theDICOMnetworking protocol, originally based on (legacy)pynetdicom.DescriptionDICOMis the international standard for medical
images and related information. It defines the formats and communication
protocols for media exchange in radiology, cardiology, radiotherapy and other
medical domains.pynetdicomis a pure Python 3.7+ package that implements the DICOM
networking protocol. Working withpydicom, it allows the easy creation
of DICOMService Class Users(SCUs) andService Class Providers(SCPs).pynetdicom’smain user class isAEand is used to represent a DICOM Application Entity. With it you can:Start the application as an SCP by specifying the supported presentation
contexts then callingAE.start_server()and waiting for incoming association requestsUse the application as an SCU by specifying the presentation contexts you
want the peer SCP to support, then requesting an association
via theAE.associate()method, which returns anAssociationthread.Once associated, the services available to the association can
be used by sendingDIMSE-CandDIMSE-Nmessages.DocumentationThepynetdicomtutorials,user guide,code examples,applicationandAPI referencedocumentation is available for thecurrent releaseas well as thedevelopment version.InstallationDependenciespydicomInstalling current releaseUsing pip:$pipinstallpynetdicomUsing conda:$condainstall-cconda-forgepynetdicomInstalling development version$pipinstallgit+https://github.com/pydicom/pynetdicom.gitSupported DIMSE ServicesSCU ServicesWhen the AE is acting as an SCU and an association has been established with a
peer SCP, the following DIMSE-C and -N services are available:DIMSE serviceAssociationmethodC-ECHOAssociation.send_c_echo()C-FINDAssociation.send_c_find(dataset, query_model)C-GETAssociation.send_c_get(dataset, query_model)C-MOVEAssociation.send_c_move(dataset, move_aet, query_model)C-STOREAssociation.send_c_store(dataset)N-ACTIONAssociation.send_n_action(dataset, action_type, class_uid, instance_uid)N-CREATEAssociation.send_n_create(dataset, class_uid, instance_uid)N-DELETEAssociation.send_n_delete(class_uid, instance_uid)N-EVENT-REPORTAssociation.send_n_event_report(dataset, event_type, class_uid, instance_uid)N-GETAssociation.send_n_get(identifier_list, class_uid, instance_uid)N-SETAssociation.send_n_set(dataset, class_uid, instance_uid)Wheredatasetis a pydicomDatasetobject,query_modelis a UID string,identifier_listis a list of pydicomTagobjects,event_typeandaction_typeare ints andclass_uidandinstance_uidare UID strings. See theAssociation documentationfor more information.SCP ServicesWhen the AE is acting as an SCP the following DIMSE-C and -N services are
available to the peer once an association has been established:DIMSE serviceIntervention EventHandler documentationC-ECHOevt.EVT_C_ECHOHandle C-ECHOC-FINDevt.EVT_C_FINDHandle C-FINDC-GETevt.EVT_C_GETHandle C-GETC-MOVEevt.EVT_C_MOVEHandle C-MOVEC-STOREevt.EVT_C_STOREHandle C-STOREN-ACTIONevt.EVT_N_ACTIONHandle N-ACTIONN-CREATEevt.EVT_N_CREATEHandle N-CREATEN-DELETEevt.EVT_N_DELETEHandle N-DELETEN-EVENT-REPORTevt.EVT_N_EVENT_REPORTHandle N-EVENT-REPORTN-GETevt.EVT_N_GETHandle N-GETN-SETevt.EVT_N_SETHandle N-SETWith the exception of the C-ECHO service, a user-defined callable function,handler, must be bound to the correspondingintervention eventin order to complete a DIMSE service request. Events
can be imported withfrom pynetdicom import evtand a handler can be
bound to an event prior to starting an association through theevt_handlerskeyword arguments inAE.start_server()andAE.associate().When an event occurs thehandlerfunction is called and passed a single
parameter,event, which is anEventobject whose specific attributes
are dependent on the type of event that occurred. Handlers bound to
intervention events must  return or yield certain values. See thehandler documentationfor information on what attributes and properties are available inEventfor each event type and the expected returns/yields for the
corresponding handlers.ApplicationsSome basic DICOM applications are included withpynetdicom:echoscpechoscufindscugetscuqrscpmovescustorescpstorescuCode ExamplesMorecode examplesare available in the documentation.Echo SCUSend a C-ECHO request to a Verification SCP (at TCP/IP addressaddr, listen port numberport):frompynetdicomimportAEae=AE(ae_title=b'MY_ECHO_SCU')# Verification SOP Class has a UID of 1.2.840.10008.1.1#   we can use the UID str directly when adding the requested#   presentation contextae.add_requested_context('1.2.840.10008.1.1')# Associate with a peer AEassoc=ae.associate(addr,port)ifassoc.is_established:# Send a DIMSE C-ECHO request to the peerstatus=assoc.send_c_echo()# Print the response from the peerifstatus:print('C-ECHO Response: 0x{0:04x}'.format(status.Status))# Release the associationassoc.release()Echo SCPCreate a blocking Echo SCP on port11112(you may optionally
bind a handler to theevt.EVT_C_ECHOevent if you want to return something
other than an0x0000Successstatus):frompynetdicomimportAE,VerificationPresentationContextsae=AE(ae_title=b'MY_ECHO_SCP')# Or we can use the inbuilt VerificationPresentationContexts list,#   there's one for each of the supported Service Classes# In this case, we are supporting any requests to use Verification SOP#   Class in the associationae.supported_contexts=VerificationPresentationContexts# Start the SCP on (host, port) in blocking modeae.start_server(("localhost",11112),block=True)Alternatively, you can start the SCP in non-blocking mode, which returns the
running server instance. This can be useful when you want to run a Storage SCP
and make C-MOVE requests within the same AE.In the next example we’ll create a non-blocking Verification SCP and bind a
handler for the C-ECHO service request eventevt.EVT_C_ECHOthat logs the
requestor’s address and port number and the timestamp for the event.importloggingfrompynetdicomimportAE,evt,debug_loggerfrompynetdicom.sop_classimportVerificationSOPClass# Setup logging to use the StreamHandler at the debug leveldebug_logger()ae=AE(ae_title=b'MY_ECHO_SCP')ae.add_supported_context(VerificationSOPClass)# Implement the EVT_C_ECHO handlerdefhandle_echo(event,logger):"""Handle a C-ECHO service request.

    Parameters
    ----------
    event : evt.Event
        The C-ECHO service request event, this parameter is always
        present.
    logger : logging.Logger
        The logger to use, this parameter is only present because we
        bound ``evt.EVT_C_ECHO`` using a 3-tuple.

    Returns
    -------
    int or pydicom.dataset.Dataset
        The status returned to the peer AE in the C-ECHO response.
        Must be a valid C-ECHO status value as either an ``int`` or a
        ``Dataset`` object containing an (0000,0900) *Status* element.
    """# Every *Event* includes `assoc` and `timestamp` attributes#   which are the *Association* instance the event occurred in#   and the *datetime.datetime* the event occurred atrequestor=event.assoc.requestortimestamp=event.timestamp.strftime("%Y-%m-%d%H:%M:%S")msg=("Received C-ECHO service request from ({},{}) at{}".format(requestor.address,requestor.port,timestamp))logger.info(msg)# Return a *Success* statusreturn0x0000# By binding using a 3-tuple we can pass extra arguments to#   the handlerhandlers=[(evt.EVT_C_ECHO,handle_echo,[logging.getLogger('pynetdicom')])]# Start the SCP in non-blocking modescp=ae.start_server(("localhost",11112),block=False,evt_handlers=handlers)# Associate and send a C-ECHO request to our own Verification SCPae.add_requested_context(VerificationSOPClass)assoc=ae.associate('localhost',11112)ifassoc.is_established:status=assoc.send_c_echo()assoc.release()# Shutdown the SCPscp.shutdown()Storage SCUSend the DICOMCT Image Storagedataset infile-in.dcmto a peer Storage
SCP (at TCP/IP addressaddr, listen port numberport):frompydicomimportdcmreadfrompydicom.uidimportImplicitVRLittleEndianfrompynetdicomimportAE,VerificationPresentationContextsfrompynetdicom.sop_classimportCTImageStorage,MRImageStorageae=AE(ae_title=b'MY_STORAGE_SCU')# We can also do the same thing with the requested contextsae.requested_contexts=VerificationPresentationContexts# Or we can use inbuilt objects like CTImageStorage.# The requested presentation context's transfer syntaxes can also#   be specified using a str/UID or list of str/UIDsae.add_requested_context(CTImageStorage,transfer_syntax=ImplicitVRLittleEndian)# Adding a presentation context with multiple transfer syntaxesae.add_requested_context(MRImageStorage,transfer_syntax=[ImplicitVRLittleEndian,'1.2.840.10008.1.2.1'])assoc=ae.associate(addr,port)ifassoc.is_established:dataset=dcmread('file-in.dcm')# `status` is the response from the peer to the store request# but may be an empty pydicom Dataset if the peer timed out or# sent an invalid dataset.status=assoc.send_c_store(dataset)assoc.release()