tu_usuario_hf
Despliegue inicial de modelos ExecuTorch FP32 en Hugging Face Spaces
d7ba778
Raw
History Blame Contribute Delete
12.4 kB
import os
import sys
import numpy as np
import cv2
import torch
import torchvision.models as models
import gradio as gr
from PIL import Image
try:
from executorch.runtime import Runtime, Program
EXECUTORCH_AVAILABLE = True
print("[INFO] Runtime de ExecuTorch cargado correctamente.")
except ImportError:
EXECUTORCH_AVAILABLE = False
print("[WARNING] ExecuTorch no est谩 disponible. Usando fallback de PyTorch.")
try:
from ultralytics import YOLO
YOLO_AVAILABLE = True
except ImportError:
YOLO_AVAILABLE = False
print("[WARNING] Ultralytics no est谩 disponible.")
PATH_MODEL_CLS = "mobilenet_v2.pte"
PATH_MODEL_SEG = "deeplabv3.pte"
PATH_MODEL_DET = "yolo26.pte"
IMAGENET_MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32)
IMAGENET_STD = np.array([0.229, 0.224, 0.225], dtype=np.float32)
COCO_CLASSES = [
'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat',
'traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat',
'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack',
'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball',
'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket',
'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake',
'chair', 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop',
'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink',
'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier',
'toothbrush'
]
np.random.seed(42)
COLOR_PALETTE = np.random.randint(0, 255, size=(100, 3), dtype=np.uint8)
def preprocesar_imagen(img_pil: Image.Image, size: tuple, normalizar: bool = True) -> torch.Tensor:
if img_pil.mode != "RGB":
img_pil = img_pil.convert("RGB")
img_resized = img_pil.resize(size)
img_np = np.array(img_resized, dtype=np.float32) / 255.0
if normalizar:
img_np = (img_np - IMAGENET_MEAN) / IMAGENET_STD
img_transposed = np.transpose(img_np, (2, 0, 1))
tensor = torch.from_numpy(img_transposed).unsqueeze(0).contiguous()
return tensor
def postprocesar_segmentacion(output_tensor: torch.Tensor, original_img: Image.Image) -> Image.Image:
mask = torch.argmax(output_tensor[0], dim=0).numpy().astype(np.uint8)
w, h = original_img.size
mask_resized = cv2.resize(mask, (w, h), interpolation=cv2.INTER_NEAREST)
mask_color = COLOR_PALETTE[mask_resized]
img_orig_np = np.array(original_img)
blended = cv2.addWeighted(img_orig_np, 0.6, mask_color, 0.4, 0)
return Image.fromarray(blended)
def postprocesar_deteccion(boxes, scores, labels, original_img: Image.Image, threshold: float = 0.25) -> Image.Image:
img_np = np.array(original_img)
h, w, _ = img_np.shape
boxes_np = boxes.detach().numpy() if isinstance(boxes, torch.Tensor) else np.array(boxes)
scores_np = scores.detach().numpy() if isinstance(scores, torch.Tensor) else np.array(scores)
labels_np = labels.detach().numpy() if isinstance(labels, torch.Tensor) else np.array(labels)
for box, score, label_idx in zip(boxes_np, scores_np, labels_np):
if score >= threshold:
xmin, ymin, xmax, ymax = int(box[0]), int(box[1]), int(box[2]), int(box[3])
xmin = max(0, min(xmin, w - 1))
ymin = max(0, min(ymin, h - 1))
xmax = max(0, min(xmax, w - 1))
ymax = max(0, min(ymax, h - 1))
label_text = f"{COCO_CLASSES[int(label_idx) % len(COCO_CLASSES)]}: {score:.2f}"
color = [int(c) for c in COLOR_PALETTE[int(label_idx) % len(COLOR_PALETTE)]]
cv2.rectangle(img_np, (xmin, ymin), (xmax, ymax), color, 3)
cv2.putText(img_np, label_text, (xmin, max(ymin - 10, 15)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
return Image.fromarray(img_np)
class ModelRunner:
def __init__(self, pte_path: str, fallback_model_fn):
self.use_executorch = EXECUTORCH_AVAILABLE and os.path.exists(pte_path)
if self.use_executorch:
print(f"[INFO] Cargando modelo ExecuTorch: {pte_path}")
try:
self.runtime = Runtime.get()
self.program = self.runtime.load_program(pte_path)
self.method = self.program.load_method("forward")
except Exception as e:
print(f"[ERROR] Error al cargar modelo ExecuTorch {pte_path}: {e}. Usando fallback.")
self.use_executorch = False
if not self.use_executorch:
print(f"[INFO] Cargando fallback de PyTorch para: {pte_path}")
try:
self.model = fallback_model_fn()
if hasattr(self.model, "eval"):
try:
self.model = self.model.eval()
except Exception:
pass
if hasattr(self.model, "model") and hasattr(self.model.model, "eval"):
try:
self.model.model.eval()
except Exception:
pass
except Exception as e:
print(f"[ERROR] Error al cargar fallback: {e}")
self.model = None
def run(self, input_tensor: torch.Tensor):
if self.use_executorch:
outputs = self.method.execute((input_tensor,))
if isinstance(outputs, list) and len(outputs) == 1:
return outputs[0]
return outputs
else:
with torch.no_grad():
return self.model(input_tensor)
runner_cls = ModelRunner(PATH_MODEL_CLS, lambda: models.mobilenet_v2(pretrained=True))
runner_seg = ModelRunner(PATH_MODEL_SEG, lambda: models.segmentation.deeplabv3_mobilenet_v3_large(pretrained=True))
runner_det = ModelRunner(PATH_MODEL_DET, lambda: YOLO("yolo26n.pt") if YOLO_AVAILABLE else None)
import urllib.request
try:
url = "https://raw.githubusercontent.com/pytorch/hub/master/imagenet_classes.txt"
imagenet_classes = urllib.request.urlopen(url).read().decode('utf-8').splitlines()
except Exception:
imagenet_classes = [f"Clase {i}" for i in range(1000)]
def predict_classification(image: Image.Image) -> dict:
if image is None:
return {}
try:
if not (runner_cls.use_executorch or (hasattr(runner_cls, "model") and runner_cls.model is not None)):
return {"Error": 1.0, "Modelo de clasificacion no cargado": 0.0}
tensor = preprocesar_imagen(image, (224, 224))
output = runner_cls.run(tensor)
if isinstance(output, list):
output = output[0]
if isinstance(output, np.ndarray):
output = torch.from_numpy(output)
probabilities = torch.nn.functional.softmax(output[0], dim=0)
top_prob, top_catid = torch.topk(probabilities, 5)
return {imagenet_classes[int(idx)]: float(prob) for prob, idx in zip(top_prob, top_catid)}
except Exception as e:
return {"Error": 1.0, str(e): 0.0}
def predict_segmentation(image: Image.Image) -> Image.Image:
if image is None:
return None
try:
if not (runner_seg.use_executorch or (hasattr(runner_seg, "model") and runner_seg.model is not None)):
img_np = np.array(image)
cv2.putText(img_np, "Modelo de segmentacion no cargado", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
return Image.fromarray(img_np)
tensor = preprocesar_imagen(image, (256, 256))
output = runner_seg.run(tensor)
if isinstance(output, dict):
output_tensor = output["out"]
else:
output_tensor = output
if isinstance(output_tensor, np.ndarray):
output_tensor = torch.from_numpy(output_tensor)
return postprocesar_segmentacion(output_tensor, image)
except Exception as e:
img_np = np.array(image)
cv2.putText(img_np, f"Error: {str(e)}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
return Image.fromarray(img_np)
def predict_detection(image: Image.Image) -> Image.Image:
if image is None:
return None
try:
if not (runner_det.use_executorch or (hasattr(runner_det, "model") and runner_det.model is not None)):
img_np = np.array(image)
cv2.putText(img_np, "Modelo de deteccion no cargado", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
return Image.fromarray(img_np)
if runner_det.use_executorch:
tensor = preprocesar_imagen(image, (640, 640), normalizar=False)
output = runner_det.run(tensor)
pred = output[0].numpy() if isinstance(output, torch.Tensor) else output[0]
predictions = pred[0].T if len(pred.shape) == 3 else pred.T
boxes = predictions[:, :4]
scores = predictions[:, 4:]
max_scores = np.max(scores, axis=1)
class_ids = np.argmax(scores, axis=1)
conf_threshold = 0.25
keep = max_scores >= conf_threshold
filtered_boxes = boxes[keep]
filtered_scores = max_scores[keep]
filtered_class_ids = class_ids[keep]
if len(filtered_boxes) > 0:
cx, cy, w_box, h_box = filtered_boxes[:, 0], filtered_boxes[:, 1], filtered_boxes[:, 2], filtered_boxes[:, 3]
orig_h, orig_w = image.size[1], image.size[0]
scale_x = orig_w / 640.0
scale_y = orig_h / 640.0
x1 = (cx - w_box / 2) * scale_x
y1 = (cy - h_box / 2) * scale_y
x2 = (cx + w_box / 2) * scale_x
y2 = (cy + h_box / 2) * scale_y
boxes_xyxy = np.stack([x1, y1, x2, y2], axis=1)
boxes_xywh = np.stack([x1, y1, w_box * scale_x, h_box * scale_y], axis=1)
indices = cv2.dnn.NMSBoxes(boxes_xywh.tolist(), filtered_scores.tolist(), conf_threshold, 0.45)
if len(indices) > 0:
indices = np.array(indices).flatten()
return postprocesar_deteccion(boxes_xyxy[indices], filtered_scores[indices], filtered_class_ids[indices], image, threshold=conf_threshold)
return image
else:
results = runner_det.model(image, verbose=False)
r = results[0]
boxes = r.boxes.xyxy.cpu().numpy()
scores = r.boxes.conf.cpu().numpy()
labels = r.boxes.cls.cpu().numpy()
return postprocesar_deteccion(boxes, scores, labels, image, threshold=0.25)
except Exception as e:
img_np = np.array(image)
cv2.putText(img_np, f"Error: {str(e)}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
return Image.fromarray(img_np)
with gr.Blocks(title="Servidor de Inferencia ExecuTorch FP32") as demo:
gr.Markdown("# Servidor de Visi贸n Artificial: ExecuTorch (Float32)")
gr.Markdown("Inferencia multimodelo optimizada con ExecuTorch y desplegada con Docker en Hugging Face Spaces.")
with gr.Tab("Clasificaci贸n de Im谩genes"):
with gr.Row():
img_in = gr.Image(type="pil")
label_out = gr.Label(num_top_classes=5)
btn_run = gr.Button("Clasificar")
btn_run.click(predict_classification, inputs=img_in, outputs=label_out)
with gr.Tab("Segmentaci贸n Sem谩ntica"):
with gr.Row():
img_in_seg = gr.Image(type="pil")
img_out_seg = gr.Image(type="pil")
btn_run_seg = gr.Button("Segmentar")
btn_run_seg.click(predict_segmentation, inputs=img_in_seg, outputs=img_out_seg)
with gr.Tab("Detecci贸n de Objetos"):
with gr.Row():
img_in_det = gr.Image(type="pil")
img_out_det = gr.Image(type="pil")
btn_run_det = gr.Button("Detectar")
btn_run_det.click(predict_detection, inputs=img_in_det, outputs=img_out_det)
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)