Skip to content

Commit 1a42dca

Browse files
author
Romain Andres
committed
ajout mock version 2
1 parent 24d328c commit 1a42dca

22 files changed

Lines changed: 1772 additions & 106 deletions

api.py

Lines changed: 67 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import os
22
import shutil
33
import tempfile
4+
from io import BytesIO
5+
from typing import List
46

57
import pydicom
68
import requests
79
from dotenv import load_dotenv
810
from flask import Flask, g, jsonify, render_template, request
911
from flask_cors import CORS
12+
from pydicom.dataset import Dataset
1013

1114
from BDD.MesuresSQLite import MesuresDB
12-
from mock import simulate_modele_transform, simulate_rtstruct_generation
15+
from mock import simulate_rtstruct_generation2
1316

1417
load_dotenv()
1518

@@ -133,78 +136,39 @@ def segmentation(study_instance_uid):
133136
return jsonify({"error": "StudyInstanceUID not found"}), 404
134137

135138
try:
136-
# Utiliser l'ID Orthanc pour récupérer les instances DICOM pour l'étude spécifiée
137-
response = requests.get(f"{ORTHANC_URL}/studies/{orthanc_study_id}/instances")
138-
if response.status_code == 200:
139-
dicom_instances = response.json()
140-
print("DICOM Instances for StudyInstanceUID", study_instance_uid, ":", dicom_instances)
141-
142-
# Ici, ajouter logique de traitement des images DICOM récupérées
143-
# Pour le moment, on simule la génération du RTStruct
144-
rtstruct_path = simulate_rtstruct_generation() # mock
145-
146-
return upload_rtstruct_mocked(rtstruct_path)
147-
else:
148-
return jsonify({"error": "Failed to retrieve DICOM instances"}), response.status_code
149-
except requests.exceptions.RequestException as e:
150-
print(f"Erreur lors du traitement du fichier DICOM : {e}")
151-
return jsonify({"error": "Server error"}), 500
152-
153-
154-
155-
@app.route('/seuillage/<study_instance_uid>', methods=['POST'])
156-
def apply_seuillage_to_study(study_instance_uid):
157-
orthanc_study_id = find_orthanc_study_id_by_study_instance_uid(study_instance_uid)
158-
if not orthanc_study_id:
159-
return jsonify({"error": "StudyInstanceUID not found"}), 404
160-
161-
try:
139+
# Récupérer les métadonnées des instances DICOM pour l'étude spécifiée
162140
response = requests.get(f"{ORTHANC_URL}/studies/{orthanc_study_id}/instances")
163141
if response.status_code != 200:
164142
return jsonify({"error": "Failed to retrieve DICOM instances"}), response.status_code
165-
166-
dicom_instances = response.json()
167-
temp_dir = tempfile.mkdtemp()
168143

169-
file_paths = []
170-
for instance in dicom_instances:
144+
# Extraire les identifiants des instances DICOM à partir de la réponse JSON
145+
instances = response.json()
146+
dicom_data = []
147+
148+
# Télécharger chaque fichier DICOM en utilisant son identifiant
149+
for instance in instances:
171150
instance_id = instance['ID']
172-
dicom_file = requests.get(f"{ORTHANC_URL}/instances/{instance_id}/file", stream=True)
173-
temp_file_path = os.path.join(temp_dir, f"{instance_id}.dcm")
174-
with open(temp_file_path, 'wb') as f:
175-
for chunk in dicom_file:
176-
f.write(chunk)
177-
file_paths.append(temp_file_path)
151+
dicom_response = requests.get(f"{ORTHANC_URL}/instances/{instance_id}/file", stream=True)
178152

179-
# Apply the seuillage transformation
180-
modified_dicoms = simulate_modele_transform(file_paths)
181-
print("test")
182-
# Optionally upload modified DICOMs back to Orthanc
153+
if dicom_response.status_code == 200:
154+
# Vous pouvez ici charger les données DICOM directement dans pydicom si nécessaire
155+
dicom_file = pydicom.dcmread(BytesIO(dicom_response.content))
156+
dicom_data.append(dicom_file)
157+
else:
158+
print(f"Failed to download DICOM file for instance ID {instance_id}")
183159

