Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PACS integration with move and query operations #644

Open
wants to merge 59 commits into
base: master
Choose a base branch
from

Conversation

hvini
Copy link

@hvini hvini commented Aug 13, 2023

What?

This PR adds the PACS integration with C-echo, C-query and C-move operations

Why?

These changes allows users to:

  • Configure they're local dicom storage server
  • Manage (add and remove) dicom nodes
  • Ping a node to know the availability
  • Query a patient by his name
  • Download patient instances and load it into the system

How?

Since gdcm is experiencing a problem that causes the application to quit, PyNetDicom was used to implement the dimse operations
UI Panels was built using python wx widget library

Testing?

I've added a docker-compose service to easily start a local PACS server (ORTHANC) and then test the functionalities.
After install docker and docker compose, run the service with docker-compose up command.
orthanc.json file, in root, allows to configure the PACS server. dicommodalities refers to the servers that has permission to make requests on PACS. on c-move operation a local server starts (can be configured on application) with ip 0.0.0.0 and port 11120. since docker cant access 0.0.0.0 or 127.0.0.1, the local ip address should be added on orthanc.json, so in this way, the orthanc server starts to allows incoming requests from the local server.
see orthanc documentation for more information about config file https://book.orthanc-server.com/users/configuration.html.
Screen Shot 2023-08-13 at 19 24 29

Screenshots

Storage server configuration into preferences

Screen Shot 2023-08-13 at 19 41 15

Retrieve DICOM FROM PACS panel

Screen Shot 2023-08-13 at 19 41 39

Patient query operation

Screen Shot 2023-08-13 at 19 42 36

Moved files

Screen Shot 2023-08-13 at 19 43 45

hvini added 30 commits June 4, 2023 10:17
@hvini
Copy link
Author

hvini commented Aug 17, 2023

I've added a progress indicator for when it's moving and in case of an exception it shows a message with the status code. hope now it gets better for user to known what is happening

@hvini
Copy link
Author

hvini commented Dec 17, 2023

Now c-find and c-move also works with dcm4chee server. The dcm4chee docker compose file was added on root of project and it can be executed by running docker-compose -f dcm4chee-compose.yml up

Testing

C-FIND

Server AET is DCM4CHEE, port 11112 and host can be localhost. no additional configuration is required on server side.

C-MOVE

To be able to move files from server to inVesalius, it's necessary to configure the inVesalius server on dcm4chee panel. To configure the server:

  • Create a new AET by accessing http://localhost:8080/dcm4chee-arc/ui2/device/aelist.
    Screen Shot 2023-12-17 at 09 21 07
  • On AET registration, the following informations should be provided:
    Name: can be anyone
    Hostname: the local ip address
    PORT: 11120
    AET Title: PYNETDICOM
  • After register the new AET, the CT Image Storage transfer capability should be assigned to it.
    Screen Shot 2023-12-17 at 09 22 33
    Screen Shot 2023-12-17 at 09 23 01
    Screen Shot 2023-12-17 at 09 23 31

With these configurations files can be moved from dcm4chee to inVesalius

@tfmoraes
Copy link
Member

Hi @hvini! Sorry for not reviewing it yet. I wasn't able to download images from dcm4chee using c-move. I tried using Weasis to test too but I had problems. With Weasis I was able to use C-Get instead of C-Move. So I tried to use C-Get in InVesalius too. I had to make some change. Here is the diff

diff --git a/invesalius/net/dicom.py b/invesalius/net/dicom.py
index 457f7708..323c645c 100644
--- a/invesalius/net/dicom.py
+++ b/invesalius/net/dicom.py
@@ -1,5 +1,6 @@
 from pynetdicom.sop_class import (PatientRootQueryRetrieveInformationModelFind,
-                                  PatientRootQueryRetrieveInformationModelMove)
+                                  PatientRootQueryRetrieveInformationModelMove, PatientRootQueryRetrieveInformationModelGet, CTImageStorage)
+                                  
 from pydicom.dataset import Dataset
 from datetime import datetime
 import pynetdicom
@@ -249,9 +250,11 @@ class DicomNet:
 
             return 0x0000
 
-        ae = pynetdicom.AE()
-        ae.add_requested_context(PatientRootQueryRetrieveInformationModelMove)
-        ae.supported_contexts = pynetdicom.StoragePresentationContexts
+        ae = pynetdicom.AE(self.aetitle_call)
+        ae.add_requested_context(PatientRootQueryRetrieveInformationModelGet)
+        ae.add_requested_context(CTImageStorage)
+        ae.supported_contexts = pynetdicom.AllStoragePresentationContexts
+        role = pynetdicom.build_role(CTImageStorage, scp_role=True)
 
         handlers = [(pynetdicom.evt.EVT_C_STORE, handle_store)]
 
@@ -262,17 +265,18 @@ class DicomNet:
         ds.SeriesInstanceUID = values['serie_id']
 
         assoc = ae.associate(self.address, int(
-            self.port), ae_title=self.aetitle)
+            self.port), ae_title=self.aetitle, ext_neg=[role], evt_handlers=handlers)
         if assoc.is_established:
 
-            scp = ae.start_server(
-                (self.ip_call, int(self.port_call)), ae_title=self.aetitle_call, block=False, evt_handlers=handlers)
+            # scp = ae.start_server(
+            #     (self.ip_call, int(self.port_call)), ae_title=self.aetitle_call, block=False, evt_handlers=handlers)
             total_responses = values['n_images']
             progress_callback(completed_responses, total_responses)
             try:
 
-                responses = assoc.send_c_move(
-                    ds, self.aetitle_call, PatientRootQueryRetrieveInformationModelMove)
+                print(f'\n\n\n\n{self.aetitle_call}\n\n\n\n')
+                responses = assoc.send_c_get(
+                    ds, PatientRootQueryRetrieveInformationModelGet)
                 for (status, identifier) in responses:
 
                     # pending status, keep moving and updating progress
@@ -300,7 +304,7 @@ class DicomNet:
             finally:
 
                 assoc.release()
-                scp.shutdown()
+                # scp.shutdown()
 
         else:
 

@hvini
Copy link
Author

hvini commented Mar 31, 2024

hello.

I will check if dcm4chee and orthanc keeps working with c-get instead c-move and also will test the weasis tool. if i dont find any problem i will integrate your changes on code.

which problem you had with dcm4chee? maybe i can help

@tfmoraes
Copy link
Member

tfmoraes commented Apr 1, 2024

Hi @hvini. I have some permission problems with dcm4chee.

@hvini
Copy link
Author

hvini commented Jun 26, 2024

hello, i tested the changes and confirmed that works for both orthanc and dcm4chee, also, with these changes there is no need anymore to configure the aet and transfer capability on dcm4chee, so, now the user needs only to upload his files on server and then will be able to query and load on invesalius.

server credentials are:

0.0.0.0, 4242, ORTHANC
0.0.0.0, 11112, DCM4CHEE

there is 2 docker files on root that allows to start orthanc and dcm4chee server, the command to execute the files is:
docker-compose -f file_name.yml up

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants