Spaces:
Sleeping
Sleeping
Update irm_cancer_module.py
Browse files- irm_cancer_module.py +42 -38
irm_cancer_module.py
CHANGED
|
@@ -1,36 +1,35 @@
|
|
| 1 |
-
import os
|
| 2 |
import nibabel as nib
|
| 3 |
import numpy as np
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
"""Charge les 4 fichiers NIfTI (FLAIR, T1, T1CE, T2)."""
|
| 7 |
-
images = []
|
| 8 |
-
for f in files:
|
| 9 |
-
img = nib.load(f)
|
| 10 |
-
images.append(img.get_fdata())
|
| 11 |
-
return np.stack(images, axis=0)
|
| 12 |
|
| 13 |
def run(files):
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
# 📂 Dossier de sortie sécurisé
|
| 19 |
-
output_dir = "/tmp/irm_outputs"
|
| 20 |
-
os.makedirs(output_dir, exist_ok=True)
|
| 21 |
|
| 22 |
-
|
| 23 |
-
data = load_patient_data(files)
|
| 24 |
|
| 25 |
-
#
|
| 26 |
-
seg
|
|
|
|
|
|
|
| 27 |
|
| 28 |
-
|
| 29 |
-
voxels = int(seg.sum())
|
| 30 |
-
voxel_volume_ml = 0.001 # ≈ volume fictif par voxel
|
| 31 |
volume_ml = voxels * voxel_volume_ml
|
| 32 |
|
| 33 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
report_text = f"""
|
| 35 |
==== Compte-rendu automatique de segmentation ====
|
| 36 |
|
|
@@ -38,33 +37,38 @@ Modalités utilisées : FLAIR, T1, T1CE, T2
|
|
| 38 |
|
| 39 |
--- Volumétrie ---
|
| 40 |
Volume tumoral total : {volume_ml:.2f} ml ({voxels} voxels)
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
--- Interprétation clinique ---
|
| 43 |
- Masse suspectée détectée.
|
| 44 |
-
-
|
| 45 |
-
-
|
|
|
|
|
|
|
| 46 |
|
| 47 |
--- Recommandations ---
|
| 48 |
1. Discussion multidisciplinaire (neuro-oncologie).
|
| 49 |
-
2. IRM avec injection + séquence de perfusion si
|
| 50 |
3. Biopsie/examen histologique recommandé.
|
| 51 |
|
| 52 |
-
⚠️ Rapport généré automatiquement, non destiné à remplacer un avis médical
|
| 53 |
"""
|
| 54 |
|
| 55 |
-
# Sauvegardes
|
|
|
|
|
|
|
| 56 |
nii_path = os.path.join(output_dir, "irm_image.nii.gz")
|
| 57 |
-
report_path = os.path.join(output_dir, "
|
| 58 |
-
mask_path = os.path.join(output_dir, "
|
| 59 |
|
| 60 |
-
# Sauvegarder image de référence (ici FLAIR uniquement pour exemple)
|
| 61 |
nib.save(nib.Nifti1Image(data[0], np.eye(4)), nii_path)
|
|
|
|
| 62 |
|
| 63 |
-
#
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
with open(report_path, "w") as f:
|
| 68 |
-
f.write(report_text)
|
| 69 |
|
| 70 |
return seg, report_text, (nii_path, report_path, mask_path)
|
|
|
|
|
|
|
| 1 |
import nibabel as nib
|
| 2 |
import numpy as np
|
| 3 |
+
import os
|
| 4 |
+
import cv2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
def run(files):
|
| 7 |
+
data = []
|
| 8 |
+
for f in files:
|
| 9 |
+
img = nib.load(f).get_fdata()
|
| 10 |
+
data.append(img)
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
+
seg = np.zeros_like(data[0], dtype=np.uint8)
|
|
|
|
| 13 |
|
| 14 |
+
# Segmentation fictive (tu remplaceras par ton modèle réel)
|
| 15 |
+
seg[data[0] > np.percentile(data[0], 99)] = 4 # Enhancing Tumor (ET)
|
| 16 |
+
seg[data[1] > np.percentile(data[1], 99)] = 1 # NCR/NET
|
| 17 |
+
seg[data[2] > np.percentile(data[2], 99)] = 2 # Edema
|
| 18 |
|
| 19 |
+
voxel_volume_ml = np.prod(nib.load(files[0]).header.get_zooms()) / 1000.0
|
| 20 |
+
voxels = int((seg > 0).sum())
|
|
|
|
| 21 |
volume_ml = voxels * voxel_volume_ml
|
| 22 |
|
| 23 |
+
# Décomposition par classes
|
| 24 |
+
ncr_voxels = int((seg == 1).sum())
|
| 25 |
+
ed_voxels = int((seg == 2).sum())
|
| 26 |
+
et_voxels = int((seg == 4).sum())
|
| 27 |
+
|
| 28 |
+
ncr_vol = ncr_voxels * voxel_volume_ml
|
| 29 |
+
ed_vol = ed_voxels * voxel_volume_ml
|
| 30 |
+
et_vol = et_voxels * voxel_volume_ml
|
| 31 |
+
|
| 32 |
+
# Rapport enrichi
|
| 33 |
report_text = f"""
|
| 34 |
==== Compte-rendu automatique de segmentation ====
|
| 35 |
|
|
|
|
| 37 |
|
| 38 |
--- Volumétrie ---
|
| 39 |
Volume tumoral total : {volume_ml:.2f} ml ({voxels} voxels)
|
| 40 |
+
- Nécrose / non rehaussé (NCR/NET) : {ncr_vol:.2f} ml ({ncr_voxels} voxels)
|
| 41 |
+
- Œdème péri-tumoral (ED) : {ed_vol:.2f} ml ({ed_voxels} voxels)
|
| 42 |
+
- Masse rehaussée (ET) : {et_vol:.2f} ml ({et_voxels} voxels)
|
| 43 |
|
| 44 |
--- Interprétation clinique ---
|
| 45 |
- Masse suspectée détectée.
|
| 46 |
+
{ "- Présence de zones nécrotiques." if ncr_voxels > 0 else "" }
|
| 47 |
+
{ "- Œdème détecté." if ed_voxels > 0 else "" }
|
| 48 |
+
{ "- Rehaussement détecté → suspicion de tumeur de haut grade." if et_voxels > 0 else "- Pas de rehaussement visible." }
|
| 49 |
+
- Volume > 100 ml → effet de masse probable.
|
| 50 |
|
| 51 |
--- Recommandations ---
|
| 52 |
1. Discussion multidisciplinaire (neuro-oncologie).
|
| 53 |
+
2. IRM avec injection + séquence de perfusion si dispo.
|
| 54 |
3. Biopsie/examen histologique recommandé.
|
| 55 |
|
| 56 |
+
⚠️ Rapport généré automatiquement, non destiné à remplacer un avis médical.
|
| 57 |
"""
|
| 58 |
|
| 59 |
+
# Sauvegardes dans /tmp
|
| 60 |
+
output_dir = "/tmp/output"
|
| 61 |
+
os.makedirs(output_dir, exist_ok=True)
|
| 62 |
nii_path = os.path.join(output_dir, "irm_image.nii.gz")
|
| 63 |
+
report_path = os.path.join(output_dir, "rapport.txt")
|
| 64 |
+
mask_path = os.path.join(output_dir, "mask_preview.png")
|
| 65 |
|
|
|
|
| 66 |
nib.save(nib.Nifti1Image(data[0], np.eye(4)), nii_path)
|
| 67 |
+
with open(report_path, "w") as f: f.write(report_text)
|
| 68 |
|
| 69 |
+
# Sauvegarde du masque visible (couleur)
|
| 70 |
+
slice_idx = seg.shape[2] // 2
|
| 71 |
+
mask_rgb = cv2.applyColorMap((seg[:,:,slice_idx] * 60).astype(np.uint8), cv2.COLORMAP_JET)
|
| 72 |
+
cv2.imwrite(mask_path, mask_rgb)
|
|
|
|
|
|
|
| 73 |
|
| 74 |
return seg, report_text, (nii_path, report_path, mask_path)
|