Imágenes médicas - DICOM#

El standard DICOM#

Para facilitar la intercomunicación entre distintas modalidades de diagnóstico por imágenes, se creó en 1993 el formato DICOM (Digital Imaging and Communications in Medicine). Este es un standard que define cómo guardar la información de una imagen, series de imágenes e incluso videos (cines), junto con datos relevantes de la misma, datos del paciente, del centro médico, de la modalidad específica de diagnóstico, etc.

La definición del standard es extensísima (pueden consultarla en https://www.dicomstandard.org/current), dado que incluye un enorme abanico de posibles tipos de datos.

Esencialmente, un archivo DICOM está estructurado en una lista de TAGS que se componen de la siguiente manera:

  • Dos números hexadecimales (denominados Grupo y Elemento) que identifican el atributo de la imagen. Cada atributo tiene un nombre asociado, que es el que se utiliza generalmente para acceder al mismo. Por ejemplo

      (0008, 0060) Modality 
    

corresponde al grupo 0008, elemento 0006 que describe el atributo Modalidad de la imagen.

  • Dos letras (Value Representation) que indican el tipo de dato que se está guardando en el tag, por ejemplo

    • ‘SH’ para short strings (hasta 16 caracteres)

    • ‘US’ para enteros sin signo (enteros mayores o iguales que cero, usando 2 bytes)

    • ‘SQ’ para secuencias de otros tags

    • y otros más

  • El valor específico del atributo en sí.

Entonces, un tag completo sería

(0008, 0080) Institution Name                    LO: 'INTECNUS'

Recomendamos usar la página https://dicom.innolitics.com/ciods para explorar los tag DICOM, dado que la página del standard es compleja para buscar información

Afortunadamente, la gran mayoría de los lenguajes provee de bibliotecas para trabajar con archivos DICOM. Python no es una excepción, y usa el módulo pydicom.

Instalando pydicom#

Desafortunadamente, pydicom no viene instalado por defecto en Anaconda. Veamos cómo instalarlo.

  1. Correr la línea de comando de miniconda.

  2. En la línea de comando,

     conda install -c conda-forge pydicom
    

Usando pydicom#

Una vez instalado pydicom, es posible usarlo como un módulo cualquiera. La documentación está acá: https://pydicom.github.io/pydicom/stable/index.html

Es importante mencionar que, además de poder acceder a todos los tags DICOM de un archivo, pydicom además guarda información del archivo en sus propias variables, para facilitar el acceso a las mismas.

import matplotlib.pyplot as plt
import numpy as np
import pydicom

#
#    pydicom provee algunos datos de ejemplo, que se pueden importar así:
#
# from pydicom.data import get_testdata_files

Abrir un archivo DICOM#

DCM = pydicom.dcmread(r'../Assets/CT/IM-0001-0200-0001.dcm')
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[2], line 1
----> 1 DCM = pydicom.dcmread(r'../Assets/CT/IM-0001-0200-0001.dcm')

File /opt/miniconda3/lib/python3.9/site-packages/pydicom/filereader.py:993, in dcmread(fp, defer_size, stop_before_pixels, force, specific_tags)
    991     caller_owns_file = False
    992     logger.debug("Reading file '{0}'".format(fp))
--> 993     fp = open(fp, 'rb')
    994 elif fp is None or not hasattr(fp, "read") or not hasattr(fp, "seek"):
    995     raise TypeError("dcmread: Expected a file path or a file-like, "
    996                     "but got " + type(fp).__name__)

FileNotFoundError: [Errno 2] No such file or directory: '../Assets/CT/IM-0001-0200-0001.dcm'
type(DCM)
pydicom.dataset.FileDataset
DCM
(0008, 0005) Specific Character Set              CS: 'ISO_IR 100'
(0008, 0008) Image Type                          CS: ['ORIGINAL', 'PRIMARY', 'AXIAL']
(0008, 0012) Instance Creation Date              DA: '20191106'
(0008, 0013) Instance Creation Time              TM: '164949'
(0008, 0016) SOP Class UID                       UI: CT Image Storage
(0008, 0018) SOP Instance UID                    UI: 1.2.840.113619.2.290.3.1225403042.932.1573045470.721.200
(0008, 0020) Study Date                          DA: '20191106'
(0008, 0021) Series Date                         DA: '20191106'
(0008, 0022) Acquisition Date                    DA: '20191106'
(0008, 0023) Content Date                        DA: '20191106'
(0008, 0030) Study Time                          TM: '164219'
(0008, 0031) Series Time                         TM: '164539'
(0008, 0032) Acquisition Time                    TM: '164650.808161'
(0008, 0033) Content Time                        TM: '164949'
(0008, 0050) Accession Number                    SH: '2473501'
(0008, 0060) Modality                            CS: 'CT'
(0008, 0070) Manufacturer                        LO: 'GE MEDICAL SYSTEMS'
(0008, 0080) Institution Name                    LO: 'INTECNUS'
(0008, 0090) Referring Physician's Name          PN: '0'
(0008, 1010) Station Name                        SH: 'petct'
(0008, 1030) Study Description                   LO: 'PET CUERPO COMPLETO-'
(0008, 103e) Series Description                  LO: 'Standard/Full'
(0008, 1050) Performing Physician's Name         PN: '"'
(0008, 1060) Name of Physician(s) Reading Study  PN: 'VENTIMIGLIA, ROMINA'
(0008, 1070) Operators' Name                     PN: 'CC'
(0008, 1090) Manufacturer's Model Name           LO: 'Discovery 710'
(0008, 1140)  Referenced Image Sequence   1 item(s) ---- 
   (0008, 1150) Referenced SOP Class UID            UI: CT Image Storage
   (0008, 1155) Referenced SOP Instance UID         UI: 1.2.840.113619.2.290.3.1225403042.932.1573045470.333.2
   ---------
(0009, 0010) Private Creator                     LO: 'GEMS_IDEN_01'
(0009, 1001) [Full fidelity]                     LO: 'CT_LIGHTSPEED'
(0009, 1002) [Suite id]                          SH: 'CT83'
(0009, 1004) [Product id]                        SH: 'Discovery 710'
(0009, 1027) [Image actual date]                 SL: 1573058739
(0009, 10e3) [Equipment UID]                     UI: ''
(0010, 0010) Patient's Name                      PN: 'PET CT INTECNUS'
(0010, 0020) Patient ID                          LO: '16682827'
(0010, 0030) Patient's Birth Date                DA: '19641026'
(0010, 0040) Patient's Sex                       CS: 'F'
(0010, 1000) Other Patient IDs                   LO: ''
(0010, 1010) Patient's Age                       AS: '055Y'
(0010, 1020) Patient's Size                      DS: "1.62"
(0010, 1030) Patient's Weight                    DS: "65.0"
(0010, 21b0) Additional Patient History          LT: ''
(0018, 0022) Scan Options                        CS: 'HELICAL MODE'
(0018, 0050) Slice Thickness                     DS: "2.500000"
(0018, 0060) KVP                                 DS: "120"
(0018, 0088) Spacing Between Slices              DS: "3.270000"
(0018, 0090) Data Collection Diameter            DS: "500.000000"
(0018, 1020) Software Version(s)                 LO: 'pet_coreload.87'
(0018, 1030) Protocol Name                       LO: '10.1 PET/CT FDG + TORAX'
(0018, 1100) Reconstruction Diameter             DS: "700.000000"
(0018, 1110) Distance Source to Detector         DS: "949.147000"
(0018, 1111) Distance Source to Patient          DS: "541.000000"
(0018, 1120) Gantry/Detector Tilt                DS: "0.000000"
(0018, 1130) Table Height                        DS: "181.500000"
(0018, 1140) Rotation Direction                  CS: 'CW'
(0018, 1150) Exposure Time                       IS: "500"
(0018, 1151) X-Ray Tube Current                  IS: "255"
(0018, 1152) Exposure                            IS: "5"
(0018, 1160) Filter Type                         SH: 'BODY FILTER'
(0018, 1170) Generator Power                     IS: "61800"
(0018, 1190) Focal Spot(s)                       DS: "1.200000"
(0018, 1210) Convolution Kernel                  SH: 'STANDARD'
(0018, 5100) Patient Position                    CS: 'FFS'
(0018, 9305) Revolution Time                     FD: 0.5
(0018, 9306) Single Collimation Width            FD: 0.625
(0018, 9307) Total Collimation Width             FD: 40.0
(0018, 9309) Table Speed                         FD: 110.0
(0018, 9310) Table Feed per Rotation             FD: 55.0
(0018, 9311) Spiral Pitch Factor                 FD: 1.375
(0019, 0010) Private Creator                     LO: 'GEMS_ACQU_01'
(0019, 1002) [Detector Channel]                  SL: 912
(0019, 1003) [Cell number at Theta]              DS: "389.750000"
(0019, 1004) [Cell spacing]                      DS: "1.023900"
(0019, 100f) [Horiz. Frame of ref.]              DS: "1995.000000"
(0019, 1011) [Series contrast]                   SS: 0
(0019, 1018) [First scan ras]                    LO: 'I'
(0019, 101a) [Last scan ras]                     LO: 'I'
(0019, 1023) [Table Speed [mm/rotation]]         DS: "55.000000"
(0019, 1024) [Mid Scan Time [sec]]               DS: "154.764603"
(0019, 1025) [Mid scan flag]                     SS: 1
(0019, 1026) [Tube Azimuth [degree]]             SL: -1
(0019, 1027) [Rotation Speed [msec]]             DS: "0.500000"
(0019, 102c) [Number of triggers]                SL: 19295
(0019, 102e) [Angle of first view]               DS: "0.000000"
(0019, 102f) [Trigger frequency]                 DS: "1968.000000"
(0019, 1039) [SFOV Type]                         SS: 1024
(0019, 1042) [Segment Number]                    SS: 0
(0019, 1043) [Total Segments Required]           SS: 0
(0019, 1047) [View compression factor]           SS: 1
(0019, 1052) [Recon post proc. Flag]             SS: 1
(0019, 106a) [Dependent on #views processed]     SS: 3
(0020, 000d) Study Instance UID                  UI: 1.2.840.113619.2.290.3.1225403042.932.1573045470.329
(0020, 000e) Series Instance UID                 UI: 1.2.840.113619.2.290.3.1225403042.932.1573045470.409.5
(0020, 0010) Study ID                            SH: '1114'
(0020, 0011) Series Number                       IS: "5"
(0020, 0012) Acquisition Number                  IS: "1"
(0020, 0013) Instance Number                     IS: "200"
(0020, 0032) Image Position (Patient)            DS: ['-350.000', '-350.000', '-552.750']
(0020, 0037) Image Orientation (Patient)         DS: ['1.000000', '0.000000', '0.000000', '0.000000', '1.000000', '0.000000']
(0020, 0052) Frame of Reference UID              UI: 1.2.840.113619.2.290.3.1225403042.932.1573045470.330.19950.5
(0020, 1040) Position Reference Indicator        LO: 'OM'
(0020, 1041) Slice Location                      DS: "-552.750"
(0021, 0010) Private Creator                     LO: 'GEMS_RELA_01'
(0021, 1003) [Series from which Prescribed]      SS: 4
(0021, 1035) [Series from which prescribed]      SS: 1
(0021, 1036) [Image from which prescribed]       SS: 2
(0021, 1091) [Biopsy position]                   SS: 0
(0021, 1092) [Biopsy T location]                 FL: 0.0
(0021, 1093) [Biopsy ref location]               FL: 0.0
(0023, 0010) Private Creator                     LO: 'GEMS_STDY_01'
(0023, 1070) [Start time(secs) in first axial]   FD: 1573058660.856057
(0027, 0010) Private Creator                     LO: 'GEMS_IMAG_01'
(0027, 1010) [Scout Type]                        SS: 0
(0027, 101c) [Vma mamp]                          SL: 0
(0027, 101e) [Vma mod]                           SL: 0
(0027, 101f) [Vma clip]                          SL: 190
(0027, 1020) [Smart scan ON/OFF flag]            SS: 2
(0027, 1035) [Plane Type]                        SS: 2
(0027, 1042) [Center R coord of plane image]     FL: 0.0
(0027, 1043) [Center A coord of plane image]     FL: 0.0
(0027, 1044) [Center S coord of plane image]     FL: -552.75
(0027, 1045) [Normal R coord]                    FL: 0.0
(0027, 1046) [Normal A coord]                    FL: 0.0
(0027, 1047) [Normal S coord]                    FL: -1.0
(0027, 1050) [Scan Start Location]               FL: 0.0
(0027, 1051) [Scan End Location]                 FL: 0.0
(0028, 0002) Samples per Pixel                   US: 1
(0028, 0004) Photometric Interpretation          CS: 'MONOCHROME2'
(0028, 0010) Rows                                US: 512
(0028, 0011) Columns                             US: 512
(0028, 0030) Pixel Spacing                       DS: ['1.367188', '1.367188']
(0028, 0100) Bits Allocated                      US: 16
(0028, 0101) Bits Stored                         US: 16
(0028, 0102) High Bit                            US: 15
(0028, 0103) Pixel Representation                US: 1
(0028, 0120) Pixel Padding Value                 SS: -2000
(0028, 1050) Window Center                       DS: "40"
(0028, 1051) Window Width                        DS: "400"
(0028, 1052) Rescale Intercept                   DS: "-1024"
(0028, 1053) Rescale Slope                       DS: "1"
(0028, 1054) Rescale Type                        LO: 'HU'
(0040, 0244) Performed Procedure Step Start Date DA: '20191106'
(0040, 0245) Performed Procedure Step Start Time TM: '164219'
(0040, 0253) Performed Procedure Step ID         SH: 'PPS ID   1114'
(0040, 0254) Performed Procedure Step Descriptio LO: 'PET CUERPO COMPLETO-'
(0043, 0010) Private Creator                     LO: 'GEMS_PARM_01'
(0043, 1010) [Window value]                      US: 400
(0043, 1012) [X-ray chain]                       SS: [99, 99, 99]
(0043, 1016) [Number of overranges]              SS: 0
(0043, 101e) [Delta Start Time [msec]]           DS: "4.562500"
(0043, 101f) [Max overranges in a view]          SL: 0
(0043, 1021) [Corrected after glow terms]        SS: 0
(0043, 1025) [Reference channels]                SS: [0, 0, 0, 0, 0, 0]
(0043, 1026) [No views ref chans blocked]        US: [0, 0, 0, 0]
(0043, 1027) [Scan Pitch Ratio]                  SH: '1.375:1'
(0043, 1028) [Unique image iden]                 OB: b'00'
(0043, 102b) [Private Scan Options]              SS: [2, 0, 0, 0]
(0043, 1031) [Recon Center Coordinates]          DS: ['0.000000', '0.000000']
(0043, 1040) [Trigger on position]               FL: 11.807700157165527
(0043, 1041) [Degree of rotation]                FL: 7058.60302734375
(0043, 1042) [DAS trigger source]                SL: 0
(0043, 1043) [DAS fpa gain]                      SL: 0
(0043, 1044) [DAS output source]                 SL: 0
(0043, 1045) [DAS ad input]                      SL: 0
(0043, 1046) [DAS cal mode]                      SL: 0
(0043, 104d) [Start scan to X-ray on delay]      FL: 0.0
(0043, 104e) [Duration of X-ray on]              FL: 9.804369926452637
(0043, 1064) [Image Filter]                      CS: ''
(0045, 0010) Private Creator                     LO: 'GEMS_HELIOS_01'
(0045, 1001) [Number of Macro Rows in Detector]  SS: 64
(0045, 1002) [Macro width at ISO Center]         FL: 0.625
(0045, 1003) [DAS type]                          SS: 24
(0045, 1004) [DAS gain]                          SS: 4
(0045, 1006) [Table Direction]                   CS: 'OUT OF GANTRY'
(0045, 1007) [Z smoothing Factor]                FL: 0.0
(0045, 1008) [View Weighting Mode]               SS: 0
(0045, 1009) [Sigma Row number]                  SS: 0
(0045, 100a) [Minimum DAS value]                 FL: 0.0
(0045, 100b) [Maximum Offset Value]              FL: 0.0
(0045, 100c) [Number of Views shifted]           SS: 0
(0045, 100d) [Z tracking Flag]                   SS: 0
(0045, 100e) [Mean Z error]                      FL: 0.0
(0045, 100f) [Z tracking Error]                  FL: 0.0
(0045, 1010) [Start View 2A]                     SS: 0
(0045, 1011) [Number of Views 2A]                SS: 0
(0045, 1012) [Start View 1A]                     SS: 0
(0045, 1013) [Sigma Mode]                        SS: 0
(0045, 1014) [Number of Views 1A]                SS: 0
(0045, 1015) [Start View 2B]                     SS: 0
(0045, 1016) [Number Views 2B]                   SS: 0
(0045, 1017) [Start View 1B]                     SS: 0
(0045, 1018) [Number of Views 1B]                SS: 0
(0045, 1021) [Iterbone Flag]                     SS: 0
(0045, 1022) [Perisstaltic Flag]                 SS: 0
(0045, 1032) [TemporalResolution]                FL: 0.5
(0045, 103b) [NoiseReductionImageFilterDesc]     LO: ''
(0053, 0010) Private Creator                     LO: 'GEHC_CT_ADVAPP_001'
(0053, 1020) [ShuttleFlag]                       IS: "0"
(0053, 1040) [IterativeReconAnnotation]          SH: 'SS50'
(0053, 1041) [IterativeReconMode]                SH: 'Slice'
(0053, 1042) [IterativeReconConfiguration]       LO: 'SS50:Slice'
(0053, 1043) [IterativeReconLevel]               SH: '50'
(0053, 1060) [reconFlipRotateAnno]               SH: ''
(0053, 1061) [highResolutionFlag]                SH: '0'
(0053, 1062) [RespiratoryFlag]                   SH: '0'
(0053, 1064) Private tag data                    IS: "1"
(0053, 1065) Private tag data                    IS: "0"
(0053, 1066) Private tag data                    LO: '120kV'
(0053, 1067) Private tag data                    IS: "0"
(0053, 1068) Private tag data                    IS: "0"
(0053, 106a) Private tag data                    IS: "0"
(0053, 106b) Private tag data                    IS: "0"
(0053, 109d) Private tag data                    LO: ''
(7fe0, 0010) Pixel Data                          OW: Array of 524288 elements

Para acceder a cada uno de los atributos del archivo DICOM se utiliza el nombre del atributo

DCM.PatientName
'PET CT INTECNUS'
DCM.StudyDescription
'PET CUERPO COMPLETO-'
DCM.ProtocolName
'10.1 PET/CT FDG + TORAX'

Pero también tengo la información desde el tag, usando el grupo y elemento en representación hexadecimal:

print("Tag  : ",DCM[0x0018,0x1030].tag)
print("VR   : ",DCM[0x0018,0x1030].VR)
print("valor: ",DCM[0x0018,0x1030].value)
Tag  :  (0018, 1030)
VR   :  LO
valor:  10.1 PET/CT FDG + TORAX

Para acceder a CAMPOS PRIVADOS debo hacerlo por el TAG#

Un campo privado se encuentran definidos por el fabricante y no estan en el diccionario del estándar. Sin embargo, se encuentran perfectamente detallados en los respectivos dicom conformance statement de cada equipo.

DCM[0x0009, 0x1004]
(0009, 1004) [Product id]                        SH: 'Discovery 710'
print(DCM[0x0009, 0x1004].tag,
      DCM[0x0009, 0x1004].VR,
      DCM[0x0009, 0x1004].value, sep='\n')
(0009, 1004)
SH
Discovery 710

Las imágenes DICOM#

Las imágenes propiamente dichas se encuentran guardadas como un array en el atributo PIXEL DATA

print(DCM[0x7fe0, 0x0010])
print(type(DCM.PixelData))
print(DCM.PixelData[0:11])
(7fe0, 0010) Pixel Data                          OW: Array of 524288 elements
<class 'bytes'>
b'0\xf80\xf80\xf80\xf80\xf80'

Sin embargo, este atributo tiene como Value Representation a ‘OW’, (Other Word String) que no parece ser muy amigable…

Por eso, pydicom guarda la imagen en la variable pixel_array, que es un array de numpy:

print(type(DCM.pixel_array))
print("Tamaño de la imagen:",DCM.pixel_array.shape)
<class 'numpy.ndarray'>
Tamaño de la imagen: (512, 512)
fig_1 = plt.figure(1, figsize=[10,10])
img1 = fig_1.add_subplot(111)
img1.imshow(DCM.pixel_array, cmap='gray')
img1.set_title('CORTE AXIAL CT, sin procesar')
plt.show()
_images/df9bbe09d68f4ad48edd4b12dcf369207ad2fe741856b3e7a9130518574c52da.png
print(DCM.pixel_array)
[[-2000 -2000 -2000 ... -2000 -2000 -2000]
 [-2000 -2000 -2000 ... -2000 -2000 -2000]
 [-2000 -2000 -2000 ... -2000 -2000 -2000]
 ...
 [-2000 -2000 -2000 ... -2000 -2000 -2000]
 [-2000 -2000 -2000 ... -2000 -2000 -2000]
 [-2000 -2000 -2000 ... -2000 -2000 -2000]]

Ajustando la imagen#

Obteniendo HUs#

Cada pixel de una imagen de CT corresponde a un determinado valor de unidades de Hounsfield. Sin embargo, los valores que se guardan en los archivos no corresponden directamente a los valores de HU, sino a una versión rescaleada (esto se debe básicamente a las limitaciones que impone el formato DICOM al tipo de dato a usar). Por eso, para obtener HU, es necesario aplicar la siguiente transformación lineal:

\[ f_t(x,y) = \texttt{DCM.RescaleSlope} \times f_{orig}(x,y) + \texttt{DCM.RescaleIntercept} \]
print(DCM.RescaleType)
print(DCM.RescaleSlope)
print(DCM.RescaleIntercept)
HU
1
-1024
imagen = DCM.pixel_array * DCM.RescaleSlope + DCM.RescaleIntercept
print(imagen)
[[-3024. -3024. -3024. ... -3024. -3024. -3024.]
 [-3024. -3024. -3024. ... -3024. -3024. -3024.]
 [-3024. -3024. -3024. ... -3024. -3024. -3024.]
 ...
 [-3024. -3024. -3024. ... -3024. -3024. -3024.]
 [-3024. -3024. -3024. ... -3024. -3024. -3024.]
 [-3024. -3024. -3024. ... -3024. -3024. -3024.]]
print(imagen[50,256])
print(np.min(imagen))
print(np.max(imagen))
imagen2 = np.clip(imagen, -1000,np.max(imagen))
print(imagen2)
-1000.0
-3024.0
1251.0
[[-1000. -1000. -1000. ... -1000. -1000. -1000.]
 [-1000. -1000. -1000. ... -1000. -1000. -1000.]
 [-1000. -1000. -1000. ... -1000. -1000. -1000.]
 ...
 [-1000. -1000. -1000. ... -1000. -1000. -1000.]
 [-1000. -1000. -1000. ... -1000. -1000. -1000.]
 [-1000. -1000. -1000. ... -1000. -1000. -1000.]]
fig_1 = plt.figure(1, figsize=[10,10])
img1 = fig_1.add_subplot(111)
img1.imshow(imagen2, cmap='gray')
img1.set_title('CORTE AXIAL CT, HUs')
plt.show()
_images/ae18b9ba733887d8641a788a63fdcb2d012e7446ae5780567072754bbf645138.png

Window and Level#

El proceso de Windowing ( o grey-level mapping, contrast stretching, histogram modification, contrast enhancement, LUT, etc.) consiste en manipular las componentes de la imagen de forma tal que se puedan realzar ciertas estructuras particulares. El brillo de la imagen se ajusta a partir del Window Level, mientras que el contraste se ajusta a partir del Window Width.

Window%20and%20level.png

Cada DICOM puede proveer un ancho y centro de ventana que viene desde el equipo de adquisición de imágenes:

print(DCM.WindowCenter)
print(DCM.WindowWidth)
40
400

Recordemos que el valor de los pixeles en el monitor van de 0 a 255

Típicamente si definimos

\begin{align} c &= \texttt{DCM.WindowCenter} \ w &= \texttt{DCM.WindowWidth} \end{align}

Entonces, la imagen transformada se obtiene como:

\begin{align} f_t(x,y) &= 0 &\textrm{si} \qquad f(x,y) < c - w /2 \ f_t(x,y) &= 255 &\textrm{si} \qquad f(x,y) > c + w /2 \ f_t(x,y) &= (f(x,y) - c) \times 255/w &\textrm{en otro caso} \end{align}

print('El centro de ventana es: ', DCM.WindowCenter, '\nEl ancho de ventana es: ', DCM.WindowWidth)
El centro de ventana es:  40 
El ancho de ventana es:  400

Veamos si podemos hacer esto sin tener que generar otra imagen transformada. Para eso, veamos los parámetros opcionales de plt.imshow:

plt.imshow?

Es decir que si pasamos el máximo y el mínimo de la ventana, ya obtendríamos el resultado esperado:

c = 40
w = 400
ventmax = c + w/2
ventmin = c - w/2

fig_1 = plt.figure(1, figsize=[12,12])
img1 = fig_1.add_subplot(111)
img1.imshow(imagen, cmap='gray', vmin=ventmin, vmax=ventmax)
img1.set_title('CORTE AXIAL CT')
plt.show()
_images/7f1268be8c5bc87117aa4abacf733a5a132d361ffc0eb3e1a7dee5a9b0a0ded0.png

Manipulando la imagen#

Finalmente, podemos manipular la imagen a voluntad, dado que es un array de numpy. Por ejemplo, podemos reemplazar los puntos de la matriz por otros valores

imagen2 = imagen.copy()
imagen2[150:160,:] = 1000
imagen2[370:380,:] = 1000


fig_1 = plt.figure(1, figsize=[15,15])
img1 = fig_1.add_subplot(111)
img1.imshow(imagen2, cmap='gray', vmin=ventmin, vmax=ventmax)
img1.set_title('CORTE AXIAL CT')
plt.show()
_images/845063c77c171cd5552cf69b938068051dfef099f44ac599ce1c12f741028620.png

O reemplazar ciertos puntos de acuerdo a una condición particular:

imagen2 = imagen.copy()
imagen2[imagen2 < -800] = 1000
fig_1 = plt.figure(1, figsize=[15,15])
img1 = fig_1.add_subplot(111)
img1.imshow(imagen2, cmap='gray', vmin=ventmin, vmax=ventmax)
img1.set_title('CORTE AXIAL CT')
plt.show()
_images/1c7839e533f0a2b225c962e060c66c1d7347949ddef577756279ff35b48318f8.png
imagen2 = imagen.copy()
imagen2[(imagen2<-800) & (imagen2>-1100)] = 1000

fig5, g = plt.subplots(1, 2, figsize=[16,10])
g[0].imshow(imagen2, cmap = 'gray', vmin=ventmin, vmax=ventmax)
g[0].set_title('IMAGEN 2')

g[1].set_title('HISTOGRAMA de IMAGEN 2')
g[1].hist(imagen2.ravel(), bins=300) #ravel devuelve un vector 1D con todos los elementosde la matrix.
g[1].set_xlabel('HOUNSFIELD UNITS')
g[1].set_xlim()
g[1].set_ylim([0, 4000])

plt.show()

print('El FOV del CT del PET es de: ', imagen2.shape[0] * DCM.PixelSpacing[0], 'mm')
_images/3ffd1a043e90c2aa2fce5cf0d2b7f0b84266cdc778936329dfc430f3012f6ed6.png
El FOV del CT del PET es de:  700.000256 mm

Ventanas de visualización típicas en CT#

  • Cabeza y cuello

    • cerebro w:80 c:40

    • subdural w:130-300 c:50-100

    • acv w:8 c:32 / w:40 c:40

    • hueso w:2800 c:600

    • Tejido blando: w:350–400 c:20–60

  • Pecho

    • Pulmones w:1500 c:-600

    • Mediastino w:350 c:50

  • Abdomen

    • Tejido blando w:400 c:50

    • Hígado w:150 c:30

  • Columna

    • Tejido blando w:250 c:50

    • Hueso w:1800 c:400