184-
delete_dicom_instance(study_instance_uid)
185-
186-
"""
187-
for modified_dicom in modified_dicoms:
188-
print("Uploading")
189-
temp_file_path = os.path.join(temp_dir, modified_dicom.filename)
190-
modified_dicom.save_as(temp_file_path)
191-
with open(temp_file_path, 'rb') as f:
192-
files = {'file': (os.path.basename(temp_file_path), f, 'application/dicom')}
193-
upload_response = requests.post(f"{ORTHANC_URL}/instances", files=files)
194-
print("Upload response:", upload_response.status_code)
195-
if upload_response.status_code not in [200, 202]:
196-
print("Failed to upload modified DICOM file to Orthanc:", upload_response.status_code)
197-
"""
198-
shutil.rmtree(temp_dir) # Clean up temporary files
160+
print("DICOM files retrieved and processed successfully.")
161+
#print(dicom_data[0])
162+
rtstruct = simulate_rtstruct_generation2(dicom_data) # mock
163+
print("mon rtstruct c'est cela")
164+
print(rtstruct)
165+
upload_rtstruct(rtstruct)
199166

167+
return jsonify({"success": "DICOM files retrieved and processed successfully."}), 200
200168
except requests.exceptions.RequestException as e:
201-
return jsonify({"error": str(e)}), 500
202-
203-
return jsonify({"success": "Seuillage applied and DICOMs uploaded."}), 200
169+
return jsonify({"error": f"Server error: {str(e)}"}), 500
204170

205171

206-
207-
208172
def find_orthanc_study_id_by_study_instance_uid(study_instance_uid):
209173
studies_response = requests.get(f"{ORTHANC_URL}/studies")
210174

@@ -228,7 +192,8 @@ def find_orthanc_study_id_by_study_instance_uid(study_instance_uid):
228192

229193
return None
230194

231-
def upload_rtstruct_mocked(rtstruct_path):
195+
"""
196+
def upload_rtstruct(rtstruct_path):
232197
try:
233198
print(f"Uploading RTStruct from {rtstruct_path}")
234199
@@ -244,6 +209,45 @@ def upload_rtstruct_mocked(rtstruct_path):
244209
except Exception as e:
245210
print(e)
246211
return jsonify({"error": "Server error"}), 500
212+
"""
213+
214+
def upload_rtstruct(rtstruct):
215+
buffer = rtstruct.save_to_memory() # Récupère le buffer en mémoire contenant le RTStruct
216+
print("Uploading RTStruct...")
217+
print(buffer)
218+
try:
219+
files = {'file': ('rtstruct.dcm', buffer, 'application/dicom')}
220+
response = requests.post(f"{ORTHANC_URL}/instances", files=files)
221+
222+
if response.status_code in [200, 202]:
223+
orthanc_response = response.json() if response.content else "No JSON content in response"
224+
return jsonify({"success": "RTStruct uploaded successfully", "OrthancResponse": orthanc_response}), response.status_code
225+
else:
226+
return jsonify({"error": "Failed to upload RTStruct to Orthanc", "OrthancResponse": response.text}), response.status_code
227+
except Exception as e:
228+
print(e)
229+
return jsonify({"error": "Server error"}), 500
230+
231+
232+
def load_dicom_datasets(dicom_folder_path: str) -> List[Dataset]:
233+
"""
234+
Charge tous les fichiers DICOM d'un dossier donné en datasets pydicom.
235+
"""
236+
dicom_datasets = []
237+
for filename in os.listdir(dicom_folder_path):
238+
if filename.endswith('.dcm'):
239+
file_path = os.path.join(dicom_folder_path, filename)
240+
try:
241+
ds = pydicom.dcmread(file_path)
242+
dicom_datasets.append(ds)
243+
except Exception as e:
244+
print(f"Erreur lors de la lecture du fichier DICOM {filename}: {e}")
245+
continue # ou lever une exception selon les besoins de votre application
246+
247+
if not dicom_datasets:
248+
raise Exception("Aucun fichier DICOM valide trouvé dans le dossier spécifié.")
249+
250+
return dicom_datasets
247251

248252
def get_db():
249253
db = getattr(g, '_database', None)

mock.py

Lines changed: 51 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,21 @@
11
import os
2+
import sys
23
import time as t
4+
from typing import List
35

46
import matplotlib.pyplot as plt
7+
import nibabel as nib
58
import numpy as np
69
import pydicom
710
import torch
11+
from pydicom.dataset import Dataset
812
from pydicom.encaps import encapsulate
9-
from MetIA.Modele import Net
13+
from scipy.ndimage import gaussian_filter, sobel
1014

11-
import nibabel as nib
15+
from rt_utils import RTStructBuilder
1216

1317
PATH_MODEL = './MetIA/resultsult/'
1418

