Download Descargar el proyecto PDF - ELAI-UPM
Document related concepts
no text concepts found
Transcript
Desarrollo de aplicación DICOM mediante librerı́as JDT (Java Dicom Toolkit) José Ma Onrubia 14 de julio de 2003 Índice general 1. Introducción y objetivos 1 2. Estado de la técnica. 2.1. DICOM (Digital Imaging and Communication in Medicine). . 2.1.1. Proceso distribuido. . . . . . . . . . . . . . . . . . . . 2.1.2. Conceptos generales de DICOM. . . . . . . . . . . . . 2.1.3. Conceptos de red DICOM. . . . . . . . . . . . . . . . 2.1.4. Conectividad (Connectivity) . . . . . . . . . . . . . . . 2.1.5. Estándar DICOM . . . . . . . . . . . . . . . . . . . . 2.2. Instancias SOP de imágenes DICOM (DICOM Image SOP Instances) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.1. Modelo de información de las imágenes . . . . . . . . 2.2.2. Instancias imagen SOP (Image SOP Instances) . . . . 2.2.3. Relaciones e indentificación . . . . . . . . . . . . . . . 2.2.4. Clasificación de los datos de imagen . . . . . . . . . . 2.2.5. Extensión de la información . . . . . . . . . . . . . . . 2.2.6. Tipos de imágenes . . . . . . . . . . . . . . . . . . . . 2.2.7. Aplicación de los datos de imágenes . . . . . . . . . . 3 3 3 5 15 22 25 3. Estudio de librerias DCMTK. 3.1. Visión general. . . . . . . . . . . . . . . . . . . 3.1.1. Introducción. . . . . . . . . . . . . . . . 3.1.2. Descripción. . . . . . . . . . . . . . . . . 3.1.3. Estructura de datos DICOM. . . . . . . 3.1.4. Servicios de red DICOM. . . . . . . . . 3.1.5. Intercambio de medios de comunicación. 3.1.6. Estatuto de conformidad. . . . . . . . . 3.1.7. Conclusión. . . . . . . . . . . . . . . . . 3.2. Instalación dcmtk351. . . . . . . . . . . . . . . 3.2.1. Dcmnet. . . . . . . . . . . . . . . . . . . 3.2.2. Dcmjpeg. . . . . . . . . . . . . . . . . . 3.3. DicomScope. . . . . . . . . . . . . . . . . . . . 3.3.1. Instalación. . . . . . . . . . . . . . . . . 51 51 51 52 52 53 53 54 54 55 57 58 58 59 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 28 32 32 36 42 43 47 4 ÍNDICE GENERAL 3.3.2. 3.3.3. 3.3.4. 3.3.5. Browser. . . . Viewer. . . . Print. . . . . Process Log. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4. JDT (Java DICOM Toolkit) 4.1. Introducción. . . . . . . . . . . . . . . . . . 4.2. Guia de usuario de JDT. . . . . . . . . . . . 4.2.1. Terminologı́a. . . . . . . . . . . . . . 4.2.2. Conjunto de datos (Datasets). . . . 4.2.3. Depósitos. . . . . . . . . . . . . . . . 4.2.4. Imágenes en JDT. . . . . . . . . . . 4.3. Estructura de JDT(Java DICOM Toolkit). . 4.3.1. Árbol de clases. . . . . . . . . . . . . 4.3.2. Paquetes. . . . . . . . . . . . . . . . 4.3.3. Conclusiones. . . . . . . . . . . . . . . . . . . . . . . . . . . . 5. Estudio de JAVA. 5.1. Introducción . . . . . . . . . . . . . . . . . . . 5.2. Estudio a través de un ejemplo. . . . . . . . . 5.2.1. Clase Ejemplo1 . . . . . . . . . . . . . 5.2.2. Clase Geometrı́a. . . . . . . . . . . . . 5.2.3. Clase Rectángulo. . . . . . . . . . . . 5.2.4. Clase Circulo. . . . . . . . . . . . . . . 5.2.5. Interface Dibujable . . . . . . . . . . . 5.2.6. Clase RectanguloGrafico. . . . . . . . 5.2.7. Clase CirculoGrafico. . . . . . . . . . . 5.2.8. Clase PanelDibujo. . . . . . . . . . . . 5.2.9. Clase VentanaCerrable. . . . . . . . . 5.3. Nomenclatura habitual en la programación en 5.4. Estructura general de un programa java. . . . 5.4.1. Concepto de Clase. . . . . . . . . . . . 5.4.2. Herencia. . . . . . . . . . . . . . . . . 5.4.3. Concepto de Interface. . . . . . . . . . 5.4.4. Concepto de Package. . . . . . . . . . 5.4.5. La jerarquı́a de clases de Java (API). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 60 61 62 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 65 66 66 66 73 75 80 80 81 83 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . java. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 85 87 87 92 93 94 96 97 99 99 103 105 106 106 107 107 107 108 . . . . . . . . . . . . . . . . . . . . 6. Desarrollo de nuestra aplicación mediante JDT, JDK y JBuilder7. 6.1. Introducción. . . . . . . . . . . . . . . . . . . . . . . . . 6.2. Uso de JBuilder 7.0 . . . . . . . . . . . . . . . . . . . . 6.2.1. Introducción. . . . . . . . . . . . . . . . . . . . . 6.2.2. Instalación de JBuilder. . . . . . . . . . . . . . . 6.2.3. Creación de una aplicación JBuilder. . . . . . . . 1.4.1 109 . . . 109 . . . 110 . . . 110 . . . 110 . . . 110 6.3. Uso de librerı́as JDK 1.4.1 . . . . . . . . . . . 6.3.1. Introducción e instalación. . . . . . . . 6.3.2. Estructura de JDK 1.4.1. . . . . . . . 6.3.3. Configuración de JDK en JBuilder. . . 6.4. Instalación de JDT en JBuilder. . . . . . . . 6.5. Implementación de nuestra aplicación. . . . . 6.5.1. Introducción. . . . . . . . . . . . . . . 6.5.2. Estructura de la GUI. . . . . . . . . . 6.5.3. Panel VisorDicom. . . . . . . . . . . . 6.5.4. Panel Crear DICOM. . . . . . . . . . 6.5.5. Panel Procesamiento. . . . . . . . . . . 6.5.6. Panel Cliente/Servidor. . . . . . . . . 6.6. Distribución de la aplicación. . . . . . . . . . 6.6.1. Generación del archivo ejecutable java. 6.6.2. Ejecución del archivo ejecutable java. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 132 133 138 139 142 142 143 144 158 168 172 173 173 175 A. ESTÁNDAR DICOM PARTE 1 177 A.1. Introducción. . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 A.2. Alcance y campo de aplicación. . . . . . . . . . . . . . . . . . 180 A.3. Definiciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 A.4. Sı́mbolos y abreviaturas. . . . . . . . . . . . . . . . . . . . . . 182 A.5. Objetivos del estándar DICOM. . . . . . . . . . . . . . . . . . 183 A.6. Contenido del estándar DICOM. . . . . . . . . . . . . . . . . 184 A.6.1. Estructura del documento. . . . . . . . . . . . . . . . 184 A.6.2. PS 3.2: Conformidad . . . . . . . . . . . . . . . . . . . 184 A.6.3. PS 3.3: Definiciones de objetos de la información (IOD´s) . . . . . . . . . . . . . . . . . . . . . . . . . . 186 A.6.4. PS 3.4: Especificaciones de las clases de servicio. . . . 188 A.6.5. PS 3.5: Estructura de datos y semántica . . . . . . . . 188 A.6.6. PS 3.6: Diccionario de datos . . . . . . . . . . . . . . . 189 A.6.7. PS 3.7: Intercambio de mensajes. . . . . . . . . . . . . 189 A.6.8. PS 3.8: Apoyo de comunicación de red para el intercambio de mensaje . . . . . . . . . . . . . . . . . . . . 190 A.6.9. PS 3.9: Soporte de comunicación para el intercambio de mensajes punto por punto. . . . . . . . . . . . . . . 191 A.7. Relaciones entre las partes del estándar. . . . . . . . . . . . . 193 6 ÍNDICE GENERAL Capı́tulo 1 Introducción y objetivos Dicom (Digital Imaging and Communications in Medicine) es el estándar industrial para transferencia de imágenes digitales e información médica entre computadoras. Dicom permite la comunicación digital entre equipos de diagnóstico, terapéuticos y entre sistemas de diferentes fabricantes. Se ve entonces, la gran importancia de este estándar, ya que da la posibilidad de interconectar sistemas informáticos de diferentes fabricantes y hace posible que se comuniquen entre sı́, lo que en un hospital, donde los aparatos médicos son de muchas marcas diferentes, debido a la especialización, es tremendamente interesante y necesario. La estandarización de archivos médicos hace posible que, mediante una transmisión segura, los datos de los pacientes puedan viajar de departamento en departamento, de hospital en hospital, lo que hace que esa información pueda ser vista remotamente de la zona de adquisición de las imágenes. Esto permite que los médicos puedan diagnosticar desde su casa, buscar diferentes opiniones de otros médicos expertos de una forma sencilla y rápida, un orden y estructura de los datos más efectivo y seguro, y muchos otros tipos de ventajas. Objetivos Los objetivos de este estándar son: lograr una interfaz común para todos los dispositivos de imágenes (tomografı́a, resonancia magnética, ultrasonido, rayos x, etc). intentar desligar Dicom de las instituciones que lo desarrollan para que realmente pueda ser un estándar independiente. debe ser aplicable a toda la esfera de las imágenes médicas, desde su 1 CAPÍTULO 1. INTRODUCCIÓN Y OBJETIVOS José Ma Onrubia transmisión hasta el tratamiento e impresión. De momento Dicom facilita, pero no garantiza, por si mismo que se cumplan todos los objetivos que se intentan lograr en los sistemas de gestión de imágenes. Debido a todas estas ventajas y posibilidades para que los hospitales y la medicina en general funcione mejor, es de gran interés avanzar por estas lı́neas de trabajo. Por esto lo interesante de avanzar por este camino. La creciente utilización de sistemas de adquisición y tratamiento digital de imágenes médicas ha hecho necesaria la adopción de estándares que posibiliten el intercambio de éstas tanto dentro de las propias instituciones como fuera de ellas. El estándar DICOM 3.0 nace en el año 1993, a partir de un rediseño completo de la publicación normalizada No 300-1988 de ACR-NEMA y pertenece al campo de la Informática Médica por lo que, en principio, esta norma se solapa con otras de este campo. El avance de la estandarización poco a poco va adquiriendo todo su significado: Se homogeneizan los estándares de codificación de la información y del conjunto de datos resultantes de utilizar los Objetos de información (imágenes) con las Clases de Servicio (impresión, almacenamiento, etc), ası́ como se especifican varias técnicas de compresión normalizadas (JPEG con y sin perdidas). Se muestran las reglas de codificación que se deben cumplir para construir un secuencia de datos para ser transmitida como un mensaje. Se especifica los servicios de comunicaciones y los protocolos necesarios para, en un entorno de red, intercambiar mensajes. Se define la utilización de un conjunto de protocolos OSI (Interconexión de Sistemas Abiertos) para asegurar una comunicación eficiente y que soporte una amplia variedad de tecnologı́as de red basadas en normas internacionales como la ISO 8802-3 CSMA/CD (la famosa red Ethernet), ATM (muy en boga actualmente), X.25, etc. Y como protocolo de transporte se puede utilizar el famoso TCP/IP que hay que recordar que es un protocolo de propósito general, por lo que el sistema, en este apartado, es realmente abierto y compatible con la mayorı́a de las redes que se están instalando actualmente en los centros sanitarios. 2 r GVA-ELAI-UPMPFC0074-03 Capı́tulo 2 Estado de la técnica. 2.1. DICOM (Digital Imaging and Communication in Medicine). En esta sección se van a explicar un número de conceptos definidos por el estándar de DICOM. Primero se describe como punto de partida el modelo de un proceso distribuido, a partir del cual vamos a introducirnos en los conceptos DICOM. Se explican las partes que tratan con la información (Clases de Servicio) y otras cuestiones. En las dos siguientes subsecciones se describen el intercambio a través de la red de la información. Finalmente, se dan las caracterı́sticas que aseguran la conectividad y una descripción de las partes del estándar. 2.1.1. Proceso distribuido. Un simple modelo de un proceso distribuido (figura 2.1) servirá para explicar el mecanismo y terminologı́a usada en el estándar DICOM. Un proceso distribuido está formado al menos por dos procesos que comparten información y confı́an el uno en el otro. Varios procesos distribuidos actuando juntos proporcionan servicios para sistemas en entornos como departamentos de radiologı́a. 3 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Figura 2.1: Proceso distribuido Antes de que los procesos puedan actuar juntos, una serie de temas tienen que ser tratados. Tienen que estar de acuerdo en la información que se va ha intercambiar y seleccionar las operaciones que cada parte realizará: El papel de cada parte debe ser definido como cliente o como servidor. La parte que utiliza la operatividad de la otra, tienen el papel de cliente. La parte contraria actuando sobre un modelo tiene el papel de servidor. El funcionamiento de ambas partes viene definida por la relación que comparten. La relación define que parte y bajo que condición toma la iniciativa en el proceso. En muchos casos los clientes provocan el proceso, pero a veces lo hace el servidor. Además de los papeles que desempeñan, ambas partes tienen que estar de acuerdo en la información que van a intercambian. La información está definida por el contexto del servicio que el proceso distribuido está realizando. La operación define como debe ser procesada la información intercambiada en la otra parte, tal como almacenar información, devolver un resultado, etc. La combinación del contexto, relación, operaciones e información es la piedra fundamental del procesamiento distribuido y tiene que definirse antes de que una aplicación se realice (un intercambio). Todos estas cuestiones son parte del dominio de la aplicación (application domain) de los procesos distribuidos. Estos no se ocupan de la forma en que la información se intercambia, pero cuentan con los servicios de menor nivel (p.e. TCP/IP) suministrados por el dominio del intercambio (exchange domain) para 4 r GVA-ELAI-UPMPFC0074-03 2.1. DICOM (DIGITAL IMAGING AND COMMUNICATION IN José Ma Onrubia MEDICINE). poder hacer frente al proceso de comunicación. Ambas partes, cliente y servidor, tienen que ser capaces de emitir peticiones a los servicios de menor nivel. Los servicios de menor nivel llevarán el intercambio y estarán ocultos para el dominio de la aplicación del cliente o servidor. La parte que solicita los servicios es el usuario del servicio (service user ). El equivalente es el proveedor del servicio (service provider ). Ambas partes pueden tener distintas implementaciones, pero comparten el mismo conocimiento sobre como se intercambian los datos (protocolo) y tienen el mismo interface lógico (formato de petición) entre sı́. Ambas partes deben determinar cómo viene representada la información en el formato de bit/byte. El proveedor del servicio debe determinar en qué formato la información fue transferida y convertida a la representación esperada por el dominio de la aplicación. La representación es conocida entre el usuario y el proveedor del servicio en cada parte. Después del intercambio, la información presentada a los procesos utilizando la información es igual en ambas partes, independientemente de como fuera intercambiada. El intercambio fı́sico entre los proveedores del servicio puede ser vı́a network o media. Cada mecanismo tiene su propia forma de manejar el conocimiento de la representación. 2.1.2. Conceptos generales de DICOM. DICOM es un estándar que cubre en parte las cuestiones planteadas en la sección anterior. Esta sección abordará los conceptos generales con respecto al mecanismo de intercambio actualmente usado. DICOM utiliza su propia terminologı́a para describir el contexto, relaciones, etc. El primer paso es el mismo modelo que para el procesamiento distribuido con la transformación de la figura anterior en la misma figura aplicando los términos equivalentes de DICOM. Clases de Servicio (Service Classes) y Clases SOP (SOP Classes). La relación entre ambas partes se define por la descripción de la Clase de Servicio. La Clase de Servicio describe explı́citamente los papeles que ambas partes desempeñan. Con DICOM ambos papeles son llamados: Usuario de la Clase de Servicio o SCU (Service Class User ) (cliente) y Proveedor de la Clase de Servicio o SCP (Service Class Provider ) (servidor). No hay que confundir SCU y SCP con el usuario del servicio y el proveedor del servicio del dominio del intercambio. r GVA-ELAI-UPMPFC0074-03 5 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Figura 2.2: Modelo de un proceso distribuido Parte de la Clase de Servicio es la descripción de la información y operaciones. En DICOM estas están combinadas con la definición de la clase, llamada Clase de Servicio de Par Objeto (Service Object Pair Class) o Clase SOP (SOP Class). En cada definición de Clase SOP una única definición de Objeto de información (Information Object Definition) o IOD es combinado con uno o más servicios. Para cada uno de estos servicios los detalles de los papeles de ambas partes que tienen que desempeñar son invariables. Más de una Clase SOP puede existir en una Clase de Servicio cuando más de un IOD está implicado. Una Clase de Servicio entiende la relación de información definida en diferentes IODs. Una Clase SOP identifica las capacidades del proceso distribuido especı́fico de una Clase de Servicio. Cuando las partes están de acuerdo en utilizar una Clase SOP, ambas partes deben asegurar que desempeñarán sus papeles como se describen, utilizando el contexto de la Clase de Servicio incluida. Antes de que la información se intercambie puede tener lugar la identificación de la Clase SOP, que es una parte importante que tiene que 6 r GVA-ELAI-UPMPFC0074-03 2.1. DICOM (DIGITAL IMAGING AND COMMUNICATION IN José Ma Onrubia MEDICINE). realizarse al principio. El mecanismo usado depende del tipo de intercambio: network o media. Utilizando la Clase de Servicio y otras definiciones derivadas, las partes en el entorno de un proceso distribuido funcionan juntas mediante los servicios proporcionados por el dominio del intercambio. Figura 2.3: Clases de servicio DICOM Definiciones de Objeto de Información (Information Objects Definitions). La parte de información de una Clase SOP es definida en los IODs. Un IOD es una colección de partes de información relacionada, agrupadas en Entidades de información (Information Entities). Cada entidad contiene información sobre un único objeto (mundo real) como un paciente, una imagen, etc. Dependiendo del contexto definido por la Clase de Servicio, un IOD consiste en una entidad de información única llamada IOD normalizado (normalized IOD) o una combinación de entidades de información llamada IOD compuesto (composite IOD). Las Clases de Servicio que llevan a cabo r GVA-ELAI-UPMPFC0074-03 7 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia funciones de administración (en su mayor parte cuestiones simples) utilizan IODs normalizados, aquellas que manejan el flujo de imágenes (estructura compleja de información) utilizan IODs compuestos. La relación entre diferentes entidades de información (estructuración) y los IODs compuestos se describe en el modelo de información (información model ) perteneciente a la Clase de Servicio. Con IODs normalizados (solo una entidad de información) no hay ninguna necesidad de estructuración. Las relaciones en otras piezas de información están hechas aludiendo a esa información. Las entidades de información consisten en atributos, describiendo una única parte de información, por ejemplo, el nombre de un paciente. Los atributos que tienen una relación están agrupados en módulos de información de objetos o IOMs (Information Object Modules). Los IOMs están definidos de tal manera que pueden ser usados en más de un IOD. Estos IOMs también tienen la ventaja de que las descripciones semánticas de los atributos descritos pueden ser agrupados juntos. La figura que se ve a continuación representa una visión general de estas relaciones. Figura 2.4: Relaciones entre IODs y atributos Un ejemplo de una IOD compuesto, imagen IOD está representada siguiente: 8 r GVA-ELAI-UPMPFC0074-03 2.1. DICOM (DIGITAL IMAGING AND COMMUNICATION IN José Ma Onrubia MEDICINE). Figura 2.5: Ejemplo de una imagen IOD compuesta. Atributos. Los atributos son la entidad de información básica y tienen que ser descritos en detalle. De un atributo, se definen las siguientes caracterı́sticas en el estándar DICOM: un único Nombre de Atributo (Attribute Name) (legible por el ser humano). una única Etiqueta de Atributo (Attribute Tag) (legible por los sistemas de información). una descripción de Atributo (Attribute Description) (semántica). r GVA-ELAI-UPMPFC0074-03 9 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia un Valor Representativo (Value Representation) (sintaxis). un Valor de Multiplicidad (Value Multiplicity). tipo de clasificación: 1, 1C, 2, 2C o 3 (usadas dependiendo del contexto de las Clases SOP, Clases de Servicio, papel que desempeña, etc.). El tipo de clase especifica el uso de los atributos especificados en las Clases SOP y SCU o el papel del SCP. Dependiendo de la situación, a cada atributo se le fuerza a tener un valor (tipo 1 ) o a que exista con o sin valor (tipo 2 ) o que sea opcional que aparezca ese atributo (tipo 3 ). Dentro de un IOD, los atributos agrupados o individuales pueden ser condicionados por la situación en la que el IOD está siendo usado. Por ejemplo, un análisis utilizando contraste puede almacenar información en un ”modulo de Contrast/Bolus”. Los atributos de este módulo están por consiguiente disponibles o no disponibles, dependiendo del uso del contraste. Si se usa, el tipo de clase especificada para los atributos debe ser obedecida (definida como tipo 1C y tipo 2C). Elementos de Servicio (Service Elements). Los Elementos de Servicio son las operaciones permitidas en los Objetos de información para una Clase SOP definida. El grupo de elementos de servicio pertenece a la Clase SOP y es llamada Grupo de Servicio (Service Group). El Grupo de Servicio de una Clase SOP se selecciona de una lista fija de Elementos de Servicio de DICOM. Algunos Elementos de Servicio están proyectados para usarse con IODs compuestos, otros para uso con IODs normalizados. Una tercera categorı́a, medios de almacenamiento (storage media) relacionados con Elementos de Servicio, manejando instancias de Clases SOP normalizadas o compuestas como archivos. El contexto descrito por la Clase de Servicio está limitado cuando se utilizan IODs compuestos (p.e., transferir imagen). Elementos de Servicio semejantes tienen un significado complejo, p.e., STORE, FIND, MOVE. No hay ninguna relación asumida entre los Elementos de Servicio individuales en una secuencia cuando se utilizan Clases de Servicio compuestos. Si existe una relación, es fuera del alcance de la Clase de Servicio y deberı́a estar definida en el proceso fluyente utilizando las Clases de Servicio. En contraste, las Clases de Servicio utilizando IODs normalizados tienen un contexto más amplio, como funciones directivas. Estas utilizan los 10 r GVA-ELAI-UPMPFC0074-03 2.1. DICOM (DIGITAL IMAGING AND COMMUNICATION IN José Ma Onrubia MEDICINE). Elementos de Servicio primitivos para operaciones con piezas sencillas de información: GET, SET, ACTION, etc. La Clase de Servicio define la relación de la secuencia de la peticiones primitivas. Con Clases de Servicio normalizadas ambas partes están al tanto del procedimiento de ambas partes, utilizando los Elementos de Servicio para controlarlos. Cada Clase SOP utiliza uno o más Elementos de Servicio de cada uno de los grupos compuestos (C-XXXX) o de los grupos normalizados (N-XXXX). Los siguientes Elementos de Servicio están disponibles: C-STORE, C-FIND, C-MOVE, C-GET, CCANCEL, C-ECHO, N-GET, N-SET, N-ACTION, N-CREATE, N-DELETE y NEVENT- REPORT. Las semánticas de los Elementos de Servicio dependen de la Clase de Servicio y de la Clase SOP en la cual están utilizados. Los Elementos de Servicio relacionados con Media, M-WRITE, M-READ, MDELETE, M-INQUIRE-FILE-SET y M-INQUIRE-FILE definen funciones primitivas para el tratamiento con archivos. Instancias SOP (SOP Instances). El esqueleto de las definiciones citadas anteriormente toma forma cuando se utilizan en un proceso distribuido. Después del acuerdo que mantienen las Clases SOP (implı́citamente la Clase de Servicio), y como los papeles de la SCU y la SCP están divididos, las instancias de la clase SOP pueden ser distribuidas entre las dos partes. Los atributos tienen que ser proporcionados con los valores correctos y almacenado en la Instancia SOP como se especifica en la definición de los atributos. Después de recopilar la información, esta será codificada a los formatos definidos por DICOM, utilizando la representación del la Etiqueta (Tag) y del Valor (Value) para crear un DICOM data set, en el cual cada atributo es codificado en un data element. Este data set es manejado por el proveedor del servicio de intercambio, el cual garantiza que la parte contraria recibe idéntico data set. Las diferencias en la representación del sistema especificado son tomadas en una cuenta durante el intercambio, asegurando que los significados semánticos permanecen intactos. El receptor del data set decodificará este para extraer la información que necesita y actuar como acuerdo de la semántica de la Clase SOP. r GVA-ELAI-UPMPFC0074-03 11 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Identificación. Como parte del proceso de creación de una Instancia SOP, una identificación es generada como atributo de la SOP Instance. La identificación se pretende para la utilización por los sistemas de información antes que por los humanos y tiene dos caracterı́sticas: la identificación de la clase (class identification) y la identificación de la instancia (instance identification). Esta identificación tiene que ser usada en un entorno de muchos vendedores en distintas partes del mundo. Para asegurar la unicidad de cada identificación en todo el mundo, se utiliza un mecanismo para generar una cadena de caracteres, llamada Identificador Único o UID (Unique Identifier), tal y como sigue: root >.<suffix < > La parte de root es proporcionada por una autoridad que garantice que nadie más utilizará este root. Este número será asignado por estándares de organizaciones y compañı́as tales como Philips u hospitales, que deberán asegurar que permanece único a lo largo de sus propios sistemas. Utilizando un sistema de identificación único, cada sistema tendrá un único root a lo largo de todo el mundo. El suffix tiene que ser creado dinámicamente por el sistema en la creación de la instancia. Un vez que una instance es identificada por un UID, esta debe ser utilizada consistentemente. Si se crean copias o la instancia se reproduce sin ninguna modificación, deberá tener el mismo UID, de lo contrario dos piezas de idéntica información coexistirı́an con diferentes identificaciones, lo que podrı́a conducir a confusión. Relaciones. Además de la identificación de la Clase SOP y la Instancia SOP, los UIDs también se utilizan para identificar una relación entre instancias. En una instancia compuesta (composite instance) que contiene una única imagen perteneciente a una secuencia de imágenes, la Entidad de información (Information Entity) que contiene la información de las secuencias será común para todas aquellas instancias. En este caso solo se requiere un UID, el atributo por sı́ mismo identifica qué tipo de entidad de información es identificada. 12 r GVA-ELAI-UPMPFC0074-03 2.1. DICOM (DIGITAL IMAGING AND COMMUNICATION IN José Ma Onrubia MEDICINE). En el caso de instancias normalizadas (normalized instances), sólo son posibles referencias a instancias fuera de sı́ mismas; aquı́ se requiere la combinación de una identificación de una clase y una instancia. Con el método de la unicidad de identificación de información utilizando UIDs, es tan sólo posible comparar si las instancias son iguales. El valor del UID no tiene ningún significado, y no puede ser utilizado para clasificar, etc. Utilizando otro método como los atributos más significativos tales como la fecha y la hora y los números de la secuencia, se puede establecer la relación entre la información. Valor representativo (Value Representation). Para cada atributo se define un Representación del Valor (VR). Una valor representativo describe como un atributo se codifica en un elemento de datos. El conocimiento de la valor representativo se comparte por las partes en el intercambio de información , el proceso de codificación y decodificación tiene que tener cuidado en la selección del VR correcto para un atributo (identificado por su etiqueta). Son posibles dos formas de compartir esta información: compartir un diccionario de datos que contiene todos los posibles atributos de intercambio, o incluyendo el valor representativo como una parte del data element. El último método incrementa los gastos de intercambio de información, pero es mucho más flexible comparado con el uso de un diccionario de datos compartido. Especialmente en un entorno de muchos proveedores, sincronizar el diccionario de datos es difı́cil. Cuando el valor representativo se incluye, el mensaje se codifica con un VR explı́cito (explicit VR). En el otro caso, la codificación tiene lugar con un VR implı́cito implicit VR). Sintaxis de transferencia (Transfer Syntax ). Antes de que el conjunto de datos (data set) de una Instancia SOP pueda ser transferida, la forma en la que el conjunto de datos está codificada en una secuencia de bytes debe ser fija, mediante un acuerdo cuando se usa el intercambio por red, o almacenados junto con los datos en un medio de almacenamiento fı́sico (disquete, CD´s ...). La forma de codificar se especifica por la Transfer Syntax. Tres caracterı́sticas se tienen que definir en la sintaxis de transferencia: r GVA-ELAI-UPMPFC0074-03 13 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Se especifica un valor representativo (VR). El orden de bytes de un número múltiple de bytes (palabras, palabras largas): little endian o big endian. En caso de compresión: el formato. El manejo de la transfer syntax es parte del proveedor del servicio. Ası́ y todo, ambos procesos tienen que iniciar el escenario para un correcta ”transfer syntax”, aceptable para ambas partes. Análogamente a una identificación de Clase SOP, una transfer syntax es identificada por un UID. Descripción Mirando la figura 2.6 se puede obtener una visión general del flujo de codificación y decodificación. Los servicios proporcionados dentro del Dominio del Intercambio tienen que garantizar que en ambas partes las Instancias SOP contienen la misma información, independientemente de la representación y método de transferencia. El proceso de codificación y decodificación tiene dos etapas: Primero se transfiere la representación interna en el formato definido por DICOM (Data Set) donde cada atributo es almacenado conforme al valor representativo definido para ese atributo. La segunda etapa transfiere el data set en una corriente de bytes que pueden ser manejados por las capas más bajas. Para la segunda etapa la ordenación de bytes tiene que ser utilizada de acuerdo con la Transfer Syntax. La aplicación que está utilizando la información debe saber el significado (semántica) de la información dentro del data object. 14 r GVA-ELAI-UPMPFC0074-03 2.1. DICOM (DIGITAL IMAGING AND COMMUNICATION IN José Ma Onrubia MEDICINE). Figura 2.6: Descripción de la codificación y decodificación de las instancias SOP. 2.1.3. Conceptos de red DICOM. En la anterior sección se han discutido los conceptos de DICOM del dominio de la aplicación. Cuando se utiliza un mecanismo de red para el intercambio de información, el dominio del intercambio debe contener r GVA-ELAI-UPMPFC0074-03 15 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia funciones requeridas para la comunicación: el dominio de la comunicación. Figura 2.7: DICOM con intercambio en red. Entidad de la aplicación (Application Entity ). Una cuestión importante en las aplicaciones distribuidas en red es cómo las aplicaciones pueden contactar entre ellas. En DICOM Network, las partes se reconocen mutuamente mediante las entidades de la aplicación. Una entidad de la aplicación es aquella parte de un proceso que negocia con la comunicación. Ella contiene el usuario de servicio del proceso, conteniendo funciones para organizar conexiones y transferencia de información. Una Entidad de la aplicación tiene un nombre, tı́tulo de la aplicación (Application Title), que tiene que se utilizado cuando se establece la comunicación. 16 r GVA-ELAI-UPMPFC0074-03 2.1. DICOM (DIGITAL IMAGING AND COMMUNICATION IN José Ma Onrubia MEDICINE). Presentación de la dirección (Presentation Address). Los tı́tulos de la aplicación son nombres simbólicos para los procesos involucrados en la comunicación. En un sistema de red real, la dirección de red tiene que ser suministrada. A esto se le llama la Presentation Address. Se le llama ası́ porque el usuario del servicio es la capa de aplicación (OSI), el proveedor del servicio, la capa de Presentación (OSI) (y niveles más bajos). La frontera entre ambos niveles es el punto de acceso de red donde los datos son transferidos desde la capa de aplicación a las capas de red. Cada punto de acceso en una red tiene una única dirección. El mapeo del tı́tulo de la aplicación a la Presentation Address no tiene que ser único, porque la Presentation Address se utiliza para la iniciación de la conexión, etc. De cualquier manera, en el nivel de aplicación, el tı́tulo de la aplicación se usa normalmente para identificar una aplicación como fuente o destino de información en un directorio o catálogo. Si esto no puede ser registrado sin ambigüedades la operación de los sistemas puede llegar a ser un problema. El formato de la Presentation Address depende del protocolo de red utilizado. Los DICOM Networks se realizan en muchos casos utilizando el protocolo TCP/IP. En este caso la valor representativo se mapea a un TCP/IP socket. En el caso de un protocolo OSI, debe utilizarse un OSI Presentation Service Address Point (PSAP) válido. Negociación de la asociación (Negotiation Association). La conexión para el intercambio de información entre dos entidades de la aplicación se llama una asociación (Association). Para una asociación, se fijan un número de asuntos de comunicación como el contexto en el cual la información puede tener cambios. Este contexto, llamado contexto de la aplicación (Application Context), se define en el estándar DICOM y ambas partes deben estar de acuerdo con la actuación conforme a la definición del contexto. Un contexto de la aplicación se define con un UID y durante la iniciación de una asociación este UID es transferido a las partes. Por comparación del UID de un contexto de la aplicación, la parte puede decidir si es capaz de manejar la petición de una asociación. Él aceptará el establecimiento de la asociación o lo rechazará. r GVA-ELAI-UPMPFC0074-03 17 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia El contexto de la aplicación cubre la operatividad global para el intercambio de información. Qué tipo de información intercambia tendrá lugar a través de la asociación que está definida por las Clases SOP y las Clases de Servicio de estas Clases SOP. La parte iniciadora de la asociación propone a la Clase SOP que será utilizada, el SCU / SCP para cada Clase SOP y la forma de representación de la información. Dependiendo de las capacidades de la otra parte, aceptará o rechazará cada Clase SOP individual. Después de este proceso de negociación, ambas partes conocen mutuamente las capacidades y limitaciones. La auténtica información distribuida puede tener lugar conforme a las reglas de una Clase de Servicio y una Clase SOP definidas para estas clases. Cuando una asociación no se requiere por más tiempo, la asociación se termina. Contexto de la presentación (Presentation Context) Para cada Clase SOP negociada durante la iniciación de la asociación tiene que alcanzarse un acuerdo entre los procesos involucrados acerca de la transfer syntax usada entre los procesos. La parte iniciadora propone todas las transfer syntaxes, fijando el contexto de la presentación para esta Clase SOP. Después de la negociación se establece un Presentation Context para cada Clase SOP aceptada. Un Presentation Context se identifica por un número acordado entre las dos partes. En el contexto de una aplicación puede existir un número de una Presentation Context. El número de la Presentation Context identifica la SOP Class para la cual el intercambio de información tiene lugar. TCP/IP Protocol Stack La combinación de TCP/IP y una extensión para los servicios de aplicación de OSI es ampliamente usada para implementar DICOM a través de las redes. Como no hay niveles más altos definidos por TCP/IP, DICOM define la operatividad de la aplicación, la presentación y sesión de la capa en el estándar. Esta operatividad se combina en una capa: la DICOM Upper Layer o DUL. La DUL utiliza el mismo interface que el protocolo TCP/IP con respecto del protocolo OSI. En el nivel más bajo de la capa DUL, hay un interface para el nivel TCP. La asociación DICOM entre las entidades de aplicación se mapea a una conexión TCP. La presentación de la dirección se mapea a 18 r GVA-ELAI-UPMPFC0074-03 2.1. DICOM (DIGITAL IMAGING AND COMMUNICATION IN José Ma Onrubia MEDICINE). un número de puerto TCP (TCP port number ), combinado con el número de IP (IP number ) o nombre del servidor (Host name). Esta combinación del número de puerto TCP y el número de IP se llama dirección de conexión (Socket Address). En una red esta combinación es siempre única. Una conexión TCP se define por la combinación de una dirección de conexión local y una remota. Manteniendo los números de IP únicos de la red y el número único de puerto en el sistema, cada conexión TCP se identifica únicamente por la combinación. La administración de las conexiones se hace por un recurso llamado interface de conexión (Socket Interface) que proporciona funciones para configurar las conexiones, transferir cadenas de bits, etc. r GVA-ELAI-UPMPFC0074-03 19 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Figura 2.8: Capas OSI 20 r GVA-ELAI-UPMPFC0074-03 2.1. DICOM (DIGITAL IMAGING AND COMMUNICATION IN José Ma Onrubia MEDICINE). El puerto TCP de la parte llamada durante la inicialización de la conexión se debe conocido. Esto puede ser por un acuerdo en el número de puerto entre las dos aplicaciones, o por un número de puerto, llamado número de puerto conocido (well known port number ), reservado para las implementaciones de DICOM (número de puerto 104). r GVA-ELAI-UPMPFC0074-03 21 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Figura 2.9: Conexión TCP 2.1.4. Conectividad (Connectivity ) Antes de que las dos implementaciones de DICOM se puedan conectar entre sı́, se necesita algo de investigación para ver si la conexión es posible. 22 r GVA-ELAI-UPMPFC0074-03 2.1. DICOM (DIGITAL IMAGING AND COMMUNICATION IN José Ma Onrubia MEDICINE). Esto alcanza desde el bajo nivel de conexión fı́sica hasta la implementación de la misma Clase de Servicio en el nivel de aplicación. El acercamiento a una conexión red es diferente comparado con un intercambio a través de medios de comunicación fı́sicos como CD´s o disquetes. Durante la negociación de la asociación en un entorno de red, un número de detalles se pueden establecer todavı́a. En el caso de utilizar medios fı́sicos no es posible y deberı́a ser dirigido de distinto modo. DICOM solventa esta cuestión utilizando perfiles de sistema (system profiles) para implementaciones y perfiles de aplicación (application Profiles) en un entorno de intercambio mediante medios fı́sicos. Estatuto de conformidad (Conformance Statement). Un perfil de sistema (System Profile) contiene una lista de las funciones soportadas y limitaciones o extensiones de estas funciones. Juntos forman un perfil que se debe ajustar al perfil de la parte que tendrá que cooperar. Estos perfiles de sistema se describen en un documento que debe ser suministrado con cada implementación de DICOM: el Conformance Statement(ver figura 2.10). Figura 2.10: Estatuto de conformidad con perfil del sistema. En el nivel de aplicación se describen una descripción funcional de la r GVA-ELAI-UPMPFC0074-03 23 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia entidad de la aplicación, las Clases SOP soportadas y el papel que ambos sistemas desempeñan. Para la implementación de los protocolos de red puede ser referido a documentación estándar apropiada, con constancia de las excepciones que restringen el uso en un entorno de red. Las posibilidades de una conexión fı́sica es también un tema que debe ser dirigido. Los objetos configurables de una implementación, tales como el tı́tulo de la aplicación (Application Title), la presentación de la dirección (Presentation Address) de ambas implementaciones y partes, que son mencionadas juntas con información de como puede ser configurado. Otros objetos configurables como el tamaño del protocol data unit (PDU) debe ser listado. Finalmente el soporte para caracteres fija otros, además del estándar ASCII (tales como extensiones para idiomas Europeos, Japonés, etc) descrito. Comparando los Conformance Statements se puede verificar si la conectividad a todos los niveles es posible. Si la implementación de la información por todas las partes involucradas es igual no se puede asegurar por verificación con la Conformance Statement. Dependiendo de cómo de estricta pueda ser interpretada la semántica de todos los atributos individuales, el nivel de interoperabilidad es más predecible. Actualmente no hay ningún método para asegurar la interoperabilidad. Al Conformance Statements pueden añadir más información describiendo en más detalle la información que manejan. Cuando está indicado qué relaciones están disponibles y que selecciones están hechas por la implementación comparada con el estándar, éste ayudará a incrementar la conectividad y la interoperatividad. Perfiles de aplicación (Application Profiles) Para ”mediaün perfil de sistema detallado tiene poco sentido porque la correspondencia no tendrá lugar antes de que se conecten los sistemas, pero por el momento el médium se lleva a otro sistema. En este caso ambos sistemas deben garantizar que se ajustan a un formato genérico que habilita la aplicación que ambos implementan. Este formato genérico se llamo perfil de la aplicación. Por ejemplo, un sistema que genera datos de imagen en un medio debe hacerlo conforme a un perfil de aplicación confirmado. Un sistema utilizando esta imagen puede confiar en este perfil de aplicación para resultar exitoso. Dos aspectos son importantes: el formato del medium y la extensión de la 24 r GVA-ELAI-UPMPFC0074-03 2.1. DICOM (DIGITAL IMAGING AND COMMUNICATION IN José Ma Onrubia MEDICINE). información capturada en el medium. Un perfil de aplicación asegura estos dos aspectos y proporciona un tipo de etiqueta que puede ser adjuntada al sistema involucrado y al medium que contiene los datos. El aspecto fı́sico del medium alude al formato definido en el estándar DICOM. La parte de información descrita por la Clase SOP es el segundo aspecto incluido en el Perfil de la aplicación. Figura 2.11: Estatuto de conformidad con perfil de aplicación 2.1.5. Estándar DICOM El estándar DICOM está dividido en varias partes, cada una de ellas describiendo una cuestión importante tal como las Clases de Servicio, los IODs, temas relacionados con Network y Media, etc. En la figura 8 se ve una visión general de la relación entre las diferentes partes. En este apartado las partes de DICOM son abordadas en el mismo orden que los temas planteados en los apartados anteriores. Puede ser usada como lı́nea directiva para empezar a leer las varias partes del estándar DICOM. r GVA-ELAI-UPMPFC0074-03 25 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia La parte 1 da una introducción y una visión general del estándar DICOM y su relación con otros sistemas de información hallados en el entorno clı́nico. (ver Apéndice A) Las Clases de Servicio y las Clases SOP incluidas en las Clases de Servicio están definidas en la parte 4. Para cada Clase de Servicio la operatividad está delineada siguiendo la descripción de las Clases SOP individuales. Para cada papel un proceso puede desempeñar los requisitos dados por los dos con detalles del uso de los atributos si es aplicable. Dependiendo del tipo de Clase de Servicio (compuesta o normalizada) la descripción es dando un pequeño contexto o uno detallado. Ası́ mismo los temas que tienen que ser descritos por cada parte de la Clase SOP en la Conformance Statement son listados. La parte 4 utiliza los IODs y los Servicios definidos en las Partes 3, 7 y 10. Figura 2.12: Diferentes partes del estándar DICOM Los IODs utilizados por las Clases SOP son descritos en la Parte 3. 26 r GVA-ELAI-UPMPFC0074-03 2.2. INSTANCIAS SOP DE IMÁGENES DICOM (DICOM IMAGE SOP José Ma Onrubia INSTANCES) Empieza con la descripción del IOD completo, dividiéndolo en los grupos de IODs compuestos y normalizados. De cada IOD se da una lista de ”Information Object Modules”. La ultima parte define los atributos individuales agrupados en detalle en los IOMs. Para los IODs compuestos todos los detalles son listados en esta parte, para los IODs normalizados el actual uso de los atributos depende del servicio aplicado y descrito en la parte 4. En la Parte 5 se describe la codificación de las Instancias SOP en un conjunto de datos. Se defeinen las reglas para los numerosos valores representativos (Value Representation) y para las sintaxis de transferencia (Transfer Syntaxes). Los Elementos de Servicio utilizados por las Clases SOP se dividen en dos partes: Parte 7 para los Servicios Network y Parte 10 para los Servicios Media. En estas partes se definen tanto la codificación del ”network message headerçomo ”media file header”. El resultado es un mensaje o archivo que puede ser manejado por el correspondiente mecanismo de intercambio. Los dos grupos de menor nivel tratan con el intercambio fı́sico de datos. En la Parte 8 se describen las cuestiones de Protocolo de Red (Network protocolo), la Parte 9 define la conexión punto-a-punto (point-to-point conexion) (raramente utilizada) y la Parte 12 define las cuestiones del formato de Physical Media. Todos los atributos y UIDs definidos por las varias partes del estándar DICOM son listadas en el Diccionario de Datos (Data Dictionary) (Parte 6). El Estatuto de Conformidad se describen en la Parte 2 incluyendo la manera en que un Conformance Statement se tiene que configurar. Los Perfiles de aplicación utilizados para el intercambio Media se discuten en la Parte 12 junto con la .Application Profile layout”. 2.2. Instancias SOP de imágenes DICOM (DICOM Image SOP Instances) En el capı́tulo precedente, se han explicado los conceptos DICOM sin describir en detalle cómo se capturan las imágenes dentro de una instancia SOP. En este capı́tulo se ve con más profundidad cómo se estructura la información. Se explicará la diferencia entre los distintos tipos de imágenes, junto a la forma en que el proceso de creación crea los datos de la imagen. r GVA-ELAI-UPMPFC0074-03 27 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Final mente, se ve qué manera es la adecuada de usar la información creada por un sistema. Las clases SOP DICOM contienen una definición de objeto (IOD) y servicios para ser aplicados a ese objeto. En la mayorı́a de las secciones que se ven, sólo se discute la definición del objeto. En el manejo de los datos de las imágenes, como lo descrito en DICOM, está sólo limitado por la transferencia (clase de almacenamiento SOP) y por el medio de almacenamiento. En este capı́tulo, además de usar los términos almacenamiento de clase e instancia SOP, el término no DICOM, Image SOP Class/Instance, se usa para referirse al proceso de los datos de la imagen. DICOM no tiene forma de describir el tipo de manejo de datos de las imágenes; el nombre de almacenamiento de clase SOP es poco claro y es confuso si se usa en otros contextos. 2.2.1. Modelo de información de las imágenes El manejo electrónico de la información requiere un modelo para representar la forma en que la información estará estructurada. Esta estructura es necesaria para tener instancias uniformes y para hacer posible la descripción de las relaciones entre instancias de forma clara. Un modelo de información de imagen deriva de la forma en que las imágenes se manejan en un departamento de radiologı́a. Las imágenes recogidas de uno u otro aparato, son recopiladas en una carpeta perteneciente al paciente correspondiente. Las imágenes son ordenadas en la carpeta conforme al tipo de examen realizado (series de imágenes que están relacionadas). Los usuarios de cada tipo de aparato tienen su propia terminologı́a para esta ordenación, como escáner, rodaja, etc. Cuando los datos de las imágenes de diferentes fuentes tienen que ser recogidas en un ambiente único, debe ser posible ordenar los datos de las imágenes de diferentes fuentes. Esto es sólo posible cuando los datos están estructurados de acuerdo al mismo modelo de información. Mapping Real World Examinations El modelo de información de imagen DICOM está basado en suposiciones sobre la forma en que la información de diferentes aparatos están relacionados. Ver figura 2.13. Los cuatro niveles de este modelo de información son paciente, estudio, serie e imagen. 28 r GVA-ELAI-UPMPFC0074-03 2.2. INSTANCIAS SOP DE IMÁGENES DICOM (DICOM IMAGE SOP José Ma Onrubia INSTANCES) Nivel de paciente El nivel del paciente contiene la identificación y la información demográfica de éste al cual el estudio le pertenece. Debido a que puede existir más de un estudio, el nivel del paciente es el nivel más alto (cuando toda la información es recogida para un solo paciente se lleva a una cuenta). Sin embargo, es de práctica normal usar el nivel del estudio para recoger la información manejada por varios sistemas para un única respuesta a este estudio. Figura 2.13: Del mundo real al modelo de información Nivel de estudio El nivel de estudio es el nivel más importante en el modelo de información. Un estudio es el resultado de una contestación a un cierto tipo de examen médico. Todas las actividades en un departamento de radiologı́a se centran en el manejo correcto del estudio. En un estudio, la información de identificación se guarda y puede contener referencias a información relacionada al mismo estudio en un sistema de administración. En general, una respuesta puede envolver procedimientos de diferentes máquinas. Esto da a lugar a una serie de una o más imágenes, dependiendo del protocolo definido por el examen realizado. Todos los datos son recogidos juntos en el mismo estudio principal. Un paciente puede tener muchos estudios como resultado de otros realizados anteriormente. r GVA-ELAI-UPMPFC0074-03 29 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Nivel de serie Después del nivel de estudio todas las imágenes se recogen. El nivel de serie identifica el tipo de aparato que crea las imágenes, la fecha y el tiempo de creación de la serie y los detalles del tipo de examen realizado y del equipo usado. Realizar una lista de los términos usados en los diferentes aparatos tiene que ser cuidadosamente considerado. Puede haber palabras que aparentemente signifiquen lo mismo, pero se usan con diferencias en distintos contextos. Las series siempre son una colección de imágenes que provienen de una único aparato. La forma en que las imágenes están agrupadas en series depende del uso médico que se les va a dar. Cómo las imágenes son adquiridas por una máquina es menos importante para ésta agrupación. Sin embargo, varios atributos definirán el tipo de adquisición y se pueden mostrar en la visualización. En algunos casos la relación entre las imágenes se define mediante la forma en que la adquisición ha tenido lugar. Cuando las adquisiciones en una secuencia tienen relación espacial o temporal, las imágenes resultantes de esta adquisición pueden ser agrupadas en series. Una serie nueva debe comenzar cuando la relación existente entre imágenes ya no existe. Otro criterio para agrupar imágenes puede ser coger los datos de una única parte del cuerpo hecho durante un estudio completo. Por ejemplo, cuando un aparato produce un número de imágenes del estómago de un paciente desde diferentes posiciones y momentos, las imágenes pueden ser agrupadas en una serie. Algunos sistemas producen más de una imagen al hacer una adquisición de datos. Por ejemplo, algunos escáneres se hacen con un sistema CT, las imágenes reconstruidas desde cada escaneamiento son recogidas en series y tienen relación espacial. El siguiente escaneamiento estará en una nueva serie, porque en muchos casos el proceso de escanear se hace desde diferentes posiciones. También, en una serie, una imagen de referencia puede ser incluida como una descripción de la posición espacial de las rodajas individuales. Ver figura. Diferentes reconstrucciones puede ser guardada en diferentes series. 30 r GVA-ELAI-UPMPFC0074-03 2.2. INSTANCIAS SOP DE IMÁGENES DICOM (DICOM IMAGE SOP José Ma Onrubia INSTANCES) Figura 2.14: Ejemplo de ”mapping”de un CT Para cada tipo de aparato médico hay reglas definiendo los contenidos que una serie debe describir. Las reglas usadas por un sistema dado son parte de un perfil de sistema en el estatuto de conformidad DICOM. Nivel de imagen El nivel más bajo del modelo de información es el nivel de imagen. Cada imagen contiene la información de adquisición y posicionamiento al igual que los datos propios de la imagen. Dependiendo del tipo de aparato, el nivel de imagen contiene datos para una sólo imagen, dos imágenes (sistema de dos planos) o una colección de imágenes cogidas en un corto espacio de tiempo (multiframe images). El uso de multiframes images guarda la información duplicada en niveles más altos, pero es sólo posible cuando la relación entre los marcos (frames) pueden ser descritos de una sola manera. Por ejemplo, los incrementos en los movimientos del sistema y del tiempo son iguales para todas los marcos. Creando un multiframe images es más complejo y gasta más recursos que r GVA-ELAI-UPMPFC0074-03 31 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia creando una imagen única. La relación entre marcos, la capacidad del aparato y la carga de datos producidos deberı́a ser usada para determinar si se debe aplicar una serie de imágenes simples o un multi marco de imágenes. 2.2.2. Instancias imagen SOP (Image SOP Instances) El modelo de información mostrado en la figura 2.13 es una simplificación del modelo de información completo de imagen DICOM de la figura 2.14. Cada bloque en este diagrama representa una entidad de información (ver IOD‘s) del IOD compuesto. Las relaciones indican los puntos cardinales para cada relación de la entidad de información usada en una instancia SOP. Cada instancia de imagen SOP tiene que contener la información estructurada de acuerdo al modelo de información DICOM. Cada instancia de imagen SOP (simple o multimarco) es una instancia compuesta SOP que contiene el árbol completo de información del modelo de información. Todas las imágenes en una serie son de un mismo paciente, estudio y serie; todas las series son del mismo paciente y estudio, etc. En cada composición, toda la información relacionada con la imágenes está disponible. Este formato hace más fácil el intercambio y el manejo (especialmente el almacenamiento) de la información pero incremente la carga de datos cuando se transfiere un estudio completo. En este caso las entidades de información del paciente y del estudio tienen múltiples instancias en la colección de las instancias SOP. En contraste, las instancias normalizadas SOP (con entidades de información simples) usan referencias a otras entidades de información , perteneciendo un protocolo más eficiente, pero requiriendo un manejo más complejo. 2.2.3. Relaciones e indentificación Cuando se recoge un grupo de Image SOP Instances las cuales tienen relación entre ellas, pero están creadas desde diferentes aparatos, es importante ser capaz de marcar las entidades de información en diferentes niveles. Son importantes dos aspectos: 1. Todas las modalidades deben tener un mapa (código) consistente de cómo pasar de unos datos de imagen a una instancia SOP. 2. Las entidades de información individuales deben contener la identificación suficiente de hacer un correcto marcado de las entidades de 32 r GVA-ELAI-UPMPFC0074-03 2.2. INSTANCIAS SOP DE IMÁGENES DICOM (DICOM IMAGE SOP José Ma Onrubia INSTANCES) información equivalente en otras instancias SOP. Figura 2.15: Modelo de información de una imagen compuesta DICOM Estructura de los datos de las imágenes El primer aspecto requiere que los datos producidos por los aparatos sea ordenado en series que tengan una relación como la descrita en la sección nivel de serie (página 30). En los niveles de serie e imagen, la secuencia de imágenes dentro de una secuencia debe ser identificado en un aparato. Las entidades de información sobre el nivel de serie deberı́a contener información perteneciente al estudio y al paciente que debe ser comparable con la información de otros aparatos. La mayorı́a de esta información viene de fuentes externas como un sistema de planificación. Puede ser suministrado al aparato por la interfaz de usuario (del papel) o mediante una conexión a un sistema de información. r GVA-ELAI-UPMPFC0074-03 33 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Identificación Si los datos de imagen tienen que ser almacenados en sistemas los cuales ordenan los datos examinando el contenido de la información de la entidad, debe haber un consentimiento y un acuerdo para indentificar la información de la entidad por todos los sistemas (aparatos, sistemas de almacenamiento, estaciones de trabajo, etc.) los cuales manejan la información. Visualizar la información es más amplio que sólo ordenar imágenes. La identificación es también usada para acceder a los datos desde otros sistemas de información. Los sistemas de información normalmente usan claves que no necesitan ser interpretadas por los seres humanos, pero tienen que ser únicos en el ambiente en el que son usados. El mecanismo DICOM que se ha definido para estas identificaciones son los UID´s como se discutió en la sección de Identificación (página 12). Cada una de las entidades de información en el modelo de información tiene su propia UID, excepto para la entidad de información del paciente. La forma en que la información debe ser identificada se define por otros sistemas de información (fuera del visor DICOM) que tratan con la administración paciente. En este caso se usa un identificador ID para el paciente. Identificación del estudio Como el estudio de un examen médico realizado por un doctor es el centro de todas las actividades en un departamento de radiologı́a, debe ser reflejada en todas las piezas de información las cuales están relacionadas con el examen realizado. Para DICOM esta información se identifica al nivel de estudio. En la mayorı́a de los casos el atributo UID de la instancia del estudio (Study Instance UID) identifica la entidad del estudio de información del estudio perteneciente al resultado del examen del mundo real. Cuando este UID se usa de una forma consistente por todos los sistemas involucrados, no es difı́cil relacionar todas las piezas de información con los datos de la imagen en la instancia DICOM SOP. Sin embargo, esto requiere una unión entre todos los sistemas involucrados para transferir la clave del sistema. UID transfer by human beings is not an acceptable practice due to length and meaningless content of the UID string, it would be error-prone. A parte de esta unión (link ), UID´s se tienen que soportar por todos los otros sistemas, no sólo los sistemas involucrados en el manejo de datos de 34 r GVA-ELAI-UPMPFC0074-03 2.2. INSTANCIAS SOP DE IMÁGENES DICOM (DICOM IMAGE SOP José Ma Onrubia INSTANCES) imágenes. Un sistema que genera los UID´s del estudio juega un mayor rol para distribuir el UID a otros sistemas involucrados. Normalmente, esto deberı́a hacerse por un Sistema de Información Radiológico (RIS) o por un Sistema de Información de Hospital (HIS), que normalmente puede no siempre soportar el concepto UID. Cuando el soporte para el UID de la instancia del estudio no está disponible, no es posible usar este UID como unión a toda la otra información. Tiene que ser reemplazada en esos casos por otras claves. RIS usan actualmente una o más claves para acceder a su información almacenada, número de registro del estudio, etc. Esas claves se imprimen en papel que pertenece al estudio. Esta información tiene que ser incluida en la entidad de información del estudio y usada como reemplazo al UID del estudio. Usar el UID del estudio como unión con las partes relacionadas de la información es un aspecto importante para proporcionar un modelo de información DICOM consistente, el cual puede ser expandido en otras partes de la información en un departamento de radiologı́a. Esta consistencia es muy difı́cil de mantener cuando el UID del estudio se reemplaza por un RIS o un método especı́fico de identificación. Otras identificaciones A parte de las claves del sistema, los usuarios necesitan acceder a la información y quieren usar identificadores que tengan sentido como el nombre del paciente, su dı́a de nacimiento, fecha del estudio médico, etc. Los aparatos médicos tienen que proporcionar una información lo más consistente posible para permitir una identificación por parte de los humanos. La información de identificación puede ser proporcionada por una única fuente cuando una unión entre sistemas es posible. Por ejemplo, el RIS da el nombre del paciente, su fecha de nacimiento, etc, como parte de la información para realizar un examen médico. Este método previene el error de máquina y permite una forma más eficiente de funcionamiento. r GVA-ELAI-UPMPFC0074-03 35 CAPÍTULO 2. ESTADO DE LA TÉCNICA. 2.2.4. José Ma Onrubia Clasificación de los datos de imagen El modelo de información define el modelo jerárquico de las entidades de información para dejar claro cómo la información dentro de diferentes instancias SOP pueden ser agrupadas en diferentes niveles. En este sección la información de la instancia SOP está clasificada de acuerdo a las funciones que tiene, pero independientemente de su lugar dentro del modelo de la información. Desde luego, hay una fuerte relación entre el proceso de modelado y el proceso de creación. La siguiente sección describe la forma de producir los datos de la imagen. En la figura 2.16 se muestra una descripción de la clasificación y la relación con la arquitectura del sistema de un aparato médico. Las diferentes clases son creadas en diferentes momentos en el tiempo cuando se realiza un examen médico. Cada subsistema añade atributos al resultado final: la instancia de imagen SOP. La siguiente sección discute las clases de información con más detalle. Información del paciente Esta clase contiene información sobre el paciente al que se le realiza un estudio. En un departamento de radiologı́a la información del paciente se sabe por otras fuentes, como sistemas de información o formularios en papel. Sólo tiene que ser registrado de una manera formal por un número de atributos como nombre del paciente, ID del paciente, fecha de nacimiento, etc. La información en esta clase es estable, excepto por la corrección de errores de escritura y cambios de nombre en caso de enlaces matrimoniales, etc. El mantenimiento de esta información se hace por sistemas que actúan como fuentes para aparatos médicos. Uno o más atributos son clave para la información en otros sistemas de información. Otros atributos identifican al paciente como una persona o dan más detalles sobre su condición. Un número de esos atributos son muy importantes para el proceso completo de identificación y conexión a otra información en un departamento de radiologı́a. Para permitir la identificación del paciente y la revisión de un estudio, el aparato médico tiene que incluir esos atributos en las instancias SOP creadas. Procesos en el hospital también tienen que enfrentarse con el manejo de 36 r GVA-ELAI-UPMPFC0074-03 2.2. INSTANCIAS SOP DE IMÁGENES DICOM (DICOM IMAGE SOP José Ma Onrubia INSTANCES) la información en casos excepcionales. Por ejemplo, cuando un paciente desconocido es examinado por emergencia, se deben realizar unos pasos para permitir que la información sea correctamente identificada cuando se conoce el nombre del paciente. r GVA-ELAI-UPMPFC0074-03 37 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Figura 2.16: Clasificación de la información de la imagen 38 r GVA-ELAI-UPMPFC0074-03 2.2. INSTANCIAS SOP DE IMÁGENES DICOM (DICOM IMAGE SOP José Ma Onrubia INSTANCES) Información del estudio La información del estudio es una clase con una mezcla de información fuente. Por un lado, la información será suministrada desde un sistema como un RIS (Sistema de Información Radiológico) que identifica el estudio a través de más de un sistema. Por otro lado, el aparato médico añadirá información sobre el paciente en el momento en que el estudio se realiza. Información de otros sistemas incluyen una identificación del estudio. Un UID de una instancia de un estudio es la forma más eficiente de identificación, pero tiene desventajas (ver página 34). Un atributo alternativo, llamado número de acceso, puede ser usado en un sistema RIS. En el caso de que no esté disponible un UID de la instancia del estudio desde fuera de un aparato médico, éste tiene que generar el UID de forma que garantice que es el único en el sistema. Cuando las imágenes de un estudio se copian desde un almacenamiento local para un destino remoto, es muy importante que se use el mismo UID de la instancia del estudio. Esto previene la existencia de imágenes con diferentes identificaciones provinientes un mismo estudio. Tales imágenes nunca pueden ser recogidas juntas sin la intervención de un operador. Otra información suministrada al aparato médico son los nombres de los médicos solicitantes o la lectura de las imágenes y la información del paciente dinámica como la edad, el peso, la ocupación, etc. La información incluida localmente por el aparato médico identifica el estudio proporcionando un valor para el atributo ID del estudio y la fecha y hora actual del estudio. El ID del estudio es sólo relevante por el aparato usado para realizar el examen médico. Información de la serie La clase de información de serie es la primera que es completamente generada por el aparato médico. En esta clase el tipo de sistema, la localización y la identificación del sistema se da. La identificación de las series consiste de un UID de la instancia de la serie, que únicamente identifica la serie en los datos de la imagen y una serie en la zona usada ID que puede ser usado para hacer una secuencia con series en un estudio. Los ID de las series tienen sólo un significado para el aparato médico en sı́ mismo, no hay una regla dada para este uso. Con la información de las series, se suministran más detalles sobre la forma en que las series son realizadas, la gente involucrada, la parte del cuerpo r GVA-ELAI-UPMPFC0074-03 39 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia examinada, etc. La parte de la información del equipo contiene información general sobre el sistema usado por esta serie. Incluye información sobre la localización, la identificación del tipo y la serie, cuestiones de calibración, etc. Estos datos pueden ser compartidos por series pertenecientes al mismo estudio y realizados mediante el mismo aparato médico. Se usa un marco de referencia para agrupar imágenes que tienen relación espacial o temporal. Esto puede ser usado para dividir series en partes o a través de más de una serie si se aplica la misma relación. Tal relación se identifica por un UID de marco de referencia compartido entre las imágenes involucradas. Información de la aplicación Los atributos en esta clase dan información sobre la imagen contenida en la instancia SOP requerida para el diagnóstico y otras aplicaciones. Varios ejemplos, desde un simple texto añadido como comentario, hasta detalles como el contraste, terapia y dispositivos usados durante el reconocimiento médico. Otro grupo describe la parte del cuerpo examinada usando valores codificados. Los ajustes del valor de interés (VOI), en la mayorı́a de los casos llamado anchura de ventana y centro de ventana (window width y window center), son miembros muy importantes de esta clase. El VOI es la selección fuera de la gama completa de los valores de pixel que son clı́nicamente significativos cuando se muestra o se imprime la imagen. Sólo el rango especificado tiene que ser convertido a nivel de grises disponibles. La información que dibuja lı́neas o agrega el texto a la imagen mostrada puede ser en forma del velo matrices que tiene que ser agregado a la demostración por una estación de inspección, o ya aplicados(aplicado) al matriz de pixel (quemado en). Sumnistrando la superposición como una información separada de los datos de imagen, la imagen puede ser mostrada con o sin la superposición, permitiendo que los datos de imagen puedan ser usados como entradas para el tratamiento remoto. Información adquirida En esta clase de información se guardan los ajustes del equipo de adquisición. El grado de información depende del tipo de aparato y puede tener un rango desde unos pocos atributos para un sistema sencillo, a una estructura compleja. Contiene detalles del sistema de adquisición como los 40 r GVA-ELAI-UPMPFC0074-03 2.2. INSTANCIAS SOP DE IMÁGENES DICOM (DICOM IMAGE SOP José Ma Onrubia INSTANCES) valores usados de los rayos X por ejemplo. Las imágenes resultantes de la misma adquisición pueden ser identificadas con un número de adquisición. Este agrupamiento depende del sistema y puede ser parte de series sencillas, pero una adquisición sencilla puede también resultar en múltiples series de imágenes, cada una con diferentes caracterı́sticas. La adquisición no tiene relación con el modelo de información DICOM y no tiene un identificador UID equivalente. Información de la posición Una clase importante es la información dada sobre el posicionamiento de la imagen dentro del paciente. Depende del tipo de aparato médico, de la forma en que se describe la matriz de la imagen posicionada usando términos sencillos como anterior, posterior, derecha, en frente, etc. Se debe tener cuidado para asegurar que hay proporcionada información suficiente con la imagen para que no haya visualizaciones ambiguas (sobre todo en cuestiones de derecha e izquierda). En una serie que tiene relación espacial, como puede ser imágenes CT o MR, muchos más detalles se tienen que suministrar sobre la posición de las imágenes en el espacio tridimensional del cuerpo del paciente. Esta información permite a sistemas como planificadores de tratamiento de radioterapia usar el posicionamiento tridimensional para el procesamiento de los datos de las imágenes. Otro uso de la información del posicionamiento es para sistemas de vasculación para describir los movimientos dinámicos. Información de los datos de las imágenes Finalmente, los datos de las imágenes provenientes del sistema de adquisición y procesadas para producir imágenes visibles en formato digital. Esta clase describe detalles sobre cómo los datos de los pı́xeles deben ser interpretados, como el tamaño de la matriz de pı́xeles, la valor representativo de pı́xel y cómo estos están codificados. Cuando los aparatos médicos son capaces de generar imágenes en color, tiene que ser suministrada la información sobre cómo los datos son ordenados en diferentes planos. A parte del formato de la información, esta clase contiene los datos de los pı́xeles en un marco sencillo, en dos marcos r GVA-ELAI-UPMPFC0074-03 41 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia para sistemas de dos planos o en multimarco. Cuando un multimarco se genera por un sistema de dos planos es posible almacenar los marcos de los dos planos juntos. En este caso los marcos de los dos planos se almacenan alternados (A-B-A-B-...). Para un multimarco las relaciones de tiempo entre los marcos individuales se describen mediante otros atributos. La imagen se identifica únicamente por el UID de la imagen. Como una instancia SOP de una clase de imagen SOP siempre incluye una porción de la imagen, el UID de la imagen se usa también como UID de la instancia SOP. Este UID se use para identificar la instancia cuando se transfiere o se recupera desde un almacen de imágenes o para identificar la entidad de la imagen usándola en un árbol jerárquico de información. 2.2.5. Extensión de la información Para el almacenamiento de toda la información descritas en las clases de arriba se definen atributos que se agrupan en IOD´s (Information Object Definitions) los cuales dan una descripción genérica de las instancias SOP para cada tipo de aparato médico. Los atributos que se usan actualmente deben ser descritos en el Estatuto de Conformidad (perfil del sistema). No siempre es posible guardar toda la información generada por un aparato en un IOD estándar. En casos el equipo tiene nuevos campos los cuales necesitan información adicional para alamacenarlos en un nuevo IOD. Se debe tener cuidado en que las partes que usan esta información serán capaces de entender esta nueva información. Estos detalles se tienen que publicar en el Estatuto de Conformidad. Si el uso se acepta, la nueva información llega a formar parte del estándar. La extensión puede no influenciar la semántica de la información guardada en los atributos estándares. Tiene que ser un subconjunto apropiado, compatible con el IOD del que deriva. En otros casos, el equipo de un vendedor único, puede añadir información para ser usada sólo en la combinación de sistemas o sólo por el mismo sistema que ha generado los datos. En esta situación, no existen detalles sobre la información que tiene que ser publicada en el Estatuto de Conformidad. No hay intención por otras partes de usar esta información adicional. Para permitir la extensión de información, DICOM ha definido dos tipos de atributos: atributos estándar y privados. Los primeros se usan para codificar los atributos descritos en el estándar IOD (ver sección .Atributos”página 9). Si no hay extensiones o cambios en 42 r GVA-ELAI-UPMPFC0074-03 2.2. INSTANCIAS SOP DE IMÁGENES DICOM (DICOM IMAGE SOP José Ma Onrubia INSTANCES) los IOD´s, la clase SOP es una clase SOP estándar. Los segundos definen atributos o usan atributos estandarizados no pertenecientes al IOD de una clase SOP especı́fica, no se pude seguir llamando clase estándar SOP y depende del efecto que cambia a uno de los siguientes tipos: Clase SOP extendida: cuando los atributos adicionales no cambian el uso de la clase SOP. En este caso es un super-conjunto, cuando se usa por sistemas los cuales no son conscientes de las añadidos, se pueden ignorar y la imagen puede ser manejada como se dice en la clase SOP estándar. Una clase SOP extendida usa el mismo UID que la clase SOP estándar. Las diferencias entre las dos clases se muestra en el Estatuto de Conformidad. Clase SOP especializada: cuando las adiciones cumplen con el modelo de información, pero la clase ya no es un super-conjunto. Como consecuencia, el UID de la clase SOP estándar puede no ser usado; se debe usar un UID privado para esta clase SOP. Los socios que manejan las instancias SOP conocen el UID privado y pueden manejar la información. Otros no pueden aceptar la clase SOP durante la negociación de la asociación o, cuando se abre un archivo DICOM desde un medio DICOM. Las Clases SOP privadas no siguen el modelo de información DICOM y se usan en un contexto completamente privado. Usan mecanismos proporcionados por DICOM para transferir la información. Las clases SOP privadas usan UID´s privados para prevenir usos incorrectos de la información. Si una de las tres clases SOP arriba mencionadas se definen con la intención de llegar a ser parte del estándar DICOM, los detalles se publican en el Estatuto de Conformidad. De otra forma, sólo se usan en un ambiente cerrado. 2.2.6. Tipos de imágenes DICOM define un número de tipos de clases de imágenes SOP, dependiendo del aparato médico que crea la los datos de las imágenes. Cada tipo tiene su propio IOD para añadir información especı́fica del aparato a la instancia de la imagen SOP. Todas las instancias de las imágenes SOP comparten un mı́nimo juego de información que permite a una aplicación visualizadora manejar las imágenes independientemente de su tipo. Una clase de imagen SOP está disponible para encapsular las imágenes que no están disponibles en el formato digital y sı́ capturadas en formato de pelı́cula o de vı́deo. r GVA-ELAI-UPMPFC0074-03 43 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Tipos genéricos de imágenes Las instancias de las clases de las imágenes SOP tienen un conjunto básico de atributos; ver figura(2.17). El conjunto mı́nimo de atributos requeridos para una instancia de imagen SOP consiste en el siguiente grupo de atributos: Atributos identificadores: UID de clase SOP, UID de la instancia del estudio, UID de la instancia de la serie y UID de la instancia de la imagen (= UID de la instancia SOP). Tipo de aparato. Descripción de la matriz de pı́xeles: muestra por pı́xel, filas, columnas. Interpretación del valor del pı́xel: interpretación fotométrica. Codificación de los pı́xeles: bits asignados, bits almacenados, bit alto, representación de pixel, configuración plana. Matriz de pı́xeles: datos de pı́xel. 44 r GVA-ELAI-UPMPFC0074-03 2.2. INSTANCIAS SOP DE IMÁGENES DICOM (DICOM IMAGE SOP José Ma Onrubia INSTANCES) Figura 2.17: Juego básico de atributos de las instancias de imagen SOP Este mı́nimo conjunto permite mostrar los datos de pı́xel y proporciona la identificación en el nivel de sistema, para el caso de la instancia SOP para adherirla modelo de la información. Añadiendo más información al menos para los tres primeros niveles del modelo de información, hace más entendible a la instancia SOP. Los atributos que identifican la instancia SOP para seres humanos y permiten que la imagen sea mostrada. Añadiendo más información para al menos los tres primeros niveles del modelo de la información, hace la instancia SOP más comprensible. Los atributos que identifican la instancia SOP para los seres humanos y permiten que la imagen sea visualizada con los correctos ajustes de ventana son: Nivel de paciente: nombre, ID, dı́a de nacimiento, sexo. Nivel de estudio: fecha del estudio, hora, nombre del médico, ID del estudio, número de acceso. r GVA-ELAI-UPMPFC0074-03 45 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Nivel de serie: número de serie, fabricante, nombre de la institución. Nivel de imagen: número de imagen, tipo de imagen. Ajustes de presentación: ancho de ventana, centro de ventana. Los atributos listados arriba son en la mayorı́a de los casos atributos del tipo 2 (deben ser suministrados, pero pueden faltar) o del tipo 3 (opcionales). Tipos de imágenes especiales El formato genérico descrito arriba se usa en la definición de cada clase SOP, pero depende del tipo de aparato, esto se extiende con la información entregada sobre la adquisición, etc. El número de imágenes especializadas está creciendo por la aparición de nuevas máquinas médicas. Normalmente, los siguientes aparatos tienen una definición de la clase de almacenamiento SOP en el estándar DICOM: Radiografı́a computada IOD, usada por los sistemas radiográficos tradicionales que trabajan con fósforo que brilla al leerse con sistemas como PCR. Tomografı́a computada IOD para escáneres CT. Para este tipo de aparatos el posicionamiento es importante, para montones de imágenes, para crear vistas tridimensionales. Resonancia magnética IOD para sistemas IOD. A parte de la misma información que para escáneres CT también se da información adicional sobre el protocolo de adquisición. Medicina nuclear IOD para cámaras usan isótopos radiactivos. Contienen imágenes de especial formato para este tipo de aparatos. Las imágenes son en multimarco formato. Ultrasonidos IOD para este tipo de equipos. Estos contienen detalles sobre la posición y la adquisición de la imagen. Las imágenes pueden ser en color y se puede usar el multimarco. Angiografı́a con rayos-X IOD para sistemas digitales cardiológico y basculares. Este formato puede capturar una cadena en multimarco o imágenes simples. Inside a run a description of which images are a mask for image subtraction can be added. Extensive information about the equipment positioning and acquisition is given to allow the image data to be processed. 46 r GVA-ELAI-UPMPFC0074-03 2.2. INSTANCIAS SOP DE IMÁGENES DICOM (DICOM IMAGE SOP José Ma Onrubia INSTANCES) Radiofluoroscopı́a de rayos-X IOD para sistemas como sistemas angiográficos, pero hacer una mesa con columnas en vez de brazos C. La mayor diferencia es la forma en que se describe la posición de las piezas del equipo. Por cada uno de los IOD´s, se describe una lista de módulos en el estándar DICOM. El uso de unos ciertos módulos a veces dependen de capacidades o condiciones de ciertos sistemas. Los módulos se seleccionan o desde un grupo de módulos comunes usados para todos los IOD´s de almacenamiento SOP, o módulos especı́ficos para sólo un tipo de IOD. Estos módulos contienen atributos especiales para ese tipo de IOD. Esos módulos, y a veces atributos individuales, los redefinen y extienden para el IOD genérico. En la parte PS3.3 del estándar DICOM los IOD, IOM y los atributos se listan. El anexo A contiene la lista de IOM para cada IOD. En el anexo C.7 se describen los módulos comunes de los IOD´s, en el anexo C.8 los módulos especı́ficos de los aparatos médicos. El anexo C.9 mediante C.12 define un número de módulos que pueden ser añadidos al objeto imagen, como un módulo más, un valor de interés (VOI), información, etc. 2.2.7. Aplicación de los datos de imágenes Las clases de imagen SOP, en general, se generan en aparatos médicos o en estaciones de trabajo dedicadas a procesamiento. Las clases de imagen SOP se visualizan en estaciones de revisión o son imprimidas sobre pelı́cula. Los sistemas de almacenamiento guardan en el búfer las imágenes en un fase intermedia o archivan estas imágenes por referencia en un estado posterior. Los requerimientos para la información contenida en la instancia de imagen SOP (Image SOP Instance) difieren para cada tipo de sistema el cual está envuelto en el ciclo de vida de una imagen. Intercambiando datos entre sistemas, cada sistema puede tener una diferente visión de la información, pero se debe tener mucho cuidado con que toda la información en la instancia de imagen SOP sea transferida entre cada sistema involucrado. Incluso cuando un sistema en una cadena no usa la información, otro sistema, que sı́ que la usa en una fase posterior, está confiando en el paso completo de la información en la cadena entera, ver figura (2.18). r GVA-ELAI-UPMPFC0074-03 47 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Figura 2.18: Ciclo de vida de Image SOP Instance Information Sistemas de almacenamiento de imágenes Los sistemas de almacenamiento de imágenes usan un número de atributos identificatorios para almacenar las instancias de imagen SOP. En primer lugar, estos atributos se usan para recoger todos los datos de las imágenes pertenecientes al mismo estudio. La instancia UID del estudio (Study Instance UID) es el atributo clave. Pero cuando éste no se usa consistentemente, se tienen que usar otros atributos como el ID del paciente, número de acceso, etc. En segundo lugar, unos atributos pueden ser usados por sistemas que quieren encontrar instancias de imagen SOP en el sistema de almacenamiento. La clave principal en este caso es la instancia UID del estudio y la instancia UID de la serie. Son posibles también búsquedas basadas en el nombre del paciente, fecha del estudio, etc. Para el uso de sistemas de almacenamiento un valor muy significativo para estos tipos de atributos se tiene que suministrar por los creadores de la instancia de la imagen SOP. Los atributos que contiene los parámetros de adquisición y los datos de la imagen se almacenan pero no tienen significado para un sistema de almacenamiento. 48 r GVA-ELAI-UPMPFC0074-03 2.2. INSTANCIAS SOP DE IMÁGENES DICOM (DICOM IMAGE SOP José Ma Onrubia INSTANCES) Estaciones de revisión Una estación de revisión es básicamente usada para visualizar las imágenes hechas en una o varios aparatos. Recoge o busca las instancias de imagen SOP, en un sistema de almacenamiento, perteneciente a un cierto estudio. Mostrará la imágen junto a la información del paciente, ajustes de adquisición, información del diagnóstico, etc. Los ajustes paraa el proceso de presentación, como los seleccionados en el aparato de captura, son muy importantes. Cuando los pasos de presentación se procesan de forma correcta, los resultados mostrados deberı́an ser iguales a los originales mostrados por el aparato médico de captura de la imagen. Para información adicional de otros sistemas, se usan atributos identificadores como la instancia UID del estudio. Estaciones de procesamiento de imágenes Las estaciones de trabajo capaces de procesar los datos de las imágenes tienen requerimientos adicionales. Se necesitan los parámetros de adquisición y posicionamiento para la realización de pasos adicionales de procesamiento. Dependiendo en el tipo de procesamiento la entrada es un conjunto de imágenes procesadas o no procesadas. En este caso la relación entre las imágenes es importante ordenar los datos de las imágenes de forma correcta para el procesamiento. Los resultados de este procesamiento son nuevos datos de pı́xeles que son almacenados en una nueva instancia de imagen SOP que tiene su propio ciclo de vida con, en la mayorı́a de los casos, relaciones con los datos originales usados por esta imagen. Reutilización de datos Una categorı́a final de las aplicaciones es el sistema que ha creado la instancia de imagen SOP. En este caso los datos antiguos de la imagen pueden ser usados cuando hay una nueva visita del mismo paciente con el mismo tipo de examinación. Los datos de adquisición y el posicionamiento pueden ser reutilizados, o la imagen puede ser visualizada como una referencia para el nuevo examen. r GVA-ELAI-UPMPFC0074-03 49 CAPÍTULO 2. ESTADO DE LA TÉCNICA. José Ma Onrubia Categorı́as de aplicación Como lo mostrado arriba, las exigencias de los sistemas individuales en el ciclo de vida de una una instancia de imagen SOP son diferentes. Cuando un sistema produce los datos de la imagen, debe estar alerta de que todos los sistemas que están intentando ser parte del ciclo de vida deben recibir suficiente información. El estatuto de conformidad (conformance statement) debe describir, para cada tipo de sistema, la información adecuada y que procesamiento no pueden ser aplicados. Para ayudar a la selección de estos tipos de sistemas, los requerimientos pueden ser divididos en categorı́as de aplicación. Una categorı́a alta numerada incluye la categorı́a baja numerada. Se definen las siguientes categorı́as: 1. Categorı́a de almacenamiento: sólo identifica los atributos requeridos. 2. Categorı́a de visualización: sólo se requieren los atributos para una correcta presentación. 3. Procesamiento simple - medidas de volumen y distancia: requiere algunos atributos más que describan qué información está en la imagen. 4. Procesamiento complejo - sustracción de imagen y MRP: requiere información especı́fica sobre el posicionamiento y relaciones. El comité de estandarización DICOM está considerando una clasificación como la lista vista arriba para ser incluida en el estatuto de conformidad. 50 r GVA-ELAI-UPMPFC0074-03 Capı́tulo 3 Estudio de librerias DCMTK. 3.1. Visión general. La tecnologı́a de la información y la comunicación poco a poco está llegando a ser omnipresente en el cuidado de la salud. Los requerimientos para un rápido acceso a los datos médicos en el momento necesario sólo se puede conseguir mediante los estándares. El estándar de DICOM facilita el intercambio y el procesamiento de imágenes biomédicas de forma digital. Los aparatos de adquisición de imágenes, archivos de imágenes y las estaciones de trabajo de diferentes vendedores, pueden estar conectadas en una infraestructura común e integradas con otros sistemas de información. Nuevas oportunidades se alzan, no sólo dentro del hospital, también para el intercambio de información entre diferentes clı́nicas y entre hospitales y prácticas médicas. 3.1.1. Introducción. La importancia del procesamiento de imágenes médicas a crecido permanentemente desde la introducción de la tomografı́a computada como primer aparato digital en 1970. La idea de unas imágenes digitales y una distribución electrónica de éstas en el hospital creó la necesidad de intercambiar las imágenes digitales entre recursos de diferentes vendedores. En 1983 se formaron los grupos de American College of Radiology (ACR) y el National Electrical Manufacturers Association (NEMA) con el objetivo de descubrir un estándar para el intercambio. Los trabajos resultaron en el estándar ACR-NEMA, el cuál 51 CAPÍTULO 3. ESTUDIO DE LIBRERIAS DCMTK. José Ma Onrubia fue publicado en 1985. Sin embargo este estándar no tuvo éxito devido a una cierta debilidad conceptual. El estándar de DICOM (Digital Imaging and Communications in Medicine) fue creado desde las bases del ACR-NEMA para crear y abrir una plataforma para la comunicación de imágenes y datos relacionados. Además de este soprte, la garantı́a de interoperatividad entre aparatos DICOM y programas fue la parte central de su éxito. DICOM fue publicado en 1993 y se está extendiendo continuamente desde entonces. DICOM fue aceptado como estándar formal en Europa en 1995. 3.1.2. Descripción. Los contenidos del standard de DICOM van más allá de una definición del formato de los datos de las imágenes médicas. DICOM define: Estructuras de datos (formatos) para imágenes médicas y datos relacio-nados, Servicios orientados a la red. Formatos para el almacenaje de medios de intercambio. Requerimientos para el acuerdo de recursos y programas. 3.1.3. Estructura de datos DICOM. Una imagen DICOM consiste en lista de elementos de datos (llamados atributos) que contiene una multitud de imagen la información relacionada: paciente (nombre, sexo, identificación, número) aparatos y procedimiento de imagen(parámetros de dispositivo, calibración, dosis de radiación, medios de comunicación de contraste), imagen (resolución, windowing) DICOM con precisión define para cada aparato, los elementos de datos son requeridos, los q son opcionales y que es requerido en ciertas circunstancias. Esta flexibilidad sin embargo, tan poderoso como es, es también una debilidad del estándar porque la experiencia práctica muestra que las imágenes son con frecuencia incompletas: los campos requeridos 52 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 3.1. VISIÓN GENERAL. omiteno contienen valores incorrectos. Esto puede conducir a problemas en el intercambio de datos. 3.1.4. Servicios de red DICOM. Los servicios de red de DICOM están basados en los concepto de cliente / servidor. Antes de que dos aplicaciones DICOM puedan intercambiar la información, ellas deben establecer una conexión y estar de acuerdo sobre los parámetros siguientes: Quien es el cliente y quien es el servidor, que servicios DICOM deben ser usados, y en que formato los datos son transmitidos (Comprimido o incomprimido). Sólo si ambas aplicaciones están de acuerdo en un unos parámetros comunes, la conexión se establece. 3.1.5. Intercambio de medios de comunicación. Además de la comunicación de imágenes médicas sobre la red, el intercambio de medios de comunicación se ha hecho otro foco en DICOM que ha sido integrado en el estándar sólo en la edición 1996. Los campos de aplicación son por ejemplo el almacenaje o la pelı́culas de angiografı́as en la cardiologı́a o el almacenaje de imágenes de ultrasonido. Para asegurarse de que los medios de comunicación de almacenaje DICOM son realmente intercambiables, el estándar define los llamados ”perfiles de aplicación ”que explica detalladamente. que aparatos pueden estar presentes en el medio que formatos de codificación y compresión pueden ser usados que medio de almacenaje debe ser usado Cada medio DICOM contiene un llamado ”DICOM directorio .además de los archivos de imagen. Este directorio contiene la información más importante (el nombre paciente, la modalidad, identificadores únicos etc.) para todas las imágenes en el medio. Esto permite hojear o buscar rápidamente sin haber de necesidad de leer el medio completo. r GVA-ELAI-UPMPFC0074-03 53 CAPÍTULO 3. ESTUDIO DE LIBRERIAS DCMTK. 3.1.6. José Ma Onrubia Estatuto de conformidad. DICOM requiere que una Declaración de Conformidad sea escrita para cada dispositivo o la reclamación de programa para ser DICOM conformant. El formato y el contenido de esta declaración son definidos en el estándar. Esto explica que servicios DICOM y opciones son soportados, que extensiones y particularidades han sido puestas en práctica por el vendedor, y como el dispositivo se comunica con otros sistemas DICOM. En la teorı́a, comparando dos declaraciones de conformidad permite determinar si dos dispositivos DICOM son capaces de comunicarse el uno con otro. En la práctica, sin embargo, las declaraciones de conformidad son sólo comprensibles para expertos y son con frecuencia inadecuados. 3.1.7. Conclusión. DICOM se ha hecho un componente indispensable para la integración de sistemas de imagenes digitales en la medicina. DICOM ofrece soluciones para una multitud de comunicación aplicaciones relacionadas. La palabra clave ”DICOM”solo, sin embargo, no es ninguna garantı́a para una integración ”de enchufar y listo”de todos los sistemas de información en hospital. Esto requiere una combinación cuidadosa de todas las soluciones parciales ofrecidas por DICOM. DCMTK es el software DICOM proporcionado gratuitamente por OFFIS-DICOM, el cual se puede descargar de la página web. DCMTK es una colección de librerı́as y aplicaciones que implementan las partes del estándar DICOM para la comunicación de imagen médica. Esto incluye el software para el examen, la construcción y la conversión archivos de imagen DICOM, el manejo medios de comunicación autónomos, el envı́o e imágenes de encubrimiento sobre una conexión de red, ası́ como la demostración del almacenaje de imagenes y servidores worklist. DCMTK incluye el código fuente completo y está escrito en una mezcla de C ANSI y la C ++. DCMTK ha sido usado en numerosas demostraciones DICOM para proporcionar central, el vendedor independiente, el almacenaje de imagen y servidores worklist (CTNs - Nodos Centrales De prueba). Es usado por hospitales y empresas en todo el mundo para una amplia variedad de propósitos desde ser una herramienta para pruebas de productos, a ser un componente básico para proyectos de investigación, prototipos y productos comerciales 54 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 3.2. INSTALACIÓN DCMTK351. El software DCMTK puede ser compilado bajo el Windows NT o una amplia gama de sistemas operativos Unix que incluyen Linux, Solaris, OSF/1, IRIX, FreeBSD y MacOS X. Todos los guiones de configuración necesarios y ”makefiles”de los proyecto son suministrados. Trabajar con esta herramienta es muy arduo y dificultoso debido a la poca documentación, a la dificultad que entraña el trabajar con todos los términos DICOM y a la amplitud del código. Por estas razones y por alguna más es más aconsejable trabajar con una nueva herramienta que cuenta con la desventaja de que no es gratuita pero con las ventajas de una gran documentación plenamente estructurada y el trabajar con Java. 3.2. Instalación dcmtk351. OfficeDicom ofrece la posibilidad de conseguir gratuitamente de su página web, http://www.offis.de, el Toolkit dcmtk351, donde se encuentra el estándar DICOM implementado en código C++. Para la instalación del toolkit de OfficeDicom en un sistema operativo basado en Windows, hay que descomprimir el archivo dcmtk531.zip. Entonces abriremos el proyecto dcmtk.dsp y lo compilaremos y construiremos con el Visual C++, este proyecto se encuentra en X:/.../Toolkit/source/dcmtk. Esto creará todos los ejecutables y archivos .obj y .lib en una carpeta llamada OpenSSL de cada una de las funciones existentes en este toolkit las cuales podrán ser utilizadas en otros proyectos, bastando incluirlas en éstos. Este Toolkit tiene diferentes funciones basadas en el estándar Dicom. Hay varias carpetas con los archivos fuente de las funciones. Las funciones son agrupadas en carpetas dependiendo de la función que desempeñen. Config: Incluye los ficheros .h necesarios para la configuración del toolkit. Dcmdata: Contiene funciones para el tratamiento de los datos de los archivos Dicom y Data Sets. Dcmimage: Fuciones para el tratamiento de los datos de los pixel de una imagen. Sólo para imágenes Dicom sin comprimir. Dcmimgle: Sirven para el tratamiento de la luminosidad de las imágenes. r GVA-ELAI-UPMPFC0074-03 55 CAPÍTULO 3. ESTUDIO DE LIBRERIAS DCMTK. José Ma Onrubia Dcmjpeg: Son funciones para la compresión/descompresión de imágenes DICOM a JPEG. Dcmnet: Funciones para el transporte de los archivos Dicom a través de la Red. Dcmpstat: Funciones para el tratamiento de escalas de grises y estados de presentación. Dcmsing: Para la creación o supresión de una firma digital para un archivo Dicom y su verificación. Dcmsr: Para la conversión de documentos Dicom SR (Structured Reporting) a HTML, XML . Dcmtls: Para la transmisión segura de archivos Dicom por la Red. Imagectn: Para el registro de archivos en una base de datos. Wlistctn: Para implementar un SCP como una Base de Datos. Nos centraremos en las funciones para el transporte de archivos y la compresión a Jpeg. Figura 3.1: Visual C++ con DCMTK 56 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 3.2.1. 3.2. INSTALACIÓN DCMTK351. Dcmnet. Echoscu: Esta aplicación implementa un Service Class User (SCU) para la verificación SOP Class.Envı́a un mensage Dicom C-ECHO a un Service Class Provider (SCP) y espera una respuesta. Findscu: Esta aplicación implementa un Service Class Query/Rerieve (petición/recuperación) y un Service Class Basic Worklist Management. Sólo soporta el mensage Dicom C-FIND, que envı́a una petición al SCP y espera respuesta. Movescu: Esta aplicación implementa un SCP para el Service Class Query/Retrieve y un SCU para el Storage Service Class. Para usar la funcionalidad Retrieve (Recuperación) usa el mensage Dicom CMOVE que envı́a una petición al SCP y espera respuesta. El SCP aceptará la asociación para recibir imágenes enviadas como resultado de la petición C-MOVE. El término MOVE es poco apropiado, ya que lo que hace es una copia de la imagen, la imagen nunca se borra del SCP. Storescp: Implementa un Service Class Provider (SCP) para el Storage Service Class. Se pone a la escucha sobre un especı́fico puerto TCP/IP, para las peticiones de asociación que puedan llegar de un Storage SCU y puede recibir imágenes según el Storage Service Class. Storescu: Implementa un Service Class User (SCU) para el Storage Service Class.Se usa para transmitir imágenes Dicom. Envı́a un mensage C-STORE a un Storage SCP y espera respuesta. Aplicación para ver la conectividad entre dos máquinas. Esta aplicación la realizaremos sobre dos máquinas, una será Galileo que actuará de SCP y otra será Gauss que será el SCU. Pondremos la opción -v la cual nos dirá en todo momento que asociación se está realizando, dando detalles sobre ella. Paso 1o : Realizamos una aplicación Dicom Storage/Verification Service Class Provider. Ponemos un puerto a la escucha (el puerto 104 es el más utilizado en Dicom) para la llegada de asociaciones. Galileo¿storescp -v 104 Paso 2o : Comenzamos una aplicación Dicom Verification Service Class User. Esto intentará construir una asociación Dicom con una aplicación que corra sobre Galileo, conectándolos por el puerto 104. Gauss enviará una petición C-ECHO y estará a la espera de una respuesta C-ECHO de Galileo. r GVA-ELAI-UPMPFC0074-03 57 CAPÍTULO 3. ESTUDIO DE LIBRERIAS DCMTK. José Ma Onrubia Esto es sólo para verificar que hay conexión entre las dos máquinas. Gauss>echoscu -v Galileo 104 Paso3o : Enviamos una imagen Dicom a Galileo (SCP). Volveremos a intentar realizar una asociación Dicom con una aplicación que corra sobre Galileo. Enviamos una petición C-STORE que contiene una imagen Dicom çraneo.dcm esperamos una respuesta C-STORE de Galileo. 2 Gauss>storescu -v Galileo 104 craneo.dcm La imagen se guardará en el lugar de trabajo donde esté realizandose la escucha del puerto 104. Por ejemplo si lo hemos hecho en un fichero C:/Dicom, la imagen será guardada en ese fichero. El nombre que tendrá la nueva imagen copiada en Gauss, será a priori su UID, que será único entre todas las imáges DICOM que tengamos. 3.2.2. Dcmjpeg. Dcmcjpeg: Realiza la compresión de una imagen DICOM a una imagen JPEG. Dcmdjpeg: Descomprime la imagen DICOM comprimida como JPEG. Dcmj2pnm: Lee una imagen DICOM y convierte los datos de los pixel según las opciones del procesamiento de imágenes seleccionadas, PPM/PGM, BMP, TIFF o JPEG. Dcmmkdir: Crea un archivo DICOMDIR de los archivos DICOM especificados según la aplicación Media de almacena. 3.3. DicomScope. El DicomScope es un programa desarrollado en C++ y su entorno de ventanas en Java. Permite visualizar, imprimir, almacenar, transmitir y recibir estudios, imágenes, estados de presentación e informes estructurados . Los informes estructurados (SR) consisten en informes completos de diagnóstico y resultados de un examen en particular. La aplicación tiene cuatro partes principales: Browser (Navegador de estudios): El cual es la base de datos local de la aplicación donde se encuentran los estudios (imágenes, structured 58 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 3.3. DICOMSCOPE. reports,...) de la aplicación. Viewer: Para tratar y mostrar imágenes Dicom, imágenes de escalas de grises, estados de presentación e informes estructurados. Print: Administra e incluye los estudios para su impresión. Process log: Muestra los procesos que se han llevado a cabo en la transmisión de archivos Dicom. 3.3.1. Instalación. Es necesario para la instalación un sistema compatible con Java v1.3 y Windows 32 bits. Antes de la instalación del DicomScope es necesaria la instalación de Java 2 SDK o JRE(Java 2 Runtime Environment), para conseguir esto, se puede bajar de la página http://java.sun.com/j2se/1.4.1/download.html. Tan sólo hay que ejecutar el Setup y seguir las instrucciones dadas. 3.3.2. Browser. Es la base de datos del DicomScope donde se almacenan los estudios Dicom. Tiene una estructura de árbol en la que cada estudio es una rama, y puede tener varias ramas a su vez, puede tener imágenes, informes estructurados, imágenes de escalas de grises y estados de presentación. Los nuevos estudios recibidos por Red o almacenados en la aplicación, se di-ferenciarán de los otros por que aparece un sı́mbolo de ”New.a su izquierda, una vez visualizados éstos, perderán el sı́mbolo. Para enviar estudios a otra máquina, hay elegir el estudio que queramos en-viar y presionar la tecla send, entonces se abrirá una ventana en la que hay que elegir la forma de enviar los estudios, hay tres formas: Transmisión insegura, sin protección de los datos en tránsito. Transmisión TLS , en la que se utilizan certificados para la transmisión de datos. Transmisión TLS y encriptación. Para poder configurar a que máquinas van a ir los estudios enviados y que puerto utilizan para la asociación, se hace mediante el archivo DICOMscope.cfg que se encuentra en .../DICOMscope351, escribiendo a que hostname quiere ser en-viado y el port. r GVA-ELAI-UPMPFC0074-03 59 CAPÍTULO 3. ESTUDIO DE LIBRERIAS DCMTK. José Ma Onrubia Figura 3.2: Browser 3.3.3. Viewer. Todas las instancias Dicom que se carguen, pasarán a verse en el Viewer. Es una herramienta para tratar las imágenes Dicom y cambiar y editar informes estructu-rados, incluso para verificarlos y crear firmas digitales. 60 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 3.3. DICOMSCOPE. Figura 3.3: Viewer 3.3.4. Print. Se pueden enviar imágenes Dicom a imprimir desde el Browser o la imagen que se está visualizando en el Viewer. Esto lleva la imagen al Print en el que se vi-sualiza la imagen que se va a imprimir. r GVA-ELAI-UPMPFC0074-03 61 CAPÍTULO 3. ESTUDIO DE LIBRERIAS DCMTK. José Ma Onrubia Figura 3.4: Print 3.3.5. Process Log. Se puede ver que procesos se producen para la transmisión de archivos Di-com. Al iniciar el DicomScope se ve que hay dos procesos que están realizándose. Uno es para que el DicomScope sirva como receptor de instancias Dicom con transmisión insegura, sin utilizar TLS. Este proceso utiliza la orden C-STORESCP, con esto recibe y guarda en la base de datos los archivos Dicom mandados a este equipo, utilizando el puerto 10004. El otro proceso recibe las instancias Dicom utilizando TLS, siendo de esta forma segura la transmisión de datos. También utiliza la orden C-STORESCP y el puerto elegido es el 10007. 62 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 3.3. DICOMSCOPE. Figura 3.5: Process log r GVA-ELAI-UPMPFC0074-03 63 CAPÍTULO 3. ESTUDIO DE LIBRERIAS DCMTK. 64 José Ma Onrubia r GVA-ELAI-UPMPFC0074-03 Capı́tulo 4 JDT (Java DICOM Toolkit) 4.1. Introducción. Java DICOM Toolkit es la ayuda para un programador en JAVA para construir una aplicación que siga lo marcado por el estándar DICOM 3.0. Combina las ventajas y la fuerza de DICOM y JAVA en una .API”muy fácil de usar. Proporciona numerosas clases y métodos que simplifican la programación de DICOM. JDT es el primer equipo de desarrollo de software DICOM que está implementado totalmente en JAVA. Con esto, los desarrolladores DICOM pueden beneficiarse de las numerosas ventajas del lenguaje de programación JAVA y desplegarlo en DICOM .applets”, aplicaciones independientes JAVA y en software de servidor. JDT viene con una .API”bien documentada que ha sido diseñada para hacer que la estructura compleja del estándar DICOM sea más accesible para los desarrolladores. JDT funciona sobre JDK 1.1.X y la JAVA 2. Caracterı́sticas: Soporte de la parte 10 del estándar DICOM. Soporte de red DICOM. Soporte para todas las ”transfer syntax”no comprimidas y conversiones. Soporte para formatos RLE y JPEG. Leer y escribir datos DICOM desde InputStreams/OutputSreams. Esquema de tipos JAVA a tipo tipos DICOM. Tolerancias en implementaciones DICOM. 65 CAPÍTULO 4. JDT (JAVA DICOM TOOLKIT) 4.2. Guia de usuario de JDT. 4.2.1. Terminologı́a. José Ma Onrubia En esta sección se usa palabras como conjunto de datos (dataset) y atributo. Conjunto de datos se usa para identificar una colección de atributos DICOM. Un atributo se identifica por su par (número de grupo, número de elemento), tiene un cierto tipo DICOM (value representation - VR) 4.2.2. Conjunto de datos (Datasets). Introducción. En JDT, todos los conjuntos de datos están representados por com.archimed.dicom.DicomObject. Esta clase es una subclase de com.archimed.dicom.GroupList. Básicamente es donde residen los atributos DICOM (com.archimed.dicom.TagValue). Manipulación de atributos. Un conjunto de datos DICOM es representado por un DicomObject. Los diferentes atributos de estos conjuntos de datos están internamente almacenados en el DicomObject como objetos com.archimed.dicom.Tagvalue. El objeto TagValue contiene un par (grupo,elemento) y los valores del atributo están almacenados en un Vector. El tamaño del Vector corresponde a la multiplicidad del atributo. El tipo JAVA que representa el valor de un atributo depende del tipo DICOM (Value Representation - VR) definido para ese atributo especı́fico. La siguiente tabla muestra la equivalencia entre los tipos DICOM y sus correspondientes tipos JAVA. Las conversiones soportadas a y desde tipos que no son los tipos por defecto están en cursiva. Manipulación de atributos simples. Hay un número de métodos set/get en DicomObject con los que se puede meter, sacar o modificar los atributos guardados en un DicomObject. La forma de hacer esto es con la clase com.archimed.dicom.DDict. La clase DDict tiene una constante definida para cada atributo definido en el estándar DICOM. Por ejemplo: DDict.dPatientName DDict.dAccessionNumber Esas constantes pueden ser usadas en los métodos set/get del DicomObject: Person p = (Person)dcm.get(DDict.dPatientName); 66 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia tipo Java Short Integer Long Float Double byte[] int[] String Person DDate DDateRange 4.2. GUIA DE USUARIO DE JDT. tipo DICOM SS US, US—SS, IS, AT, SS, SL, UL SL, UL FL, DS FD OB, OW, OW—OB AT (int[2]) AS, AE, CS, LO, LT, SH, ST, UI, TM, PN, DA, DS, IS, SS, US, SL, UL, FL, FD, OB, OW, OW—OB PN DA DA Cuadro 4.1: tipo Java / tipo DICOM Integer acnumber = (Integer)dcm.get(DDict.dAccessionNumber); dcm.set(DDict.dPatientName, new Person(”Jose”)); dcm.set(DDict.dAccessionNumber, new Integer(12345)); El método set implı́citamente convierte el valor del argumento dado al tipo DICOM correcto. También se puede acceder a los atributos de un DicomObject a través del par (grupo.elemento). Person p = (Person)dcm.get ge(0x0010, 0x0010); Integer acnumber = (Integer)dcm.get ge(0x0008,0x0050,DDict.dAccessionNumber); dcm.set ge(0x0010, 0x0010, new Person(”Jose”)); dcm.set ge(0x0008, 0x0050, new Integer(12345)); Hay también dos métodos get que convierten directamente a String o int: String s = dcm.getS(DDict.dPatientName); int i = dcm.getI(DDict.dAccessionNumber); r GVA-ELAI-UPMPFC0074-03 67 CAPÍTULO 4. JDT (JAVA DICOM TOOLKIT) tipo DICOM AE AT AT CS DA DS FL FD IS LO LT OB OW OB—OW PN SH SL SS ST TM UI UL US US—SS José Ma Onrubia tipo Java String String Integer (0xggggeeee) String DDate or DDateRange Float Float Double Integer String String byte[] byte[] byte[] Person String Long Short String String String Long Integer Integer Cuadro 4.2: tipo DICOM / tipo Java Para manipular atributos con una multiplicidad mayor de uno, se usan los métodos get/set con un argumento adicional. La multiplicidad de un atributo se obtiene con getSize(). Por ejemplo, suponiendo que un DicomObject dcm contiene un atributo ImageType con multiplicidad 2 y valores DERIVED/SECONJDARY, entonces se puede coger esos valores de la siguiente forma: int multiplicity = dcm.getSize(DDict.dImageType); //devolverá el valor 2 String str1 = (String)dcm.get(DDict.dImageType,0); valor ’DERIVED’ //devolverá el String str2 = (String)dcm.get(DDict.dImageType,1); //devolverá el 68 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 4.2. GUIA DE USUARIO DE JDT. valor ’SECONDARY’ Insertando el valor de un atributo ocurre lo mismo: dcm.set(DDict.dImageType,0,”DERIVED”); dcm.set(DDict.dImageType,1,”SECONDARY”); Secuencias. Las secuencias se tratan de una forma parecida que los valores de multiplicidad mayor de uno. Cuando un atributo en un DicomObject representa una secuencia (tipo SQ DICOM), entonces los artı́culos de la secuencia pueden ser insertados y cogidos con los mismos métodos get/set del DicomObject que ahora aceptan y devuelven los DicomObejct´s completos. Por ejemplo, suponiendo que el DicomObject dcm contiene un atributo DirectoryRecordSequence con un cierto número de artı́culos: int numberofitems = dcm.getSize(DDict.dDirectoryRecordSequence); DicomObject item0 = (DicomObject)dcm.get(DDict.dDirectoryRecordSequence,0); DicomObject item1 = (DicomObject)dcm.get(DDict.dDirectoryRecordSequence,1); Para añadir un valor a una secuencia (Sequence), simplemente usando el método set que tiene el DicomObject que representa el artı́culo y el ı́ndice del artı́culo. Para un rápido acceso, hay una utilidad de la clase com.archimed.dicom.tools.Sequences que contiene varios métodos para acceder a las secuencias. También cuida de todas las diferentes excepciones encontradas. El nombre elegido para los métodos son muy similares que los encontrados en el DicomObject. Comentarios: las longitudes de los grupos no se almacenan en un DicomObject, se calculan cuando se necesitan, ası́ que no hay acceso a ellos. cuando usamos set para insertar valores de atributos, los valores se alamacenan por referencia y no se copian. cuando usamos get para sacar valores de atributos, se devuelve una referencia al valor. r GVA-ELAI-UPMPFC0074-03 69 CAPÍTULO 4. JDT (JAVA DICOM TOOLKIT) José Ma Onrubia Manipular grupos de atributos. Para usuarios que quieran manipular los grupos enteros del conjunto de datos, hay el acceso al grupo proporcionado por com.archimed.dicom.GroupList, el cual es la superclase de com.archimed.DicomObject. Un grupo es un conjunto de atributos que tienen el mismo número de grupo en su par (grupo,elemento). DicomObject copyGroup(int groupnr) copia el grupo con el número de grupo y lo devuelve como un DicomObject nuevo. Notar que sólo se hace una copia no profunda, sólo se copian referencias a los atributos. Void addGroups(DicomObject o) añade todos los grupos encontrados al conjunto de datos. Sólo se añaden los grupos que no estuvisen presentes en este conjunto de datos. Otra vez, se hace sólo una copia superflua. DicomObject removeGroup(int groupnr) borra el grupo entero con el número de grupo del conjunto de datos y lo devuelve como un DicomObject. I/O de conjuntos de datos DICOM. Nosotros tenemos varias formas para leer o escribir datasets DICOM desde y por InputStreams/OutputStreams. En ambos casos hay un método básico y varios métodos que lo hacen posible para especificar todo tipo de parámetros. Leer conjunto de datos(dataset). void read(java.io.InputStream in) Este método lee todos los atributos DICOM desde un inputStream a un DicomObject. El inputStream puede contener tanto un archivo DICOM como un conjunto de datos DICOM. Cuando el inputStream contiene un archivo de la parte 10 DICOM, se lee y se almacena la Meta File Information en un DicomObject separado que se puede coger mediante getFileMetaInformation(). La transfer syntax se asume que es Implicit VR Little Endian cuando el inputStream contiene un conjunto de datos DICOM. Cuando el inputStream contiene un archivo de la parte 10 de DICOM, el transfer syntax se detecta desde la File Meta Information. void read(java.io.InputStream in, boolean process) Este método hace lo mismo que el anterior pero además permite especificar si se procesa o no se procesa los datos de pixel. Salva el tiempo de proceso cuando no se necesita el pixel data. 70 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 4.2. GUIA DE USUARIO DE JDT. void read(java.io.InputStream in, int ts, boolean process) El parámetro ts especifica que transfer syntax se usa. Este parámetro puede venir dado , cuando se conoce por adelantado con qué transfer syntax se codificó el conjunto de datos. Escribir conjunto de datos(dataset). void write(java.io.OutputStream out, boolean f ) Este es el más básico método para exportar. Si la bandera f es ”true”, el conjunto de datos se codifica como la parte 10 de DICOM. En este caso, la File Meta Information se usa si está presente, o se crea cuando se necesita. Nota que la creación de la File Meta Information puede lanzar una exección cuando faltan atributos requeridos (SOP Class UID, SOP Instance UID). void write(java.io.OutputStream out, boolean f, int ts, boolean seqUndef ) Los parámetros adicionales ts y seqUndef puede ser usado para escribir tu conjunto de datos casi en todos los posibles tipos DICOM. Los parámetros ts y seqUndef pueden ser usados para insertar la Transfer Syntax. Toda posible combinación de parámetros ts y seqUndef se permiten, pero no todas las combinaciones siguen los dictados del estándar de DICOM. Si escribes un conjunto de datos con f ”true ts otra cosa que no sea Implicit VR Little Endian, es imposible para otras aplicaciones para decodificar ya que ellas no pueden detectar la sintaxis de transferencia usada. El parámetro seqUndef indica al escribir secuencias si se usa indefinido (true) o definida (false) la longitud. 2 Visualizar conjunto de datos. DicomObject contiene dos métodos para imprimir todos lor atributos que contiene hacia un OutputStream de forma ordenada para que el ser humano sea capaz de interpretarla. void dumpVRs(java.io.OutputStream os) void dumpVRs(java.io.OutputStream os, boolean metainfo) Poner metainfo a ”true”si se quiere ver File Meta Information. Ejemplos. r GVA-ELAI-UPMPFC0074-03 71 CAPÍTULO 4. JDT (JAVA DICOM TOOLKIT) José Ma Onrubia Estos trozos de código demuestran el fácil uso de JDT. Con sólo unas lı́neas de código, es posible construir soluciones poderosas DICOM. El código siguiente lee un archivo DICOM desde un archivo y muestra los atributos (incluidos los contenidos en la meta information si existe) por la pantalla. DicomObject dcm = new DicomObject; dcm.read(new FileInputStream(”foo.dcm”)); dcm.dumpVRs(System.out, true); El siguiente ejemplo lee los datos de un archivo y lo escribe en otro archivo usando una transfer syntax y una secuencia de codificación especı́ficas. DicomObject dcm = new DicomObject; dcm.read(new FileInputStream(”in.dcm”)); dcm.write(new FileOutputStream(.out.dcm”), tax.ExplicitVRBigEndian, true); true, TransferSyn- Métodos útiles. Enumerar los atributos de un conjunto de datos(dataset). Con el método enumerateVRs es posible conseguir una enumeración (clase de java) de todos los atributos sontenidos en un DicomObject. Enumeration e = dcm.enumerateVRs(false); enumerateVRs() devuelve una enumeración de todos los atributos de dcm como objetos TagValue. TagValue tiene métodos para coger el valor y el par (grupo,elemento) de un atributo. File Meta Information. DicomObject fmi = dcm.getFileMetaInformation(); Devuelve la File Meta Information del DicomObject dcm si existe. Se puede usar ahora fmi para añadir o alterar atributos especı́ficos de esta información como se quiera. 72 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 4.2. GUIA DE USUARIO DE JDT. Información general. Unosm métodos de com.archimed.dicom.GroupList proporcionan información general sobre un conjunto de datos: int numberOfElements() : devuelve el número total de atributos. int numberOfGroups() : devuelve el número total de grupos. boolean isEmpty() : devuelve si el GroupList contiene algún atributo. boolean containsGroup(int g) : devuleve si el GroupList contiene el grupo especificado. 4.2.3. Depósitos. Hay unas clases en el paquete com.archimed.dicom que hacen más fácil para los programadores para usar los conjuntos de datos y las UID´s registradas (se ha tratado de cubrir todo lo especificado en el estándar 3.6). Clase DDict: depósito de atributos. La clase com.archimed.dicom.DDict es un almacen de los VRs (value representations) y los elementos de datos. Contiene un número de constantes que representan los diferentes VRs y un gran conjunto de constantes para los elementos de datos: DDict.tPN // representa el tipo DICOM PERSON. DDict.tUS // representa el tipo DICOM UNSIGNED SHORT. DDict.dAccesionNumber // representa el atributo AccessionNumber. DDict.dPatientName // representa el atributo PatientName. Los métodos de DDict. La clase DDict tiene métodos para obtener el par (grupo,elemento) de un atributo, un tipo de atributo, una descripción del atributo y para consultar una constante DDict del atributo basada en su par (grupo,elemento). r GVA-ELAI-UPMPFC0074-03 73 CAPÍTULO 4. JDT (JAVA DICOM TOOLKIT) José Ma Onrubia static int getGroup(int dct) Devuelve el número de grupo para una constante DDict que representa un atributo. static int getElement(int dct) Devuelve el número de elemento para una constante DDict que representa un atributo. static int getTypeCode(int dct) Devuelve el tipo para una constante DDict que representa un atributo. Los elementos de datos que no están en la lista de arriba tendrán un DDict.tUNKNOWN. static java.lang.String getDescription(int dct) Da una descripción elaborada para una constante DDict que representa un atributo. static int lookupDDict(int g, int e) Devuelve la constante DDict que representa el atributo con número de grupo g y número de elemento e. Si no se encuentran constantes, el método devolverá DDict.dUNDEFINED. Clase UID: depósitos UID. La clase com.archimed.dicom.UID contiene un depósito de los UIDs DICOM registrados (Parte 6 DICOM).Las constantes que representan los UIDs y que tienen que ser usadas a través de JDT se definen en subclases de UID: com.archimed.dicom.TransferSyntax : constantes que representan las transfer syntax. com.archimed.dicom.SOPClass: constantes que representan las clases SOP. com.archimed.dicom.MetaSOPClass: constantes que representan las clases Meta SOP. com.archimed.dicom.SOPInstance: constantes que representan las bien conocidas instancias SOP. El objeto com.archimed.dicom.UIDEntry representa un único identificador y se obtiene con la clase UID de dos formas. Usando una constante de una de las subclases UID: UIDEntry entry = UID.getUIDEntry(TransferSyntax.JPEGBaseline); o usando una cadena UID: 74 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 4.2. GUIA DE USUARIO DE JDT. UIDEntry entry = UID.getUIDEntry(”1.2.840.10008.1.2.4.50”); Cada uno de esos métodos de consulta devulve un objeto UIDEntry el cual es contiene varias propiedades de el identificador único especı́fico. Esos atributos pueden ser obtenidos usando los métodos get. 4.2.4. Imágenes en JDT. Esta parte habla de las capacidades de imagen de JDT. Todas las clases relacionadas con las imágenes pueden ser encontradas en el paquete com.archimed.dicom.image. La clase DicomImage. La clase base para las imágenes es com.archimed.dicom.image.DicomImage. A parte de los métodos set/get básicos para manipular los datos, inherentes a la DicomObject, contiene otras formas de coger e insertar los atributos requeridos (tipo 1 y tipo 2) que son comunes a todos los DICOM Image Modality IODs (ver parte 3 de DICOM). Cuando se escribe una DicomImage a un outputstream, no hay un reconocimiento de si todos los atributos requeridos están presentes. Insertar datos que no son de imagen. DicomImage contiene un número de métodos para insertar los atributos requeridos que no están relacionados con los datos de imágen.Una corta descripción son estos métodos: void patientData(String patientName, String patientID, String birthDate, String sex) inserta los datos del paciente. void generalStudyData(String instanceUID, String date, String time, String physName, String studyID, String orderNumber) inserta los atributos del estudio general. generalSeriesData(String modality, String instanceUID, String seriesNumber) añade los datos generales de serie. generalEquipmentData(String manufacturer) generalImageData(String imageNumber) r GVA-ELAI-UPMPFC0074-03 75 CAPÍTULO 4. JDT (JAVA DICOM TOOLKIT) José Ma Onrubia sopCommonData(String sopClassUID, String sopInstanceUID) inserta los datos comunes SOP. SOP Class UID es tı́picamente uno de las diferentes image storage SOP Classes, que pueden ser encontrados en com.archimed.dicom.SOPClass. Insertar datos de imagen con los métodos de DicomImage. Hay también métodos en com.archimed.dicom.image.DicomImage que pueden ser usados para insertar los datos de los pı́xeles de la imagen. La clase contiene métodos para imágenes en escala de grises, en paleta de colores y en RGB. Una definición de todos los parámetros pueden ser encontrados en la API. DicomImage dcmi = new DicomImage(); byte[] pixels = new byte[640*480]; // rellena este array con pı́xeles. byte[] red = new byte[256]; // rellena este array con un LUT rojo. byte[] green = new byte[256]; // rellena este array con un LUT verde. byte[] blue = new byte[256]; // rellena este array con un LUT azul. dcmi.imagePixelData(480, 640, pixels, red, green, blue); Esto da una DicomImage con una interpretación fotométrica PALETTE COLOR, tamaño 640x480 y con una profundidad de pixel de 8. Insertando los datos de imagen con ImageProducer. Hay otra forma de insertar los datos de la imagen. Dado una ImageProducer ip, construyebdo un objeto ImageIO y usando el método setImageProducer() para copiar los datos de imagen desde la ImageProducer a la DicomImage: DicomImage dcmi = new DicomImage(); ImageIO imgio = new ImageIO(dcmi); imgio.setImageProducer(ip); Cuando se llama al método setImageProducer(), todo los datos se copian dentro de la DicomImage. 76 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 4.2. GUIA DE USUARIO DE JDT. Esto es en teorı́a; después de muchas prácticas no ha sido posible demostrar la validez de éste método, por lo que se recomiendo usar los otros. (También se puede tratar de haver funcionar éste). Obtenier una ImageProducer desde una DicomImage es también posible. Todo marco simple de una imagen DICOM se puede coger como una ImageProducer usando uno de los métodos siguientes: java.awt.image.ImageProducer getImageProducer() java.awt.image.ImageProducer getImageProducer(int i) java.util.Vector getImageProducers() Para imágenes de escalas de grises con profundidad de pixel mayor que 8 bits, hay un color model (clase de java) especial com.archimed.dicom.image.GrayColorModel. Este color model se usa en combinación con un array int de pı́xeles, para producir imágenes de 256 grises sin pérdida de datos. Con este GrayColorModel, también es posible meter el Window/Level usado en la producción de la imagen. Insertar imagen con DicomObject. Insertar y sacar los datos de imagen puede ser hecho directamente con los set/get de la clase DicomObject. Los atributos de imagen como bits por pı́xel, filas y coumnas se meten o sacan como cualquier otro atributo en un DicomObject mediante las constantes de DDict. Los datos de pı́xel de una imagen se almacenan en un DicomObject como un array o un número de arrays dependiendo del formato de compresión: no-comprimido transfer syntax, una imagen: un array de bytes que mantiene el formato original de los datos de pı́xel definido en el estándar DICOM, ejemplos: • imagen MONOCHROME2, 8 bits asignados, 8 bits alamcenados: 1 elemento del array de bytes por pixel. • imagen MONOCHROME2, 16 bits asignados, 12 bits alamcenados: 2 elementos consecutivos del array de bytes por pixel. • imagen MONOCHROME2, 12 bits asignados, 12 bits alamcenados: 1 elemento y medio del array de bytes por pixel. • imagen RGB, configuración plana color por pixel: 3 bytes consecutivos representando 1 pixel. r GVA-ELAI-UPMPFC0074-03 77 CAPÍTULO 4. JDT (JAVA DICOM TOOLKIT) José Ma Onrubia • imagen RGB, configuración plana color por plano: el array de bytes está dividivo en 3 partes de igual longitud cada una representando un color de plano (Red-Green-Blue). Los array de bytes se cogen e insertan con los métodos get(DDict.dPixelData) y set(DDict.dPixelData,¡byte array¿) respectivamente. no-comprimido transfer syntax, varias imágenes: un array de bytes que contienen marcos consecutivos. comprimido transfer syntax, una imagen: un array de bytes en el formato original de compresion (ver compression). Compression. Los datos de pı́xel de una imagen DICOM se almacena en el atributo DDict.dPixelData. Cuando una imagen DICO comprimida se lee desde un inputstream, los pixeldata no se decodifican pero se almacenan internamente en arrays de bytes en su formato de compresión por razones de rendimineto. El tipo de codificación puede ser sacado de la sintaxis de transferencia en el File Meta Information. DcmObject dcm = new DicomObject(); dcm.read(new FileInputStream(ç://encodedimage.dcm”)); DicomObject fmi = dcm.getFileMetaInformation(); UIDEntry tsentry = UID.getUIDEntry(fmi.get(DDict.dTransferSyntaxUID)); System.out.println(”encoding: ”+ tsentry.getName()); Una utilidad de la clase com.archimed.dicom.codec.Compression es que proporciona un número de decodificadores: JPEG baseline JPEG lossless RLE Lossless Todos los decodificadores están limitados a 8 bits por pı́xel. El decodificador JPEG puede manejar 1 o 3 muestras por pı́xel. RLE es sólo capaz de 78 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 4.2. GUIA DE USUARIO DE JDT. descomprimir 1 muestra por pı́xel. El camino más fácil para descomprimir imagen entera DICOM es construir un objeto Compression desde un DicomObject y después llamar al método decompress. Éste descodifica los datos del array de pı́xeles del DicomObject y los reemplaza por los datos descomprimidos. En el caso de una imagen DICOM multimarco, se puede decodificar el array de bytes correspondioente a un marco individual. Primero obteniendo un array de bytes de un marco con get(DDict.dPixelData, ¡index of frame¿) y luego usando el método estático decompressframe() del objeto Compression para obtener un array de bytes decodificado desde el array de bytes comprimido. static byte[] decompressframe(int encoding, byte[] frame, int width, int heigth); Los argumentos de codificación, ancho y alto que tienen que ser suministrados para usar decompressframe() se encuentran en la imagen DICOM original. r GVA-ELAI-UPMPFC0074-03 79 CAPÍTULO 4. JDT (JAVA DICOM TOOLKIT) 4.3. 4.3.1. José Ma Onrubia Estructura de JDT(Java DICOM Toolkit). Árbol de clases. class java.lang.Object class com.archimed.dicom.network.Association class java.awt.image.ColorModel (implements java.awt.Transparency) class com.archimed.dicom.image.GrayColorModel class com.archimed.dicom.codec.Compression class com.archimed.dicom.DDate class com.archimed.dicom.DDateRange class com.archimed.dicom.DDict class com.archimed.dicom.DDictEntry class com.archimed.dicom.Debug class com.archimed.dicom.network.DimseUtil class com.archimed.dicom.network.ExtendedNegotiation class com.archimed.dicom.GroupList class com.archimed.dicom.DicomObject class com.archimed.dicom.image.DicomImage class com.archimed.dicom.image.SCImage class com.archimed.dicom.image.ImageIO class com.archimed.dicom.Jdt class com.archimed.dicom.Offsets class com.archimed.dicom.Person class com.archimed.dicom.network.Request class com.archimed.dicom.network.Response class com.archimed.dicom.network.Abort class com.archimed.dicom.network.Acknowledge class com.archimed.dicom.network.Reject class com.archimed.dicom.network.ResponsePolicy class com.archimed.dicom.tools.Sequences class com.archimed.dicom.TagValue class java.lang.Throwable (implements java.io.Serializable) class java.lang.Exception class com.archimed.dicom.DicomException class com.archimed.dicom.IllegalValueException class com.archimed.dicom.UnknownUIDException class com.archimed.dicom.UID class com.archimed.dicom.MetaSOPClass class com.archimed.dicom.SOPClass class com.archimed.dicom.SOPInstance class com.archimed.dicom.TransferSyntax class com.archimed.dicom.UIDEntry class com.archimed.dicom.image.WL 80 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia4.3. ESTRUCTURA DE JDT(JAVA DICOM TOOLKIT). 4.3.2. Paquetes. Com.archimed.dicom.network. Abort : Representa el aborto de una asociación. Los dos parámetros son, la fuente y la razón del aborto. Acknowledge : Estos objetos tienen los parámetros de la asociación Acknowledge, contiene todos los contextos de presentación del Acknowledge y son numerados desde 0 hasta el no de contextos de presentación menos uno. Puede actuar como receptor de asociación o como iniciador de la asociación. Association : Contiene métodos para construir y destruir una asociación entre dos entidades de aplicación DICOM y mandar/ recibir comandos y datos (data sets) una vez que la asociación está establecida. DimseUtil : Las funciones DICOM que pueden hacerse. ExtendedNegotiation: Representa los datos de negociación para un sintaxis abstracto. Reject: Representa el rechazo de una asociación. Sus parámetros son fuenta, resultado y razón. Request : Representa todos los parámetros relevantes de una asociación Request. Contiene todos los contextos de presentación y son numerados desde 0 hasta el no de contextos de presentación menos uno. Puede actuar como receptor de asociación o como iniciador de la asociación. Response: Representa una respuesta de un par de entidades DICOM. Las clases implementadas son: Reject, Acknowledge y Abort. ResponsePolicy: contiene dos tipos de métodos. Métodos para creación de objetos Response, basados en una peticón dada y una polı́tica de aceptación de la asociación. Métodos para el análisis de respuestas Acknowledge, en el contexto de una petición dada. Com.archimed.dicom.codec Compression : Es una clase que proporciona métodos para la descompresión de datos de Pixel. DICOM soporta muchos tipos de técnicas de compresión. r GVA-ELAI-UPMPFC0074-03 81 CAPÍTULO 4. JDT (JAVA DICOM TOOLKIT) José Ma Onrubia Com.archimed.dicom.image DicomImage: Esta clase provee de métodos para construir una imagen Dicom. GrayColorModel : Esta clase representa un ColorModel para el empleo con imágenes de Escala de gris. ImageIO: Es una clase que proporciona métodos de convertir datos de imagen almacenados a un ImageProducer (Productor de imagen) y viceversa. SCImage: Esta clase proporciona métodos para construir una imagen SC. WL: Es un contenedor básico para un par Ventana/Nivel. Com.archimed.dicom.tools Sequences: Estos objetos proveen los .atajos”para coger/poner valores dentro de secuencias. La conversión de valores está hecha usando el esquema de conversión DicomType-JavaType dada en la clase DicomObject. Com.archimed.dicom DDate: Esta clase representa la fecha de un objeto, conteniendo el dı́a, mes y año. Se ha desarollado en correspondencia al tipo Dicom DA Es útil, cuando se quiere buscar en una gama de fechas. DDateRange: Esta clase representa la gama de fechas. DDict: Esta clase provee de un diccionario (Diccionario de datos Dicom) para los VRs y todos los elementos de datos.Cada par (grupo,elemento) es accesible por una variable static int. Por ejemplo, (0010,0010) es representado por Ddic.dPatientName. DDictEntry: Un objeto para una etiqueta que puede ser almacenado en el diccionario de datos. Debug: Proporciona una variable static int por si hay que imprimir información de reparación de los errores a System.err. DicomObject: Esta es la clase base de todos los datasets. Los métodos de acceso proporcionados aquı́ son el modo de obtener/poner elementos de dato (data elements) dentro de un objeto Dicom (DicomObject). GroupList: Provee de métodos para el acceso de datos por grupo. 82 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia4.3. ESTRUCTURA DE JDT(JAVA DICOM TOOLKIT). Jdt: Para obtener información sobre este Jdt MetaSOPClass: Extensión de la clase UID. Offsets : Esta clase proporciona utilidades para calcular compensaciones en un DicomObject que va a ser escrito. Person: Esta clase representa el PN (Person Name) en Dicom. SOPClass: Extensión de la clase UID. SOPInstance: Extensión de la clase UID. TagValue: Representa un par (grupo, elemento) y su valor. TransferSintax : Extensión de la clase UID. UID: Esta clase contiene un depósito de todos los UIDs Dicom certificados. Cada UID está en función del Transfer Syntax, SOPClass, Meta- SOPClass y SOPInstance. UIDEntry: Representa la entrada de un UID en el depósito, almacenándose. DicomExcepcion: Indica que la aplicación ha violado la estructura y codificación del Dicom Data. IllegalValueExcepcion: Indica que un valor ilegal es leı́do durante una asociación o cuando un valor ilegal es argumento de un método. UnknownUIDExcepcion: Indica que el UID incluido no está definido en la clase UID. 4.3.3. Conclusiones. Una vez trabajado y experimentado con los dos toolkit de DICOM, dcmtk y JDT, se llegan a las siguientes conclusiones: El toolkit dcmtk351 cuenta con la gran ventaja de ser un producto gratuito, pero si no se es un gran experto tanto en materia DICOM como en programación en C++, el estudio y la creación de una aplicación, nuestro objetivo, se antoja demasiado complicado debido a la poca documentación proporcionada con dicha herramienta y a la gran dificultad y amplitud del código. Por el contrario, JDT es un producto no gratuito, pero cuenta con las grandes ventajas que el toolkit dcmtk351 no tiene. Lo primero a señalar es que el lenguaje de programación es JAVA, con las ventajas que conlleva r GVA-ELAI-UPMPFC0074-03 83 CAPÍTULO 4. JDT (JAVA DICOM TOOLKIT) José Ma Onrubia con respecto a C++. Por otro lado, contiene una documentación amplia y muy bien estructurada, lo que facilita enormemente el trabajo y su comprensión. Contiene un árbol de clases, información de los diferentes packages, ejemplos, las diferentes clases se estudian con detalle, es decir, de que clases derivan, que métodos usan, que interfaces implementan, etc, lo que todo junto hace mucho más fácil la implememtación. Por todo lo visto anteriormente, a partir de este punto se empieza a trabajar con JDT, lo que nos conduce al estudio profundo de JAVA, por lo que la siguiente sección trata de esto mismo: El lenguaje de programación JAVA. 84 r GVA-ELAI-UPMPFC0074-03 Capı́tulo 5 Estudio de JAVA. 5.1. Introducción Java es la plataforma ideal para las soluciones de computación en red. Ejecutable sobre las más diversas plataformas, desde servidores a teléfonos móviles y tarjetas inteligentes, Java ofrece una infraestructura unica para crear soluciones en red para su negocio. Java está soportado por una gran comunidad de desarrolladores que activamente trabajan en productos y servicios alrededor de Java, que al mismo tiempo contribuyen a la evolución de la plataforma mediante el Java Community Process, una organización estándar, abierta y basada en comunidades. Java está en teléfonos móviles, ordenadores portatiles, PDAs, en la web, e incluso en los sistemas de las carreras de Formula 1. De hecho, encontrará Java en cualquier lugar. Beneficios de JAVA: Java es simple: cuando usa un teléfono Java, juega o accede a su red corporativa, Java ofrece la base de la verdadera movilidad. El excelente compromiso entre movilidad y seguridad de la plataforma Java hace posible el desarrollo y despliege de soluciones móviles e inalambricas. Java es el entorno ideal para Web services: Java y XML son los dos lenguajes más ampliamente aceptados en el mundo, ofreciendo una plataforma real para cualquiera, en cualquier lugar, en cualquier momento, desde cualquier dispositivo. Java está en todos los niveles de su negocio: Java ofrece un simple y 85 CAPÍTULO 5. ESTUDIO DE JAVA. José Ma Onrubia unificado modelo de programación que puede conectar todos los elementos de una infraestructura de negocio. Java surgió en 1991 cuando un grupo de ingenieros de Sun Microsystems trataron de diseñar un nuevo lenguaje de programación destinado a electrodomésticos. La reducida potencia de cálculo y memoria de los electrodomésticos llevó a desarrollar un lenguaje sencillo capaz de generar código de tamaño muy reducido. Debido a la existencia de distintos tipos de CPUs y a los continuos cambios, era importante conseguir una herramienta independiente del tipo de CPU utilizada. Desarrollan un código ”neutro”que no depende del tipo de electrodoméstico, el cual se ejecuta sobre una ”máquina hipotética o virtual”denominada Java Virtual Machine (JVM). Es la JVM quien interpreta el código neutro convirtiéndolo a código particular de la CPU utilizada. Esto permitı́a lo que luego se ha convertido en el principal lema del lenguaje: ”Write Once, Run Everywhere”. Al programar en Java no se parte de cero. Cualquier aplicación que se desarrolle çuelga”(o se apoya, según como se quiera ver) en un gran número de clases preexistentes. Algunas de ellas las ha podido hacer el propio usuario, otras pueden ser comerciales, pero siempre hay un número muy importante de clases que forman parte del propio lenguaje (el API o Application Programming Interface de Java). Java incorpora muchos aspectos que en cualquier otro lenguaje son extensiones propiedad de empresas de software o fabricantes de ordenadores (threads, ejecución remota, componentes, seguridad, acceso a bases de datos, etc). Por eso es un lenguaje ideal para aprender la informática moderna, porque incorpora todos estos conceptos de un modo estándar, mucho más sencillo y claro que con las citadas extensiones de otros lenguajes. Esto es consecuencia de haber sido diseñado más recientemente y por un único equipo. El principal objetivo del lenguaje Java es llegar a ser el ”nexo universal”que conecte a los usuarios con la información, esté ésta situada en el ordenador local, en un servidor de Web, en una base de datos o en cualquier otro lugar. La compañı́a Sun describe el lenguaje Java como ”simple, orientado a objetos, distribuido,interpretado, robusto, seguro, de arquitectura neutra, portable, de altas prestaciones, multitarea y dinámico”. Los programas desarrollados en Java presentan diversas ventajas frente a los desarrollados en otros lenguajes como C/C++. La ejecución de programas en Java tiene muchas posibilidades: ejecución como aplicación independiente (Stand-alone Application), ejecución como applet, ejecución como servlet, etc.. Un applet es una aplicación especial que se ejecuta dentro 86 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 5.2. ESTUDIO A TRAVÉS DE UN EJEMPLO. de un navegador o browser (por ejemplo Netscape Navigator o Internet Explorer) al cargar una página HTML desde un servidor Web. El applet se descarga desde el servidor y no requiere instalación en el ordenador donde se encuentra el browser. Un servlet es una aplicación sin interface gráfica que se ejecuta en un servidor de Internet. La ejecución como aplicación independiente es análoga a los programas desarrollados con otros lenguajes. Además de incorporar la ejecución como Applet, Java permite fácilmente el desarrollo tanto de arquitecturas clienteservidor como de aplicaciones distribuidas, consistentes en crear aplicaciones capaces de conectarse a otros ordenadores y ejecutar tareas en varios ordenadores simultáneamente, repartiendo por lo tanto el trabajo. Aunque también otros lenguajes de programación permiten crear aplicaciones de este tipo, Java incorpora en su propio API estas funcionalidades. 5.2. Estudio a través de un ejemplo. Este ejemplo contiene algunas de las caracterı́sticas más importantes de Java: clases, herencia, interfaces, gráficos, polimorfismo, etc. 5.2.1. Clase Ejemplo1 En realidad, este programa principal lo único que hace es utilizar la clase Geometrı́a y sus clases derivadas. Es pues un programa puramente üsuario”, a pesar de lo cual hay que definirlo dentro de una clase, como todos los programas en Java. 1. // fichero Ejemplo1.java 2. import java.util.Vector; 3. import java.awt.*; 4. class Ejemplo1 { 5. public static void main(String arg[]) throws InterruptedException 6. { 7. System.out.println(”Empieza main()...”); 8. Circulo c = new Circulo(2.0, 2.0, 4.0); 9. System.out.println(”Diámetro/2 = ”+ c.r ); 10. System.out.println(”Punto medio = (”+ c.x + ”,”+ c.y + ”) r GVA-ELAI-UPMPFC0074-03 87 CAPÍTULO 5. ESTUDIO DE JAVA. José Ma Onrubia unidades.”); 11. Circulo c1 = new Circulo(1.0, 1.0, 2.0); 12. Circulo c2 = new Circulo(0.0, 0.0, 3.0); 13. c = c1.elMayor(c2); 14. System.out.println(”El mayor radio es ”+ c.r + ”.”); 15. c = new Circulo(); // c.r = 0.0; 16. c = Circulo.elMayor(c1, c2); 17. System.out.println(”El mayor radio es ”+ c.r + ”.”); 18. VentanaCerrable ventana = 19. new VentanaCerrable(”Ventana abierta al mundo...”); 20. Vector v = new Vector(); 21. CirculoGrafico cg1 = new CirculoGrafico(200, 200, 100, Color.red); 22. CirculoGrafico cg2 = new CirculoGrafico(300, 200, 100, Color.blue); 23. RectanguloGrafico rg = new 24. RectanguloGrafico(50, 50, 450, 350, Color.green); 25. v.addElement(cg1); 26. v.addElement(cg2); 27. v.addElement(rg); 28. PanelDibujo mipanel = new PanelDibujo(v); 29. ventana.add(mipanel); 30. ventana.setSize(500, 400); 31. ventana.setVisible(true); 32. System.out.println(”Termina main()...”); 33. } // fin de main() 34. } // fin de class Ejemplo1 Las sentencias 2 y 3 ”importançlases de los packages de Java, esto es, hacen posible acceder a dichas clases utilizando nombres cortos. Por ejemplo, se puede acceder a la clase Vector simplemente con el nombre Vector en lugar de con el nombre completo java.util.Vector, por haber introducido la sentencia import de la lı́nea 2. Un package es una agrupación de clases que tienen una finalidad relacionada. Existe una jerarquı́a de packages que se refleja en nombres compuestos, separados por un punto (.). Es habitual nombrar los packages con letras minúsculas (como java.util o java.awt), mientras que los nombres de las clases suelen empezar siempre por una letra mayúscula (como Vector). El asterisco (*) de la sentencia 3 indica que se importan todas las clases del package. Hay un package, llamado java.lang, que se importa siempre automáticamente. Las clases de java.lang se pueden utilizar directamente, sin importar el package. En Java todo son clases: no se puede definir una variable o una función 88 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 5.2. ESTUDIO A TRAVÉS DE UN EJEMPLO. que no pertenezca a una clase. En este caso, la clase Ejemplo1 tiene como única finalidad acoger al método main(), que es el programa principal del ejemplo. Las clases que utiliza main() son mucho más importantes que la propia clase Ejemplo1. Las lı́neas 5-33 contienen la definición del programa principal de la aplicación, que en Java siempre se llama main(). La ejecución siempre comienza por el programa o método main(). La sentencia 8 (Circulo c = new Circulo(2.0, 2.0, 4.0);) es muy propia de Java. En ella se crea un objeto de la clase Circulo, que se define en el apartado 1.3.4. Esta sentencia es equivalente a las dos sentencias siguientes: Circulo c; c = new Circulo(2.0, 2.0, 4.0); que quizás son más fáciles de explicar. En primer lugar se crea una referencia llamada c a un objeto de la clase Circulo. Crear una referencia es como crear un ”nombre”válido para referirse a un objeto de la clase Circulo. A continuación, con el operador new se crea el objeto propiamente dicho. Puede verse que el nombre de la clase va seguido por tres argumentos entre paréntesis. Estos argumentos se le pasan al constructor de la clase como datos concretos para crear el objeto (en este caso los argumentos son las dos coordenadas del centro y el radio). Interesa ahora insistir un poco más en la diferencia entre clase y objeto. La clase Circulo es lo genérico: es el patrón o modelo para crear cı́rculos concretos. El objeto c es un cı́rculo concreto, con su centro y su radio. De la clase Circulo se pueden crear tantos objetos como se desee; la clase dice que cada objeto necesita tres datos (las dos coordenadas del centro y el radio) que son las variables miembro de la clase. Cada objeto tiene sus propias copias de las variables miembro, con sus propios valores, distintos de los demás objetos de la clase. La sentencia 9 (System.out.println(Radio = ”+ c.r + ünidades.”);) imprime por la salida estándar una cadena de texto que contiene el valor del radio. Esta cadena de texto se compone de tres subcadenas, unidas mediante el operador de concatenación (+). Obsérvese cómo se accede al radio del objeto c: el nombre del objeto seguido del nombre de la variable miembro r, unidos por el operador punto (c.r). El valor numérico del radio se convierte automáticamente en cadena de caracteres. La sentencia 10 es similar a la 9, imprimiendo las coordenadas del centro del cı́rculo. La sentencia 13 (c = c1.elMayor(c2);) utiliza el método r GVA-ELAI-UPMPFC0074-03 89 CAPÍTULO 5. ESTUDIO DE JAVA. José Ma Onrubia elMayor() de la clase Circulo. Este método compara los radios de dos cı́rculos y devuelve como valor de retorno una referencia al cı́rculo que tenga mayor radio. Esa referencia se almacena en la referencia previamente creada c. Un punto importante es que todos los métodos de Java (excepto los métodos de clase o static) se aplican a un objeto de la clase por medio del operador punto (c1.elMayor()). El otro objeto (c2) se pasa como argumento entre paréntesis. Obsérvese la forma .asimétrica”en la que se pasan los dos argumentos al método elMayor(). De ordinario se llama argumento implı́cito a c1, mientras que c2 serı́a el argumento explı́cito del método. La sentencia 14 imprime el resultado de la comparación anterior y la sentencia 15 crea un nuevo objeto de la clase Circulo guardándolo en la referencia c. En este caso no se pasan argumentos al constructor de la clase. Eso quiere decir que deberá utilizar algunos valores ”por defecto”para el centro y el radio. Esta sentencia anula o borra el resultado de la primera comparación de radios, de modo que se pueda comprobar el resultado de la segunda comparación. La sentencia 16 (c = Circulo.elMayor(c1, c2);) vuelve a utilizar un método llamado elMayor() para comparar dos cı́rculos: ¿Se trata del mismo método de la sentencia 13, utilizado de otra forma? No. Se llama de un método diferente, aunque tenga el mismo nombre. A las funciones o métodos que son diferentes aunque tienen el mismo nombre se les llama funciones sobrecargadas (overloaded). Las funciones sobrecargadas se diferencian por el numero y tipo de sus argumentos. El método de la sentencia 13 tiene un único argumento, mientras que el de la sentencia 16 tiene dos (en todos los casos objetos de la clase Cı́rculo). En realidad, el método de la sentencia 16 es un método static (o método de clase), esto es, un método que no necesita ningún objeto como argumento implı́cito. Los métodos static suelen ir precedidos por el nombre de la clase y el operador punto (Java también permite que vayan precedidos por el nombre de cualquier objeto, pero es considerada una nomenclatura más confusa.). La sentencia 16 es absolutamente equivalente a la sentencia 13, pero el método static de la sentencia 16 es más ”simétrico”. Las sentencias 17 y 18 no requieren ya comentarios especiales. Las sentencias 18-31 tienen que ver con la parte gráfica del ejemplo. En las lı́neas 18-19 (VentanaCerrable ventana = new VentanaCerrable(”Ventana abierta al mundo...”);) se crea una ventana para dibujar sobre ella. Una ventana es un objeto de la clase Frame, del package java.awt. La clase VentanaCerrable añade a la clase Frame la capacidad de responder a los eventos que provocan el cierre de una ventana. La cadena que se le pasa como argumento es el tı́tulo que aparecerá en la ventana. En la sentencia 90 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 5.2. ESTUDIO A TRAVÉS DE UN EJEMPLO. 20 (Vector v = new Vector();) se crea un objeto de la clase Vector (en el package java.util). La clase Vector permite almacenar referencias a objetos de distintas clases. En este caso se utilizará para almacenar referencias a varias figuras geométricas diferentes. Las siguientes sentencias 21-27 crean elementos gráficos y los incluyen en el vector v para ser dibujados más tarde en el objeto de la clase DrawWindow. Los objetos de la clase Circulo creados anteriormente no eran objetos aptos para ser dibujados, pues sólo tenı́an información del centro y el radio, y no del color de lı́nea. Las clases RectanguloGrafico y CirculoGrafico, definidas en los Apartados 1.3.4 y 1.3.7, derivan respectivamente de las clases Rectangulo (Apartado 1.3.3) y Circulo (Apartado 1.3.4), heredando de dichas clases sus variables miembro y métodos, añadiendo la información y los métodos necesarios para poder dibujarlos en la pantalla. En las sentencias 21-22 se definen dos objetos de la clase CirculoGrafico; a las coordenadas del centro y al radio se une el color de la lı́nea. En la sentencia 23-24 se define un objeto de la clase RectanguloGrafico, especificando asimismo un color, además de las coordenadas del vértice superior izquierdo, y del vértice inferior derecho. En las sentencias 25-27 los objetos gráficos creados se añaden al vector v, utilizando el método addElement() de la clase Vector. En la sentencia 28 (PanelDibujo mipanel = new PanelDibujo(v);) se crea un objeto de la clase PanelDibujo. Por decirlo de alguna manera, los objetos de dicha clase son paneles, esto es superficies en las que se puede dibujar. Al constructor de PanelDibujo se le pasa como argumento el vector v con las referencias a los objetos a dibujar. La sentencia 29 (ventana.add(mipanel);) añade o incluye el panel (la superficie de dibujo) en la ventana; la sentencia 30 (ventana.setSize(500, 400);) establece el tamaño de la ventana en pixels; finalmente, la sentencia 31 (ventana.setVisible(true);) hace visible la ventana creada. ¿Cómo se consigue que se dibuje todo esto? La clave está en la serie de órdenes que se han ido dando al computador. La clase PanelDibujo deriva de la clase Container a través de Panel, y redefine el método paint() de Container. En este método, se realiza el dibujo de los objetos gráficos creados. El usuario no tiene que preocuparse de llamar al método paint(), pues se llama de modo automático cada vez que el sistema operativo tiene alguna razón para ello (por ejemplo cuando se crea la ventana, cuando se mueve, cuando se minimiza o maximiza, cuando aparece después de haber estado oculta, etc.). r GVA-ELAI-UPMPFC0074-03 91 José Ma Onrubia CAPÍTULO 5. ESTUDIO DE JAVA. 5.2.2. Clase Geometrı́a. En este apartado se describe la clase más importante de esta aplicación. Es la más importante no en el sentido de lo que hace, sino en el de que las demás clases ”derivan”de ella, o por decirlo de otra forma, se apoyan o cuelgan de ella. La Figura 1.2 muestra la jerarquı́a de clases utilizada en este ejemplo. La clase Geometrı́a es la base de la jerarquı́a. En realidad no es la base, pues en Java la clase base es siempre la clase Object. Siempre que no se diga explı́citamente que una clase deriva de otra, deriva implı́citamente de la clase Object (definida en el package java.lang). De las clases: Dibujable RectanguloGrafico Rectangulo Dibujable CirculoGrafico Circulo Geometrı́a Rectangulo y Circulo derivan respectivamente las clases RectanguloGrafico y CirculoGrafico. En ambos casos está por en medio un elemento un poco especial donde aparece la palabra Dibujable. En términos de Java, Dibujable es una interface. Se suele utilizar la nomenclatura de superclase y subclase para referirse a la clase padre o hija de una clase determinada. Un aspecto importante a considerar es que no va a haber nunca objetos de la clase Geometria, es decir ”geometrı́as a secas”. Una clase de la que no va a haber objetos es una clase abstracta, y como tal puede ser declarada. A continuación se muestra el fichero Geometrı́a.java en el que se define dicha clase: 1. 2. 3. 4. 5. 6. // fichero Geometria.java public abstract class Geometria { // clase abstracta que no puede tener objetos public abstract double perimetro(); public abstract double area(); } La clase Geometrı́a se declara como public para permitir que sea utilizada por cualquier otra clase, y como abstract para indicar que no se permite crear objetos de esta clase. Es caracterı́stico de las clases tener variables y funciones miembro. La clase Geometrı́a no define ninguna variable miem92 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 5.2. ESTUDIO A TRAVÉS DE UN EJEMPLO. bro, pero sı́ declara dos métodos: perı́metro() y area(). Ambos métodos se declaran como public para que puedan ser llamados por otras clases y como abstract para indicar que no se da ninguna definición, es decir ningún código para ellos. Interesa entender la diferencia entre declaración (la primera lı́nea o header del método) y definición (todo el código del método, incluyendo la primera lı́nea). Se indica también que su valor de retorno -el resultado- va a ser un double y que no tienen argumentos (obtendrán sus datos a partir del objeto que se les pase como argumento implı́cito). Es completamente lógico que no se definan en esta clase los métodos perı́metro() y area(): la forma de calcular un perı́metro o un área es completamente distinta en un rectángulo y en un cı́rculo, y por tanto estos métodos habrá que definirlos en las clases Rectángulo y Circulo. En la clase Geometria lo único que se puede decir es cómo serán dichos métodos, es decir su nombre, el número y tipo de sus argumentos y el tipo de su valor de retorno. 5.2.3. Clase Rectángulo. Según el diagrama de clases de la Figura 1.2 la clase Rectangulo deriva de Geometria. Esto se indica en la sentencia 2 con la palabra extends (en negrita en el listado de la clase). 1. // fichero Rectangulo.java 2. public class Rectangulo extends Geometria { 3. // definición de variables miembro de la claes 4. private static int numRectangulos = 0; 5. protected double x1, y1, x2, y2; 6. // constructores de la clase 7. public Rectangulo(double p1x, double p1y, double p2x, double p2y) { 8. x1 = p1x; 9. x2 = p2x; 10. y1 = p1y; 11. y2 = p2y; 12. numRectangulos++; 13. } 14. public Rectangulo(){ this(0, 0, 1.0, 1.0); } 15. // definición de métodos 16. public double perimetro() { return 2.0 * ((x1-x2)+(y1-y2)); } 17. public double area() { return (x1-x2)*(y1-y2); } 18. } // fin de la clase Rectángulo En la sentencia 4 (private static int numRectangulos = 0;) se define una r GVA-ELAI-UPMPFC0074-03 93 CAPÍTULO 5. ESTUDIO DE JAVA. José Ma Onrubia variable miembro static. Las variables miembro static se caracterizan por ser propias de la clase y no de cada objeto. De la variable numRectangulos, que en la sentencia 4 se inicializa a cero, se mantiene una única copia para toda la clase. Además esta variable es privada (private), lo cual quiere decir que sólo las funciones miembro de esta clase tienen permiso para utilizarla. El declararlas como protected indica que sólo esta clase, las clases que deriven de ella y las clases del propio package tienen permiso para utilizarlas. 5.2.4. Clase Circulo. A continuación se presenta la definición de la clase Circulo, también derivada de Geometria. 1. // fichero Circulo.java 2. public class Circulo extends Geometria { 3. static int numCirculos = 0; 4. public static final double PI=3.14159265358979323846; 5. public double x, y, r; 6. public Circulo(double x, double y, double r) { 7. this.x=x; this.y=y; this.r=r; 8. numCirculos++; 9. } 10. public Circulo(double r) { this(0.0, 0.0, r); } 11. public Circulo(Circulo c) { this(c.x, c.y, c.r); } 12. public Circulo() { this(0.0, 0.0, 1.0); } 13. public double perimetro() return 2.0 * PI * r; } 14. public double area() { return PI * r * r; } 15. // método de objeto para comparar cı́rculos 16. public Circulo elMayor(Circulo c) { 17. if (this.r>=c.r) return this; else return c; 18. } 19. // método de clase para comparar cı́rculos 20. public static Circulo elMayor(Circulo c, Circulo d) { 21. if (c.r>=d.r) return c; else return d; 22. } 23. } // fin de la clase Circulo La sentencia 3 (static int numCirculos = 0;) define una variable static o de clase análoga a la de la clase Rectangulo. En este caso no se ha definido como private. Cuando no se especifican permisos de acceso (public, private o protected) se supone la opción por defecto, que es package. Con esta opción la variable o método correspondiente puede ser utilizada por todas 94 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 5.2. ESTUDIO A TRAVÉS DE UN EJEMPLO. las clases del package y sólo por ellas. Como en este ejemplo no se ha definido ningún package, se utiliza el package por defecto que es el directorio donde están definidas las clases. Ası́ pues, la variable numCirculos podrá ser utilizada sólo por las clases que estén en el mismo directorio que Circulo. La sentencia 4 (public static final double PI=3.14159265358979323846;) define también una variable static, pero contiene una palabra nueva: final. Una variable final tiene como caracterı́stica el que su valor no puede ser modificado, o lo que es lo mismo, es una constante. Es muy lógico definir el número PI como constante, y también es razonable que sea una constante static de la clase Circulo, de forma que sea compartida por todos los métodos y objetos que se creen. La sentencia 6-9 define el constructor general de la clase Circulo. En este caso tiene una peculiaridad y es que el nombre de los argumentos (x, y, r) coincide con el nombre de las variables miembro. Esto es un problema, porque como se verá más adelante los argumentos de un método son variables locales que sólo son visibles dentro del bloque ... del método, que se destruyen al salir del bloque y que ocultan otras variables de ámbito más general que tengan esos mismos nombres. En otras palabras, si en el código del constructor se utilizan las variables (x, y, r) se está haciendo referencia a los argumentos del método y no a las variables miembro. La sentencia 7 indica como se resuelve este problema. Para cualquier método no static de una clase, la palabra this es una referencia al objeto -el argumento implı́cito- sobre el que se está aplicando el método. De esta forma, this.x se refiere a la variable miembro, mientras que x es el argumento del constructor. Las sentencias 10-12 representan otros tres constructores de la clase (métodos sobrecargados), que se diferencian en el número y tipo de argumentos. Los tres tienen en común el realizar su papel llamando al constructor general previamente definido, al que se hace referencia con la palabra this (en este caso el significado de this no es exactamente el del argumento implı́cito). Las sentencias 13 y 14 definen los métodos perimetro() y area(), declarados como abstract en la clase Geometria, de modo adecuado para los cı́rculos. Las sentencias 16-18 definen elMayor(), que es un método de objeto para comparar cı́rculos. Uno de los cı́rculos le llega como argumento implı́cito y el otro como argumento explı́cito. En la sentencia 17 se ve cómo al radio del argumento implı́cito se accede en la forma this.r (se podrı́a acceder también simplemente con r, pues no hay ninguna variable local que la oculte), y al del argumento explı́cito como c.r, donde c es el nombre del objeto pasado como argumento. La sentencia return devuelve una referencia al objeto r GVA-ELAI-UPMPFC0074-03 95 CAPÍTULO 5. ESTUDIO DE JAVA. José Ma Onrubia cuyo radio sea mayor. Cuando éste es el argumento implı́cito se devuelve this. Las sentencias 20-22 presentan la definición de otro método elMayor(), que en este caso es un método de clase (definido como static), y por tanto no tiene argumento implı́cito. Los dos objetos a comparar se deben pasar como argumentos explı́citos, lo que hace el código muy fácil de entender. Es importante considerar que en ambos casos lo que se devuelve como valor de retorno no es el objeto que constituye el mayor cı́rculo, sino una referencia (un nombre, por decirlo de otra forma). 5.2.5. Interface Dibujable . El diagrama de clases de la Figura 1.2 indica que las clases RectanguloGrafico y CirculoGrafico son el resultado tanto de las clases Rectangulo y Circulo, de las que derivan, como de la interface Dibujable, que de alguna manera interviene en el proceso. El concepto de interface es muy importante en Java. A diferencia de C++, Java no permite herencia múltiple, esto es, no permite que una clase derive de dos clases distintas heredando de ambas métodos y variables miembro. La herencia múltiple es fuente de problemas, pero en muchas ocasiones es una caracterı́stica muy conveniente. Las interfaces de Java constituyen una alternativa a la herencia múltiple con importantes ventajas prácticas y de ”estilo de programación”. Una interface es un conjunto de declaraciones de métodos (sin implementación, es decir, sin definir el código de dichos métodos). La declaración consta del tipo del valor de retorno y del nombre del método, seguido por el tipo de los argumentos entre paréntesis. Cuando una clase implementa una determinada interface, se compromete a dar una definición a todos los métodos de la interface. En cierta forma una interface se parece a una clase abstract cuyos métodos son todos abstract. La ventaja de las interfaces es que no están sometidas a las más rı́gidas normas de las clases; por ejemplo, una clase no puede heredar de dos clases abstract, pero sı́ puede implementar varias interfaces. Una de las ventajas de las interfaces de Java es el establecer pautas o modos de funcionamiento similares para clases que pueden estar o no relacionadas mediante herencia. En efecto, todas las clases que implementan una determinada interface soportan los métodos declarados en la interface y en este sentido se comportan de modo similar. Las interfaces pueden también relacionarse mediante mecanismos de herencia, con más flexibilidad que las clases. El fichero Dibujable.java define la interface Dibujable, 96 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 5.2. ESTUDIO A TRAVÉS DE UN EJEMPLO. mostrada a continuación. 1. 2. 3. 4. 5. 6. // fichero Dibujable.java import java.awt.Graphics; public interface Dibujable { public void setPosicion(double x, double y); public void dibujar(Graphics dw); } La interface Dibujable está dirigida a incorporar, en las clases que la implementen, lacapacidad de dibujar sus objetos. El listado muestra la declaración de los métodos setPosicion() y dibujar(). La declaración de estos métodos no tiene nada de particular. Como el método dibujar() utiliza como argumento un objeto de la clase Graphics, es necesario importar dicha clase. Lo importante es que si las clases RectanguloGrafico y CirculoGrafico implementan la interface Dibujable sus objetos podrán ser representados gráficamente en pantalla. 5.2.6. Clase RectanguloGrafico. La clase RectanguloGrafico deriva de Rectangulo (lo cual quiere decir que hereda sus métodos y variables miembro) e implementa la interface Dibujable (lo cual quiere decir que implementa los métodos declarados en dicha interface). A continuación se incluye la definición de dicha clase. 1. // Fichero RectanguloGrafico.java r GVA-ELAI-UPMPFC0074-03 97 CAPÍTULO 5. ESTUDIO DE JAVA. José Ma Onrubia 2. import java.awt.Graphics; 3. import java.awt.Color; 4. class RectanguloGrafico extends Rectangulo implements Dibujable { 5. // nueva variable miembro 6. Color color; 7. // constructor 8. public RectanguloGrafico(double x1, double y1, double x2, double y2, 9. Color unColor) { 10. // llamada al constructor de Rectangulo 11. super(x1, y1, x2, y2); 12. this.color = unColor; // en este caso this es opcional 13. } 14. // métodos de la interface Dibujable 15. public void dibujar(Graphics dw) { 16. dw.setColor(color); 17. dw.drawRect((int)x1, (int)y1, (int)(x2-x1), (int)(y2-y1)); 18. } 19. public void setPosicion(double x, double y) { 20. ; // método vacı́o, pero necesario de definir 21. } 22. } // fin de la clase RectanguloGrafico La sentencia 4 indica que RectanguloGrafico deriva de la clase Rectangulo e implementa la interface Dibujable. Recuérdese que mientras que sólo se puede derivar de una clase, se pueden implementar varias interfaces, en cuyo caso se ponen en el encabezamiento de la clase separadas por comas. La sentencia 6 (Color color;) define una nueva variable miembro que se suma a las que ya se tienen por herencia. Esta nueva variable es un objeto de la clase Color. Las sentencias 14-18 y 19-21 definen los dos métodos declarados por la interface Dibujable. El método dibujar() recibe como argumento un objeto dw de la clase Graphics. Esta clase define un contexto para realizar operaciones gráficas en un panel, tales como el color de las lı́neas, el color de fondo, el tipo de letra a utilizar en los rótulos, etc. Más adelante se verá con más detenimiento este concepto. La sentencia 16 (dw.setColor(color);) hace uso un método de la clase Graphics para determinar el color con el que se dibujarán las lı́neas a partir de ese momento. Java obliga a implementar o definir siempre todos los métodos declarados por la interface, aunque no se vayan a utilizar. Esa es la razón de que las sentencias 19-21 definan un método vacı́o, que sólo contiene un carácter 98 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 5.2. ESTUDIO A TRAVÉS DE UN EJEMPLO. punto y coma. Como no se va a utilizar no importa que esté vacı́o, pero Java obliga a dar una definición o implementación. 5.2.7. Clase CirculoGrafico. A continuación se define la clase CirculoGrafico, que deriva de la clase Circulo e implementa la interface Dibujable. // fichero CirculoGrafico.java import java.awt.Graphics; import java.awt.Color; public class CirculoGrafico extends Circulo implements Dibujable { // se heredan las variables y métodos de la clase Circulo Color color; // constructor public CirculoGrafico(double x, double y, double r, Color unColor) { // llamada al constructor de Circulo super(x, y, r); this.color = unColor; } // métodos de la interface Dibujable public void dibujar(Graphics dw) { dw.setColor(color); dw.drawOval((int)(x-r),(int)(y-r),(int)(2*r),(int)(2*r)); } public void setPosicion(double x, double y) { ; } } // fin de la clase CirculoGrafico 5.2.8. Clase PanelDibujo. La clase que se describe en este apartado es muy importante y quizás una de las más difı́ciles de entender en este capı́tulo introductorio. La clase PanelDibujo es muy importante porque es la responsable final de que los rectángulos y cı́rculos aparezcan dibujados en la pantalla. Esta clase deriva de la clase Panel, que deriva de Container, que deriva de Component, que deriva de Object. Ya se ha comentado que Object es la clase más general de Java. La clase r GVA-ELAI-UPMPFC0074-03 99 José Ma Onrubia CAPÍTULO 5. ESTUDIO DE JAVA. Component comprende todos los objetos de Java que tienen representación gráfica, tales como botones, barras de desplazamiento, etc. Los objetos de la clase Container son objetos gráficos del AWT (Abstract Windows Toolkit; la librerı́a de clases de Java que permite crear interfaces gráficas de usuario) capaces de contener otros objetos del AWT. La clase Panel define los Container más sencillos, capaces de contener otros elementos gráficos (como otros paneles) y sobre la que se puede dibujar. La clase PanelDibujo contiene el código que se muestra a continuación. 1. // fichero PanelDibujo.java 2. import java.awt.*; 3. import java.util.Vector; 4. import java.util.Enumeration; 5. public class PanelDibujo extends Panel { 6. // variable miembro 7. private Vector v; 8. // constructor 9. public PanelDibujo(Vector vect) { 10. super(new FlowLayout()); 11. this.v = vect; 12. } 13. // redefinición del método paint() 14. public void paint(Graphics g) { 15. Dibujable dib; 16. Enumeration e; 17. e = v.elements(); 18. while(e.hasMoreElements()) { 19. dib = (Dibujable)e.nextElement(); 20. dib.dibujar(g); 21. } 22. } 23. } // Fin de la clase PanelDibujo La clase Vector y la interface Enumeration pertenecen al package java.util, y sirven para tratar colecciones o conjuntos, en este caso conjuntos de figuras dibujables. La sentencia 5 indica que la clase PanelDibujo deriva de la clase Panel, heredando de ésta y de sus superclases Container y Component todas sus capacidades gráficas. 100 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 5.2. ESTUDIO A TRAVÉS DE UN EJEMPLO. La sentencia 7 (private Vector v;) crea una variable miembro v que es una referencia a un objeto de la clase Vector (nótese que no es un objeto, sino una referencia o un nombre de objeto). Las sentencias 9-12 definen el constructor de la clase, que recibe como argumento una referencia vect a un objeto de la clase Vector. En este vector estarán almacenadas las referencias a los objetos -rectángulos y cı́rculosque van a ser dibujados. En la sentencia 10 (super(new FlowLayout());) se llama al constructor de la superclase panel, pasándole como argumento un objeto recién creado de la clase FlowLayout. Al hablar de construcción de interfaces gráficas con el AWT, la clase FlowLayout se ocupa de distribuir de una determinada forma (de izquierda a derecha y de arriba abajo) los componentes gráficos que se añaden a un çontainer”tal como la clase Panel. Hay que introducir ahora un aspecto muy importante de Java y, en general, de la programación orientada a objetos. Tiene que ver con algo que es conocido con el nombre de Polimorfismo. La idea básica es que una referencia a un objeto de una determinada clase es capaz de servir de referencia o de nombre a objetos de cualquiera de sus clases derivadas. Por ejemplo, es posible en Java hacer lo siguiente: Geometria geom1, geom2; geom1 = new RectanguloGrafico(0, 0, 200, 100, Color.red); geom2 = new CirculoGrafico(200, 200, 100, Color.blue); Obsérvese que se han creado dos referencias de la clase Geometria que posteriormente apuntan a objetos de las clases derivadas RectanguloGrafico y Circulo-Grafico. Sin embargo, hay una cierta limitación en lo que se puede hacer con las referencias geom1 y geom2. Por ser referencias a la clase Geometria sólo se pueden utilizar las capacidades definidas en dicha clase, que se reducen a la utilización de los métodos perimetro() y area(). De la misma forma que se ha visto con la clase base Geometria, en Java es posible utilizar una referencia del tipo correspondiente a una interface para manejar objetos de clases que implementan dicha interface. Por ejemplo, es posible escribir: r GVA-ELAI-UPMPFC0074-03 101 CAPÍTULO 5. ESTUDIO DE JAVA. José Ma Onrubia Dibujable dib1, dib2; dib1 = new RectanguloGrafico(0, 0, 200, 100, Color.red); dib2 = new CirculoGrafico(200, 200, 100, Color.blue); donde los objetos referidos por dib1 y dib2 pertenecen a las clases RectanguloGrafico y CirculoGrafico, que implementan la interface Dibujable. También los objetos dib1 y dib2 tienen una limitación: sólo pueden ser utilizados con los métodos definidos por la interface Dibujable. El poder utilizar nombres de una superclase o de una interface permite tratar de un modounificado objetos distintos, aunque pertenecientes a distintas subclases o bien a clases que implementan dicha interface. Esta es la idea en la que se basa el polimorfismo. Ahora ya se está en condiciones de volver al código del método paint(), definido en las sentencias 14-22 de la clase PanelDibujo. El método paint() es un método heredado de Container, que a su vez redefine el método heredado de Component. En la clase PanelDibujo se da una nueva definición de este método. Una peculiaridad del método paint() es que, por lo general, el programador no tiene que preocuparse de llamarlo: se encargan de ello Java y el sistema operativo. El programador prepara por una parte la ventana y el panel en el que va a dibujar, y por otra programa en el método paint() las operaciones gráficas que quiere realizar. El sistema operativo y Java llaman a paint() cada vez que entienden que la ventana debe ser dibujada o redibujada. El único argumento de paint() es un objeto g de la clase Graphics que, como se ha dicho antes, constituye el contexto gráfico (color de las lı́neas, tipo de letra, etc.) con el que se realizarán las operaciones de dibujo. La sentencia 15 (Dibujable dib;) crea una referencia de la clase Dibujable, que como se ha dicho anteriormente, podrá apuntar o contener objetos de cualquier clase que implemente dicha interface. La sentencia 16 (Enumeration e;) crea una referencia a un objeto de la interface Enumeration definida en el package java.util. La interface Enumeration proporciona los métodos hasMoreElements(), que chequea si la colección de elementos que se está recorriendo tiene más elementos y nextElement(), que devuelve el siguiente elemento de la colección. Cualquier colección de elementos (tal como la clase Vector de Java, o como cualquier tipo de lista vinculada programada por el usuario) puede implementar esta interface, y ser por 102 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 5.2. ESTUDIO A TRAVÉS DE UN EJEMPLO. tanto utilizada de un modo uniforme. La sentencia 19 (dib = (Dibujable)e.nextElement();) contiene bastantes elementos nuevos e importantes. El método e.nextElement() devuelve el siguiente objeto de la enumeración. En principio este objeto podrı́a ser de cualquier clase. Los elementos de la clase Vector son referencias de la clase Object, que es la clase más general de Java, la clase de la que derivan todas las demás. Esto quiere decir que esas referencias pueden apuntar a objetos de cualquier clase. El nombre de la interface (Dibujable) entre paréntesis representa un cast o conversión entre tipos diferentes. En Java como en C++, la conversión entre variables u objetos de distintas clases es muy importante. Por ejemplo, (int)3.14 convierte el número double 3.14 en el entero 3. Evidentemente no todas las conversiones son posibles, pero sı́ lo son y tienen mucho interés las conversiones entre clases que están en la misma lı́nea jerárquica (entre sub-clases y super-clases), y entre clases que implementan la misma interface. Lo que se está diciendo a la referencia dib con el cast a la interface Dibujable en la sentencia 19, es que el objeto de la enumeración va a ser tratado exclusivamente con los métodos de dicha interface. En la sentencia 20 (dib.dibujar(g);) se aplica el método dibujar() al objeto referenciado por dib, que forma parte de la enumeración e, obtenida a partir del vector v. Lo que se acaba de explicar puede parecer un poco complicado, pero es tı́pico de Java y de la programación orientada a objetos. La ventaja del método paint() ası́ programado es que es absolutamente general: en ningún momento se hace referencia a las clases RectanguloGrafico y CirculoGrafico, cuyos objetos son realmente los que se van a dibujar. Esto permite añadir nuevas clases tales como TrianguloGrafico, PoligonoGrafico, LineaGrafica, etc., sin tener que modificar para nada el código anterior: tan sólo es necesario que dichas clases implementen la interface Dibujable. Esta es una ventaja no pequeña cuando se trata de crear programas extensibles (que puedan crecer), flexibles (que se puedan modificar) y reutilizables (que se puedan incorporar a otras aplicaciones). 5.2.9. Clase VentanaCerrable. Es una clase de ütilidad”que mejora algo las caracterı́sticas de la clase Frame de Java, de la que deriva. La clase Frame estándar tiene una limitación y es que no responde a las acciones normales en Windows para cerrar una ventana o una aplicación (por ejemplo, clicar en la cruz de la esquina superior derecha). En ese caso, para cerrar la aplicación es necesario recurrir por ejemplo al comando End Task del Task Manager de Windows NT (que aparece con Ctrl+Alt+Supr). Para evitar esta molestia r GVA-ELAI-UPMPFC0074-03 103 CAPÍTULO 5. ESTUDIO DE JAVA. José Ma Onrubia se ha creado la clase VentanaCerrable, que deriva de Frame e implementa la interface WindowListener. 1. // Fichero VentanaCerrable.java 2. import java.awt.*; 3. import java.awt.event.*; 4. class VentanaCerrable extends Frame implements WindowListener { 5. // constructores 6. public VentanaCerrable() { 7. super(); 8. } 9. public VentanaCerrable(String title) { 10. super(title); 11. setSize(500,500); 12. addWindowListener(this); 13. } 14. // métodos de la interface WindowsListener 15. public void windowActivated(WindowEvent e) {;} 16. public void windowClosed(WindowEvent e) {;} 17. public void windowClosing(WindowEvent e) {System.exit(0);} 18. public void windowDeactivated(WindowEvent e) {;} 19. public void windowDeiconified(WindowEvent e) {;} 20. public void windowIconified(WindowEvent e) {;} 21. public void windowOpened(WindowEvent e) {;} 22. } // fin de la clase VentanaCerrable La sentencia 12 (addWindowListener(this);) es muy importante y significativa sobre la forma en que el AWT de Java gestiona los eventos sobre las ventanas y en general sobre lo que es la interface gráfica de usuario. Cuando un elemento gráfico -en este caso la ventana- puede recibir eventos del usuario es necesario indicar quién se va a encargar de procesar esos eventos. De ordinario al producirse un evento se debe activar un método determinado que se encarga de procesarlo y realizar las acciones pertinentes (en este caso cerrar la ventana y la aplicación). La sentencia 12 ejecuta el método addWindowListener() de la clase Frame (que a su vez lo ha heredado de la clase Window). El argumento que se le pasa a este método indica qué objeto se va a responsabilizar de gestionar los eventos que reciba la ventana implementando la interface WindowListener. En este caso, como el argumento que se le pasa es this, la propia clase VentanaCerrable debe ocuparse de gestionar los eventos que reciba. Ası́ es, puesto que dicha clase implementa la interface WindowListener según se ve 104 r GVA-ELAI-UPMPFC0074-03 5.3. NOMENCLATURA HABITUAL EN LA PROGRAMACIÓN EN José Ma Onrubia JAVA. en la sentencia 4. Puede notarse que como el constructor por defecto de las sentencias 6-8 no utiliza el método addWindowListener(), si se construye una VentanaCerrable sin tı́tulo no podrá ser cerrada del modo habitual. Ası́ se ha hecho deliberadamente en este ejemplo para que el lector lo pueda comprobar con facilidad. La interface WindowListener define los siete métodos necesarios para gestionar los siete eventos con los que se puede actuar sobre una ventana. Para cerrar la ventana sólo es necesario definir el método windowClosing(). Sin embargo, el implementar una interface obliga siempre a definir todos sus métodos. Por ello en las sentencias 15-21 todos los métodos están vacı́os (solamente el punto y coma entre llaves), excepto el que realmente interesa, que llama al método exit() de la clase System. El argumento ”0”indica terminación normal del programa. 5.3. Nomenclatura habitual en la programación en java. Los nombres de Java son sensibles a las letras mayúsculas y minúsculas. Ası́, las variables masa, Masa y MASA son consideradas variables completamente diferentes. Las reglas del lenguaje respecto a los nombres de variables son muy amplias y permiten mucha libertad al programador, pero es habitual seguir ciertas normas que facilitan la lectura y el mantenimiento de los programas de ordenador. Se recomienda seguir las siguientes instrucciones: 1. En Java es habitual utilizar nombres con minúsculas, con las excepciones que se indican en los puntos siguientes. 2. Cuando un nombre consta de varias palabras es habitual poner una a continuación de otra, poniendo con mayúscula la primera letra de la palabra que sigue a otra (Ejemplos: elMayor(), VentanaCerrable, RectanguloGrafico, addWindowListener()). 3. Los nombres de clases e interfaces comienzan siempre por mayúscula (Ejemplos: Geometria, Rectangulo, Dibujable, Graphics, Vector, Enumeration). 4. Los nombres de objetos, los nombres de métodos y variables miembro, y los nombres de las variables locales de los métodos, comienzan siempre por minúscula (Ejemplos: main(), dibujar(), numRectangulos, x, y, r). r GVA-ELAI-UPMPFC0074-03 105 CAPÍTULO 5. ESTUDIO DE JAVA. José Ma Onrubia 5. Los nombres de las variables finales, es decir de las constantes, se definen siempre con mayúsculas (Ejemplo: PI) 5.4. Estructura general de un programa java. El anterior ejemplo presenta la estructura habitual de un programa realizado en cualquier lenguaje orientado a objetos u OOP (Object Oriented Programming), y en particular en el lenguaje Java. Aparece una clase que contiene el programa principal (aquel que contiene la función main()) y algunas clases de usuario (las especı́ficas de la aplicación que se está desarrollando) que son utilizadas por el programa principal. Los ficheros fuente tienen la extensión *.java, mientras que los ficheros compilados tienen la extensión *.class. Un fichero fuente (*.java) puede contener más de una clase, pero sólo una puede ser public. El nombre del fichero fuente debe coincidir con el de la clase public (con la extensión *.java. Si la clase no es public, no es necesario que su nombre coincida con el del fichero. Una clase puede ser public o package (default), pero no private o protected. De ordinario una aplicación está constituida por varios ficheros *.class. Cada clase realiza unas funciones particulares, permitiendo construir las aplicaciones con gran modularidad e independencia entre clases. La aplicación se ejecuta por medio del nombre de la clase que contiene la función main() (sin la extensión *.class). Las clases de Java se agrupan en packages, que son librerı́as de clases. Si las clases no se definen como pertenecientes a un package, se utiliza un package por defecto (default) que es el directorio activo. Los packages se estudian con más detenimiento en siguientes apartados. 5.4.1. Concepto de Clase. Una clase es una agrupación de datos (variables o campos) y de funciones (métodos) que operan sobre esos datos. A estos datos y funciones pertenecientes a una clase se les denomina variables y métodos o funciones miembro. La programación orientada a objetos se basa en la programación de clases. Un programa se construye a partir de un conjunto de clases. Una vez definida e implementada una clase, es posible declarar elementos de esta clase de modo similar a como se declaran las variables del lenguaje (int, double, String, . . . ). Los elementos declarados de una clase se denom106 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 5.4. ESTRUCTURA GENERAL DE UN PROGRAMA JAVA. inan objetos de la clase. De una única clase se pueden declarar o crear numerosos objetos. La clase es lo genérico: es el patrón o modelo para crear objetos. Cada objeto tiene sus propias copias de las variables miembro, con sus propios valores, en general distintos de los demás objetos de la clase. Las clases pueden tener variables static, que son propias de la clase y no de cada objeto. 5.4.2. Herencia. La herencia permite que se pueden definir nuevas clases basadas en clases existentes, lo cual facilita reutilizar código previamente desarrollado. Si una clase deriva de otra (extends) hereda todas sus variables y métodos. La clase derivada puede añadir nuevas variables y métodos y/o redefinir las variables y métodos heredados. En Java, a diferencia de otros lenguajes orientados a objetos, una clase sólo puede derivar de una única clase, con lo cual no es posible realizar herencia múltiple en base a clases. Sin embargo es posible ”simular”la herencia múltiple en base a las interfaces. 5.4.3. Concepto de Interface. Una interface es un conjunto de declaraciones de funciones. Si una clase implementa (implements) una interface, debe definir todas las funciones especificadas por la interface. Una clase puede implementar más de una interface, representando una forma alternativa de la herencia múltiple. Una interface puede derivar de otra o incluso de varias interfaces, en cuyo caso incorpora todos los métodos de las interfaces de las que deriva. 5.4.4. Concepto de Package. Un package es una agrupación de clases. Existen una serie de packages incluidos en el lenguaje (ver jerarquı́a de clases que aparece en el API de Java). Además el usuario puede crear sus propios packages. Lo habitual es juntar en packages las clases que estén relacionadas. Todas las clases que formen parte de un package deben estar en el mismo directorio. r GVA-ELAI-UPMPFC0074-03 107 José Ma Onrubia CAPÍTULO 5. ESTUDIO DE JAVA. 5.4.5. La jerarquı́a de clases de Java (API). Durante la generación de código en Java, es recomendable y casi necesario tener siempre a la vista la documentación on-line del API de Java . En dicha documentación es posible ver tanto la jerarquı́a de clases, es decir la relación de herencia entre clases, como la información de los distintos packages que componen las librerı́as base de Java. Hay que resaltar el hecho de que todas las clases en Java son derivadas de la clase java.lang.Object, por lo que heredan todos los métodos y variables de ésta. 108 r GVA-ELAI-UPMPFC0074-03 Capı́tulo 6 Desarrollo de nuestra aplicación mediante JDT, JDK 1.4.1 y JBuilder7. 6.1. Introducción. Una vez elegidas las librerı́as JDT y hecho un estudio de Java, se está listo para comenzar a crear nuestra aplicación DICOM. El primer paso que se tiene que dar es la elección del entorno de desarrollo a utilizar. La disyuntiva que aparece es el uso de JBuilder o de Visual J++; pero la elección está casi tomada desde un principio a favor de JBuilder de Borland debido a lo mucho más completo, especı́fico y con muchas más prestaciones que VJ++. Por lo tanto nos encontramos con un entorno de desarrollo llamado JBuilder (en nuestro caso la versión 7.0 Enterprise), con unas librerı́as de DICOM llamadas JDT y con las propias librerı́as Java, JDK (1.4.1), mediante las cuales se va a implementar nuestra aplicación DICOM. Paso a paso se va a ver la progresión seguida para este proceso de creación de la aplicación, cuyo orden podrı́a ser el siguiente: Uso de JBuilder 7.0 Uso de librerı́as JDK 1.4.1 Uso de librerı́as JDK y JDT en JBuilder Implementación conjunta. 109 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia 6.2. Uso de JBuilder 7.0 6.2.1. Introducción. En esta sección se van a ver los aspectos más importantes a la hora de crear una aplicación (interfaz de usuario) mediante este programa de desarrollo. 6.2.2. Instalación de JBuilder. Es un proceso muy sencillo en el cual hemos tenido algunas peculiaridades. Pasos para la instalación: 1. Ir a la página web de sun (http://www.borland.com/products/ downloads/download builder.html) y descagar la versión de JBuilder deseada; en nuestro caso JBuilder 7.0 Enterprise. 2. Pedir via email un archivo de activación necesario para poder trabajar con el programa. 3. Ejecutar JBuilder y cuando pide el archivo de activación indicarle la ruta de éste. 4. Cambiar el archivo localizado en .../JBuilder7/bin jbuilder.jar y sustituirlo por el proporcionado para que la licencia dure en vez de un mes, unos cuantos dı́as más. En este momento es posible trabajar con el programa sin preocuparnos de problemas de tiempo con la licencia. 6.2.3. Creación de una aplicación JBuilder. Se va a crear un simple editor de texto para ver cómo realizar una aplicación. El estudio de este ejemplo proporciona una base espléndida para, a partir de éste, tener las nociones básicas en JBuilder y poder avanzar rápida y fácilmente en la creación de una interfaz de usuario. Pasos: Paso 1: Creación de un proyecto. Para crear un proyecto se usa el asistente para proyectos y el asistente para aplicaciones, lo que acelera este proceso de creación, de esta forma: 110 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.2. USO DE JBUILDER 7.0 1. Elija Archivo—Nuevo proyecto para iniciar el Asistente para proyectos. 2. Realice los siguientes cambios en el Paso 1: Nombre: Editor de texto Tipo (tipo de archivo): .jpx Seleccione la opción Crear archivo de notas del proyecto. Al seleccionar esta opción, el Asistente para proyectos crea un archivo HTML para las notas del proyecto y lo añade al mismo. 3. Acepte todas las demás opciones por defecto en el Paso 1, 2 y 3. Figura 6.1: Asistente para proyectos: paso 1 Una vez creado nuestro proyecto podemos pasar a crear nuestra aplicación dentro de él mediante el asistente para aplicaciones: 1. Abra la galerı́a de objetos seleccionando Archivo—Nuevo. 2. Haga doble clic en el en el icono Aplicación para abrir el Asistente para aplicaciones. r GVA-ELAI-UPMPFC0074-03 111 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia 3. Cambie el nombre de clase de la aplicación en el Paso 1. 4. Pulse Siguiente para ir al Paso 2 del Asistente para proyectos. 5. Cambie el nombre y el tı́tulo de la clase de marco en el Paso 2 6. Seleccione todas las opciones del Paso 2. El asistente genera automáticamente el código correspondiente a las opciones seleccionadas. (Fı́jese en el significado de cada opción según las va marcando). 7. Haga clic en el botón Finalizar. El Asistente para aplicaciones añade al proyecto un .java y archivos de imagen. 8. Guarde el proyecto utilizando Archivo—Guardar proyecto. 9. Pulse la pestaña Diseño del archivo abierto, TextEditFrame.java. La pestaña Diseño, ubicada en la parte inferior de la ventana del Visualizador de aplicaciones, abre el diseñador de interfaces de usuario. Observe estos cambios en el IDE de JBuilder: En el panel de contenido aparece el diseñador de interfaces. El árbol de componentes aparece en el panel de estructura. El Inspector aparece a la derecha del diseñador. 112 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.2. USO DE JBUILDER 7.0 Figura 6.2: Partes de aplicación. Paso 2: Añadir un área de texto. En este paso se crea un área de texto que rellena por completo el marco de la interfaz de usuario entre la barra de menús por encima y la barra de estado por debajo. Para lograrlo, el gestor de diseños del contenedor principal de la interfaz de usuario debe utilizar BorderLayout. Como consecuencia de la utilización del Asistente para aplicaciones, el principal contenedor de esta interfaz de usuario, que aparece como this en el árbol de componentes, contiene un JPanel denominado contentPane que ya se ha cambiado a BorderLayout. Lo único que hay que hacer ahora es añadir los componentes del área de texto a contentPane. Para ello, se añadirá un panel de desplazamiento en el contentPane y después se colocará un componente de área de texto dentro del panel de desplazamiento. El panel de desplazamiento proporciona barras de desplazamiento al área de texto. r GVA-ELAI-UPMPFC0074-03 113 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia 1. Haga clic en la pestaña TextEditFrame del editor, y a continuación seleccione la pestaña Diseño. 2. Haga clic en el componente contentPane del árbol de componentes para seleccionarlo, como se muestra a continuación. Figura 6.3: contentPane 3. Pulse la pestaña Contenedores Swing en la paleta de componentes y seleccione el componente JScrollPane. Figura 6.4: JScrollPane 4. Haga clic en el centro de contentPane del diseñador de interfaces. Esto coloca el componente JScrollPane en el panel de contenido y deberı́a darle una restricción de centro BorderLayout, haciendo que ocupe completamente el área situada entre la barra de herramientas y la barra de desplazamiento. Si no lo consigue, seleccione Edición—Deshacer e inténtelo de nuevo. 5. Seleccione el nuevo componente jScrollPane1 en el árbol de componentes. 114 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.2. USO DE JBUILDER 7.0 6. Fı́jese en el valor de la propiedad constraints del Inspector y compruebe que se le está asignando el valor Center. Si no es ası́, seleccione Center en la lista desplegable. Figura 6.5: constraints 7. Pulse la pestaña Swing en la paleta y seleccione el componente JTextArea. Figura 6.6: JTextArea 8. Haga clic sobre el componente jScrollPane1 en el árbol de componentes o arrástrelo al diseñador de interfaces para colocar JTextArea en el panel de desplazamiento. 9. En el Inspector, haga clic con el botón derecho sobre la propiedad text y seleccione Borrar el valor de la propiedad para eliminar jTextArea1 del área de texto. r GVA-ELAI-UPMPFC0074-03 115 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia Figura 6.7: text 10. Por último, debe definir algunas propiedades en jTextArea1 para que ajuste automáticamente las lı́neas de texto y lo haga en los espacios entre palabras. En el Inspector, asigne los siguientes valores: lineWrap = true wrapStyleWord = true background = white A continuación, compile el programa y ejecútelo para ver qué aspecto ofrece. 1. Seleccione Proyecto—Ejecutar Make del proyecto en el menú. Esto compila todos los archivos del proyecto y crea un archivo TextEditClass.class y un TextEditFrame.class en una carpeta de clases dentro de la carpeta de proyectos. Deberı́a compilarse sin errores. 2. Haga clic en el botón Ejecutar en la barra de herramientas de JBuilder o seleccione Ejecutar—Ejecutar proyecto en el menú. Ahora la interfaz de usuario ofrecerá un aspecto similar al siguiente: 116 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.2. USO DE JBUILDER 7.0 Figura 6.8: Ejecutado 3. En la aplicación ”Editor de texto”, seleccione Archivo—Salir para cerrar la ventana de ejecución. Paso 3: Crear menús. En este paso se van a crear estos menús: Figura 6.9: Menús 1. Haga clic en la pestaña Diseño de TextEditFrame.java si aún no está seleccionada. 2. Haga doble clic sobre jMenuBar1 en la carpeta Menu del árbol de componentes para abrir el diseñador de menús. (También puede seleccionar un elemento de menú del árbol de componentes y pulsar Intro.) 3. Seleccione el elemento de menú Archivo—Salir en el diseñador de menús o jMenuFileExit en el árbol de componentes. El diseñador de menús resaltará el elemento seleccionado. 4. Haga clic en el botón Insertar elemento de menú de la barra de herramientas del diseñador de menús, o pulse la tecla Insert del teclado. Encima de Salir se introduce un nuevo elemento de menú resaltado y vacı́o. r GVA-ELAI-UPMPFC0074-03 117 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia 5. Escriba Nuevo en el área resaltada. 6. Pulse Flecha abajo para aceptar la nueva entrada y baje al siguiente elemento (en este caso, el elemento de menú Salir). 7. Haga clic con el botón derecho en Salir y seleccione Insertar elemento de menú en el menú contextual. Escriba Abrir. Nota: También se puede acceder a todas las acciones de la barra de herramientas desde el menú emergente que aparece al hacer clic con el botón derecho. 8. De la misma forma, añada los elementos de menú Guardar y Guardar como. 9. Seleccione Salir y haga clic en el botón Separador de la barra de herramientas para insertar una barra. El menú Archivo ya está terminado. 10. Haga clic con el botón derecho en la barra principal de menús y seleccione Insertar menú. Ası́ se crea un menú entre los menús Archivo y Ayuda. Escriba Edición como nombre de este menú. 11. Pulse Intro para descender hacia la siguiente entrada vacı́a. No es necesario pulsar Insert aquı́ porque este menú no contiene ningún elemento después de la entrada actual. Sugerencia: Para borrar una entrada, selecciónela y haga clic en el botón Borrar de la barra de herramientas, o pulse la tecla Supr dos veces. La primera vez que se pulsa la tecla Supr se borra el texto de la entrada. La segunda vez elimina la entrada del menú. 12. Prosiga con la construcción del menú Edición tal y como se indica en la siguiente imagen, añadiendo tres elementos: Fuente (JBuilder SE y Enterprise), Color de texto y Color de fondo. Si alguna entrada tiene una longitud superior al área de edición, el texto se desplazará automáticamente a medida que escriba. Cuando pulse Intro, el diseñador de menús ajustará el ancho del menú para mostrar el elemento más largo de la lista. 13. Cierre el diseñador de menús con un doble clic en cualquier componente de la sección de la interfaz de usuario del árbol de componentes. Esto hará que el panel de contenido cambie al diseñador de interfaces. 14. Guarde el archivo y ejecute la aplicación. Ahora la interfaz de usuario ofrecerá un aspecto similar al siguiente: 118 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.2. USO DE JBUILDER 7.0 Figura 6.10: Editor Debe poder experimentar con la interfaz de usuario y escribir texto en el área de texto, pero los botones no funcionarán todavı́a aunque sı́ lo harán los menús Archivo—Salir y Ayuda—Acerca de. Paso 4: Añadir un cuadro de diálogo Selector de fuentes. Comencemos a enlazar los sucesos de menú, empezando con el elemento de menú Edición—Fuente que es el que va a mostrar el cuadro de diálogo Selector de fuentes. En primer lugar, para poder utilizar esta opción de menú, debe añadir un componente cuadro de diálogo Selector de fuentes a la clase TextEditFrame: 1. Abra TextEditFrame.java en el diseñador de interfaces. 2. Seleccione la pestaña Más dbSwing de la paleta de componentes y haga clic en el componente FontChooser . 3. Haga clic en cualquier lugar del árbol de componentes o en el diseñador de interfaces, para añadir el FontChooser al diseño. Esto situará el componente dentro de la clase como fontChooser1 y lo mostrará en la carpeta Otros del árbol de componentes. Sólo verá el componente cuadro de diálogo en el árbol de componentes, no en el diseñador de interfaces. Creación de un suceso para lanzar el Selector de fuentes Creación de un suceso para el elemento de menú Edición—Fuente, que lanzará el Selector de fuentes: r GVA-ELAI-UPMPFC0074-03 119 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia 1. Seleccione el elemento de menú Edición—Fuente en el árbol de componentes. Deberı́a ser jMenuItem5 (en el segundo nodo de menú, llamado jMenu1.) Obsérve que la propiedad text para este elemento de menú del Inspector dice ”Fuente”. No importa si su elemento de menú Fuente tiene un número diferente a éste. Pero asegúrese de seleccionar el correspondiente al menú Fuente. 2. Haga clic en la pestaña sucesos en el Inspector, y haga doble clic en el campo de valor (la segunda columna) del suceso actionPerformed. En los menús, botones y otros muchos componentes de la interfaz de usuario de Java, actionPerformed es el suceso principal de usuario, que deberı́a capturar para responder al usuario cuando utiliza el menú o el botón. El nombre del método de tratamiento de sucesos aparece en el campo de valor. Si el método no existe todavı́a, esta operación muestra el nombre propuesto por defecto para el nuevo método de gestión del suceso. Para este nuevo manejador de sucesos, el nombre propuesto es jMenuItem5 actionPerformed. Figura 6.11: actionPerformed 3. Haga doble clic en el valor de este suceso, o pulse Intro para crear el suceso. Si el método de tratamiento del suceso es nuevo, esta operación generará un stub vacı́o para el método en el código fuente. Independientemente de si el método es nuevo o ya existe, el foco de ventana cambiará a código fuente en el editor y colocará el cursor dentro del método de tratamiento de sucesos. En el caso de un método nuevo de tratamiento de sucesos, como es el caso, verá que la sección principal del método no contiene todavı́a código alguno. 4. Escriba esta lı́nea de código en el cuerpo de este nuevo método vacı́o (entre las llaves de apertura y cierre): fontChooser1.showDialog(); 120 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.2. USO DE JBUILDER 7.0 Ahora el método deberı́a parecerse a éste: void jMenuItem5 actionPerformed(ActionEvent e) { fontChooser1.showDialog(); } 5. Guarde y ejecute la aplicación. El elemento de menú Edición—Fuente deberı́a abrir el cuadro de diálogo Selector de fuentes. Si no, compruebe que la propiedad frame tiene el valor this. Aunque intente cambiar la fuente, no sucederá nada. Esto se debe a que no se utiliza el resultado del FontChooser para cambiar el texto del área de edición. Esto será lo siguiente que hagamos. 6. Cierre la aplicación ”EditorDeTexto”. Paso 5: Vinculación de sucesos de elemento de menú al cuadro de diálogo Selector de fuentes. Vamos a utilizar el cuadro de diálogo Selector de fuentes para modificar la propiedad font de textArea1. 1. Haga clic en la pestaña Fuente y seleccione el método de tratamiento del suceso del elemento de menú Fuente ( jMenuItem5 actionPerformed(ActionEvent e))) recién creado. r GVA-ELAI-UPMPFC0074-03 121 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia Figura 6.12: jMenu 2. Introduzca este código en el método de tratamiento de sucesos para el elemento de menú Fuente (jMenuItem5), entre las llaves de apertura y cierre, asegurándose de reemplazar el código antiguo fontChooser1.showDialog();: // Gestiona el elemento de menú ”Edición Fuente” // Obtiene la fuente existente en el área de texto // y la lleva al Selector de fuente antes de mostrarlo // para que se modifique la fuente // que ya existe fontChooser1.setSelectedFont(jTextArea1.getFont()); // Obtiene la nueva fuente del Selector de fuentes // Comprueba primero el valor devuelto por showDialog() para // ver si el usuario pulsó Aceptar. if (fontChooser1.showDialog()) { // Asigna a la fuente de jTextArea1 el valor // seleccionado por el usuario antes de pulsar Aceptar jTextArea1.setFont(fontChooser1.getSelectedFont()); } //pinta el menú de nuevo una vez que el elemento se ha seleccionado this.repaint(); //Vuelve a dibujar el texto correctamente si hay texto selecionado al 122 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.2. USO DE JBUILDER 7.0 cambiar la fuente. jTextArea1.repaint(); 3. Guarde y ejecute la aplicación y escriba algo en el área de texto. 4. Seleccione el texto y utilice el elemento de menú Edición—Fuente para cambiar la fuente. En esta aplicación, se cambia la fuente de la totalidad del área de texto (no solamente del texto seleccionado). No espere que la configuración de fuentes se mantenga. No introduciremos código para activar esa caracterı́stica. 5. Cierre la aplicación ”EditorDeTexto”. Paso 6: Vinculación de sucesos de elementos de menú a JColorChooser. A continuación se crean los sucesos de menú Edición—Color de texto y Edición—Color de fondo y se los vincula con el cuadro de diálogo JColorChooser de Swing. Al no necesitar asignar valores a ninguna de las propiedades de JColorChooseren el diseñador, no es preciso añadirlo a la interfaz de usuario del diseñador. Puede llamarlo directamente desde el manejador del suceso actionPerformed() de un elemento de menú del siguiente modo: 1. Vuelva al diseñador de TextEditFrame.java. 2. Seleccione el segundo elemento de menú del árbol de componentes en Edición (jMenuItem6) que tiene escrito Çolor de texto”en la propiedad actionCommand. 3. Seleccione la pestaña Sucesos en el Inspector y haga clic tres veces en el suceso actionPerformed() para crear el manejador del suceso: void jMenuItem6 actionPerformed(ActionEvent e) { } 4. Añada el código siguiente en el stub del manejador del suceso r GVA-ELAI-UPMPFC0074-03 123 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia (incluyendo los comentarios si lo desea): //Gestiona el elemento de menú Çolor de texto” Color color = JColorChooser.showDialog(this,Çolor de texto”, jTextArea1.getForeground()); if (color != null) { jTextArea1.setForeground(color); } //pinta el menú de nuevo una vez que el elemento se ha seleccionado this.repaint(); 5. Vuelva al diseñador. 6. Seleccione el tercer elemento de menú en el árbol de componentes, en Edición (menuItem7), que debe tener la etiqueta Çolor de fondo”en la propiedad actionCommand. Cree un suceso actionPerformed () para él, tal como hizo con jMenuItem6. 7. Inserte el siguiente código en el suceso actionPerformed() de jMenuItem7: // Gestiona el elemento de menú Çolor de fondo” Color color = JColorChooser.showDialog(this,Çolor de fondo”, jTextArea1.getBackground()); if (color != null) { jTextArea1.setBackground(color); } //pinta el menú de nuevo una vez que el elemento se ha seleccionado this.repaint(); 8. Guarde el archivo, compile y ejecute la aplicación. Escriba texto y haga pruebas con los colores de primer plano y de fondo. La aplicación ofrecerá el siguiente aspecto, si elige texto blanco con fondo negro: 124 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.2. USO DE JBUILDER 7.0 Figura 6.13: editor en negro 9. Cierre la aplicación ”EditorDeTexto”. Paso 7: Adición de un manejador a un suceso de menú para borrar el área de texto. Vamos a capturar el elemento de menú Archivo—Nuevo con un manejador que borra el contenido del área de texto. 1. Vuelva al diseñador. 2. Seleccione el elemento de menú Archivo—Nuevo del árbol de componentes (jMenuItem1). 3. Cree un suceso actionPerformed() e introduzca en él este código: // Gestiona el elemento de menú Archivo—Nuevo. // Borra el texto del área del texto. jTextArea1.setText(); 4. Guarde y ejecute la aplicación, escriba algo en el área de texto y vea qué sucede al seleccionar Archivo—Nuevo. Deberı́a borrar el contenido. Observe que no pregunta si desea guardar el archivo antes. Para poder tratar este aspecto, tendrá que configurar la infraestructura para la lectura y escritura de archivos de texto, para controlar si el archivo ha cambiado y necesita guardarse, etcétera. Comenzaremos la utilización de archivos en el paso siguiente. 5. Cierre la aplicación ”EditorDeTexto”. r GVA-ELAI-UPMPFC0074-03 125 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia Paso 8: Añadir un cuadro de diálogo selector de archivos. Vamos a enlazar el elemento de menú Archivo—Abrir con un manejador de un suceso que presenta al usuario un JFileChooser (cuadro de diálogo para abrir archivos) para archivos de texto. Cuando el usuario selecciona un archivo y hace clic en Aceptar el manejador del suceso abre el archivo de texto y coloca su contenido dentro de JTextArea. 1. Vuelva al diseñador y seleccione el componente JFileChooser de la ficha Swing Containers de la paleta de componentes. 2. Haga clic en la carpeta IU del árbol de componentes para colocar el componente. (Si hace clic en el diseñador de interfaces, el componente se colocará en una sección equivocada del árbol.) 3. Seleccione el elemento de menú Archivo—Abrir en el árbol de componentes (jMenuItem2). 4. Cree un suceso actionPerformed() e introduzca este código: // Gestionar el elemento de menú Archivo—Abrir. //Utilizar la versión OPEN del cuadro de diálogo, comprobar el valor devuelto de Aceptar/Cancelar if (JFileChooser.APPROVE OPTION == jFileChooser1.showOpenDialog(this)) { // Muestra el nombre del directorio y archivos abiertos en la barra de estado. statusBar.setText(.Abierto ”+jFileChooser1.getSelectedFile().getPath()); // El código debe ir aquı́ para cargar realmente el texto // en el TextArea. } 5. Salvar y ejecutar la aplicación. En el menú Archivo—Abrir, seleccione un archivo y pulse aceptar. Debe aparecer el nombre del archivo y el directorio completo en la lı́nea de estado en la parte inferior de la ventana. Sin embargo, el área de texto seguirá vacı́a. Nos ocuparemos de ello en el siguiente paso. 6. Cierre la aplicación ”EditorDeTexto.antes de continuar. 126 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.2. USO DE JBUILDER 7.0 Paso 9: Añadir código para leer texto de un archivo. Posteriormente, vamos a añadir código para leer el texto del archivo seleccionado por el usuario y ponerlo en el JTextArea. En primer lugar, habrá que añadir un nuevo método a la clase para realizar la operación de apertura del archivo. Este método se llamará openFile(): 1. Cambie el editor a TextEditFrame.java e introduzca el siguiente método openFile(). Puede ponerlo en cualquier lugar de la clase (fuera de otros métodos). Un buen lugar para ubicarlo es justo después del código del método jbInit() y justo antes del suceso jMenuFileExit actionPerformed(). // Abrir el archivo con nombre; lee el texto del archivo al jTextArea1; informar a la barra de estado. void openFile(String fileName) { try { // Abrir un archivo con nombre. File file = new File(fileName); // Obtener el tamaño del archivo abierto. int size = (int)file.length(); // Asignar cero a un contador para realizar un recuento de // los caracteres que se han leı́do del archivo. int chars read = 0; // Crear un lector de entrada basado en el archivo, para leer los datos. // FileReader gestiona las conversiones de código de caracteres internacionales. FileReader in = new FileReader(file); // Crea una matriz de caracteres del tamaño del archivo, // para utilizarla como búfer de datos, en el que leer // los datos del texto. char[] data = new char[size]; r GVA-ELAI-UPMPFC0074-03 127 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia // Leer todos los caracteres disponibles en el búfer. while(in.ready()) // Incrementar el recuento de cada carácter leı́do, // y acumularlos en el búfer de datos. chars read += in.read(data, chars read, size - chars read); } in.close(); // Crear una cadena temporal que contenga los datos, // y asignar la cadena a JTextArea. jTextArea1.setText(new String(data, 0, chars read)); // Muestra el nombre del directorio y archivos abiertos en la barra de estado. statusBar.setText(.Abierto ”+fileName); } catch (IOException e) { statusBar.setText(”Error al abrir ”+fileName); } 2. Añada la importación siguiente a la lista de importaciones de la parte superior del archivo: import java.io.*; 3. Haga clic en el manejador del suceso de Archivo—Abrir (jMenuItem2 actionPerformed(ActionEvent)) del panel de estructura para buscarlo rápidamente en el código fuente. 4. Reemplace el código fuente en el manejador del suceso de Archivo—Abrir if() que contenı́a previamente: // Muestra el nombre del directorio y archivos abiertos en la barra de estado. statusBar.setText(.Abierto ”+jFileChooser1.getSelectedFile().getPath()); // El código debe ir aquı́ para cargar realmente el texto // desde el archivo al JTextArea. con este nuevo método openFile(), empleando el nombre de directorio y archivo concatenados. // Llamar a openFile para intentar cargar el texto desde el archivo al 128 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.2. USO DE JBUILDER 7.0 JTextArea openFile(jFileChooser1.getSelectedFile().getPath()); //pinta el menú de nuevo una vez que el elemento se ha seleccionado this.repaint(); 5. Pruebe el programa ahora y vea si funciona. Guarde y ejecute el programa, y abra un archivo de texto en el editor. El editor de textos debe tener algún contenido. 6. Cierre la aplicación ”EditorDeTexto”. Paso 10: Adición de código a los elementos de menú para guardar un archivo Ahora se precisa un código que vuelva a grabar el archivo a disco cuando se seleccione Archivo—Guardar y Archivo—Guardar como. Para ello, es necesario añadir una variable de instancia String para almacenar el nombre del archivo abierto, además de añadir métodos para escribir de nuevo el texto en este y en otros archivos. 1. Haga clic en jFileChooser1 en el panel de estructura. Esto le llevará a la última entrada de la lista de declaraciones de variables de instancia (dado que jFileChooser1 fue la última declaración realizada). 2. Añada las siguientes declaraciones al final de la lista después de jFileChooser1: String currFileName = null; //Vı́a completa con nombre de archivo. null significa nuevo / sin tı́tulo. boolean dirty = false; // True significa texto modificado. 3. Haga clic en el método openFile(String fileName) del panel de estructura para buscarlo rápidamente en el código fuente. Sitúe el cursor en el método, a continuación de la lı́nea siguiente que lee el archivo en JTextArea: jTextArea1.setText(new String(data, 0, chars read)); 4. Inserte el siguiente código en esta posición: // Almacenar en caché el nombre de archivo abierto actualmente para r GVA-ELAI-UPMPFC0074-03 129 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia utilizarlo al guardar... this.currFileName = fileName; // ...y marcar la sesión de modificación como borrada this.dirty = false; 5. Cree un método saveFile() al que pueda llamar desde el manejador del suceso de Archivo—Guardar. Puede colocarlo justo después del método openFile(). Este método escribe el nombre de archivo en la barra de estado al guardar. // Guardar archivo actual; gestionar los que no tienen nombre de archivo; informar a la barra de estado. boolean saveFile() { //Gestionar donde aún no exista nombre de archivo. if (currFileName == null) { return saveAsFile(); } try { // Abrir el archivo del nombre actual. File file = new File (currFileName); // Crear un escritor de salida que escribirá ese archivo. // FileWriter gestiona las conversiones de códigos de caracteres internacionales. FileWriter out = new FileWriter(file); String text = jTextArea1.getText(); out.write(text); out.close(); this.dirty = false; // Muestra el nombre del directorio y archivos abiertos en la barra de estado. statusBar.setText(”Error al guardar ”+currFileName); return true; } catch(IOException e) { statusBar.setText(”Error al guardar ”+currFileName); } return false; } 130 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.2. USO DE JBUILDER 7.0 6. Cree el método saveAsFile() al que se llama desde saveFile() si no existe nombre de archivo actual. Lo utilizará también desde el menú Archivo—Guardar como. Añada el código siguiente justo a continuación del método saveFile(): // Guardar el archivo actual, preguntando al usuario el nuevo nombre de destino. // Informar a la barra de estado. boolean saveAsFile() { //Utilizar la versión SAVE del cuadro de diálogo, comprobar el valor devuelto de Aceptar/Cancelar if (JFileChooser.APPROVE OPTION == jFileChooser1.showSaveDialog(this)) { // Asignar al nombre de archivo actual la selección del usuario // a continuación realizar un saveFile normal currFileName = jFileChooser1.getSelectedFile().getPath(); //pinta el menú de nuevo una vez que el elemento se ha seleccionado this.repaint(); return saveFile(); } else { this.repaint(); return false; } } 7. Vuelva al diseñador y cree un manejador del suceso actionPerformed() para el elemento de menú Archivo—Guardar (jMenuItem3). Inserte el siguiente código: //Gestionar el elemento de menú Archivo—Guardar. saveFile(); 8. Cree un manejador de sucesos actionPerformed() para el elemento de menú Archivo—Guardar como (jMenuItem4) e introduzca este código: //Gestionar el elemento de menú Archivo—Guardar como. saveAsFile(); 9. Guarde y compile el archivo. Ejecútelo e intente guardar texto en un r GVA-ELAI-UPMPFC0074-03 131 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia archivo. 10. Cierre la aplicación ”EditorDeTexto”. Con estos pasos se tiene un conocimiento básico de cómo crear un GUI (graphical user interface) que es nuestro punto de base para construir nuestra aplicación DICOM. 6.3. Uso de librerı́as JDK 1.4.1 6.3.1. Introducción e instalación. Para poder realizar aplicaciones Java, se necesitan las librerı́as propias de Java, las cuales se pueden descargar directamente de la página web de sun. En nuestro caso se ha trabajado con la última versión actual JDK 1.4.1 que se ha podido descargar de http://java.sun.com/j2se/1.4.1/download.html. Una vez descargado se procede a la instalación, la cual es muy sencilla. Con esta descarga se consigue el archivo j2re-1 4 0 03-windows-i586-i.exe el cual con sólo ejecutarlo nos instala la colección de librerı́as de Java JDK 1.4.1 las cuales están listas para ser usadas. En nuestro proyecto de JBuilder bastará realizar un import del paquete o clase querida para poder trabajar con sus clases o métodos, como ya se entiende que se sabe. Para poder trabajar con JDK es muy importante poder continuamente ver el conjunto de paquetes y de clases (API )para poder buscar y encontrar lo necesitado en cada momento. Esto se ha conseguido con tener constantemente contacto con la página web de sun que muestra esta API. La figura 6.14 muestra la organización de esta API de Java: 132 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.3. USO DE LIBRERÍAS JDK 1.4.1 Figura 6.14: API de JDK 1.4.1. También debido a la gran amplitud de paquetes, clases y métodos es muy importante contar con la posibilidad de un grupo de noticias en el cual poder mostrar nuestras dudas, sugerencias, ver preguntas frecuentes e intentar consultar nuestros problemas. Se ha contado con el grupo news.upm.es para dichas consultas. Esta API es altamente extensa por lo que para usar lo que uno necesita el mayor problema es encontrarlo, por lo que, como hemos dicho es muy importante tener un buen servicio de consulta. 6.3.2. Estructura de JDK 1.4.1. Los paquetes usados de esta API para nuestra aplicación son unos pocos, por lo que nos vamos a centrar en comentar someramente sólo éstos debido a lo expuesto anteriormente referente a la amplitud. Se señalan también clases e interfaces importantes de cada paquete: r GVA-ELAI-UPMPFC0074-03 133 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia java.awt Contiene todas las clases para la creación de interfaces de usuario y para la representación gráfica de imágenes. Hay objetos de interfaz de usuario como un botón o un scrollbar, en la terminologı́a awt, un componente. La clase Component es la raı́z de todos los componentes awt. Ver la clase Component para una descripción detallada de las propiedades las que todos los componentes awt comparten. Algunos componentes lanzan eventos cuando un usuario interactúa sobre los componentes. La clase AWTEvent y sus subclases se usan para representar los acontecimientos los que awt componentes pueden lanzar. Ver AWTEvent para una descripción del modelo de eventos de awt. Un container es un componente que puede contener componentes y otros contenedores. Una container también puede tener a un gerente de disposición que controla la colocación visual de componentes en el contenedor. El paquete de programas de awt contiene a varios gerentes de disposición clases y un interfaz para construir su propio gerente de disposición. Ver las clases Container y LayoutManager para más información. Clases importantes: AWTEvent: La clase de eventos raı́z para todos los eventos AWT. BorderLayout: Un BorderLayout pone de un contenedor, arreglando y redimensionando su componente para tener cinco regiones: norte, sur, este, oeste, y centro. Component: Un componente es un objeto que tiene una representación gráfica que puede ser mostrada sobre la pantalla y que puede interactuar con el usuario. Container : Un Abstract Window Toolkit(AWT) el objeto de contenedor es un componente que puede contener otros componentes AWT. Graphics: La clase Graphics es la clase abstracta base para todos los contextos de gráficos que permiten a una aplicación dibujar sobre los componentes que están comprendidos sobre varios dispositivos, ası́ como sobre imágenes fuera de pantalla. GridBagLayout:La clase GridBagLayout es un gerente de disposición flexible que alinea componentes verticalmente y horizontalmente, sin requerir que que los componentes fueran del mismo tamaño. 134 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.3. USO DE LIBRERÍAS JDK 1.4.1 Image: La clase abstracta la Imagen es la superclase de todas las clases que representan imágenes gráficas. MediaTracker : La clase MediaTracker es una clase de utilidad para rastrear el estado de un número de objetos media. ... java.awt.event Proporciona interfaces y clases para ocuparse de los diferentes tipos de eventos lanzados por los componentes awt. Ver la clase java.awt.event para más detalles sobre el modelo de acontecimiento awt. Los acontecimientos se lanzan por fuentes de eventos. Un oyente de eventos (event listener) se registra con una fuente de evento para recibir notificaciones sobre los eventos de un tipo particular. Este paquete de programas define eventos y oyentes de eventos, ası́ como adaptadores de oyente de acontecimiento, que son clases que convienen para hacer más fácil el proceso de escribir a oyentes de acontecimiento. ActionEvent: Un evento que indica que ocurrió una acción definida de un componente. ComponentAdapter : Una clase de adaptador abstracta para recibir eventos de componentes. ComponentEvent: Un acontecimiento de bajo nivel que indica que un componente se ha movido, el tamaño cambiado o la visibilidad cambiada (también es la clase de raı́z para otros acontecimientos de nivel componente). MouseEvent: Un evento que indica que una acción de ratón ocurrió en un componente. Ver también MouseMotionAdapter y MouseWheelEvent. Interfaz ActionListener : El interfaz de oyente (listener interface) para recibir eventos de acción. ... java.awt.image Proporciona clases para la creación y la modificación de imágenes. Las imágenes son procesadas usando un marco que corre que implica a una image producer, filtros de imagen opcionales y una image consumer. Este marco hace posible para progresivamente dar una imagen mientras este se r GVA-ELAI-UPMPFC0074-03 135 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia trae y se genera. Además, el marco permite un uso desechar el almacenaje usado por una imagen y regenerarlo en cualquier momento. Este paquete proporciona a un número de image producers, consumers, y filtros que se pueden configurar para nuestras necesidades de procesamiento de imágenes. Clases importantes de este paquete son: BufferedImage: La subclase BufferedImage describe una Imagen con un bufer accesible de datos de imagen. ColorModel : La clase abstracta de ColorModel encapsula los métodos para traducir un valor de pixel a un componente de color (por ejemplo, rojo, verde, y azul) y un componente alfa. PixelGrabber : La clase PixelGrabber implementa un ImageConsumer que puede ser atado a una Imagen o a un objeto ImageProducer para coger los datos de los pı́xeles de esa imagen. ... java.io Proporciona la entrada y salida del sistema a través de corrientes de datos, serialización y el archivo de sistema. Si se pasa un argumento nulo a un constructor o a algún método de cualquier clase o interfaz en este paquete hará que un NullPointerException se lance. Clases importantes: BufferedInputStream: Un BufferedInputStream agrega la funcionalidad a otra entrada corriente conocida, la capacidad de bufer la entrada y apoyar la señal y poner resetear métodos. BufferedOutputStream: La clase implementa un bufferedOutputStream. FileInputStream: coge bytes de entrada desde un archivo y lo pasa a un archivo de sistema. FileOutputStream: es una corriente de salida para la escritura de datos a un File o a un FileDescriptor. ... 136 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.3. USO DE LIBRERÍAS JDK 1.4.1 java.lang.Object Proporciona las clases que son fundamentales para el diseño del lenguaje de programación Java. Las clases más importantes son el Object, que es la raı́z de la jerarquı́a de clase, y la Class. Con frecuencia es necesario representar un valor de tipo primitivo como si esto fuera un objeto. Las clases Boolean, Character, Integer, Long, Float, y Double sirven para este objetivo. Un objeto tipo Doble, por ejemplo, contiene un campo cuyo tipo es doble, representando aquel valor de tal modo que una referencia a ello puede ser almacenada en una variable de tipo de referencia. Estas clases también proporcionan unos métodos para la conversión entre valores primitivos, ası́ como métodos tan estándar como equals y hashCode. La clase Void es una clase no instanciable que sostiene una referencia a un objeto Clase que representa el tipo primitivo void. La clase Math proporciona comúnmente funciones matemáticas como el seno, el coseno, y la raı́z cuadrada. Las clases String y StringBuffer proporcionan de modo similar operaciones comúnmente usadas sobre cadenas de caracteres. Las clases ClassLoader, Process, Runtime, SecurityManager, y System provorcionan .operaciones de sistema”que manejan la carga dinámica de clases, crean procesos externos, reciben preguntas como la hora del dı́a, y la ejecutan una polı́tica de seguridad. La clase Throwable abarca los objetos que pueden ser lanzados por la declaración de lanzamiento. Las subclases de Throwable representan errores y excepciones. Clases importantes: Boolean, Byte, Character, Class, Integer, Long, Math, Double, Float, Process, Runtime, String ... javax.swing Proporciona un juego fácil de usar de componentes (para GUIs) que, al máximo grado posible, trabajan sobre todo tipo de plataformas. Clases importantes: JButton, JComboBox, JFileChooser, JFrame, JLabel, JPanel, JScrollPane, ... r GVA-ELAI-UPMPFC0074-03 137 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia 6.3.3. Configuración de JDK en JBuilder. Por defecto al estar en un proyecto de JBuilder se usa (en esta versión 7) la API 1.3 de JDK. En el caso de que queramos usar otra versión por la causa que sea (en nuestro caso queremos cambiar a JDK 1.4.1 para poder usar unas determinadas funciones)se debe proceder de la siguiente manera: 1. Herramientas 2. Configurar JDK... Figura 6.15: Configurar JDK. 3. En la pantalla siguiente se debe mostrar el camino de la nueva API mediante el botón Cambiar. 138 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.4. INSTALACIÓN DE JDT EN JBUILDER. Figura 6.16: Nuevo camino de JDK. De esta forma cambiarı́amos la API para poder trabajar con las nuevas actualizaciones de ésta. De todas formas a nuestro nivel es casi inútil trabajar con la versión última y nos podrı́amos conformar con versiones anteriores sin mermar nuestras posibilidades. 6.4. Instalación de JDT en JBuilder. Debido a que se necesita trabajar con estas librerı́as de DICOM, es necesario indicarle al programa cómo usarlas, es decir, instalarlas. Este proceso, que serı́a equivalente para cualquier tipo de librerı́as, consta de las siguientes partes: 1. En la barra de herramientas seleccionar Proyecto. 2. Propiedades de proyecto. r GVA-ELAI-UPMPFC0074-03 139 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia Figura 6.17: Propiedades de proyecto. 3. En la pantalla siguiente en la pestaña Vias de acceso y dentro de ésta en la pestaña Bibliotecas necesarias y pulsando el botón Añadir y en Nuevo y mediante el asistente de bibliotecas se añade las nuevas bibliotecas: 140 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.4. INSTALACIÓN DE JDT EN JBUILDER. Figura 6.18: Propiedades de proyecto. Añadir. Seleccionar camino: en nuestro caso D:/classpath/jdt.jar y D:/classpath/jdt.key como podemos ver en la figura. r GVA-ELAI-UPMPFC0074-03 141 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia Figura 6.19: Propiedades de proyecto. 4. Hecho ésto en nuestro código podremos ya incluir nuestros nuevos paquetes como por ejemplo: import com.archimed.dicom.*; y una vez hecho ésto podremos definir nuevos objectos de estas nuevas clases de DICOM como por ejemplo: private SOPClass sopClass = new SOPClass(); Por lo que ya tenemos instaladas nuestras librerı́as JDT y podemos empezar a trabajar con ellas. 6.5. Implementación de nuestra aplicación. 6.5.1. Introducción. Una vez instalados y comprendidos todos nuestros recursos para desarrollar la aplicación DICOM, se comienza a implementarla. 142 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. 6.5.2. Estructura de la GUI. Lo primero, y a través de los componentes de javax.swing es construir un marco que concuerde con las expectativas. En nuestro caso se ha dispuesto varias pestañas que albergan paneles mediante las clases JTabbedPane y JPanel para que quede una estructura de interfaz como la representada en la figura 6.20. Figura 6.20: Estructura básica de la aplicación. Y de esta forma poder poner en cada panel lo deseado. En nuestro caso se ha dispuesto de los paneles que aparecen en la figura: panelProcesamiento, panelVisorDicom, panelCrearDicom y panelCliente-Servidor (también se han incluido dos paneles para posibles funciones posteriores). Vamos a ver para que sirven cada uno de estos paneles y una pequeña muestra de cómo han sido implementados. r GVA-ELAI-UPMPFC0074-03 143 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia 6.5.3. Panel VisorDicom. Este panel sirve para poder visualizar todo tipo de archivos DICOM: archivos comprimidos, no comprimidos, en color, en escala de grises y de una o varias imágenes, también es capaz de insertar datos de texto en los campos ya existentes de un archivo DICOM y también de crear nuevos campos e insertar datos en ellos. Se ha conseguido a través de la implementación de una clase llamada Imagedos en el proyecto de JBuilder, la cual nos va a permitir en última instancia ser capaces de visualizar la imagen de los archivos DICOM. Se recomienda ver el código fuente de esta clase. Figura 6.21: Panel Visor DICOM. Visualizar datos: texto e imágenes. Esta clase tiene un constructor de la forma public Imagedos(DicomObject dcm) al cual vamos a llamar y a pasarle un objeto de la clase DicomObject. Este parámetro que se pasa es un archivo DICOM sacado de archivos .dcm (formato DICOM) los cuales se han conseguido de páginas web que 144 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. dejan bajar ejemplos de archivos DICOM. La forma de pasar de un archivo .dcm a una instancia de la clase DicomImage (subclase de DicomObject, ver API de JDT) se consigue mediante este código, implementado en nuestra clase principal MarcoCuatro.java en el método open(): ....... if (JFileChooser.APPROVE OPTION == jFileChooser1.showOpenDialog(this)) openFileName = jFileChooser1.getSelectedFile().getAbsolutePath(); f = new File(openFileName); fin = new FileInputStream(f ); bis = new BufferedInputStream(fin); dcm.read(bis, true); ....... en donde con la primera lı́nea de código conseguimos que se abra un selector de archivos, donde elegimos el archivo DICOM deseado (ver clase JFileChooser en la API de JAVA). Con la lı́nea openFileName = jFileChooser1.getSelectedFile().getAbsolutePath(); conseguimos guardar el camino completo del archivo, el cual necesitamos para crear un objeto de la clase File, depués uno de la clase FileInputStream y finalmente uno de la clase BufferedInputStream para poder usar el método read de la clase DicomObject, el cual lee desde el BufferedInputStream el archivo DICOM. De esta forma tenemos en dcm (DicomImage dcm = new DicomImage();) nuestro archivo DICOM .dcm. Para sacar los datos de texto de este archivo DICOM usamos: ReSystemOut systemArea = new ReSystemOut(areaTextoDatosDicom); dcm.dumpVRs(systemArea,true); donde la clase ReSystemOut es una implementación realizada para poder mostrar estos datos en un objeto de la clase JTextArea, que es lo que nos interesa para poder visualizar estos datos en nuestro GUI (en este caso en la instancia areaTextoDatosDicom de JTextArea: ver código de la clase ReSystemOut). El paso siguiente es coger los datos reunidos en este objecto. Para r GVA-ELAI-UPMPFC0074-03 145 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia ello necesitamos la clase Imagedos. Llamamos a su constructor public Imagedos(DicomObject dcm) con lo que tenemos un objeto de esta clase: imagedcm = new Imagedos(dcm); Mediante el método getBufferedImage(int numeroDeImagen) podemos sacar las imágenes y poderlas visualizar de esta forma: imagen = imagedcm.getBufferedImage(numeroImagen); etiquetaMostrarImagen.setIcon(new ImageIcon(imagen)); donde etiquetaMostrarImagen es un objeto de la clase JLabel la cual mediante el método setIcon(ImageIcon a) puede mostrar nuestras imágenes. La instancia de la clase ImageIcon se crea como se ve arriba mediante su constructor (ver clase en API). Hacer zoom in o zoom out. Una vez visualizada la imagen, se ha dotado de la posibilidad de hacer zoom de dos formas diferentes: 1. Botones Zoom In y Zoom Out: Figura 6.22: Zoom mediante botones Zoom In y Zoom Out. La implementación básica es (para el caso de Zoom In): int altura = zoomImagen.getHeight(null); int anchura = zoomImagen.getWidth(null); zoomImagen = imagen.getScaledInstance(anchura*2,altura*2,imagen.SCALE DEFAULT); etiquetaMostrarImagen.setIcon(new ImageIcon(zoomImagen)); donde con getHeight y getWidth (métodos de la clase Image) sacamos la altura y anchura de la imagen zoomImagen. Y mediante el método 146 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. getScaledInstance hacemos una imagen a escala de la anterior, para lo que necesitamos nuevos datos de alto/ancho, que después con el método antes visto setIcon podemos visualizamos en el mismo objeto JLabel, etiquetaMostrarImagen. 2. Mediante eventos de ratón: Figura 6.23: Zoom mediante cliqueos de ratón. En este caso se ha conseguido que mediante dos cliqueos con el ratón sobre la imagen se haga zoom IN de la zona selecionada. Se hace, creando para la JLabel deseada (etiquetaMostrarImagen), un código que sea capaz de recoger un evento, en este caso el chasquido del ratón. La implementación es: etiquetaMostrarImagen.addMouseListener(new MouseAdapter() r GVA-ELAI-UPMPFC0074-03 147 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia { public void mousePressed(MouseEvent e) { try if(x1==-1&y1==-1) { x1 = e.getX(); y1 = e.getY(); } else if(x2==-1&y2==-1) { x2 = e.getX(); int ancho = Math.abs(x2) - Math.abs(x1); int alto = y2 - y1; ancho = Math.abs(ancho); alto = Math.abs(alto); zoomBufferedImagen = ImageToBufferedImage.toBufferedImage(zoomImagen); if((x1>x2)&(y1>y2)) { zonaBImagen= zoomBufferedImagen.getSubimage(x2,y2,ancho,alto); } if((x1<x2)&(y1>y2)) { zonaBImagen= zoomBufferedImagen.getSubimage(x1,y2,ancho,alto); } if((x1<x2)&(y1>y2)) { zonaBImagen= zoomBufferedImagen.getSubimage(x1,y1,ancho,alto); } if((x1>x2)&(y1<y2)) { zonaBImagen= zoomBufferedImagen.getSubimage(x2,y1,ancho,alto); } Image zonaZoomImage = zonaBImagen.getScaledInstance(4*ancho,4*alto,Image.SCALE DEFAULT); Marco PantallaCompleta marcoZonaZoom = new Marco PantallaCompleta(zonaZoomImage); Dimension dlgSize = marcoZonaZoom.getPreferredSize(); Dimension frmSize = getSize(); Point loc = getLocation(); marcoZonaZoom.setLocation((frmSize.width - dlgSize.width) / 2 + 148 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. loc.x, (frmSize.height - dlgSize.height) / 2 + loc.y); marcoZonaZoom.setModal(true); marcoZonaZoom.pack(); } } }); donde se crea un listener de evento MouseEvent el cual nos permite saber la posición del ratón en el momento que éste es pulsado, por lo que podemos sacar la zona de interés (mediante los métodos getX() y getY() ) y donde Marco PantallaCompleta es una clase que permite visualizar imágenes en un panel aparte. Se sugiere ver código de esta última clase. También, si se quisiese detectar, por ejemplo el movimiento del ratón sobre un componente o el rotar de la rueda central del ratón se pueden usar clases del mismo paquete MouseAdapter, MouseEvent, MouseMotionAdapter o/y MouseWheelEvent para tener efectos homólogos a lo anteriormente visto. Visualizar todas las imágenes. Ya se ha comentado la forma de conseguir visualizar los datos de la imagen de los archivos DICOM, pero también sabemos que un archivo DICOM puede contener más de una imagen, por lo que debemos ser capaces de visualizarlas todas y cada una de ellas. Al abrir un archivo DICOM se muestra en pantalla (en una instancia de la clase JLabel) la primera imagen y en el caso de que haya más de una imagen en dicho archivo, los botones Siguiente, Anterior y Seguidas realizarán las funciones siguientes. 1.- Botones Siguiente y Anterior : Figura 6.24: Botones Anterior y Siguiente r GVA-ELAI-UPMPFC0074-03 149 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia Estos dos botones permiten ver, si existe, la imagen siguiente o la anterior como indica su nombre. La implementación es muy sencilla. Primero, se ve si hay más de una imagen mediante la captura del dato del objeto DICOM dcm.getI(DDict.dNumberOfFrames) (ver página 66) y si existe, se muestra en pantalla. La parte importante de esta implementación es: void botonImagenSiguiente actionPerformed(ActionEvent e) { ..... ..... numeroImagen++; imagen=imagedcm.getBufferedImage(numeroImagen); etiquetaMostrarImagen.setIcon(new ImageIcon(imagen)); ..... ..... } Donde imagedcm es un objeto de la clase Imagedos que puede coger la imagen de número seleccionada mediante el int numeroImagen (Ver implementación de Imagedos.java). Se visualiza como antes. 2.- Botón Seguidas: Figura 6.25: Botón Seguidas Se basa en lo anterior, pero se consigue que las imágenes pasen una detrás de otra (formando una especie de pelı́cula por supuesto en el caso de que haya más de una). La pelı́cula se para si se pulsa con el ratón sobre el panel donde aparece esta secuencia. Se va a ver una parte importante de la implementación: 150 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. void botonImagenesSeguidas actionPerformed(ActionEvent e) { ..... ..... Image [] secuencia = new Image[dcm.getI(DDict.dNumberOfFrames)]; for(int n=0;n¡dcm.getI(DDict.dNumberOfFrames);n++) {); secuencia[n]=imagedcm.getBufferedImage(n); System.out.println(n); } cargado = true; ImageSequenceTimer controller = new ImageSequenceTimer(); controller.secuencia(secuencia,dcm.getI(DDict.dNumberOfFrames)); ..... ..... } donde, como se puede ver, se cargan todas las imágenes en un array de Images y más tarde se crea una instancia de la clase ImageSequenceTimer con la cual se crea el panel donde se va a visualizar la secuencia. Ver implementación de la clase ImageSequenceTimer. Guardar imágenes como JPEG. Figura 6.26: Botón Guardar JPEG Se puede guardar la imagen visualizada en formato JPG mediante las posibilidades de JDK 1.4.1. Esta función sólo se encuentra en esta versión de la API JAVA, por lo que aquı́ vemos el porqué del uso de esta versión. Para implementar esto necesitamos importar el paquete: r GVA-ELAI-UPMPFC0074-03 151 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia import javax.imageio.*; debido a que en éste se encuentra lo buscado, que es el método de la clase javax.imageio.ImageIO que nos permite guardar los datos de una instancia de la clase Image en un archivo de formato JPEG. La implementación esencial es: void botonGuardarIamgenComoJpg actionPerformed(ActionEvent e) { ..... ..... if (JFileChooser.APPROVE OPTION == jFileChooser1.showSaveDialog(this)) { String salvarJPG = jFileChooser1.getSelectedFile().getAbsolutePath(); FileOutputStream salvar = new FileOutputStream(salvarJPG); File save = new File(salvarJPG); BufferedOutputStream salida = new BufferedOutputStream(salvar); javax.imageio.ImageIO.write(imagen,”JPEG”,save); } ..... ..... } donde la lı́nea importante es javax.imageio.ImageIO.write(imagen,”JPEG”,save); que realiza la función deseada. Meter y modificar datos de texto en el archivo DICOM. En un archivo DICOM, como sabemos tenemos datos de imagen y también de texto, es decir el nombre del paciente, que aparato ha realizado la captura de la imagen ... La función, que se ha hecho para esta GUI y para este panel, es la capacidad de poder añadir datos de texto al archivo visualizado. Esto se puede hacer de dos formas diferentes. Se puede insertar datos en campos ya existentes, como el nombre del paciente, o se puede crear nuestro propio campo como por ejemplo número de moléculas infectadas. Del primer tipo se han dispuesto una serie de campos, los cuales pueden 152 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. ser válidos o no. En este aspecto deben ser los profesionales de la medicina los que deben suministar información para saber los datos que quieren insertar para poder adaptarnos a ellos. Ya se vió de que manera se puede visualizar todos los datos del archivo DICOM (ver páginas 71 y 145 en seciones 4.2.2 y 6.5.3 respectivamente); aquı́ vamos a cambiar o entrar nuevos datos en determinados campos y podremos visualizar si estas modificaciones se llegan a producir. Para ésto se han dispuesto dos instancias de la clases JComboBox, dos botones de la clase JButton para insertar y para ver lo insertado y de dos objetos JTextArea siendo éstas las zonas de visualización. Todo esto se puede ver en la figura 6.27 Figura 6.27: Zona de insertar datos. La forma de implementar esto es creando dos instancias de la clase JComboBox con los campos que tengan que aparecer según lo comentado más arriba: private JComboBox jComboBox1 = new JComboBox(opcionesCombobox); siendo en este caso opcionesCombobox : String[] opcionesCombobox = {”dAdditionalPatientHistory” ,”dBeamName”,”dBitsAllocated”,”dBodyPartExamined”,”dContrastAllergies” ,”dHistogramData”,”dImageComments”,”dInterventionDrugName”,”dModality” ,”dPatientName”,”dPixelData”,”dROIArea”,”dSeriesNumber”,”dSmokingStatus” ,”dNumberOfFrames”}; r GVA-ELAI-UPMPFC0074-03 153 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia por lo que al pinchar sobre la flecha del combobox, aparecen todos estos campos. Ahora hay que interconectarlos para que al seleccionar uno y escribir sobre las JTextArea se inserte y visualicen los datos. Esto se consigue, a grandes rasgos, mediante esta codificación: Para insertar: void botonInsertarDato actionPerformed(ActionEvent e) { try { String opcion = (String)jComboBox1.getSelectedItem(); String dato = areaTextoInsertarDato.getText(); if(opcion==”dPatientName”) dcm.set(DDict.dPatientName, new Person(dato)); ..... donde new Person(dato) es el dato del nombre del paciente que se inserta en el campo del archivo DICOM DDict.dPatientName. Hecho esto, el dato estarı́a en el objeto DicomImage pero no todavı́a en el archivo .dcm, para lo que se implementa estas otras lı́neas de código: FileOutputStream save = new FileOutputStream(openFileName); dcm.write(save,true); siendo write un método de la clase DicomObject que lo que hace es escribir a través de un FileOutputStream todos los datos de la instancia DicomImage en un archivo .dcm que, si no existe, crea uno. Para ver lo insertado: void botonVerDato actionPerformed(ActionEvent e) { String opcion2 = (String)jComboBox2.getSelectedItem(); if(opcion2==”dPatientName”) { String cadena = dcm.getS(DDict.dPatientName); areaTextoVerDato.setText(dcm.getS(DDict.dPatientName)); ..... Para ver que el funcionamiento de lo anterior es correcto se puede volver 154 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. a abrir el archivo DICOM y se podrá observar que el nombre del paciente ha cambiado o aparece cosa que antes no harı́a. La figura siguiente pretende servir de ejemplo de lo descrito. Figura 6.28: Ver funcionamiento. Cómo añadir campos nuevos al archivo DICOM La otra posibilidad, como se ha comentado, es la de insertar nuestros propios campos. Se ha visto cómo insertar datos en los campos del archivo DICOM existentes ya, como el sexo del paciente, nombre del fabricante, ID del paciente, etc, pero es muy importante ser capaces de crear nuestros propios campos, como por ejemplo el número de virus, diagnóstico del médico, y en definitiva, los que se crean convenientes. Para ello se han dispuesto en la GUI ciertos botones (figura 6.29) que son capaces hacer este servicio. r GVA-ELAI-UPMPFC0074-03 155 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia Se tiene la posibilidad de crear campos donde se albergan datos de tipo Integer o datos de tipo String, es decir, campos para datos numéricos y campos para datos de texto. Figura 6.29: Botones para campos nuevos. El código básicamente es: void jButton6 actionPerformed(ActionEvent e) { ....... DDictEntry entrarDDict1 = new DDictEntry(new Integer(campoGrupoNumero.getText()) .intValue(), new Integer (campoElementoNumero.getText()).intValue(), DDict.tUS,campoNuevoNumero.getText(), ”1”); diccionario.addEntry(entrarDDict1); dcm.set ge(new Integer(campoGrupoNumero.getText()).intValue(), new Integer(campoElementoNumero.getText()).intValue(),new Integer(numero.getText())); ....... } Como se ve es la implementación que se ejecuta en el momento en que el 156 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. botón en cuestión es pulsado. Lo primero que se hace es crear una instancia de la clase DDictEntry. Al pasar los parámetros vistos fijamos el número de grupo, el número de elemento, la descripción del campo y el tipo de dato DICOM que se puede meter en éste campo. Ver clases DDictEntry y DDict en la API de JDT la clase DDictEntry para más detalles. Una vez hecho esto, creamos un objeto de la clase DDict, en la cual vamos a insertar nuestro nuevo campo por medio del método addEntry(DDictEntry a). En este momento tenemos la posibilidad de poder meter en el objeto de la clase DicomObject (dcm) el nuevo dato mediante el método set ge(grupo,elemento,dato), que en este caso debe ser un Integer ya que el tipo US DICOM es tipo Integer en Java (ver conversiones DICOM/JAVA, JAVA/DICOM tablas en páginas 67 y 68). Se puede comprobar que el nuevo campo se inserta bien: Figura 6.30: Comprobar inserción de nuevo campo. r GVA-ELAI-UPMPFC0074-03 157 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia Para el caso de insertar un campo que alberge un dato de tipo String se ha procedido de la misma forma: void jButton5 actionPerformed(ActionEvent e) { ....... DDictEntry entrarDDict2 = new DDictEntry(new Integer(campoGrupoTexto.getText()) .intValue(), new Integer (campoElementoTexto.getText()).intValue(), DDict.tST,campoNuevoTexto.getText(), ”1”); diccionario.addEntry(entrarDDict2); dcm.set ge(new Integer(campoGrupoTexto.getText()).intValue(), new Integer(campoElementoTexto.getText()).intValue(),new Integer(numero.getText())); ....... } donde lo único que cambia es el tipo de dato que vamos a insertar, por lo que el tercer parámetro que se pasa al constructor de la clase DDictEntry es DDict.tST, que es lo que indica esto. 6.5.4. Panel Crear DICOM. La función de este panel es la de poder crear un archivo DICOM a partir de imágenes de formato jpeg y por supuesto datos de texto. Los datos de texto como el nombre del paciente, el aparato que hace la imagen, la parte del cuerpo examinada, etc, se insertan de la misma forma que la vista en la sección 6.5.3 y (página 154). Consultar también la sección 4.2.2. La parte nueva con respecto al panel de visualización de archivos DICOM (páginas 144 hasta 155) es la zona de meter los datos de imágenes nuestras en formato jpeg dentro del nuevo archivo DICOM. Los campos de texto a añadir en el archivo DICOM a crear pueden ser cambiados, como ya se ha visto. Estructura del panel Crear DICOM. Esta estructura la podemos ver en la figura siguiente: 158 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. Figura 6.31: Panel Crear archivo DICOM. Este panel consta de varias partes: 1. Zona de visualización de imágenes a insertar (figura 6.32). r GVA-ELAI-UPMPFC0074-03 159 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia Figura 6.32: Imagen a insertar. 2. Zona de inserción de datos de texto (figura 6.33). 160 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. Figura 6.33: Datos de texto a insertar. Inserción de una sóla imagen. En esta parte vamos a ver la forma de cómo insertar los datos de una sóla imagen. Lo primero que se hace es visualizar la imagen a insertar en el área destinada mediante el botón Abrir imagen y después mediante los botones Monochrome y RGB metemos la imagen según sea en escala de grises o en color respectivamente. Al abrir la imagen se graba la imagen en una instancia de la clase Image r GVA-ELAI-UPMPFC0074-03 161 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia (Image imagenJPG;) con la que se trabaja de esta manera: 1. Botón Monochrome: La implementación de la función de este botón es de la forma: void botonCrearDicomGris actionPerformed(ActionEvent e) { ....... String rutaArchivoDicomGuardar = jFileChooser1.getSelectedFile().getAbsolutePath(); int[] pix = image2IntArray(imagenJPG); dcmNueva.set(DDict.dSOPClassUID,”1.2.840.10008.5.1.4.1.1.7”); dcmNueva.set(DDict.dSOPInstanceUID,”1.2.840.10008.5.1.4.1.1.7”); dcmNueva.imagePixelData(imagenJPG.getHeight(null),imagenJPG.getWidth(null), 8,8,7,pix); dcmNueva.write(salvar,true); ....... } donde image2IntArray es un método que mediante la clase PixelGrabber (ver API JDK) es capaz de coger los datos de la imagen pixel a pixel, por lo que el resultado es un array de int donde se encuentran estos datos. Las dos lı́neas siguientes se colocan debido a que son datos que se deben encontrar obligatoriamente en un archivo DICOM, ya que sin ellos no es posible crear el archivo. Más tarde, en nuestra instancia de la clase DicomImage (DicomImage dcmNueva = new DicomImage();) se insertan estos datos de imagen mediante el método de esta clase imagePixelData(int filas, int columnas, int planarConf, int[] pixelData) (ver API JDT). También se han probado los métodos vistos en la sección 4.2.4 donde, por lo comprobado, sólo funcionan los métodos de DicomObject y DicomImage y no ası́ los métodos mediante ImageProducer. Una vez hecho ésto se puede comprobar que los resultados son satisfactorios al ir al panel VisorDicom y tratar de abrir el archivo creado, donde podemos ver los resultados siguientes: 162 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. Figura 6.34: Comprobar la creación del archivo DICOM en escala de grises. Vemos que la imagen se ha grabado junto con los datos metidos, en este caso el nombre del paciente, los datos de la imagen y los datos SOP (”Service Object Pair”). 1. Botón RGB : Para el caso de la imagen en color se realiza de la misma forma; la diferencia es que se usa otra implemtentación del método imagePixelData ya que es un método sobrecargado y se utiliza debido al cambio de parámetros que se pasan a éste: imagePixelData(int rows, int cols, int planarConf, int[] pixelData); por lo que queda de la siguiente manera: dcmNueva.imagePixelData(imagenJPG.getHeight(null),imagenJPG.getWidth(null), 0,pix); Los resultados se pueden comprobar igualmente visualizando el archivo r GVA-ELAI-UPMPFC0074-03 163 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia creado: Figura 6.35: Comprobar la creación del archivo DICOM en color. Compresión de archivos DICOM (Monochrome). Se ha visto cómo crear un archivo DICOM de escala de grises, pero al hacer ésto se tiene un problema: el tamaño de los archivos es muy grande debido a que no se ha realizado ningún tipo de compresión a los datos de los pı́xeles, por lo que el espacio que ocupan es bastante grande. Para ello se ha recurrido a la clase de la API de JDT llamada Compression, la cual proporcina métodos para realizar compresiones de estos datos. Para realizar la compresión, como vimos en la sección 4.2.4, página 78, se actúa de la siguiente forma: void botonComprir actionPerformed(ActionEvent e) 164 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. { ....... Compression c = new Compression(dcmNueva); c.compress(TransferSyntax.JPEGLossless); ....... } se crea una instancia de la clase Compression (c) que después usa su método compress, el cual necesita saber el tipo de compresión a usar. En este momento se tiene que decir que el único tipo de compresión que realiza este servicio es el señalado arriba, TransferSyntax.JPEGLossless, mientras que con los otros tipos no consiguen lo buscado con la limitación de que funciona sólo con imágenes monochrome. La diferencia de tamaño de un archivo comprimido a uno sin comprimir es: No comprimido: tamaño = X Comprimido: tamaño = X / 2 Tamaño archivo sin comprimir x 2 = Tamaño archivo comprimido (aproximadamente.) Inserción de varias imágenes. Para este caso se ha introducido en un array de Image todas las imágenes que se quieren insertar en el archivo DICOM. Una vez insertadas, se procede a sacar los datos de pı́xeles de estas instancias Image. En este caso se van a guardar estos datos en una matriz de bytes en vez de array de int. Teniendo la matriz de bytes (byte[][]), se continúa uniendo todos estos bytes en un sólo array de bytes (byte[]) donde van uno detrás de otro. Una vez hecho ésto se procederá a insertar estos datos en el archivo DICOM a crear. Para todo esto se usan diferentes botones que realizan las diferentes funciones vistas antes: 1. Botón No de imágenes: donde se señala el número de imágenes que vamos a insertar. En el código se da valor a una variable que es r GVA-ELAI-UPMPFC0074-03 165 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia utilizada para saber el número de instancias Image que se deben guardar en el array Image[]. La implementación es: void iniciarArrayImages actionPerformed(ActionEvent e) { imagenSecuencia = new Image [new ger(datoPantalla.getText()).intValue()]; } Inte- 2. Botón Abrir imágenes a insertar : mediante este botón se van almacenando el array las imágenes, hasta que se almacena la última, y es en este momento cuando aparece un nuevo cuadro que te pregunta por el lugar y el nombre en donde insertar este arhivo DICOM. La implementación básica es: void botonAbrirImagen actionPerformed(ActionEvent e) { ........ imagenSecuencia[numeroDeImagen] = imagenJPG; numeroDeImagen++; int imagenesTotales = new Integer(datoPantalla.getText()).intValue(); if(numeroDeImagen == imagenesTotales) { byte[][] secuenciaBytes = new byte[imagenesTotales][]; int n,nBytes=0; for(n=0;n<imagenesTotales;n++) { byte[] ver = image2ByteArray(imagenSecuencia[n]); secuenciaBytes[n]=image2ByteArray(imagenSecuencia[n]); } arrayDeArrayDeBytes=secuenciaBytes; byte[] todoJunto = unirBytesArray(secuenciaBytes,imagenesTotales); todosBytesJuntos = todoJunto; if (JFileChooser.APPROVE OPTION == jFileChooser1.showSaveDialog(this)) { try { String rutaArchivoDicomGuardar = jFileChoos166 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. er1.getSelectedFile().getAbsolutePath(); ........ dcmNueva.imagePixelData (imagenJPG.getHeight(null), JPG.getWidth(null), 8,8,7,todosBytesJuntos); ........ dcmNueva.write(salvar,true); ........ } } } imagen- como se ve en este código, una vez recogidas todas las instancias Image en el array, se pasa a sacar de cada objeto Image sus datos de pı́xeles y se reunen en una matriz de bytes llamada arrayDeArrayDeBytes. Una vez hecho esto, se unen esos array de bytes en uno sólo mediante el método unirBytesArray(byte[][] secuencia, int numeroDeImagenes), el cual es el dato a insertar en nuestro archivo DICOM, donde se recogen todas nuestras imágenes. Una vez hecho lo anterior, podemos ver los resultados: Figura 6.36: Resultados de nuestra secuencia. Comprimir archivo DICOM de varias imágenes monochrome. Este archivo está sin compresión. Se puede realizar una compresión como la indicada antes (164) y la reducción de tamaño es de la relación 2 a 1 aproximadamente, es decir el tamaño del archivo comprimido es la segunda parte del archivo sin comprimir. r GVA-ELAI-UPMPFC0074-03 167 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia Igual que lo anteriormente comentado, el único sistema de compresión soportado sigue siendo JPEGLossless de estas librerı́as y más particularmente de la clase Compression. 6.5.5. Panel Procesamiento. Este panel (figura 6.37) sirve para poder procesar una imagen mediante un algoritmo desarrollado por el GVA. Este panel visualiza la imagen de origen a procesar y ejecuta el procesamiento y cuando acaba éste se visualiza en el panel de al lado imagen procesada. Para realizar este panel se ha contado con la ayuda de los creadores del algoritmo para poder instalar las librerı́as necesarias. Se a procedido de la siguiente manera: Figura 6.37: Panel procesamiento. Introducción de algoritmo en la aplicación. Pasos a seguir: 168 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. 1. Lo primero que se debe hacer es poner en las variables de entorno los caminos para encontrar las librerı́as necesarias: D:/jmonrubia/AWouter/bin/win32; D:/jmonrubia/AWouter; c:/matlab6p5/bin/win32; Esto significa que antes se ha debido copiar en estas carpetas los archivos solicitados. En la carpeta D:/jmonrubia/AWouter se deben copiar los archivos applylut.dll y dataread.dll. 2. Después se debe ejecutar el setup mglinstaller.exe que instala las librerı́as necesarias de Matlab. 3. Por supuesto se debe D:/jmonrubia/AWouter. copiar el ejecutable en la carpeta 4. Después se tiene que crear una carpeta donde se ponen las librerı́as para sólo este algoritmo: C:/matlab6p5/toolbox/images/images/private y se incluyen en ésta bwlabel1.dll y bwlabel2.dll. Esto es a nivel de sistema. Una vez hecho ésto se debe realizar una implementación para que este algoritmo se pueda ejecutar desde nuestro GUI (graphical user interface). Para hacer esto se dan estos pasos: En el panel de procesamiento se han creado dos JLabel donde poner las imágenes de origen y la de salida o procesada. Se ha incluido además una JTextArea para decir el camino y el nombre de la imagen procesada y dos botones, uno para abrir la imagen a procesar y otro para empezar el procesamiento. Esto se puede ver en la figura 6.37. Pasos en JBuider7.0: 1.- Incluir en el proyecto los archivos CntVirRel.exe y files.txt mediante el botón que se muestra en la figura 6.38: r GVA-ELAI-UPMPFC0074-03 169 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia Figura 6.38: Botón para añadir archivos. 2.- Se ha implementado de esta forma. En el botón Imagen a tratar (figura 6.39): void botonSeleccionImagen actionPerformed(ActionEvent e) { ..... fotoEntradaCamino = jFileChooser1.getSelectedFile().getAbsolutePath(); ImageIcon iconoEntrada = new ImageIcon(fotoEntradaCamino); imagenEntrada = iconoEntrada.getImage(); etiquetaImagenEntrada.setIcon(iconoEntrada); String caminoFicheroTxt = ”files.txt”; File filesTxt= new File(caminoFicheroTxt); FileOutputStream chorro = new FileOutputStream(filesTxt); PrintStream escribirEnArchivo = new PrintStream(chorro); escribirEnArchivo.println(fotoEntradaCamino); imagenProcesada = textoImagenSalida.getText(); escribirEnArchivo.println(imagenProcesada); etiquetaSeleccionImagen.setText(fotoEntradaCamino); ...... 170 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia6.5. IMPLEMENTACIÓN DE NUESTRA APLICACIÓN. donde se muestra la imagen de entrada (setIcon) y se escribe el camino de entrada y de salida en el fichero files.txt de donde después con el botón que se ve acontinuación se saca esta información. Figura 6.39: Botón para abrir imagen a procesar. En el botón Procesar imagen (figura 6.40) básicamente se codifica de la siguiente manera: void botonProcesamiento actionPerformed(ActionEvent e) { Process p = Runtime.getRuntime().exec(”d:/jmonrubia/AWouter/CntVirRel.exe”); int valorRetorno = p.waitFor(); ImageIcon iconoSalida = new ImageIcon(textoImagenSalida.getText()); etiquetaImagenSalida.setIcon(iconoSalida); ..... donde las clases importantes son Process con su método getRuntime().exec() para ejecutar en este caso un .exe y también el método waitFor() que hace que la ejecución del código se pare hasta que el proceso p no finaliza. Una vez hecho esto sólo falta mostrar la imagen de salida en pantalla, cosa que ya se estudió en su debido momento (ver sección 6.5.3 en página 146). Figura 6.40: Botón para procesar imagen. r GVA-ELAI-UPMPFC0074-03 171 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia 6.5.6. Panel Cliente/Servidor. Uno de los mayores objetivos de esta aplicación es la posibilidad de poder mandar y recibir los archivos DICOM a través de la red. Para esta funcionalidad se ha creado este panel (figura 6.41), el cual permite enviar un archivo en formato DICOM por la red. Figura 6.41: Panel Cliente/Servidor. Para la implementación de este panel se ha insertado en él varias etiquetas y cuatro instancias de la clase JTextArea que se usarán para este servicio. La implementación consta de básicamente: void enviar actionPerformed(ActionEvent e) 172 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.6. DISTRIBUCIÓN DE LA APLICACIÓN. { open(); .......... Storagescu enviar = new Storagescu(servidor,puerto,Tservidor,Tcliente,openFileName); .......... int ok=enviar.store(); if(ok==enviar.STORE OK) etiquetaEnvio.setText(”Enviado con éxito”); else etiquetaEnvio.setText(”Enviado sin éxito”); .......... donde los parámetros servidor,puerto,Tservidor,Tcliente,openFileName corresponden a los datos necesarios para enviar más tarde el archivo mediante el método store() de la clase Storagescu, que emplazamos a que sea estudiada. 6.6. 6.6.1. Distribución de la aplicación. Generación del archivo ejecutable java. Una vez hecha esta versión Beta de nuestra aplicación DICOM es importante poder distribuirla para que sea ejecutada donde sea necesario, que es uno de los objetivos más importantes. Contamos con las ventajas de Java, que en este caso concreto es que independientemente del sistema operativo se puede ejecutar la aplicación. Para hacer ésto se cuenta con un asistente de JBuilder que se llama asistente Creador de recopilatorios, el cual se puede seleccionar de la forma en que muestra la figura 6.42: r GVA-ELAI-UPMPFC0074-03 173 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia Figura 6.42: Asistente creador de recopilatorios. Pasos a seguir: 1. Paso 1: seleccionar Aplicación. 2. Paso 2: escribir el nombre del ejecutable java. Figura 6.43: Asistente creador de recopilatorios. Paso2. 174 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia 6.6. DISTRIBUCIÓN DE LA APLICACIÓN. 3. Paso 3: En este paso se selecciona la opción de Incluir siempre todas las clases y recursos necesarios. 4. Paso 4: Lo mostrado en la figura siguiente son las opciones que se eligen. Figura 6.44: Asistente creador de recopilatorios. Paso3. 5. En los pasos 5,6 y 7 se aceptan las opciones por defecto. Una vez hecho esto se debe compilar el proyecto otra vez, con lo que conseguimos el archivo recopilado en este caso Cuatro.jar. 6.6.2. Ejecución del archivo ejecutable java. Generado este archivo es posible ejecutar esta aplicación con la sóla teniencia de la herramienta de JDK instalada (en este caso 1.4.1). La forma de proceder para poder ejecutar este archivo es muy sencilla. En la lı́nea de comandos donde se encuentre el archivo se debe escribir: java -jar cuatro.jar con lo que la aplicación aparece en pantalla. r GVA-ELAI-UPMPFC0074-03 175 CAPÍTULO 6. DESARROLLO DE NUESTRA APLICACIÓN MEDIANTE JDT, JDK 1.4.1 Y JBUILDER7. José Ma Onrubia 176 r GVA-ELAI-UPMPFC0074-03 Apéndice A ESTÁNDAR DICOM PARTE 1 A.1. Introducción. Descripción. ACR (Colegio Americano de Radiologı́a) y NEMA (la Asociación de Fabricantes Nacional Eléctrica) formaron un comité conjunto para desarrollar un estándar para la imagen digital y las comunicaciones en medicina. Este estándar (DICOM) ha sido desarrollado según los procedimientos NEMA. Ha sido desarrollado conjuntamente con otras organizaciones de estandarización entre las que se incluyen el centro TC251 en Europa y el JIRA en Japón, contando también con la supervisión de otras organizaciones en las que se incluyen IEEE, HL7 Y ANSI en EE.UU. El estándar DICOM está estructurado como un documento dividido en varias partes que usan las directrices establecidas en el documento siguiente: - ISO/IEC Directrices, 1989 Parte 3: Esquema y presentación de Normas Internacionales. Este documento es una parte del estándar DICOM, que consiste en las siguientes partes: PS 3.1: Introducción y descripción. PS 3.2: Estatuto de conformidad. PS 3.3: Definiciones de objeto de información (IOD´s). PS 3.4: Datos especı́ficos de clase de servicio. PS 3.5: Estructura de datos y codificación. 177 APÉNDICE A. ESTÁNDAR DICOM PARTE 1 José Ma Onrubia PS 3.6: Diccionario de datos. PS 3.7: Intercambio de mensajes. PS 3.8: Soporte de comunicación en red para el intercambio de mensajes. PS 3.9: Soporte para la comunicación punto a punto para el intercambio de mensajes. PS 3.10: Almacenamiento y formato de archivos para el intercambio de datos. PS 3.11: Almacenamiento media de perfiles de aplicación. PS 3.12: Funciones de almacenamiento y formatos media para intercambio de datos. PS 3.13: Soporte para la impresión en una comunicación punto a punto. PS 3.14: Función de visualización estándar en escala de grises. PS 3.15: Perfiles de seguridad. PS 3.16: Çontent mapping resource”. Estas partes están relacionadas, pero son documentos independientes. Su nivel de desarrollo y el su nivel aprobación pueden diferenciarse. Se va a ver el primer documento. Historia Con la introducción de la tomografı́a calculada (CT) seguido por otros tipos de imágenes digitales para diagnósticos en los años 1970, y el empleo creciente de ordenadores en aplicaciones clı́nicas, el Colegio Americano de Radiologı́a (ACR) y la Asociación de Fabricantes Nacional Eléctrica (NEMA) reconoció la necesidad emergente de un método estándar para la transferencia de imágenes y la asociación de información entre dispositivos fabricados por diferentes vendedores. Estos dispositivos producen una variedad de formatos de imagen digitales. El Colegio Americano de Radiologı́a (ACR) y la Asociación de Fabricantes Nacional Eléctrica (NEMA) formó un comité conjunto en 1983 para desarrollar un estándar que: Promueve la comunicación de información de imágenes digitales, independientemente del fabricante del dispositivo de captura. 178 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia A.1. INTRODUCCIÓN. Facilita el desarrollo y la expansión para archivar las imágenes y para sistemas de comunicación (PACS) que también puede servir como interfaz con otros sistemas de información de otros hospitales Permite la creación de las bases de datos de información de diagnósticos que pueden ser solicitados desde una amplia variedad de dispositivos distribuidos geográficamente. Las normas no 300-1985 de ACR-NEMA publicada en 1985 era la versión 1.0. Al estándar le ha seguido seguido dos revisiones: no 1, octubre de 1986 y no 2, enero de 1988. Las normas no 300-1988 de ACR-NEMA publicada en 1988 era la versión 2.0. Ésta incluyó la versión 1.0, las revisiones publicadas, y revisiones adicionales. También proporciona el soporte de comandos para dispositivos de visualización, presenta un esquema de jerarquı́a nuevo de identificación de una imagen, y añade datos para incrementar la especificidad a la hora de describir una imagen. Estas publicaciones de normas definieron una interfaz de soporte fı́sico, un juego mı́nimo de instrucciones de software, y un juego constante de formatos de datos. El estándar DICOM El estándar incorpora unas mejoras principales respecto a las versiones anteriores del estándar: 1. Se aplica a un ambiente de red. Las versiones anteriores eran aplicables sólo a ambientes de punto a punto; para la operación en un ambiente de red una unidad de interfaz de Red (NIU) era requerida. La Versión 3.0 de DICOM puede soportar los estándares de comunicación en red que se usan en industria, como los protocolos OSI Y TCP/IP. 2. Especifica cómo los dispositivos deben cumplir con el estándar para que los datos y las instrucciones sean intercambiadas. Las versiones anteriores han sido limitadas a la transferencia de datos, pero la Versión 3.0 DICOM especifica, por el concepto de clases de servicio, la semántica de órdenes y datos asociados. 3. Especifica los niveles de conformidad. Las versiones anteriores especificaron un nivel mı́nimo de conformidad. DICOM la Versión 3.0, explı́citamente, describe como una implementación debe estructurar una declaración de conformidad para seleccionar opciones especı́ficas. r GVA-ELAI-UPMPFC0074-03 179 APÉNDICE A. ESTÁNDAR DICOM PARTE 1 José Ma Onrubia 4. Está estructurado como un documento dividivo en varias partes. Ésto, facilita la rápida evolución del estándar simplificando la adición de rasgos nuevos. Las directrices ISO que definen como estructurar documentos de multi-partes han sido seguidas en la construcción del estándar DICOM. 5. Introduce objetos de información explı́citos no sólo para imágenes y los gráficos, también para estudios, informes, etc. 6. Especifica una técnica establecida para la identificación única de cualquier objeto de información lo que facilita las definiciones inequı́vocas de las relaciones entre objetos de información como ellos son interpretados a través de la red. A.2. Alcance y campo de aplicación. El Estándar DICOM facilita la interoperabilidad de un equipo médico visualizador y creador de imágenes especificando: un juego de protocolos para que sean cumplidos por los dispositivos. la sintaxis y la semántica de órdenes y de la información asociada que puede ser intercambiada usando estos protocolos. la información que debe ser suministrada para una realización para cumpla con el estado de conformidad. El Estándar DICOM no especifica: los detalles de cualquiera de los rasgos del estándar para que un dispositivo reclamando conformidad para una implementación. el juego total de rasgos y funciones de un sistema integrado por un grupo de dispositivos cada uno siguiendo la norma del estándar. un procedimiento de pruebas y validación para evaluar de que forma se cumple con el estándar. El estándar DICOM pertenece al campo de la informática médica. Dentro de este campo, dirige el intercambio de información digital entre equipos médicos. Como el equipo médico puede funcionar con otros dispositivos médicos, el alcance de estas necesidades estándar para trasladar a otras áreas de informática médica, como mostrado en la figura. Sin embargo, el estándar DICOM no dirige la anchura de este campo. 180 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia A.3. A.3. DEFINICIONES. Definiciones. Atributo: una caracterı́stica de un objeto de información. Un atributo tiene un nombre y un valor que es independiente de cualquier esquema de codificación. Por ejemplo el nombre del paciente. Comando: un medio genérico de transportar una petición para funcionar sobre objetos de información a través de una interfaz o una red. r GVA-ELAI-UPMPFC0074-03 181 APÉNDICE A. ESTÁNDAR DICOM PARTE 1 José Ma Onrubia Elemento de comando: una codificación de un parámetro de un comando que transporta el valor de este parámetro. Chorro de comandos: el resultado de codificar un conjunto de elementos de comando DICOM que usan el esquema de codificación DICOM. Estatuto de conformidad : una declaración formal asociada con una realización especı́fica del estándar DICOM. Especifica las clases de servicio, los objetos de información, y los protocolos de comunicación que soportan por las implementaciones. Diccionario de datos: un registro de los elementos de datos DICOM que tienen una etiqueta única, un nombre, un valor, y la semántica de cada elemento de datos. Elemento de datos: una unidad de información como definido por una partida simple en el diccionario de datos. Conjunto de datos: información intercambiada que consiste en un conjunto estructurado de valores de atributos directa o indirectamente relacionados con los objetos de información. El valor de cada atributo en un conjunto de datos es expresado como un elemento de datos. Corriente de Datos: el resultado de codificar un conjunto de datos que usa el esquema de codificación DICOM. Objeto de Información: una abstracción de una entidad verdadera de la información (p.ej., CT la Imagen, el Estudio, etc.) que es interpretado por uno o más comandos DICOM. Clase de objeto de información: una descripción formal de un objeto de información que incluye una descripción de su propósito y los atributos que posee. No incluye los valores de estos atributos. A.4. Sı́mbolos y abreviaturas. ACSE Association Control Service Element CT Computed Tomography DICOM Digital Imaging and Communications in Medicine 182 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia A.5. OBJETIVOS DEL ESTÁNDAR DICOM. HIS Hospital Information System NIU Network Interface Unit OSI Open Systems Interconnection PACS Picture Archiving and Communication Systems RIS Radiology Information System TCP/IP Transmission Control Protocol/Internet Protocol A.5. Objetivos del estándar DICOM. El estándar DICOM facilita la interoperabilidad de dispositivos que cumplen con éste. En particular: Dirige la semántica de comandos y datos asociados. En dispositivos que actúan recı́procamente, debe haber normas sobre cómo se espera que estos dispositivos reaccionen a esas órdenes y esos datos asociados, no sólamente a cómo la información debe ser intercambiada entre dispositivos. Explicita en la definición de las exigencias de conformidad de las puestas en práctica del estándar. En particular, una declaración de conformidad debe especificar bastante información para determinar las funciones para las que pueden esperar la interoperabilidad con otro dispositivo reclamando la conformidad. Facilita la operación en un ambiente conectado a una red, sin la exigencia de unidades de interfaz de red. Está estructurado para acomodar la introducción de servicios nuevos, facilitando ası́ el soporte para usos futuros de aplicaciones con imágenes médicas. Hace uso de normas existentes internacionales, y ellas mismas siguen las directrices de documentación establecidas para normas internacionales. Si bien el estándar DICOM tiene el potencial para facilitar las puestas en práctica de soluciones PACS, el empleo del estándar sólo, no garantiza que r GVA-ELAI-UPMPFC0074-03 183 APÉNDICE A. ESTÁNDAR DICOM PARTE 1 José Ma Onrubia todos los objetivos de un PACS sean cumplidos. El estándar facilita la interoperabilidad de sistemas siguiendo la conformidad en un ambiente de multivendedor, pero por sı́ mismo no garantiza la interoperabilidad. Ha sido desarrollado con énfasis en el diagnóstico médico como el practicado en radiologı́a y en disciplinas relacionadas; sin embargo, se piensa que también es aplicable a una amplia gama de información de imágenes relacionadas en los ambiente clı́nicos. A.6. Contenido del estándar DICOM. A.6.1. Estructura del documento. DICOM la versión 3.0 consiste en el siguiente nueve partes: PS 3.1: Introducción y descripción PS 3.2: Conformidad PS 3.3: Definiciones de objeto de información PS 3.4: Datos especı́ficos de clase de Servicio PS 3.5: Estructura de datos y Codificación PS 3.6: Diccionario de datos PS 3.7: Intercambio de mensajes PS 3.8: Soporte de comunicación en red para el intercambio de mensajes PS 3.9: Soporte de comunicación puntop por punto para el intercambio de mensajes Estas partes del estándar están relacionadas, pero son documentos independientes. Una corta descripción de las Partes 2 a 9 se proporcionan en esta sección. A.6.2. PS 3.2: Conformidad PS 3.2 del Estándar DICOM define principios que las implementaciones deben seguir: 184 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia A.6. CONTENIDO DEL ESTÁNDAR DICOM. Exigencias de conformidad. PS 3.2 especifica las exigencias generales que deben ser encontradas por cualquier realización. Hace referencia a secciones de conformidad de otras partes del estándar. Declaración de conformidad. PS 3.2 define la estructura de un estado de conformidad. Especifica la información que debe estar presente en una declaración de conformidad. Hace referencia a las secciones del estado de conformidad de otras partes del estándar. PS 3.2 no especifica procedimientos de pruebas y validación para evaluar la conformidad de una implementación del estándar. La figura representa el proceso de construcción para una declaración de conformidad. Una declaración de conformidad consiste en tres partes principales: el conjunto de los objetos de información que son reconocidos por esta implementación el conjunto de las clases de servicio que es reconocido en ésta implementación. el conjunto de los protocolos de comunicaciones que esta implementación soporta. r GVA-ELAI-UPMPFC0074-03 185 APÉNDICE A. ESTÁNDAR DICOM PARTE 1 A.6.3. José Ma Onrubia PS 3.3: Definiciones de objetos de la información (IOD´s) PS 3.3 del estándar DICOM especifica unas clases de objeto de información que proporcionan una definición abstracta de entidades del mundo real, aplicables a la comunicación de imágenes digitales médicas. Cada IOD consiste en la descripción de su objetivo y los atributos que lo definen. Un IOD no incluye los valores para los atributos que comprenden su definición. Para facilitar el crecimiento futuro del estándar y mantener la compatibilidad con las versiones anteriores del estándar, dos tipos de IOD´s están definidos: normalizado y compuesto. Los IOD´s normalizados incluyen sólo aquellos atributos inherentes a la entidad de mundo real representada. Por ejemplo, la clase de objeto de 186 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia A.6. CONTENIDO DEL ESTÁNDAR DICOM. información de estudio, que está definida como normalizado, contiene los atributos de los estudios de fecha y tiempo porque son inherentes al estudio actual. El nombre del paciente, sin embargo, no es un atributo de la clase de objeto de información de estudio porque ésto, es inherente al paciente sobre el que el estudio ha sido realizado y no sobre el estudio en sı́ mismo. Las clases de objeto de información compuestas pueden además incluir los atributos que tienen relación, pero que no son inherentes a la entidad del mundo real. Por ejemplo, la clase de objeto de información de imagen de tomográfica calculada, que está definida como compuesta, contiene los atributos que son inherentes a la imagen (p.ej. la fecha de imagen) y los atributos que están relacionados, pero que no son inherentes a la imagen (p.ej. el nombre paciente). Las clases de objeto de información compuestas proporcionan un marco estructurado para expresar las exigencias de comunicación de las imágenes que han sido definidos en las versiones anteriores del estándar. Para simplificar las definiciones de clase de objeto de información, los atributos de cada clase de objeto de información están divididos en atributos similares, agrupados juntos. Estas agrupaciones de atributos son especificadas como módulos independientes y pueden ser reutilizadas por una o más clases de objeto de información compuestas. Para representar un acontecimiento de una entidad del mundo real, se crea una instancia de objeto de información, en la cual se incluyen valores para los atributos de la clase de objeto de información. Los valores de atributo de esta instancia de objeto de información pueden cambiar con tiempo para reflejar con exactitud el estado que se cambia de la entidad a la que esto representa. Ésto se logra realizando operaciones básicas diferentes sobre la instancia de objeto de información para dar un juego especı́fico de servicios definidos como una clase de servicio. Estas clases de servicio son definidas en PS 3.4 del Estándar. PS 3.3 también está relacionado con otras partes del estándar DICOM: PS 3.5, Estructura de datos y semántica, define la estructura del conjunto de datos y la codificación para convey DICOM Information Object Attributes PS 3.6, Dicconario de datos, define la semántica de los elementos de datos DICOM la cual tranporta los atributos de objetos de información definidos en PS 3.3. r GVA-ELAI-UPMPFC0074-03 187 APÉNDICE A. ESTÁNDAR DICOM PARTE 1 A.6.4. José Ma Onrubia PS 3.4: Especificaciones de las clases de servicio. PS 3.4 del estándar DICOM define un número de clases de servicio. Una clase de servicio asocia uno o más objetos de información con una o más órdenes para ser realizadas sobre estos objetos. PS 3.4 del estándar DICOM define las caracterı́sticas compartidas por todas las clases de servicio, y como una declaración de conformidad a una clase de servicio individual está estructurada. Contiene unos anexos normativos que describen clases de servicio individuales detalladamente. Los ejemplos de Clases de Servicio incluyen lo siguiente: Clase de servicio de almacenamiento. Clase de servicio de pregunta. Clase de servicio de recuperación. Clase de servicio de gestión de estudio. PS 3.4 define las operaciones realizadas sobre los objetos de información definidos en PS 3.3. PS 3.7 define las órdenes y protocolos para usar los comandos para lograr las operaciones descritas en PS 3.4. A.6.5. PS 3.5: Estructura de datos y semántica PS 3.5 del estándar DICOM especifica como las entidades de aplicación DICOM construyen y codifican la información de conjunto de datos el siendo resultado del empleo de los objetos de información y clases de servicios definidas en las partes 3 y 4 del estándar DICOM. PS 3.5 del estándar DICOM, especifica como entidades de aplicación DICOM construyen y codifican la información del conjunto de datos, siendo conjunto el resultado del empleo de los objetos de información y clases de servicios definidas en las partes 3 y 4 del estándar DICOM. PS 3.5 direcciona las reglas de codificación necesarias para construir un flujo de datos para ser transportada en un mensaje como se especifica en PS 3.7 del estándar DICOM. Estos datos son producidos de la colección de elementos de datos que forman el conjunto de datos. Varios conjuntos de datos pueden ser referidos o doblados en un conjunto de datos compuesto. Un conjunto de datos compuesto es usado para transferir en ün paquete”el 188 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia A.6. CONTENIDO DEL ESTÁNDAR DICOM. contenido de objetos de información, ofreciendo una capacidad de carpeta. PS 3.5 también define la semántica de un número de unas funciones genéricas que son comunes a muchos objetos de información. A.6.6. PS 3.6: Diccionario de datos PS 3.6 del estándar DICOM es el registro centralizado que define la colección de todos los elementos de datos DICOM disponibles para representar la información. Para cada elemento de datos, PS 3.6: asigna una etiqueta única, que consiste en grupo y el número de elemento le da un nombre especifica sus caracterı́sticas de valor (la cadena de caracteres, el número entero, etc.) define su semántica (como debe ser interpretado). PS 3.6, en la conjunción con PS 3.5, es usado para construir conjunto de datos, y representar objetos de información como conjuntos de datos en la conjunción con PS 3.3 Y PS 3.5. A.6.7. PS 3.7: Intercambio de mensajes. PS 3.7 del estándar DICOM especifica tanto el servicio como el protocolo usado por una entidad de aplicación en un ambiente de imágenes médicas para intercambiar mensajes sobre los servicios de apoyo de comunicaciones definidos en PS 3.8 O PS 3.9. Un mensaje está compuesto por un flujo de órdenes definidos en PS 3.7 seguido por un flujo de datos opcional como lo definido en PS 3.5. Esta parte especifica lo siguiente: reglas para establecer y finalizar las asociaciones proporcionadas por el apoyo de comunicaciones especificado en PS 3.8 O PS 3.9, y las repercusiones sobre transacciones excepcionales. reglas que gobiernan el intercambio de peticiones y respuestas de órdenes. r GVA-ELAI-UPMPFC0074-03 189 APÉNDICE A. ESTÁNDAR DICOM PARTE 1 José Ma Onrubia reglas codificadas necesarias para construir flujo de órdenes y mensajes. Además, PS 3.7 está relacionado con otras partes del estándar: PS 3.3, las definiciones de objeto de información, especifican el juego de clases de objeto de información a las que las Órdenes definidas en PS 3.7 pueden ser aplicadas. PS 3.5, la estructura de datos y la semántica, dirigen las reglas de codificación necesarias para construir un flujo de datos para ser transportada en un mensaje especificado en PS 3.7 del estándar DICOM. PS 3.7, el intercambio de mensajes, define las órdenes y protocolos para usar las órdenes para lograr las operaciones descritas en PS 3.4. A.6.8. PS 3.8: Apoyo de comunicación de red para el intercambio de mensaje PS 3.8 del estándar DICOM especifica los servicios de comunicación y los protocolos de capas superiores necesarios para soportar, en una red, la comunicación entre entidades de aplicación DICOM según lo especificado en PS 3.3, PS 3.4, PS 3.5, PS 3.6, Y PS 3.7. Estos servicios de comunicación y protocolos aseguran que la comunicación entre entidades de aplicación DICOM sea realizada de una manera eficiente y coordinada a través de la red. Los servicios de comunicación especificados en PS 3.8 son un subconjunto apropiado de los servicios ofrecidos por el servicio de presentación OSI (LA ISO 8822) y del elemento de servicio de control de asociación OSI (ACSE) (LA ISO 8649). Se refieren al servicio de capa superior, que permite a un par entidades de aplicación el establecimiento de asociaciones, mensajes de transferencia y el fin de las asociaciones. Esta definición del servicio de capa superior permite el empleo de los protocolos OSI (Capas 1 a 6 más ACSE) para alcanzar una comunicación robusta y eficiente. Ésto, soporta una gran variedad de estándares internacionales basados en tecnologı́as de red que usan una amplia opción de redes fı́sicas como la ISO 8802-3 CSMA/CD (Ethernet a menudo llamada), FDDI, ISDN, X.25, dedicated digital circuits y muchas otras LAN y tecnologı́as de red WAN. Además, este mismo servicio de capa superior también puede ser proporcionado por el protocolo de capa DICOM superior usado conjuntamente 190 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia A.6. CONTENIDO DEL ESTÁNDAR DICOM. con protocolos de transporte de TCP/IP. Por lo tanto, una amplia gama de ambientes de red se pueden usar. La definición de un servicio de capa superior común a OSI y a ambientes TCP/IP, permite la migración de un TCP/IP a un ambiente OSI sin chocar entidades de aplicación DICOM. Estos protocolos de comunicación especificados por PS 3.8 son protocolos de comunicación de objetivos generales (OSI, TCP/IP) y no versiones especı́ficas para el estándar DICOM. La figura muestra estos dos protocolos. A.6.9. PS 3.9: Soporte de comunicación para el intercambio de mensajes punto por punto. El PS 3.9 del estándar DICOM especifica los servicios y los protocolos usados para las comunicaciones punto por punto de una manera compatible con ACR-NEMA 2.0. Especifica un interfaz fı́sico y protocolos señalados. Define r GVA-ELAI-UPMPFC0074-03 191 APÉNDICE A. ESTÁNDAR DICOM PARTE 1 José Ma Onrubia el OSI enlace para transmisión de datos y protocolos sesión/transporte/red y los servicios del montón de protocolo para ser usado sobre este interfaz fı́sico. Los servicios de capa de sesión/transporte/red especificados y protocolos soportan la comunicación entre entidades de aplicación DICOM como lo especificado en las partes 3, 4, 5, 6, y 7. Estos servicios son un subconjunto de los servicios de capa superiores especificados en PS 3.8 del estándar DICOM. Esta caracterı́stica del subconjunto permite la interconexión de un dispositivo con un interfaz de punto-punto a un ambiente de comunicación totalmente conectado a una red con soporte OSI Y TCP/IP. Tal interconexión requiere una unidad de interfaz de red interventor (NIU). La figura presenta como un interfaz de punto-punto y un ambiente conectado a una red coexisten. 192 r GVA-ELAI-UPMPFC0074-03 José Ma Onrubia A.7. RELACIONES ENTRE LAS PARTES DEL ESTÁNDAR. A.7. Relaciones entre las partes del estándar. Las relaciones siguientes existen entre las partes del estándar: PS 3.1: la introducción y la descripción describen la estructura total del estándar. PS 3.2: la conformidad especifica las exigencias generales que deben ser encontradas por puestas en práctica reclamando la conformidad y el contenido de una Declaración de conformidad. PS 3.3: las definiciones de objeto de información especifican la estructura y los atributos de los objetos que son manejados por clases de servicio (PS 3.4). Estos objetos incluyen imágenes, estudios, y pacientes. PS 3.4: los datos especı́ficos de clase de servicio definen las operaciones que pueden ser realizadas sobre los casos de objetos de información (PS 3.3) para proporcionar un servicio especı́fico. Estos servicios incluyen el almacenaje de imagen, la recuperación, y la impresión. PS 3.5: la estructura de datos y la Semántica especifican la codificación del contenido de datos de los mensajes que son cambiados para lograr las operaciones usadas por las clases de servicio (PS 3.4). PS 3.6: el diccionario de datos define los atributos individuales de la información que representan el contenido de datos (PS 3.5) de los casos de objetos de información. PS 3.7: el cambio de mensaje especifica las operaciones y el protocolo usado para cambiar mensajes. Estas operaciones son usadas para lograr los servicios definidos por las clases de servicio (PS 3.4). PS 3.8: el apoyo de comunicación de red para el cambio de mensaje define los servicios y protocolos usados para cambiar mensajes (PS 3.7) directamente sobre REDES de TCP/IP y OSI. PS 3.9: el apoyo de comunicación punto a punto para el intercambio de mensajes define los servicios y los protocolos usados para el intercambio de mensajes (PS 3.7) en la interfaz de 50 pines. Para más imformación dirigirse a la parte del estándar deseada. Todas las partes del estándar se pueden encontrar en la red. r GVA-ELAI-UPMPFC0074-03 193 APÉNDICE A. ESTÁNDAR DICOM PARTE 1 194 José Ma Onrubia r GVA-ELAI-UPMPFC0074-03 Índice de figuras 2.1. 2.2. 2.3. 2.4. 2.5. 2.6. 4 6 7 8 9 Proceso distribuido . . . . . . . . . . . . . . . . . . . . . . . . Modelo de un proceso distribuido . . . . . . . . . . . . . . . . Clases de servicio DICOM . . . . . . . . . . . . . . . . . . . . Relaciones entre IODs y atributos . . . . . . . . . . . . . . . Ejemplo de una imagen IOD compuesta. . . . . . . . . . . . . Descripción de la codificación y decodificación de las instancias SOP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7. DICOM con intercambio en red. . . . . . . . . . . . . . . . . 2.8. Capas OSI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.9. Conexión TCP . . . . . . . . . . . . . . . . . . . . . . . . . . 2.10. Estatuto de conformidad con perfil del sistema. . . . . . . . . 2.11. Estatuto de conformidad con perfil de aplicación . . . . . . . 2.12. Diferentes partes del estándar DICOM . . . . . . . . . . . . . 2.13. Del mundo real al modelo de información . . . . . . . . . . . 2.14. Ejemplo de ”mapping”de un CT . . . . . . . . . . . . . . . . 2.15. Modelo de información de una imagen compuesta DICOM . . 2.16. Clasificación de la información de la imagen . . . . . . . . . . 2.17. Juego básico de atributos de las instancias de imagen SOP . . 2.18. Ciclo de vida de Image SOP Instance Information . . . . . . 15 16 20 22 23 25 26 29 31 33 38 45 48 3.1. 3.2. 3.3. 3.4. 3.5. Visual C++ Browser . . Viewer . . . Print . . . . Process log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 60 61 62 63 6.1. 6.2. 6.3. 6.4. 6.5. 6.6. 6.7. Asistente para proyectos: paso 1 Partes de aplicación. . . . . . . . contentPane . . . . . . . . . . . . JScrollPane . . . . . . . . . . . . constraints . . . . . . . . . . . . JTextArea . . . . . . . . . . . . . text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 113 114 114 115 115 116 con DCMTK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 . . . . . ÍNDICE DE FIGURAS José Ma Onrubia 6.8. Ejecutado . . . . . . . . . . . . . . . . . . . . . . . . 6.9. Menús . . . . . . . . . . . . . . . . . . . . . . . . . . 6.10. Editor . . . . . . . . . . . . . . . . . . . . . . . . . . 6.11. actionPerformed . . . . . . . . . . . . . . . . . . . . 6.12. jMenu . . . . . . . . . . . . . . . . . . . . . . . . . . 6.13. editor en negro . . . . . . . . . . . . . . . . . . . . . 6.14. API de JDK 1.4.1. . . . . . . . . . . . . . . . . . . . 6.15. Configurar JDK. . . . . . . . . . . . . . . . . . . . . 6.16. Nuevo camino de JDK. . . . . . . . . . . . . . . . . . 6.17. Propiedades de proyecto. . . . . . . . . . . . . . . . . 6.18. Propiedades de proyecto. . . . . . . . . . . . . . . . . 6.19. Propiedades de proyecto. . . . . . . . . . . . . . . . . 6.20. Estructura básica de la aplicación. . . . . . . . . . . 6.21. Panel Visor DICOM. . . . . . . . . . . . . . . . . . . 6.22. Zoom mediante botones Zoom In y Zoom Out. . . . 6.23. Zoom mediante cliqueos de ratón. . . . . . . . . . . . 6.24. Botones Anterior y Siguiente . . . . . . . . . . . . . 6.25. Botón Seguidas . . . . . . . . . . . . . . . . . . . . . 6.26. Botón Guardar JPEG . . . . . . . . . . . . . . . . . 6.27. Zona de insertar datos. . . . . . . . . . . . . . . . . . 6.28. Ver funcionamiento. . . . . . . . . . . . . . . . . . . 6.29. Botones para campos nuevos. . . . . . . . . . . . . . 6.30. Comprobar inserción de nuevo campo. . . . . . . . . 6.31. Panel Crear archivo DICOM. . . . . . . . . . . . . . 6.32. Imagen a insertar. . . . . . . . . . . . . . . . . . . . 6.33. Datos de texto a insertar. . . . . . . . . . . . . . . . 6.34. Comprobar la creación del archivo DICOM en escala 6.35. Comprobar la creación del archivo DICOM en color. 6.36. Resultados de nuestra secuencia. . . . . . . . . . . . 6.37. Panel procesamiento. . . . . . . . . . . . . . . . . . . 6.38. Botón para añadir archivos. . . . . . . . . . . . . . . 6.39. Botón para abrir imagen a procesar. . . . . . . . . . 6.40. Botón para procesar imagen. . . . . . . . . . . . . . 6.41. Panel Cliente/Servidor. . . . . . . . . . . . . . . . . 6.42. Asistente creador de recopilatorios. . . . . . . . . . . 6.43. Asistente creador de recopilatorios. Paso2. . . . . . . 6.44. Asistente creador de recopilatorios. Paso3. . . . . . . 196 . . . . . 117 . . . . . 117 . . . . . 119 . . . . . 120 . . . . . 122 . . . . . 125 . . . . . 133 . . . . . 138 . . . . . 139 . . . . . 140 . . . . . 141 . . . . . 142 . . . . . 143 . . . . . 144 . . . . . 146 . . . . . 147 . . . . . 149 . . . . . 150 . . . . . 151 . . . . . 153 . . . . . 155 . . . . . 156 . . . . . 157 . . . . . 159 . . . . . 160 . . . . . 161 de grises.163 . . . . . 164 . . . . . 167 . . . . . 168 . . . . . 170 . . . . . 171 . . . . . 171 . . . . . 172 . . . . . 174 . . . . . 174 . . . . . 175 r GVA-ELAI-UPMPFC0074-03