Il y a quelques jours, j'ai passé une IRM. Ils m'ont glissé dans un grand tube et pendant 15 minutes, la machine autour de moi a bourdonné, bourdonné et cliqué. A la fin de l'examen, j'ai reçu un CD contenant les données. Que fait un bon développeur dans une telle situation ? Bien sûr, dès leur retour chez eux, ils commencent à examiner les données et à réfléchir à la manière dont ils pourraient les extraire à l'aide . de Python Le CD contenait un tas de DLL, un EXE et quelques fichiers supplémentaires pour une visionneuse Windows, qui sont évidemment inutiles sous Linux. Le point crucial se trouvait dans un dossier nommé DICOM. Il contenait une série de fichiers sans extension répartis dans huit dossiers. ChatGPT m'a aidé à comprendre que est un format standard généralement utilisé pour stocker les images IRM, CT et radiographiques. Il s'agit essentiellement d'un simple format d'emballage contenant des images en niveaux de gris ainsi que des métadonnées. DICOM Le permet d'exporter facilement les fichiers au format PNG : code suivant 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) Le code attend deux paramètres. Le premier est le chemin d'accès au fichier DICOMDIR et le second est un répertoire dans lequel les images seront enregistrées au format PNG. Pour lire les fichiers DICOM, nous utilisons la bibliothèque . La structure est chargée à l'aide de la fonction , à partir de laquelle les métadonnées (telles que le nom du patient) et les études contenant les images peuvent être extraites. Les données d'image peuvent également être lues avec . Les données brutes sont accessibles via le champ , que la fonction convertit en PNG. Ce n'est pas trop compliqué. Pydicom pydicom.dcmread dcmread pixelarray save_image Le résultat est des images en niveaux de gris de quelques carrés (généralement 512 x 512), qui peuvent être facilement utilisées ultérieurement. Pourquoi ai-je pensé qu’il était important d’écrire cette nouvelle sur mon aventure au format DICOM ? Lorsqu'un programmeur entend parler du traitement de données médicales, il peut penser que c'est quelque chose de sérieux, quelque chose que seules les universités et les instituts de recherche peuvent gérer (du moins, c'est ce que je pensais). Comme vous pouvez le voir, nous parlons d'images simples en niveaux de gris, très faciles à traiter et idéales pour des choses comme le traitement des réseaux neuronaux. Considérez les possibilités que cela ouvre. Pour quelque chose comme un simple détecteur de tumeur, tout ce dont vous avez besoin est un réseau convolutif relativement simple et un nombre suffisant d’échantillons. En réalité, la tâche n’est pas très différente de la reconnaissance de chiens ou de chats sur des images. Pour illustrer à quel point cela est vrai, permettez-moi de donner un exemple. Après une brève recherche sur Kaggle, dans lequel les tumeurs cérébrales sont classées sur la base de données IRM (images en niveaux de gris). L'ensemble de données classe les images en quatre groupes : trois types de tumeurs cérébrales et une quatrième catégorie avec des images de cerveaux sains. L'architecture du réseau est la suivante : j'ai trouvé un cahier ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ 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 │ └─────────────────────────────────┴────────────────────────┴───────────────┘ Comme vous pouvez le voir, les premières couches sont constituées d'un sandwich de convolution et de pooling maximum, qui extrait les modèles de tumeurs, suivi de deux couches denses qui effectuent la classification. Il s'agit exactement de la même architecture que celle utilisée dans qui utilise l'ensemble de données CIFAR-10. Je m'en souviens bien, car c'était l'un des premiers réseaux de neurones que j'ai rencontré. L'ensemble de données contient 60 000 images classées en dix classes, dont deux classes pour les chiens et les chats, comme je l'ai mentionné plus tôt. L'architecture du réseau ressemble à ceci : l'exemple de code CNN de TensorFlow CIFAR-10 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 _________________________________________________________________ Il est clair que nous avons affaire à la même architecture, mais avec des paramètres légèrement différents, donc reconnaître des tumeurs cérébrales sur des images IRM n'est en effet pas beaucoup plus difficile que d'identifier des chats ou des chiens sur des images. De plus, le carnet Kaggle mentionné est capable de classer les tumeurs avec une efficacité de 99 % ! Je veux juste souligner à quel point il peut être simple d'extraire et de traiter des données médicales dans certains cas et comment des problèmes qui semblent complexes, comme la détection de tumeurs cérébrales, peuvent souvent être résolus de manière assez efficace avec des solutions simples et traditionnelles. Par conséquent, j’encourage tout le monde à ne pas craindre les données médicales. Comme vous pouvez le constater, nous avons affaire à des formats relativement simples, et en utilisant des technologies relativement simples et bien établies (réseaux convolutifs, transformateurs de vision, etc.), un impact significatif peut être obtenu ici. Si vous rejoignez un projet de santé open source pendant votre temps libre, simplement comme passe-temps et que vous parvenez à apporter ne serait-ce qu'une légère amélioration au réseau neuronal utilisé, vous pourriez potentiellement sauver des vies !