Artículo
· 5 abr, 2023 Lectura de 10 min

Envío de imagen DICOM entre IRIS for Health y un PACS

Bienvenidos miembros de la comunidad a un nuevo artículo, en esta ocasión trataremos las capacidades de interoperabilidad que nos proporciona IRIS for Health para trabajar con ficheros DICOM.

Para ello vamos a montar un pequeño ejemplo haciendo uso de Docker. Al final del artículo podréis encontrar la URL de acceso a GitHub por si queréis verlo en acción en vuestros propios equipos.

Pero antes de empezar vamos a explicar que es DICOM:

  • DICOM corresponde a las iniciales de Digital Imaging and Communication in Medicine y es un estándar de transmisión de imágenes y datos médicos. Este protocolo incluye la definición del formato del fichero y el protocolo de comunicación está basado en TCP/IP.
  • Los ficheros DICOM dan soporte a imágenes y documentación clínica (los ficheros DICOM pueden incluir imágenes propiamente dichas o documentos "dicomizados").
  • El protocolo DICOM permite definir una serie de operaciones sobre los propios documentos DICOM, estás operaciones van desde el almacenamiento de una imagen (C-STORE) hasta la búsqueda (C-FIND) o el movimiento de imágenes entre sistemas (C-MOVE). Aquí podemos ver qué servicios tenemos a nuestra disposición.
  • Los sistemas involucrados en una comunicación DICOM esperarán recibir como respuesta un documento DICOM a su vez.

Un ejemplo típico de arquitectura de trabajo con DICOM sería el siguiente:

General scheme of DICOM Network architecture

Dispondremos de una serie de "modalidades" (las cuales pueden ser los aparatos capturadores de imágenes o los sistemas receptores) identificados con el llamado AE Title o AET (Application Entity Title). Este AET será único para cada modalidad y deberá ser configurado en aquellas otras modalidades o sistemas que vayan a comunicarse con ella, de tal forma que se permite la comunicación entre ambas modalidades.

Como véis en el gráfico, las modalidades están configuradas para almacenar sus imágenes en un servidor de ficheros DICOM que puede pertenecer o no a un PACS (Picture Archiving and Communication System) que posteriormente es consultado desde una interfaz web del PACS. Cada vez es más común incluir un sistema de VNA (Vendor Neutral Archive) en las organizaciones que se encargue del almacenamiento y visualización centralizada de todos los archivos DICOM usados por la organización.

Por lo general en las modalidades más modernas se puede configurar el destino de las imágenes generadas, pero en muchas ocasiones puede ser necesario o bien realizar algún tipo de acción sobre los campos de la imagen DICOM (modificar el identificador del paciente, incluir el episodio clínico al con el que se relaciona, etc) o bien, por incapacidad de la modalidad, encargarse de la captura y el reenvio de la imagen generada al sistema responsable del archivado. Es en estos casos en los que es necesaria la existencia de un motor de integración que nos proporcione dicha funcionalidad, y ¡no hay ninguno mejor que IRIS for Health!

Para nuestro ejemplo vamos a plantear el siguiente escenario:

  • Una determinada modalidad está generando imágenes que necesitan ser enviadas a un PACS para su registro.
  • Nuestro servidor DICOM o PACS recibirá dichas imágenes y deberá reenviarlas a una determinada VNA.

Para simular nuestro PACS utilizaremos Orthanc, una herramienta open source que nos proveerá de las funcionalidades básicas de archivado y visualización de imágenes DICOM (más información aquí). Orthanc tiene la amabilidad de facilitarnos su uso mediante una imagen que podremos montar en Docker sin ninguna complicación. Finalmente desplegaremos un contenedor de IRIS for Health (depende de cuando leas este artículo es posible que la licencia haya podido caducar, en ese caso sólo deberás actualizar el fichero docker-compose del código) en el que podremos montar nuestra producción.

Vamos a echar un vistazo al docker-compose que hemos configurado:

version: '3.1'  # Secrets are only available since this version of Docker Compose
services:
  orthanc:
    image: jodogne/orthanc-plugins:1.11.0
    command: /run/secrets/  # Path to the configuration files (stored as secrets)
    ports:
      - 4242:4242
      - 8042:8042
    secrets:
      - orthanc.json
    environment:
      - ORTHANC_NAME=orthanc
    volumes:
      - /tmp/orthanc-db/:/var/lib/orthanc/db/
    hostname: orthanc
  iris:
    container_name: iris
    build:
      context: .
      dockerfile: iris/Dockerfile
    ports:
    - "52773:52773"
    - "2010:2010"
    - "23:2323"
    - "1972:1972"
    volumes:
    - ./shared:/shared
    command:
      --check-caps false
    hostname: iris
