# Load DICOM data via "networking" with python script?

**URL:** <https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693>\
**Category:** Support\
**Created:** [January 27, 2021, 12:17pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693 "2021-01-27T12:17:07Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [January 27, 2021, 12:17pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/1 "2021-01-27T12:17:07Z")

</div>

Hi,

what would be the recommended way to realize DICOM network access via python script? We would need to load all CT scans of a patient (defined by patient ID) of a certain time period into Slicers DICOM database, then process data later. DICOM networking via GUI works well here.

Thanks and best regards  
Rudolf

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [January 27, 2021, 2:05pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/2 "2021-01-27T14:05:18Z")

</div>

Hi @rbumm -

I’m guessing you mean traditional dicom networking (a.k.a. “DIMSE”) since that is the very common standard, compared to the newer DICOMweb standard (if your PACS or VNA supports DICOMweb that’s cleaner).

For DIMSE, Slicer uses a combination of C++ and python. The CFIND, CMOVE type transactions are in [CTK](https://github.com/commontk/CTK/tree/master/Libs/DICOM/Core) C++ but should all be exposed and usable in python. For the listener (CSTORE SCP) we use the dcmtk command line [executable in a separate process](https://github.com/Slicer/Slicer/blob/master/Modules/Scripted/DICOMLib/DICOMProcesses.py) in python.

If your PACS supports CGET, you are probably best to use [getscu](https://support.dcmtk.org/docs-dcmrt/getscu.html) into a temp directory and then load into the database. Let us know if you need more pointers.

---

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [January 27, 2021, 4:03pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/3 "2021-01-27T16:03:02Z")

</div>

Thank you Steve.

To be able to pull from our GE-PACS, we needed to

- specify Calling AETitle
- specify the StorageAETitle
- specify three port adresses
- the 4100 port
- **disable** CGET

in slicers DICOM Query/Receive. This works reliably.  
So probably getscu is not the appropriate script option?

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [January 27, 2021, 7:52pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/4 "2021-01-27T19:52:28Z")

</div>

Right, if you can’t use CGET then you need to be running the Slicer listener and then do the CFIND to get the list of instances and CMOVE to tell them to be transferred to Slicer’s listener. This can be done using dcmtk command line tools but that might not be cleanest.

It’s been been about a decade since we did all that code, but I’m pretty sure everything you need is exposed in python if you use the ctkDICOMQuery and ctkDICOMRetrieve classes. These are what the GUI uses under the hood and it looks like all the methods are exposed. The ctk doxygen is down, but if you look in the header files you’ll find what you need.

> **[commontk/CTK](https://github.com/commontk/CTK/tree/master/Libs/DICOM/Core)**
>
> master/Libs/DICOM/Core

And you can see how they are used by the widget code - it should just be a matter of using the same API from python. Hope that helps!

> <https://github.com/commontk/CTK/blob/master/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp#L151-L395>

---

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [February 2, 2021, 12:57pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/5 "2021-02-02T12:57:29Z")

</div>

Thank you Steve.

In my DICOM retrieval extension

`
import ctk
…
query = ctk.ctkDICOMQuery
query.setCalledAETitle(“SLICER”)
query.setHost(“SRVXXX”)
query.setPort(1234)
query.setPreferCGET(False)
QMap<QString,QVariant> filters
filters[“ID”] = QString(studyID)
query.setFilters(filters)
`

results in

![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/1/5/152d7397a38d84c0de6b2a1723f50ea2f8965d9e.png)

Am I missing anything here ?

Thanks & best regards  
Rudolf

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [February 2, 2021, 1:10pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/6 "2021-02-02T13:10:54Z")

</div>

`ctkDICOMQuery` is a Qt class and [`calledAETitle` is a property](https://github.com/commontk/CTK/blob/master/Libs/DICOM/Core/ctkDICOMQuery.h#L42-L46), which can be read/written in Python by its name (without `set...` prefix). See more information about how to translate C++ to Python [here](https://slicer.readthedocs.io/en/latest/developer_guide/api.html#doxygen-style-documentation).

---

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [February 2, 2021, 7:22pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/7 "2021-02-02T19:22:58Z")

</div>

Thanks Andras, but I am nowhere near it , sorry.

What I tried:

```
query = ctk.ctkDICOMQuery
query.CalledAETitle = "SLICER"
query.Host = "SRVxxx"
query.Port = nnnn
query.PreferCGET= False
filters = {'Name':'', 'Study':'', 'Series':'', 'ID':'', 'Modalities':'', 'StartDate':'', 'EndDate':''}  
filters["ID"] = studyID 
query.Filters = filters
query.query(???)

```

Is the problem related to

[https://www.slicer.org/wiki/Documentation/Nightly/Developers/Python\_scripting#Why\_can.27t\_I\_access\_my\_C.2B.2B\_Qt\_class\_from\_python](https://www.slicer.org/wiki/Documentation/Nightly/Developers/Python_scripting#Why_can.27t_I_access_my_C.2B.2B_Qt_class_from_python)

maybe ?

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [February 2, 2021, 8:49pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/8 "2021-02-02T20:49:38Z")

</div>

I’v added a complete working example to the script repository: [Documentation/Nightly/ScriptRepository - Slicer Wiki](https://www.slicer.org/wiki/Documentation/Nightly/ScriptRepository#Query_and_retrieve_data_from_a_PACS_using_classic_DIMSE_DICOM_networking)

I had to [fix a few small issues in the ctkDICOMQuery](https://github.com/commontk/CTK/commit/f670e1d0e513bae9d08257b883aaf669f546053d) class Python wrapping, so you can use this code with Slicer Preview Release that you download tomorrow or later.

---

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [February 2, 2021, 9:11pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/9 "2021-02-02T21:11:54Z")

</div>

Never ever saw such a great support anywhere.  
At least I was on the right track somehow 🙂

Thank you Andras!!  
I will implement that and report back during the next days …

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [February 2, 2021, 10:16pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/10 "2021-02-02T22:16:29Z")

</div>

It is nice to hear this. It would be great if you could write a few sentences about this good experience here:

> **[Success stories](https://discourse.slicer.org/c/community/success-stories/29)**
>
> This is the place where you can share how Slicer helped your work. Describe your project and how Slicer was useful in achieving your goal. Include reference to any publication(s) and if possible also add a few nice images or links to videos.

Having a list of success stories could help with getting grant funding for Slicer.

---

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [February 3, 2021, 10:16am UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/11 "2021-02-03T10:16:52Z")

</div>

Of course, Andras. Just done.

---

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [February 4, 2021, 11:52am UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/12 "2021-02-04T11:52:13Z")

</div>

I just tried the above mentioned code after installation of  
Slicer preview release 4.13.0 rev 29684 build 21-02-04

tempDb.open(’’)

throws the following error:

AttributeError: ctkDICOMDatabase has no attribute named ‘open’

---

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [February 4, 2021, 12:56pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/13 "2021-02-04T12:56:45Z")

</div>

Looked into [CTK/ctkDICOMDatabase.h at master · commontk/CTK · GitHub](https://github.com/commontk/CTK/blob/master/Libs/DICOM/Core/ctkDICOMDatabase.h)

and changed

tempDb.open(’’)

to

**tempDb.openDatabase(’’)**

DICOM Query and Receive works great now !

---

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [February 5, 2021, 4:46pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/14 "2021-02-05T16:46:40Z")

</div>

During the implementation of @lassoan 's script example I face the following problem:  
The script works well with the public DICOM server, but does not work within the hospital setting. It returns a “failed” move operation upon retrieving the images.

The query itself seems to work fine and finds three CT series for a specific patient ID (which is correct)

All parameters are set exactly as I would do in the DICOM networking widget.

Is it possible that the function

dicomRetrieve.setMoveDestinationAETitle(“XXXXX”)

is not exposed yet?

I also tried

dicomRetrieve.moveDestinationAETitle = “XXXXX”

with no luck.

I need to set the retriever (“XXXXX”, the computer which makes the DICOM retrieve call) in the widget form field “Storage AETitle”, otherwise, the widget networking call would not work.

Second info:

I normally use it from the widget with CGET unchecked, but from the widget, it also works with CGET checked.

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [February 5, 2021, 7:30pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/15 "2021-02-05T19:30:38Z")

</div>

Debugging DIMSE networking is always a challenge. Both sides need to be configured just right, but it sounds like you are close.

> [@rbumm](#):
>
> Is it possible that the function
> 
> dicomRetrieve.setMoveDestinationAETitle(“XXXXX”)
> 
> is not exposed yet?
> 
> I also tried
> 
> dicomRetrieve.moveDestinationAETitle = “XXXXX”
> 
> with no luck.

Hmm, these appear be exposed correctly and that should match what’s happening in the GUI implementation.

> [@rbumm](#):
>
> I normally use it from the widget with CGET unchecked, but from the widget, it also works with CGET checked.

In this case then you could try using the `bool ctkDICOMRetrieve::getStudy(const QString& studyInstanceUID)` or `bool getSeries( const QString& studyInstanceUID, const QString& seriesInstanceUID )` methods? If the PACS side supports it then this is less tricky to configure.

---

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [February 6, 2021, 8:11am UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/16 "2021-02-06T08:11:16Z")

</div>

This widget call works with query and retrieve:

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/7/c/7ca30040a4abf88d7977c8cc731a546c499b8b71.png)

The next call works with query, but not with receive (I only changed the Storage AETitle to a wrong parameter):

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/f/2/f2ba0d809ab35abae74de3a72ef5b6b7cf6d108b.png)

I get only a short popup and no DICOM download.

So retrieve works from the widget and setting the Storage AE Title correctly is crucial.

---

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [February 6, 2021, 9:33am UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/18 "2021-02-06T09:33:56Z")

</div>

Then I run:

```auto
# Query
dicomQuery = ctk.ctkDICOMQuery()
dicomQuery.callingAETitle = "CKL1757"
dicomQuery.calledAETitle = "GPACS"
dicomQuery.host = "S078"
dicomQuery.port = 4001
dicomQuery.preferCGET = False
dicomQuery.filters = {'ID':'xxxxxxxxxx', 'Modalities':'CT'}    

# temporary in-memory database for storing query results
tempDb = ctk.ctkDICOMDatabase()
tempDb.openDatabase('')
dicomQuery.query(tempDb)

# Retrieve
dicomRetrieve = ctk.ctkDICOMRetrieve()
dicomRetrieve.callingAETitle = dicomQuery.callingAETitle
dicomRetrieve.calledAETitle = dicomQuery.calledAETitle
dicomRetrieve.host = dicomQuery.host
dicomRetrieve.port = dicomQuery.port
dicomRetrieve.moveDestinationAETitle = "CKL1757"
dicomRetrieve.setDatabase(slicer.dicomDatabase)

for study in dicomQuery.studyInstanceUIDQueried:
    print(f"ctkDICOMRetrieveTest: Retrieving >{study}<")
    slicer.app.processEvents()
    if dicomQuery.preferCGET:
        print(f"getting ...")
        success = dicomRetrieve.getStudy(study)
    else:
        print(f"moving ...")
        success = dicomRetrieve.moveStudy(study)
    print(f" - {'success' if success else 'failed'}")

slicer.dicomDatabase.updateDisplayedFields()

```

and get three times "moving → “failed” for the three correctly detected CT datasets

Same happens if I set

dicomQuery.preferCGET = True

→ three times "getting → “failed” for the three correctly detected CT datasets

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/1/0/10ca3d32967cb1fafa348800df22b0a6175c1127.png)

Any idea what could be the difference between the widget call (working) and the script call (failed) ?

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [February 6, 2021, 2:13pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/19 "2021-02-06T14:13:32Z")

</div>

The widget also sets how associations are handled, try to set that in your script:

> <https://github.com/commontk/CTK/blob/f670e1d0e513bae9d08257b883aaf669f546053d/Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp#L299>

---

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [February 6, 2021, 4:41pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/20 "2021-02-06T16:41:10Z")

</div>

Unfortunately, neither

dicomRetrieve.setKeepAssociationOpen(True)

nor

dicomRetrieve.keepAssociationOpen = True

changed anything …

---

<div class="post-metadata">

**Author:** ![rbumm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/rbumm/32/9404_2.png) [@rbumm](https://discourse.slicer.org/u/rbumm)\
**Post date:** [February 6, 2021, 5:51pm UTC](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693/21 "2021-02-06T17:51:42Z")

</div>

The observation with the widget is: Takes quite a long time interval until the popup “Got a move request” is displayed (20 s), then it is again quite slow but realiable to retieve the data.

With the script, the query takes place almost instantly, the failed retrieval requests print messages are within 3 s.  
I suspect a timing problem … if I could I would check logs on the server side, but I have no access myself and it is extremely difficult to get hold of the PACS guy

[Next page](https://discourse.slicer.org/t/load-dicom-data-via-networking-with-python-script/15693.md?page=2)
