Há alguns dias, fiz uma ressonância magnética. Eles me colocaram em um tubo grande e, por 15 minutos, a máquina ao meu redor zumbiu, zumbiu e clicou. Ao final do exame, recebi um CD contendo os dados. O que um bom desenvolvedor faz nessa situação? É claro que, assim que chegam em casa, eles começam a examinar os dados e a pensar em como poderiam extraí-los usando Python .
O CD continha um monte de DLLs, um EXE e alguns arquivos adicionais para um visualizador do Windows, que são obviamente inúteis no Linux. O ponto crucial estava em uma pasta chamada DICOM. Ele continha uma série de arquivos sem extensão em oito pastas. ChatGPT me ajudou a descobrir que DICOM é um formato padrão normalmente usado para armazenar imagens de ressonância magnética, tomografia computadorizada e raios-X. Essencialmente, é um formato de pacote simples que contém imagens em tons de cinza junto com alguns metadados.
O código a seguir permite exportar facilmente os arquivos no formato PNG:
import os import pydicom from PIL import Image import sys def save_image(pixel_array, output_path): if pixel_array.dtype != 'uint8': pixel_array = ((pixel_array - pixel_array.min()) / (pixel_array.max() - pixel_array.min()) * 255).astype('uint8') img = Image.fromarray(pixel_array) img.save(output_path) print(f"Saved image to {output_path}") def process_dicom_directory(dicomdir_path, save_directory): if not os.path.exists(save_directory): os.makedirs(save_directory) dicomdir = pydicom.dcmread(dicomdir_path) for patient_record in dicomdir.patient_records: if 'PatientName' in patient_record: print(f"Patient: {patient_record.PatientName}") for study_record in patient_record.children: if 'StudyDate' in study_record: print(f"Study: {study_record.StudyDate}") for series_record in study_record.children: for image_record in series_record.children: if 'ReferencedFileID' in image_record: file_id = list(map(str, image_record.ReferencedFileID)) file_id_path = os.path.join(*file_id) print(f"Image: {file_id_path}") image_path = os.path.join(os.path.dirname(dicomdir_path), file_id_path) modified_filename = '_'.join(file_id) + '.png' image_save_path = os.path.join(save_directory, modified_filename) try: img_data = pydicom.dcmread(image_path) print("DICOM image loaded successfully.") save_image(img_data.pixel_array, image_save_path) except FileNotFoundError as e: print(f"Failed to read DICOM file at {image_path}: {e}") except Exception as e: print(f"An error occurred: {e}") if __name__ == "__main__": if len(sys.argv) < 3: print("Usage: python dicomsave.py <path_to_DICOMDIR> <path_to_save_images>") sys.exit(1) dicomdir_path = sys.argv[1] save_directory = sys.argv[2] process_dicom_directory(dicomdir_path, save_directory)
O código espera dois parâmetros. O primeiro é o caminho para o arquivo DICOMDIR e o segundo é um diretório onde as imagens serão salvas no formato PNG.
Para ler os arquivos DICOM, usamos a biblioteca Pydicom . A estrutura é carregada através da função pydicom.dcmread , da qual podem ser extraídos metadados (como o nome do paciente) e estudos contendo as imagens. Os dados da imagem também podem ser lidos com dcmread . Os dados brutos são acessíveis através do campo pixelarray , que a função save_image converte para PNG. Não é muito complicado.
O resultado são algumas imagens quadradas (normalmente 512x512) em tons de cinza, que podem ser facilmente utilizadas posteriormente.
Por que achei importante escrever este conto sobre minha aventura no formato DICOM? Quando um programador ouve falar em processamento de dados médicos, pode pensar que é algo sério, algo que apenas universidades e institutos de pesquisa podem lidar (pelo menos foi o que pensei). Como você pode ver, estamos falando de imagens simples em tons de cinza, que são muito fáceis de processar e ideais para coisas como processamento de redes neurais.
Considere as possibilidades que isso abre. Para algo como um simples detector de tumor, tudo o que você precisa é de uma rede convolucional relativamente simples e de um número suficiente de amostras. Na realidade, a tarefa não é muito diferente de reconhecer cães ou gatos em imagens.
Para ilustrar como isso é verdade, deixe-me dar um exemplo. Após uma breve pesquisa no Kaggle, encontrei um caderno onde os tumores cerebrais são classificados com base em dados de ressonância magnética (imagens em escala de cinza). O conjunto de dados categoriza as imagens em quatro grupos: três tipos de tumores cerebrais e uma quarta categoria com imagens de cérebros saudáveis. A arquitetura da rede é a seguinte:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 164, 164, 64) │ 1,664 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 54, 54, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_1 (Conv2D) │ (None, 50, 50, 64) │ 102,464 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 16, 16, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_2 (Conv2D) │ (None, 13, 13, 128) │ 131,200 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 6, 6, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_3 (Conv2D) │ (None, 3, 3, 128) │ 262,272 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_3 (MaxPooling2D) │ (None, 1, 1, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten (Flatten) │ (None, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense (Dense) │ (None, 512) │ 66,048 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_1 (Dense) │ (None, 4) │ 2,052 │ └─────────────────────────────────┴────────────────────────┴───────────────┘
Como você pode ver, as primeiras camadas consistem em um sanduíche de convolução e pooling máximo, que extrai padrões de tumores, seguidas por duas camadas densas que realizam a classificação. Esta é exatamente a mesma arquitetura usada no código de exemplo CNN do TensorFlow que usa o conjunto de dados CIFAR-10. Lembro-me bem, pois foi uma das primeiras redes neurais que encontrei. O conjunto de dados CIFAR-10 contém 60.000 imagens categorizadas em dez classes, incluindo duas classes para cães e gatos, como mencionei anteriormente. A arquitetura da rede é assim:
Model: “sequential” _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d (Conv2D) (None, 30, 30, 32) 896 _________________________________________________________________ max_pooling2d (MaxPooling2D) (None, 15, 15, 32) 0 _________________________________________________________________ conv2d_1 (Conv2D) (None, 13, 13, 64) 18496 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 6, 6, 64) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 4, 4, 64) 36928 _________________________________________________________________ flatten (Flatten) (None, 1024) 0 _________________________________________________________________ dense (Dense) (None, 64) 65600 _________________________________________________________________ dense_1 (Dense) (None, 10) 650 ================================================================= Total params: 122,570 Trainable params: 122,570 Non-trainable params: 0 _________________________________________________________________
É claro que estamos lidando com a mesma arquitetura, apenas com parâmetros ligeiramente diferentes, portanto reconhecer tumores cerebrais em imagens de ressonância magnética não é de fato muito mais difícil do que identificar cães ou gatos em imagens. Além disso, o referido caderno Kaggle é capaz de classificar tumores com 99% de eficácia!
Quero apenas salientar como pode ser simples extrair e processar dados médicos em certos casos e como problemas que parecem complexos, como a detecção de tumores cerebrais, podem muitas vezes ser resolvidos de forma bastante eficaz com soluções simples e tradicionais.
Portanto, eu encorajaria todos a não se intimidarem com os dados médicos. Como você pode ver, estamos lidando com formatos relativamente simples e, usando tecnologias relativamente simples e bem estabelecidas (redes convolucionais, transformadores de visão, etc.), um impacto significativo pode ser alcançado aqui. Se você participar de um projeto de saúde de código aberto em seu tempo livre, apenas como hobby, e puder fazer até mesmo uma pequena melhoria na rede neural usada, você poderá salvar vidas!