15-
def simulate_modele_transform(file_paths):
16-
modified_dicoms = [] # Liste pour stocker les objets DICOM modifiés
17-
18-
for file_path in file_paths:
19-
# Chargement du fichier DICOM
20-
dicom_data = pydicom.dcmread(file_path)
21-
22-
# Extraction de l'image des données DICOM
23-
image = dicom_data.pixel_array
24-
25-
# Application du seuillage simple
26-
thresholded_image = np.where(image > 1000, image, 0)
27-
28-
# Vérifiez si les données sont compressées et encapsulez-les si nécessaire
29-
if dicom_data.file_meta.TransferSyntaxUID in [pydicom.uid.JPEGBaseline, pydicom.uid.JPEGExtended, pydicom.uid.JPEG2000]:
30-
# Encapsulate the modified data
31-
dicom_data.PixelData = encapsulate([thresholded_image.tobytes()])
32-
else:
33-
dicom_data.PixelData = thresholded_image.tobytes()
34-
35-
dicom_data.Rows, dicom_data.Columns = thresholded_image.shape
36-
37-
# Ajout de l'objet DICOM modifié à la liste
38-
modified_dicoms.append(dicom_data)
39-
40-
# Retourne la liste des données DICOM modifiées
41-
return modified_dicoms
42-
4319

4420
def simulate_rtstruct_generation():
4521
print()
@@ -53,19 +29,51 @@ def simulate_rtstruct_generation():
5329
rtstruct_path = '/home/romain/Documents/P_R_O_J_E_C_T_S/projetIRM/BrainMetaSegmentatorUI-Back/1-1.dcm'
5430
return rtstruct_path
5531

32+
# pour détecter les contours d'une image DICOM
33+
def detect_contours(image):
34+
"""
35+
Si tu ne comprends rien :
36+
Un filtre de Sobel est appliqué pour trouver les gradients dans les deux directions x et y.
37+
Le gradient total (magnitude) est calculé et un seuil est appliqué pour obtenir un masque de contour.
38+
"""
39+
40+
# lissage de l'image pour réduire le bruit avant la détection des contours
41+
smoothed_image = gaussian_filter(image, sigma=2)
42+
43+
# calcul du gradient de l'image lissée
44+
sx = sobel(smoothed_image, axis=0, mode='constant')
45+
sy = sobel(smoothed_image, axis=1, mode='constant')
46+
sobel_mag = np.hypot(sx, sy)
47+
48+
# seuil de détection du contour
49+
threshold = np.percentile(sobel_mag, 95)
50+
51+
# matrice boolenne
52+
return sobel_mag > threshold
53+
54+
def simulate_rtstruct_generation2(dicom_datasets: List[Dataset]):
5655

57-
def prediction(ArrayDicoms):
58-
# conversion des dicoms en nifti
59-
# ArrayDicoms : liste des dicoms
60-
# prediction : liste des nifti
61-
prediction = []
62-
for dicom in ArrayDicoms:
63-
# co
56+
dicom_files = dicom_datasets
57+
# lire et trier les fichiers dicoms par 'InstanceNumber' #si c pas fait, les rois corespondront pas aux dicoms
58+
dicom_files.sort(key=lambda x: x.InstanceNumber if 'InstanceNumber' in dir(x) else 0)
59+
# creation du rtstruct vierge
60+
rtstruct = RTStructBuilder.create_new_from_memory(dicom_datasets)
61+
62+
63+
# init du masque 3D basé sur le nombre de fichiers et la taille des images
64+
num_slices = len(dicom_files)
65+
mask_shape = (dicom_files[0].Rows, dicom_files[0].Columns, num_slices)
66+
full_mask = np.zeros(mask_shape, dtype=bool)
67+
68+
# pour chaque fichier dicom :
69+
for i, dicom_data in enumerate(dicom_files):
70+
# calculer le masque correspondant et le mettre dans le masque 3D
71+
image = dicom_data.pixel_array
72+
contour_mask = detect_contours(image)
73+
full_mask[:, :, i] = contour_mask # remplissage du masque 3D
6474

65-
# prediction du modele, recharger le modele :
66-
model = Net()
67-
model.load_state_dict(torch.load(PATH_MODEL))
68-
model.eval()
69-
# prediction : application du modele sur les images converties en nifti
75+
# ajouter le masque au fichier RTStruct
76+
rtstruct.add_roi(mask=full_mask, color=[255, 0, 0], name='Brain Contours')
7077

71-
return prediction
78+
# sauvegarde du fichier rtstruct
79+
return rtstruct

rt_utils/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .rtstruct import RTStruct
2+
from .rtstruct_builder import RTStructBuilder
3+
from .rtstruct_merger import RTStructMerger
350 Bytes
Binary file not shown.
6.71 KB
Binary file not shown.
7.93 KB
Binary file not shown.
4.98 KB
Binary file not shown.
3.79 KB
Binary file not shown.
1.35 KB
Binary file not shown.
3.87 KB
Binary file not shown.

0 commit comments

Comments
 (0)