pydicom / pynetdicom Goto Github PK
View Code? Open in Web Editor NEWA Python implementation of the DICOM networking protocol
Home Page: https://pydicom.github.io/pynetdicom
License: MIT License
A Python implementation of the DICOM networking protocol
Home Page: https://pydicom.github.io/pynetdicom
License: MIT License
Public Dicom Address = http://www.dicomserver.co.uk/
website: link
I am using the following code
from pynetdicom3 import AE, StorageSOPClassList
from pydicom import read_file
from pydicom.uid import UID
import os
# The Verification SOP Class has a UID of 1.2.840.10008.1.1
# we can use the UID string directly
ae = AE(scu_sop_class=['1.2.840.10008.1.1'] + StorageSOPClassList)
addr='213.165.94.158'
port = 11112
dir_name = "/Users/shashankkumar/Downloads/54879843/DICOM/Doe^Pierre [54879843]/20060101 000000 [ - CRANE POLYGONE]/Series 002 [CT - Crane SPC]"
# Associate with a peer DICOM AE
assoc = ae.associate(addr, port)
if assoc.is_established:
# Send a DIMSE C-ECHO request to the peer
assoc.send_c_echo()
print('echo sent')
for file in os.listdir(dir_name):
if file.endswith(".dcm"):
print(file)
dataset = read_file(os.path.join(dir_name, file))
assoc.send_c_store(dataset)
# Release the association
assoc.release()`
Gives the following error:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/local/lib/python3.5/site-packages/pynetdicom3-0.1.0-py3.5.egg/pynetdicom3/utils.py", line 226, in add_transfer_syntax
transfer_syntax.is_valid()
File "/usr/local/lib/python3.5/site-packages/pydicom-1.0.0a1-py3.5.egg/pydicom/uid.py", line 137, in is_valid
raise InvalidUID('UID is not a valid format: %s' % self)
pydicom.uid.InvalidUID: 'UID is not a valid format: '
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 914, in _bootstrap_inner
self.run()
File "/usr/local/lib/python3.5/site-packages/pynetdicom3-0.1.0-py3.5.egg/pynetdicom3/dul.py", line 242, in run
if self._is_transport_event() and self._idle_timer is not None:
File "/usr/local/lib/python3.5/site-packages/pynetdicom3-0.1.0-py3.5.egg/pynetdicom3/dul.py", line 463, in _is_transport_event
self._check_incoming_pdu()
File "/usr/local/lib/python3.5/site-packages/pynetdicom3-0.1.0-py3.5.egg/pynetdicom3/dul.py", line 366, in _check_incoming_pdu
self.primitive = self.pdu.ToParams()
File "/usr/local/lib/python3.5/site-packages/pynetdicom3-0.1.0-py3.5.egg/pynetdicom3/pdu.py", line 779, in ToParams
primitive.presentation_context_definition_results_list.append(ii.ToParams())
File "/usr/local/lib/python3.5/site-packages/pynetdicom3-0.1.0-py3.5.egg/pynetdicom3/pdu.py", line 2367, in ToParams
primitive.add_transfer_syntax(self.transfer_syntax_sub_item.ToParams())
File "/usr/local/lib/python3.5/site-packages/pynetdicom3-0.1.0-py3.5.egg/pynetdicom3/utils.py", line 228, in add_transfer_syntax
raise ValueError('Presentation Context attempted to add a '
ValueError: Presentation Context attempted to add a invalid UID
Plus test coverage for same.
There should be an option to require user identity, however I think it should be defaulted to ignoring UI requests.
User Identity Negotiation should be left to the end user (ae.on_user_identity_negotiation perhaps?). In the included apps, any UIN requests should be ignored and the Association accepted by default (unless there are other reasons to reject, of course)
hey @scaramallion ! I'm fairly new to this space, so apologies in advance if this is common knowledge. I noticed for the Find Service Class Provider and Echo Service Class Provider the default pdu_max (in bytes) is just slightly different:
# This is for Echo
net_opts.add_argument("-pdu", "--max-pdu", metavar='[n]umber of bytes',
help="set max receive pdu to n bytes (4096..131072)",
type=int,
default=16382)
# and this is for Find
net_opts.add_argument("-pdu", "--max-pdu", metavar='[n]umber of bytes',
help="set max receive pdu to n bytes",
type=int,
default=16384)
is it trivial and not meaningful, or is there something important about those two bytes? Thanks for your wisdom!
These are some great examples and tutorials. Do you have one for downloading a DICOM from a remote PACS? Thanks
From my reading of the DICOM standard, some of the Status values weren't supported by pynetdicom, nor were the associated optional command set elements. Unfortunately, supporting them properly will require that the user ae.on_c_*
methods return a pydicom.dataset
with a Status element instead of an int which is a fairly annoying (though not particular difficult to convert) change.
This will also mean that the current service_class status attributes will need to be removed (it wasn't particularly maintainable anyway as the same service class might have different status return values for the same nominal status depending on which of the DIMSE services it used).
I'm still thinking about the replacement, but it will need to be more adaptable.
Hello,
Does pynetdicom3 has a DICOM conformance statement?
Which transfer syntax does it support?
Thanks
Something similar to the SOP Class Status objects might be useful
Needs to be rewritten to handle the (status, dataset) generator.
debug_send_associate_rq()
debug_send_associate_ac()
debug_receive_associate_rq()
debug_receive_associate_ac()
The PDU items should inherit generic encode, decode, to_primitive, from_primitive methods from the base class. The PDU items themselves should only be used to define the attributes/property getters and setters.
It should follow the pydicom documentation, using sphinx with numpydoc to build and the output going to pydicom.github.io/pynetdicom3 and read the docs. Using circle to automate things would be nice
Also need to update/expand the user documentation:
Currently slow and confusing
And overwrought
Also, add the ability to have more than 126 contexts when acting as an SCP (#118).
By my count there are about three different locations where the sockets are opened for connecting and at least another three locations where the sockets are being closed. These all need to be consolidated and modified to follow the state machine more closely.
It's also a bit strange for the AE to be directly handling low level network issues.
debug_send_associate_rq()
debug_send_associate_ac()
debug_receive_associate_rq()
debug_receive_associate_ac()
Hi,
is there a way to find out the senders IP address? I made a little DICOM receiver application and I need to get the address of the peer.
Need to standardise the callback parameter for A-ABORT
When transferring datasets via C-STORE using Association.send_c_store
, elements with ambiguous VR (such as 'US or SS') aren't encodable by pydicom (a ValueError
gets raised in pydicom.filewriter).
Current status is that most elements with ambiguous VR are corrected, however there are some that I haven't been able to locate any information on how to handle them. See below.
Handling of ambiguous VR elements has been passed off to pydicom instead (pydicom/pydicom@2443a4c)
For Java's dcm4che, there is MPPS supported.
https://dcm4che.atlassian.net/wiki/display/ee2/Modality+Performed+Procedure+Step+SCP
I believe python's pynedicom3 cloud also achieve this.
Could you kindly provide MPPS example?
Thank you for your kind attention and help.
Hi,
thanks for porting pynetdicom to python3. I am currently using dcmtk (called from python) but would love to use pynetdicom3.
What I am not sure is how I would translate the following dcmtk findscu command to pynetdicom3 equivalent
findscu -v -S -k 0008,0052=SERIES -aec <AEC> <IP> -aet <AE_TITLE> -k PatientName -k PatientBirthDate -k PatientID -k PatientSex -k StudyID -k StudyDate -k Modality -k AccessionNumber -k BodyPartExamined -k StudyDescription -k SeriesDescription -k SeriesNumber -k InstanceNumber -k ReferringPhysicianName -k InstitutionName -k StudyInstanceUID -k SeriesInstanceUID -k StudyDate=20160101 -k SeriesDate=20160101 -k SeriesTime=000000-075959
So it does a Series level query and retrieves those attributes (all -k ones). The filter is set on a specific time. The basic example from (here)[https://github.com/scaramallion/pynetdicom3/blob/master/docs/scu_examples.rst] is running.
Thanks a lot for your time. If there are some easy fixes (documentation, code) I can try to contribute.
The five association related callbacks (requested, accepted, rejected, released, aborted) all need verification that they're being called correctly. Probably a good idea to do this through the unit testing.
The on_association_aborted callback parameters also need sorting out.
As far as I can tell it's not stated anywhere. DCMTK accepts the association then aborts.
hey @scaramallion! I'm a software engineer at Stanford, and I'm looking to implement a dicom receiver for some applications in python (we are using the old MRCTP and it's time for an update!) I found your repo as a fork off of the base https://github.com/patmun/pynetdicom and they seem to be moving in separate directions. Is the difference that this repo has python 3 support? Is there plans to integrate back into pynetdicom, or something else? I'm looking to see which I'd start with for our work (and will likely contribute to its development) so I want to understand how the projects are related. Thanks!
Is it just for when the peer sends association related primitives or is it when either AE does?
Needs better doc info, too
C-ECHO
C-STORE
C-FIND
C-GET
C-MOVE
N-EVENT-REPORT
N-GET
N-SET
N-ACTION
N-CREATE
N-DELETE
I have written a dicom server. When association accomplished and start to transferring dicom files. I found that pynetdicom3 much slower than pynetdicom0.8.1, I don't know why.
pynetdicom3 storage server code:
scp = StorageSCP(ae_title=ae_title, port=port, on_c_store=my_on_c_store)
scp.ae.maximum_associations = 10
scp.start()
pynetdicom0.8.1 storage server code:
ae = AE(ae_title, port, [PatientRootFindSOPClass,
PatientRootMoveSOPClass,
VerificationSOPClass],
[StorageSOPClass, CTImageStorageSOPClass, RTImageStorageSOPClass,
MRImageStorageSOPClass], )
ae.OnReceiveStore = my_on_c_store
Change the offending statements to yield Status, None
and change docstring to reflect change (yields Status and Dataset or None)
This should save on memory when transferring huge messages.
Traceback (most recent call last):
File "/opt/python/3.5.2/lib/python3.5/threading.py", line 914, in _bootstrap_inner
self.run()
File "/home/travis/build/scaramallion/pynetdicom3/pynetdicom3/DULprovider.py", line 484, in run
if self.CheckNetwork():
File "/home/travis/build/scaramallion/pynetdicom3/pynetdicom3/DULprovider.py", line 383, in CheckNetwork
return self.is_transport_connection_event()
File "/home/travis/build/scaramallion/pynetdicom3/pynetdicom3/DULprovider.py", line 450, in is_transport_connection_event
read_list, _, _ = select.select([self.scu_socket], [], [], 0)
ValueError: file descriptor cannot be a negative integer (-1)
I think the cause is the thread being killed before the socket has time to close properly, possibly due to a race condition between the Assoc and DUL threads.
Occurs during/after association release.
The Association thread calls assoc.release(), which causes the peer to send a release confirmation then disconnect. In addition, assoc.release() is used to stop the DUL run loop. The DUL thread, on the other hand, is checking for network connection roughly every 0.001 s, so occasionally what happens is that the signal to kill the DUL happens after the peer disconnects but before the run loop is stopped. As a result, the DUL checks the socket, which returns -1 as its been closed by the peer, which in turn raises a ValueError.
debug_send_associate_rq()
debug_send_associate_ac()
debug_receive_associate_rq()
debug_receive_associate_ac()
applicationentity.py, line 385.
Should be removed. We don't necessarily want to exit from python when the application entity ends.
The following should be completed before the v1.0.0 release:
pydicom.dataset.Dataset
objects and determine a more flexible approach for the Service Class DIMSE Statuses (and make sure all the Statuses are supported).PresentationContextManager
, because it sucks (#229).pdu
module (#165)context
and info
parameters to the on_c_* callbacks (#167)pydicom
1.2 release and use it as the supported version.debug_send_associate_rq()
debug_send_associate_ac()
debug_receive_associate_rq()
debug_receive_associate_ac()
Traceback (most recent call last):
File "/opt/python/3.3.5/lib/python3.3/threading.py", line 901, in _bootstrap_inner
self.run()
File "./pynetdicom3/DULprovider.py", line 487, in run
if self.CheckNetwork():
File "./pynetdicom3/DULprovider.py", line 387, in CheckNetwork
return self.is_transport_connection_event()
File "./pynetdicom3/DULprovider.py", line 456, in is_transport_connection_event
self.CheckIncomingPDU()
File "./pynetdicom3/DULprovider.py", line 329, in CheckIncomingPDU
self.pdu = Socket2PDU(bytestream, self)
File "./pynetdicom3/DULprovider.py", line 582, in Socket2PDU
acse = dul.association.acse
AttributeError: 'Association' object has no attribute 'acse'
I've only seen it during Travis builds, weirdly.
Traceback (most recent call last):
File "/opt/python/3.4.2/lib/python3.4/threading.py", line 921, in _bootstrap_inner
self.run()
File "/home/travis/build/scaramallion/pynetdicom3/test/test_ae.py", line 95, in run
self.ae.start()
File "/home/travis/build/scaramallion/pynetdicom3/pynetdicom3/applicationentity.py", line 284, in start
self._bind_socket()
File "/home/travis/build/scaramallion/pynetdicom3/pynetdicom3/applicationentity.py", line 320, in _bind_socket
self.local_socket.bind(('', self.port))
OSError: [Errno 98] Address already in use
Due to the next unit test run trying to set an SCP on the same port of a previous unit test that failed to close it properly, which in this case is the assoc.abort() test.
Either when I run my own code to create an scp or when running the scp tools located in app directory the CPU load of the application is 100%
I suppose start()
uses a while(true)
which is not so efficient?
Hi,
I am trying to set up a storescp to receive Dicom images.
This is what the code looks like now
def on_c_store(dataset):
pydicom.write_file(filename='sample.dcm', dataset=dataset)
return StorageServiceClass.Success
# Or we can use the inbuilt Verification SOP Class
ae = AE(ae_title="local", port=11112, scp_sop_class=['1.2.840.10008.5.1.4.1.1.2', VerificationSOPClass],
transfer_syntax=[ExplicitVRLittleEndian, ImplicitVRLittleEndian, ExplicitVRBigEndian, JpegLossLess])
ae.on_c_store=on_c_store
# Start the SCP
ae.start()
So when i send a dicom image i get the following error
File "C:/Users/Adarsh/PycharmProjects/Insight/storescp.py", line 20, in on_c_store
pydicom.write_file(filename='sample.dcm', dataset=dataset)
File "C:\Python\Python35\lib\site-packages\pydicom-1.0.0a1-py3.5.egg\pydicom_init_.py", line 47, in write_file
return write_file(*args, **kwargs)
File "C:\Python\Python35\lib\site-packages\pydicom-1.0.0a1-py3.5.egg\pydicom\filewriter.py", line 556, in write_file
file_meta = dataset.file_meta
File "C:\Python\Python35\lib\site-packages\pydicom-1.0.0a1-py3.5.egg\pydicom\dataset.py", line 322, in getattr
"'{0:s}'.".format(name))
AttributeError: Dataset does not have attribute 'file_meta'.
The storescp example on your project is empty.
Can you guide me as to what i am doing wrong here? The dicom file i am sending has the file meta tag in it.
This is great, but could these be ENUM or something similar so that the user doesn't have to remember or lookup the UUIDs?
ae = AE(scu_sop_class=['1.2.840.10008.1.1'])
--> ae = AE(scu_sop_class=[AE.VerificationSOPClass])
Low priority until I'm satisfied with the unit test coverage.
PresentationContextManager is to be replaced with PresentationService
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.