secrets:
  orthanc.json:
    file: orthanc.json

El acceso al visor web de Orthanc se realizará por el puerto 8042 (http://localhost:8042), la IP destinada a recibir imágenes vía TCP/IP será el 4242 y su configuración se realizará desde el archivo orthanc.json. El portal de gestión de nuestro IRIS for Health será el 52773.

Veamos que contiene orthanc.json:

{
    "Name" : "${ORTHANC_NAME} in Docker Compose",
    "RemoteAccessAllowed" : true,
    "AuthenticationEnabled": true,
    "RegisteredUsers": {
        "demo": "demo-pwd"
    },
    "DicomAssociationCloseDelay": 0,
    "DicomModalities" : {
        "iris" : [ "IRIS", "host.docker.internal", 2010 ]
      }
}

Como podéis observar hemos definido un usuario demo con una contraseña demo-pwd y hemos declarado una modalidad llamada IRIS que usará el puerto 2010 para recibir las imágenes desde Orthanc, "host.docker.internal" es la máscara usada por Docker para tener acceso a otros contenedores desplegados.

Comprobemos que despues de ejecutar los docker-compose build y docker-compose up -d podemos acceder a nuestro IRIS for Health y a Orthanc sin problemas:

IRIS for Health está correctamente desplegado.

Orthanc también funciona, pues venga, ¡al lío!

Accedamos al namespace llamado DICOM y abramos su producción. En ella podremos observar que tenemos los siguientes business components:

De momento vamos a fijarnos en los necesarios para gestionar el primer caso que hemos presentado. Una modalidad que genera imágenes DICOM pero desde la que no los podemos enviar a nuestro PACS. Para ello utilizaremos un Business Service de la clase estándar EnsLib.DICOM.Service.File configurado para leer todos los archivos .dcm presentes en el directorio /shared/durable/in/ y remitirlos al Business Process de la clase Workshop.DICOM.Production.StorageFile.

Analicemos más en detalle el método principal de dicho Business Process:

/// Messages received here are instances of EnsLib.DICOM.Document sent to this
/// process by the service or operation config items. In this demo, the process is ever
/// in one of two states, the Operation is connected or not.
Method OnMessage(pSourceConfigName As %String, pInput As %Library.Persistent) As %Status
{
    #dim tSC As %Status = $$$OK
    #dim tMsgType As %String
    do {
        
        If pInput.%Extends("Ens.AlarmResponse") {
            
            #; We are retrying, simulate 1st call
            #; Make sure we have a document
            Set pInput=..DocumentFromService
            $$$ASSERT(..CurrentState="OperationNotConnected")
        }
            
        #; If its a document sent from the service
        If pSourceConfigName'=..OperationDuplexName {
            
            #; If the operation has not been connected yet
            If ..CurrentState="OperationNotConnected" {
                
                #; We need to establish a connection to the operation,
                #; Keep hold of the incoming document
                Set ..DocumentFromService=pInput
                
                #; We will be called back at OnAssociationEstablished()
                Set tSC=..EstablishAssociation(..OperationDuplexName)
                
            } elseif ..CurrentState="OperationConnected" {
                
                #; The Operation is connected
                #; Get the CommandField, it contains the type of request, it should ALWAYS be present
                Set tMsgType=$$$MsgTyp2Str(pInput.GetValueAt("CommandSet.CommandField",,.tSC))
                If $$$ISERR(tSC) Quit
                #; We are only handling storage requests at present
                $$$ASSERT(tMsgType="C-STORE-RQ")
        		
        		// set patientId = pInput.GetValueAt("DataSet.PatientID",,.tSC)
        		// Set ^PatientImageReceived(patientId) = pInput.GetValueAt("DataSet.PatientName",,.tSC)
                #; We can forward the document to the operation
                Set tSC=..SendRequestAsync(..OperationDuplexName,pInput,0)
            }
            
        } elseif pSourceConfigName=..OperationDuplexName {
            
            #; We have received a document from the operation
            Set tMsgType=$$$MsgTyp2Str(pInput.GetValueAt("CommandSet.CommandField",,.tSC))
            If $$$ISERR(tSC) Quit
            #; Should only EVER get a C-STORE-RSP
            $$$ASSERT(tMsgType="C-STORE-RSP")

            #; Now close the Association with the operation, we will be called back at
            #; OnAssociationReleased()
            Set tSC=..ReleaseAssociation(..OperationDuplexName)
            
            #; Finished with this document
            Set ..DocumentFromService="",..OriginatingMessageID=""
        }
    } while (0)
    
    Quit tSC
}

