Giter VIP home page Giter VIP logo

pynetdicom's Introduction

unit-tests type-hints doc-build test-coverage Python version PyPI version DOI

pydicom

pydicom is a pure Python package for working with DICOM files. It lets you read, modify and write DICOM data in an easy "pythonic" way. As a pure Python package, pydicom can run anywhere Python runs without any other requirements, although if you're working with Pixel Data then we recommend you also install NumPy.

Note that pydicom is a general-purpose DICOM framework concerned with reading and writing DICOM datasets. In order to keep the project manageable, it does not handle the specifics of individual SOP classes or other aspects of DICOM. Other libraries both inside and outside the pydicom organization are based on pydicom and provide support for other aspects of DICOM, and for more specific applications.

Examples are pynetdicom, which is a Python library for DICOM networking, and deid, which supports the anonymization of DICOM files.

Installation

Using pip:

pip install pydicom

Using conda:

conda install -c conda-forge pydicom

For more information, including installation instructions for the development version, see the installation guide.

Documentation

The pydicom user guide, tutorials, examples and API reference documentation is available for both the current release and the development version on GitHub Pages.

Pixel Data

Compressed and uncompressed Pixel Data is always available to be read, changed and written as bytes:

>>> from pydicom import dcmread
>>> from pydicom.data import get_testdata_file
>>> path = get_testdata_file("CT_small.dcm")
>>> ds = dcmread(path)
>>> type(ds.PixelData)
<class 'bytes'>
>>> len(ds.PixelData)
32768
>>> ds.PixelData[:2]
b'\xaf\x00'

If NumPy is installed, Pixel Data can be converted to an ndarray using the Dataset.pixel_array property:

>>> arr = ds.pixel_array
>>> arr.shape
(128, 128)
>>> arr
array([[175, 180, 166, ..., 203, 207, 216],
       [186, 183, 157, ..., 181, 190, 239],
       [184, 180, 171, ..., 152, 164, 235],
       ...,
       [906, 910, 923, ..., 922, 929, 927],
       [914, 954, 938, ..., 942, 925, 905],
       [959, 955, 916, ..., 911, 904, 909]], dtype=int16)

Decompressing Pixel Data

JPEG, JPEG-LS and JPEG 2000

Converting JPEG, JPEG-LS or JPEG 2000 compressed Pixel Data to an ndarray requires installing one or more additional Python libraries. For information on which libraries are required, see the pixel data handler documentation.

RLE

Decompressing RLE Pixel Data only requires NumPy, however it can be quite slow. You may want to consider installing one or more additional Python libraries to speed up the process.

Compressing Pixel Data

Information on compressing Pixel Data using one of the below formats can be found in the corresponding encoding guides. These guides cover the specific requirements for each encoding method and we recommend you be familiar with them when performing image compression.

JPEG-LS, JPEG 2000

Compressing image data from an ndarray or bytes object to JPEG-LS or JPEG 2000 requires installing the following:

RLE

Compressing using RLE requires no additional packages but can be quite slow. It can be sped up by installing pylibjpeg with the pylibjpeg-rle plugin, or gdcm.

Examples

More examples are available in the documentation.

Change a patient's ID

from pydicom import dcmread

ds = dcmread("/path/to/file.dcm")
# Edit the (0010,0020) 'Patient ID' element
ds.PatientID = "12345678"
ds.save_as("/path/to/file_updated.dcm")

Display the Pixel Data

With NumPy and matplotlib

import matplotlib.pyplot as plt
from pydicom import dcmread
from pydicom.data import get_testdata_file

# The path to a pydicom test dataset
path = get_testdata_file("CT_small.dcm")
ds = dcmread(path)
# `arr` is a numpy.ndarray
arr = ds.pixel_array

plt.imshow(arr, cmap="gray")
plt.show()

Contributing

We are all volunteers working on pydicom in our free time. As our resources are limited, we very much value your contributions, be it bug fixes, new core features, or documentation improvements. For more information, please read our contribution guide.

If you have examples or extensions of pydicom that don't belong with the core software, but that you deem useful to others, you can add them to our contribution repository: contrib-pydicom.

pynetdicom's People

Contributors

alcir avatar amosonn avatar cancan101 avatar catetrai avatar chsasank avatar dependabot[bot] avatar dimitripapadopoulos avatar edmcdonagh avatar eschivo avatar kosh-jp avatar martink84 avatar moloney avatar mrbean-bremen avatar mself avatar musicinmybrain avatar olieidel avatar or150 avatar patmun avatar pipitone avatar quantifiedcode-bot avatar r1b avatar rongcong avatar scaramallion avatar simonbiggs avatar sjswerdloff avatar sloria avatar tirkarthi avatar vsaase avatar woonchancho avatar yasushi-saito avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pynetdicom's Issues

Intermittent OSError during association abort

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.

ae.start() method works on full load (100% CPU)

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?

pynetdicom3 vs. pynetdicom

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!

Move transport connection code deeper and consolidate it

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.

AE association related callbacks not all implemented?

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.

pydicom.uid.InvalidUID Exception when sending C-STORE to public dicom server

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

Improve end user documentation

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

  • Configure Circle CI to build using sphinx/numpydoc and output to Github Pages

Also need to update/expand the user documentation:

  • Add examples for the various SCU/SCP services
  • Add examples for how to write DICOM files when acting as an SCP
  • Add examples for handling the file meta when writing to DICOM file format
  • Check that included examples all work

Bytes differences for Find vs Echo SCP

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!

DIMSE-C Statuses not fully supported

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.

Intermittent ValueError during association release

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.

Improve handling of elements with ambiguous VRs

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)

REQ: UUID as an ENUM?

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])

Moving from dcmtk to pynetdicom3

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.

Req: StoreScp example

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.

C-MOVE SCU/SCP not possible?

  • Due to the way the program structure works, its not possible to run a C-MOVE operation with the SCU as the move destination due to a C-MOVE invoking a separate Association request from the SCP for the C-STORE request (unlike C-GET which uses the same Association to receive the C-STORE request).
  • I'm not actually sure if this is a bug or if this is the desired behaviour. Why use a C-MOVE to fetch to the running AE instead of a C-GET?

Genericise PDU class methods

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.

Dicoms transferring slower than pynetdicom(0.8.1)

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

When user identity is requested, terminate association if identity incorrect

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)

Intermittent AttributeError after DUL start but before Association attempted

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.

Version 1.0.0 Release

The following should be completed before the v1.0.0 release:

Tasks

  • Update the DIMSE Status to use pydicom.dataset.Dataset objects and determine a more flexible approach for the Service Class DIMSE Statuses (and make sure all the Statuses are supported).
  • Implement support for extended negotiation items.
    • SCP/SCU Role Selection (#229)
    • Implement AE callback for User Identity (#233)
    • Asynchronous Window (only use of extended negotiation supported, not actual async operations) (#236)
    • SOP Class Extended (#236)
    • SOP Class Common Extended (needs ACSE update first) (#249)
  • Update the end user documentation and add it to github pages (complementary with pydicom).
  • Replace the PresentationContextManager, because it sucks (#229).
  • Update pdu module (#165)
  • Update ACSE methods to use PEP8 naming, and to have better control over association. (#238)
  • As per @patmun, change package name to pynetdicom (thanks!) (#250)
  • Add context and info parameters to the on_c_* callbacks (#167)
  • Implement DIMSE-N services (SCU only) #216, #219, #225, #226

Release related

  • Get a pypi account and fix the setup/install config as it requires.
  • Wait for pydicom 1.2 release and use it as the supported version.

Get the senders IP address.

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.