Spaces:
Running
on
Zero
Running
on
Zero
Change methode request gpu
Browse files- app.py +95 -109
- translator.py +38 -0
- utilities/distributed.py +0 -1
app.py
CHANGED
@@ -20,16 +20,24 @@ subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'SimpleITK'])
|
|
20 |
# Imports Hugging Face
|
21 |
from huggingface_hub import hf_hub_download, login
|
22 |
import spaces
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
|
24 |
# Imports locaux
|
25 |
from modeling.BaseModel import BaseModel
|
26 |
from modeling import build_model
|
27 |
-
from utilities.distributed import init_distributed
|
28 |
from utilities.arguments import load_opt_from_config_files
|
29 |
from utilities.constants import BIOMED_CLASSES
|
30 |
-
from
|
31 |
from inference_utils.output_processing import check_mask_stats
|
32 |
-
|
|
|
|
|
|
|
33 |
|
34 |
def init_huggingface():
|
35 |
"""Initialize Hugging Face connection and download the model."""
|
@@ -45,151 +53,124 @@ def init_huggingface():
|
|
45 |
)
|
46 |
return pretrained_path
|
47 |
|
48 |
-
|
49 |
-
|
50 |
-
print(f"Configuration distribuée appliquée : {opt}")
|
51 |
-
|
52 |
def init_distributed(opt):
|
53 |
-
"""Initialize distributed mode without premature CUDA initialization."""
|
54 |
opt['CUDA'] = opt.get('CUDA', True) and torch.cuda.is_available()
|
55 |
if 'OMPI_COMM_WORLD_SIZE' not in os.environ:
|
56 |
-
#
|
|
|
57 |
opt['env_info'] = 'no MPI'
|
58 |
opt['world_size'] = 1
|
59 |
opt['local_size'] = 1
|
60 |
opt['rank'] = 0
|
61 |
-
opt['local_rank'] = 0
|
62 |
opt['master_address'] = '127.0.0.1'
|
63 |
opt['master_port'] = '8673'
|
64 |
else:
|
65 |
-
#
|
|
|
66 |
opt['world_size'] = int(os.environ['OMPI_COMM_WORLD_SIZE'])
|
67 |
opt['local_size'] = int(os.environ['OMPI_COMM_WORLD_LOCAL_SIZE'])
|
68 |
opt['rank'] = int(os.environ['OMPI_COMM_WORLD_RANK'])
|
69 |
opt['local_rank'] = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK'])
|
70 |
|
|
|
71 |
if not opt['CUDA']:
|
72 |
-
assert opt['world_size'] == 1, '
|
73 |
opt['device'] = torch.device("cpu")
|
74 |
else:
|
75 |
-
|
|
|
76 |
|
77 |
-
apply_distributed(opt)
|
78 |
return opt
|
79 |
|
80 |
def setup_model():
|
81 |
"""Initialize the model on CPU without CUDA initialization."""
|
82 |
opt = load_opt_from_config_files(["configs/biomedparse_inference.yaml"])
|
83 |
opt = init_distributed(opt)
|
84 |
-
opt['device'] = 'cpu'
|
85 |
-
|
86 |
pretrained_path = init_huggingface()
|
87 |
-
model = BaseModel(opt, build_model(opt))
|
88 |
-
|
89 |
-
model.load_state_dict(state_dict, strict=False)
|
90 |
-
|
91 |
-
# Initialize train_class_names
|
92 |
-
model.train_class_names = BIOMED_CLASSES + ["background"]
|
93 |
-
|
94 |
-
return model.eval()
|
95 |
-
|
96 |
-
import numpy as np
|
97 |
-
from PIL import Image
|
98 |
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
#
|
106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
|
108 |
-
#
|
109 |
-
|
110 |
-
|
111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
|
113 |
-
|
|
|
|
|
114 |
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
# Convert PIL Image to numpy array if needed
|
120 |
-
if isinstance(image, Image.Image):
|
121 |
-
image = np.array(image)
|
122 |
-
|
123 |
-
# Ensure image is in float32 and normalized
|
124 |
-
image = image.astype(np.float32) / 255.0
|
125 |
-
|
126 |
-
# Transpose from HWC to CHW format
|
127 |
-
if len(image.shape) == 3:
|
128 |
-
image = np.transpose(image, (2, 0, 1))
|
129 |
-
|
130 |
-
# Add batch dimension if needed
|
131 |
-
if len(image.shape) == 3:
|
132 |
-
image = np.expand_dims(image, axis=0)
|
133 |
-
|
134 |
-
# Convert to tensor
|
135 |
-
image_tensor = torch.from_numpy(image)
|
136 |
-
|
137 |
-
# Move to GPU if available
|
138 |
-
if torch.cuda.is_available():
|
139 |
-
device = torch.device("cuda", 0)
|
140 |
-
model = model.to(device)
|
141 |
-
image_tensor = image_tensor.to(device)
|
142 |
-
else:
|
143 |
-
device = torch.device("cpu")
|
144 |
-
|
145 |
-
# Create batched input
|
146 |
-
batched_inputs = [{
|
147 |
-
"image": image_tensor,
|
148 |
-
"prompt": prompts,
|
149 |
-
"height": image_tensor.shape[-2],
|
150 |
-
"width": image_tensor.shape[-1]
|
151 |
-
}]
|
152 |
-
|
153 |
-
with torch.no_grad():
|
154 |
-
pred_masks = model(batched_inputs)
|
155 |
-
|
156 |
-
# Move back to CPU if needed
|
157 |
-
if device.type == "cuda":
|
158 |
-
model = model.to("cpu")
|
159 |
-
pred_masks = [mask.cpu() for mask in pred_masks]
|
160 |
-
|
161 |
-
return pred_masks
|
162 |
-
|
163 |
-
except Exception as e:
|
164 |
-
print(f"Error processing image: {str(e)}")
|
165 |
-
raise
|
166 |
|
167 |
-
def process_image(image,
|
168 |
"""Process image with proper error handling."""
|
169 |
try:
|
170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
if not prompts:
|
172 |
raise ValueError("No valid prompts provided")
|
173 |
|
174 |
pred_masks = predict_image(model, image, prompts)
|
175 |
-
|
176 |
-
# Create visualization
|
177 |
-
fig = plt.figure(figsize=(5 * (len(pred_masks) + 1), 5))
|
178 |
-
|
179 |
-
# Show original image
|
180 |
plt.subplot(1, len(pred_masks) + 1, 1)
|
181 |
-
plt.imshow(
|
182 |
-
plt.title(
|
183 |
plt.axis('off')
|
184 |
-
|
185 |
-
# Show predictions
|
186 |
for i, mask in enumerate(pred_masks):
|
187 |
-
plt.subplot(1, len(pred_masks) + 1, i+2)
|
188 |
-
plt.imshow(
|
189 |
-
plt.imshow(mask
|
190 |
plt.title(prompts[i])
|
191 |
plt.axis('off')
|
192 |
-
|
193 |
return fig
|
194 |
|
195 |
except Exception as e:
|
@@ -199,6 +180,7 @@ def process_image(image, text, model):
|
|
199 |
def setup_gradio_interface(model):
|
200 |
"""Configure l'interface Gradio."""
|
201 |
return gr.Interface(
|
|
|
202 |
fn=lambda img, txt: process_image(img, txt, model),
|
203 |
inputs=[
|
204 |
gr.Image(type="numpy", label="Image médicale"),
|
@@ -209,8 +191,12 @@ def setup_gradio_interface(model):
|
|
209 |
)
|
210 |
],
|
211 |
outputs=gr.Plot(),
|
212 |
-
title="Core IA - Traitement d'image medicale",
|
213 |
-
description="Chargez une image médicale et spécifiez les éléments à segmenter
|
|
|
|
|
|
|
|
|
214 |
examples=[
|
215 |
["examples/144DME_as_F.jpeg", "Dans cette image donne moi l'œdème"],
|
216 |
["examples/T0011.jpg", "disque optique, cupule optique"],
|
@@ -224,7 +210,7 @@ def main():
|
|
224 |
"""Entry point avoiding CUDA initialization in main process."""
|
225 |
try:
|
226 |
init_huggingface()
|
227 |
-
model = setup_model()
|
228 |
interface = setup_gradio_interface(model)
|
229 |
interface.launch(debug=True)
|
230 |
except Exception as e:
|
|
|
20 |
# Imports Hugging Face
|
21 |
from huggingface_hub import hf_hub_download, login
|
22 |
import spaces
|
23 |
+
import numpy as np
|
24 |
+
from PIL import Image
|
25 |
+
import torch
|
26 |
+
import torch.nn.functional as F
|
27 |
+
from torchvision import transforms
|
28 |
+
from translator import translate_text
|
29 |
|
30 |
# Imports locaux
|
31 |
from modeling.BaseModel import BaseModel
|
32 |
from modeling import build_model
|
|
|
33 |
from utilities.arguments import load_opt_from_config_files
|
34 |
from utilities.constants import BIOMED_CLASSES
|
35 |
+
from modeling.language.loss import vl_similarity
|
36 |
from inference_utils.output_processing import check_mask_stats
|
37 |
+
|
38 |
+
t = []
|
39 |
+
t.append(transforms.Resize((1024, 1024), interpolation=Image.BICUBIC))
|
40 |
+
transform = transforms.Compose(t)
|
41 |
|
42 |
def init_huggingface():
|
43 |
"""Initialize Hugging Face connection and download the model."""
|
|
|
53 |
)
|
54 |
return pretrained_path
|
55 |
|
56 |
+
@torch.no_grad()
|
57 |
+
@spaces.GPU
|
|
|
|
|
58 |
def init_distributed(opt):
|
|
|
59 |
opt['CUDA'] = opt.get('CUDA', True) and torch.cuda.is_available()
|
60 |
if 'OMPI_COMM_WORLD_SIZE' not in os.environ:
|
61 |
+
# application was started without MPI
|
62 |
+
# default to single node with single process
|
63 |
opt['env_info'] = 'no MPI'
|
64 |
opt['world_size'] = 1
|
65 |
opt['local_size'] = 1
|
66 |
opt['rank'] = 0
|
67 |
+
opt['local_rank'] = 0
|
68 |
opt['master_address'] = '127.0.0.1'
|
69 |
opt['master_port'] = '8673'
|
70 |
else:
|
71 |
+
# application was started with MPI
|
72 |
+
# get MPI parameters
|
73 |
opt['world_size'] = int(os.environ['OMPI_COMM_WORLD_SIZE'])
|
74 |
opt['local_size'] = int(os.environ['OMPI_COMM_WORLD_LOCAL_SIZE'])
|
75 |
opt['rank'] = int(os.environ['OMPI_COMM_WORLD_RANK'])
|
76 |
opt['local_rank'] = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK'])
|
77 |
|
78 |
+
# set up device
|
79 |
if not opt['CUDA']:
|
80 |
+
assert opt['world_size'] == 1, 'multi-GPU training without CUDA is not supported since we use NCCL as communication backend'
|
81 |
opt['device'] = torch.device("cpu")
|
82 |
else:
|
83 |
+
torch.cuda.set_device(opt['local_rank'])
|
84 |
+
opt['device'] = torch.device("cuda", opt['local_rank'])
|
85 |
|
|
|
86 |
return opt
|
87 |
|
88 |
def setup_model():
|
89 |
"""Initialize the model on CPU without CUDA initialization."""
|
90 |
opt = load_opt_from_config_files(["configs/biomedparse_inference.yaml"])
|
91 |
opt = init_distributed(opt)
|
|
|
|
|
92 |
pretrained_path = init_huggingface()
|
93 |
+
model = BaseModel(opt, build_model(opt)).from_pretrained(pretrained_path).eval()
|
94 |
+
return model
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
|
96 |
+
@torch.no_grad()
|
97 |
+
@spaces.GPU
|
98 |
+
def predict_image(model, image, prompts):
|
99 |
+
model = model.cuda()
|
100 |
+
print("====================== Model moved to GPU via predict_image ======================")
|
101 |
+
prompts = [translate_text(p, "fr", "en") for p in prompts]
|
102 |
+
# Initialiser les embeddings textuels avant l'évaluation
|
103 |
+
lang_encoder = model.model.sem_seg_head.predictor.lang_encoder
|
104 |
+
lang_encoder.get_text_embeddings(BIOMED_CLASSES + ["background"], is_eval=True)
|
105 |
+
model.model.sem_seg_head.predictor.lang_encoder.get_text_embeddings(BIOMED_CLASSES + ["background"], is_eval=True)
|
106 |
+
# Préparer l'image
|
107 |
+
image_resize = transform(image)
|
108 |
+
width = image.size[0]
|
109 |
+
height = image.size[1]
|
110 |
+
image_resize = np.asarray(image_resize)
|
111 |
+
image = torch.from_numpy(image_resize.copy()).permute(2,0,1).cuda()
|
112 |
+
|
113 |
+
# Préparer les données d'entrée
|
114 |
+
data = {"image": image, 'text': prompts, "height": height, "width": width}
|
115 |
|
116 |
+
# Configurer les tâches
|
117 |
+
model.model.task_switch['spatial'] = False
|
118 |
+
model.model.task_switch['visual'] = False
|
119 |
+
model.model.task_switch['grounding'] = True
|
120 |
+
model.model.task_switch['audio'] = False
|
121 |
+
|
122 |
+
# Évaluation
|
123 |
+
batch_inputs = [data]
|
124 |
+
results, image_size, extra = model.model.evaluate_demo(batch_inputs)
|
125 |
+
|
126 |
+
# Traitement des prédictions
|
127 |
+
pred_masks = results['pred_masks'][0]
|
128 |
+
v_emb = results['pred_captions'][0]
|
129 |
+
t_emb = extra['grounding_class']
|
130 |
+
|
131 |
+
# Normalisation
|
132 |
+
t_emb = t_emb / (t_emb.norm(dim=-1, keepdim=True) + 1e-7)
|
133 |
+
v_emb = v_emb / (v_emb.norm(dim=-1, keepdim=True) + 1e-7)
|
134 |
+
|
135 |
+
# Calcul de similarité
|
136 |
+
temperature = lang_encoder.logit_scale
|
137 |
+
out_prob = vl_similarity(v_emb, t_emb, temperature=temperature)
|
138 |
|
139 |
+
# Sélection des masques
|
140 |
+
matched_id = out_prob.max(0)[1]
|
141 |
+
pred_masks_pos = pred_masks[matched_id,:,:]
|
142 |
|
143 |
+
# Redimensionnement à la taille d'origine
|
144 |
+
pred_mask_prob = F.interpolate(pred_masks_pos[None,], (data['height'], data['width']),
|
145 |
+
mode='bilinear')[0,:,:data['height'],:data['width']].sigmoid().cpu().detach().numpy()
|
146 |
+
return pred_mask_prob
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
|
148 |
+
def process_image(image, prompts, model):
|
149 |
"""Process image with proper error handling."""
|
150 |
try:
|
151 |
+
if isinstance(image, str):
|
152 |
+
image = Image.open(image)
|
153 |
+
else:
|
154 |
+
image = Image.fromarray(image)
|
155 |
+
|
156 |
+
prompts = [p.strip() for p in prompts.split(',')]
|
157 |
+
|
158 |
if not prompts:
|
159 |
raise ValueError("No valid prompts provided")
|
160 |
|
161 |
pred_masks = predict_image(model, image, prompts)
|
162 |
+
fig = plt.figure(figsize=(10, 5))
|
|
|
|
|
|
|
|
|
163 |
plt.subplot(1, len(pred_masks) + 1, 1)
|
164 |
+
plt.imshow(image)
|
165 |
+
plt.title('Image originale')
|
166 |
plt.axis('off')
|
167 |
+
|
|
|
168 |
for i, mask in enumerate(pred_masks):
|
169 |
+
plt.subplot(1, len(pred_masks) + 1, i + 2)
|
170 |
+
plt.imshow(image)
|
171 |
+
plt.imshow(mask, alpha=0.5, cmap='Reds')
|
172 |
plt.title(prompts[i])
|
173 |
plt.axis('off')
|
|
|
174 |
return fig
|
175 |
|
176 |
except Exception as e:
|
|
|
180 |
def setup_gradio_interface(model):
|
181 |
"""Configure l'interface Gradio."""
|
182 |
return gr.Interface(
|
183 |
+
theme=gr.Theme.from_hub("JohnSmith9982/small_and_pretty"),
|
184 |
fn=lambda img, txt: process_image(img, txt, model),
|
185 |
inputs=[
|
186 |
gr.Image(type="numpy", label="Image médicale"),
|
|
|
191 |
)
|
192 |
],
|
193 |
outputs=gr.Plot(),
|
194 |
+
title=" 🇬🇦 Core IA - Traitement d'image medicale",
|
195 |
+
description="""Chargez une image médicale de type (IRM , Echographie, ) et spécifiez les éléments à segmenter : Les cas d’utilisation incluent des tâches variées d’analyse d’images médicales. En imagerie CT, le modèle peut détecter et segmenter des organes et pathologies dans des régions telles que l’abdomen, le côlon, le foie, les poumons ou le bassin. Avec l’IRM, il est capable de traiter des structures abdominales, cérébrales, cardiaques et prostatiques selon les différentes séquences (FLAIR, T1-Gd, T2). En radiographie, il peut identifier des anomalies pulmonaires ou des infections liées à la COVID-19.
|
196 |
+
|
197 |
+
Le modèle couvre également d’autres modalités comme la dermoscopie, l’endoscopie, le fond d’œil, et la pathologie, avec des applications pour la détection de lésions, de polypes ou de cellules néoplasiques dans divers tissus et organes. En échographie, il identifie des anomalies mammaires, cardiaques ou fœtales. Enfin, en TOCT, il peut analyser des structures rétiniennes.
|
198 |
+
|
199 |
+
Bien que performant dans ces contextes, il est important de considérer les spécificités des ensembles de données externes et de procéder à un ajustement pour des résultats précis.""",
|
200 |
examples=[
|
201 |
["examples/144DME_as_F.jpeg", "Dans cette image donne moi l'œdème"],
|
202 |
["examples/T0011.jpg", "disque optique, cupule optique"],
|
|
|
210 |
"""Entry point avoiding CUDA initialization in main process."""
|
211 |
try:
|
212 |
init_huggingface()
|
213 |
+
model = setup_model()
|
214 |
interface = setup_gradio_interface(model)
|
215 |
interface.launch(debug=True)
|
216 |
except Exception as e:
|
translator.py
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import requests
|
3 |
+
|
4 |
+
def translate_text(text, source_lang, target_lang):
|
5 |
+
url = "https://google-translator9.p.rapidapi.com/v2"
|
6 |
+
payload = {
|
7 |
+
"q": text,
|
8 |
+
"source": source_lang,
|
9 |
+
"target": target_lang,
|
10 |
+
"format": "text"
|
11 |
+
}
|
12 |
+
headers = {
|
13 |
+
"x-rapidapi-key": os.getenv("RAPIDAPI_KEY"),
|
14 |
+
"x-rapidapi-host": "google-translator9.p.rapidapi.com",
|
15 |
+
"Content-Type": "application/json"
|
16 |
+
}
|
17 |
+
|
18 |
+
try:
|
19 |
+
response = requests.post(url, json=payload, headers=headers)
|
20 |
+
response.raise_for_status()
|
21 |
+
print('=====================', response.json())
|
22 |
+
translations = response.json().get('data', {}).get('translations', [])
|
23 |
+
if translations:
|
24 |
+
translated_text = translations[0].get('translatedText', '')
|
25 |
+
return translated_text
|
26 |
+
else:
|
27 |
+
return text
|
28 |
+
except requests.exceptions.RequestException as e:
|
29 |
+
print(f"An error occurred: {e}")
|
30 |
+
return text
|
31 |
+
|
32 |
+
if __name__ == "__main__":
|
33 |
+
text_to_translate = "Dans cette image donne moi l'œdème"
|
34 |
+
source_language = "fr"
|
35 |
+
target_language = "en"
|
36 |
+
translation = translate_text(text_to_translate, source_language, target_language)
|
37 |
+
if translation:
|
38 |
+
print(translation)
|
utilities/distributed.py
CHANGED
@@ -30,7 +30,6 @@ def apply_distributed(opt):
|
|
30 |
init_method=init_method_url,
|
31 |
world_size=world_size,
|
32 |
rank=rank)
|
33 |
-
|
34 |
def init_distributed(opt):
|
35 |
opt['CUDA'] = opt.get('CUDA', True) and torch.cuda.is_available()
|
36 |
if 'OMPI_COMM_WORLD_SIZE' not in os.environ:
|
|
|
30 |
init_method=init_method_url,
|
31 |
world_size=world_size,
|
32 |
rank=rank)
|
|
|
33 |
def init_distributed(opt):
|
34 |
opt['CUDA'] = opt.get('CUDA', True) and torch.cuda.is_available()
|
35 |
if 'OMPI_COMM_WORLD_SIZE' not in os.environ:
|