Como podemos observar, esta clase está configurada para comprobar el origen del fichero DICOM, si no procede del Business Operation definido en el parámetro OperationDuplexName significará que debemos reenviarlo al PACS y por lo tanto el metadato del mensaje DICOM ubicado en la sección CommandSet bajo el nombre CommandField deberá ser del tipo C-STORE-RQ (solicitud de almacenamiento) previo establecimiento de la conexión. En esta URL podréis comprobar los diferentes valores que puede tomar dicho metadato (en hexadecimal).

En el caso de que el mensaje proceda del Business Operation indicado es señal de que corresponde con un mensaje DICOM de respuesta a nuestro DICOM enviado previamente, por ello está validando que el CommandField de dicho mensaje sea del tipo C-STORE-RSP.

Analicemos un poco más en detalle la configuración clave del Business Operation EnsLib.DICOM.Operation.TCP utilizado para el envío de nuestro DICOM a nuestro PACS vía TCP/IP:

Hemos declarado como IP el nombre del hostname especificado en el docker-compose en el que se encuentra desplegado Orthanc al igual que el puerto.

Hemos configurado dos elementos clave para el envío a PACS, el AET de nuestro IRIS for Health (IRIS) y el AET de nuestro PACS (ORTHANC). Sin esta configuración no es posible ningún envío de imágenes, ya que tanto IRIS como Orthanc validarán que la modalidad que envía/recibe tiene permiso para hacerlo.

¿Dónde se configura a qué modalidades se pueden enviar imágenes desde IRIS y qué modalidades nos pueden enviar imágenes? Muy sencillo, tenemos desde el portal de gestión de IRIS acceso a la funcionalidad de DICOM settings:

Desde este menú no sólo podemos indicar qué modalidades pueden enviarnos y a cuales podemos enviar imágenes DICOM, también podemos indicar qué tipo de imágenes vamos a poder enviar y recibir, de tal forma que podremos rechazar cualquier imagen que se salga de esa parametrización. Como veis en la imagen superior tenemos configurado conexiones tanto de IRIS a Orthanc como de Orthanc a IRIS. Por defecto Orthanc admite cualquier tipo de imagen, por lo que no necesitamos modificar nada en su configuración.

Para no tener problemas con las imagenes que podemos enviar y recibir desde IRIS configuraremos la llamada "Presentation Context", formada por "Abstract Syntax" compuestas por la combinación servicios DICOM (Store, Get, Find...) y un objeto (imágenes MR, CT, etc...) y la "Transfer Syntax" que define como se intercambia la información y como se representan los datos.

Bien, ya tenemos configurada cualquier posible conexión entre IRIS y Orthanc y viceversa. Procedamos a lanzar una prueba incluyendo un fichero DICOM en la ruta definida en nuestro Business Service:

¡Muy bien! Aquí tenemos registrados nuestros ficheros DICOM y podemos ver como han pasado por nuestra producción hasta ser enviados a Orthanc. Entremos en más detalles revisando un mensaje.

Aquí tenemos nuestro mensaje con su CommandField definido con el valor 1, correspondiente a C-STORE-RQ, revisemos ahora la respuesta que hemos recibido desde Orthanc:

Podemos ver que el valor del CommandFile 32769 corresponde en hexadecimal al 8001, que como hemos visto en esta URL equivale al tipo C-STORE-RSP. También podemos observar que el mensaje de respuesta es un mensaje DICOM que únicamente contiene los valores definidos en el Command Set.

Comprobemos desde Orthanc que hemos recibido correctamente los mensajes:

Aquí están nuestros mensajes archivados en nuestro PACS con éxito. ¡Objetivo conseguido! Ya podemos almacenar las imágenes DICOM de nuestra modalidad en nuestro PACS sin ningún problema.

En el próximo artículo trataremos el sentido opuesto de la comunicación, el envío desde el PACS a nuestra modalidad configurada en IRIS.

Aquí tenéis disponible el código utilizado para este artículo: https://github.com/intersystems-ib/workshop-dicom-orthanc

Comentarios (0)1
Inicie sesión o regístrese para continuar