tu_usuario_hf commited on
Commit
d7ba778
·
1 Parent(s): 073435d

Despliegue inicial de modelos ExecuTorch FP32 en Hugging Face Spaces

Browse files
Files changed (3) hide show
  1. Dockerfile +1 -1
  2. app.py +140 -59
  3. yolo26.pte +3 -0
Dockerfile CHANGED
@@ -13,7 +13,7 @@ ENV HOME=/home/user PATH=/home/user/.local/bin:$PATH
13
 
14
  WORKDIR $HOME/app
15
 
16
- RUN pip install --no-cache-dir --upgrade pip && pip install --no-cache-dir --quiet executorch torch==2.11.0 torchvision numpy opencv-python-headless gradio gradio_client
17
 
18
  COPY --chown=user app.py ./app.py
19
  COPY --chown=user *.pte ./
 
13
 
14
  WORKDIR $HOME/app
15
 
16
+ RUN pip install --no-cache-dir --upgrade pip && pip install --no-cache-dir --quiet executorch torch==2.11.0 torchvision numpy opencv-python-headless gradio gradio_client ultralytics
17
 
18
  COPY --chown=user app.py ./app.py
19
  COPY --chown=user *.pte ./
app.py CHANGED
@@ -15,38 +15,45 @@ except ImportError:
15
  EXECUTORCH_AVAILABLE = False
16
  print("[WARNING] ExecuTorch no está disponible. Usando fallback de PyTorch.")
17
 
 
 
 
 
 
 
 
18
  PATH_MODEL_CLS = "mobilenet_v2.pte"
19
  PATH_MODEL_SEG = "deeplabv3.pte"
20
- PATH_MODEL_DET = "ssdlite.pte"
21
 
22
  IMAGENET_MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32)
23
  IMAGENET_STD = np.array([0.229, 0.224, 0.225], dtype=np.float32)
24
 
25
  COCO_CLASSES = [
26
- '__background__', 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus',
27
- 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'N/A', 'stop sign',
28
- 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
29
- 'elephant', 'bear', 'zebra', 'giraffe', 'N/A', 'backpack', 'umbrella', 'N/A',
30
- 'N/A', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball',
31
  'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket',
32
- 'bottle', 'N/A', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl',
33
- 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza',
34
- 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'N/A', 'dining table',
35
- 'N/A', 'N/A', 'toilet', 'N/A', 'tv', 'laptop', 'mouse', 'remote', 'keyboard',
36
- 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'N/A',
37
- 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush'
38
  ]
39
 
40
  np.random.seed(42)
41
  COLOR_PALETTE = np.random.randint(0, 255, size=(100, 3), dtype=np.uint8)
42
 
43
- def preprocesar_imagen(img_pil: Image.Image, size: tuple) -> torch.Tensor:
44
  if img_pil.mode != "RGB":
45
  img_pil = img_pil.convert("RGB")
46
  img_resized = img_pil.resize(size)
47
  img_np = np.array(img_resized, dtype=np.float32) / 255.0
48
- img_normalized = (img_np - IMAGENET_MEAN) / IMAGENET_STD
49
- img_transposed = np.transpose(img_normalized, (2, 0, 1))
 
50
  tensor = torch.from_numpy(img_transposed).unsqueeze(0).contiguous()
51
  return tensor
52
 
@@ -59,24 +66,24 @@ def postprocesar_segmentacion(output_tensor: torch.Tensor, original_img: Image.I
59
  blended = cv2.addWeighted(img_orig_np, 0.6, mask_color, 0.4, 0)
60
  return Image.fromarray(blended)
61
 
62
- def postprocesar_deteccion(boxes: torch.Tensor, scores: torch.Tensor, labels: torch.Tensor, original_img: Image.Image, threshold: float = 0.5) -> Image.Image:
63
  img_np = np.array(original_img)
64
  h, w, _ = img_np.shape
65
 
66
- boxes_np = boxes[0].detach().numpy() if isinstance(boxes, torch.Tensor) else boxes[0]
67
- scores_np = scores[0].detach().numpy() if isinstance(scores, torch.Tensor) else scores[0]
68
- labels_np = labels[0].detach().numpy() if isinstance(labels, torch.Tensor) else labels[0]
69
 
70
  for box, score, label_idx in zip(boxes_np, scores_np, labels_np):
71
  if score >= threshold:
72
- ymin, xmin, ymax, xmax = int(box[0] * h / 320), int(box[1] * w / 320), int(box[2] * h / 320), int(box[3] * w / 320)
73
 
74
  xmin = max(0, min(xmin, w - 1))
75
  ymin = max(0, min(ymin, h - 1))
76
  xmax = max(0, min(xmax, w - 1))
77
  ymax = max(0, min(ymax, h - 1))
78
 
79
- label_text = f"{COCO_CLASSES[int(label_idx)]}: {score:.2f}"
80
  color = [int(c) for c in COLOR_PALETTE[int(label_idx) % len(COLOR_PALETTE)]]
81
 
82
  cv2.rectangle(img_np, (xmin, ymin), (xmax, ymax), color, 3)
@@ -90,12 +97,31 @@ class ModelRunner:
90
 
91
  if self.use_executorch:
92
  print(f"[INFO] Cargando modelo ExecuTorch: {pte_path}")
93
- self.runtime = Runtime.get()
94
- self.program = self.runtime.load_program(pte_path)
95
- self.method = self.program.load_method("forward")
96
- else:
 
 
 
 
 
97
  print(f"[INFO] Cargando fallback de PyTorch para: {pte_path}")
98
- self.model = fallback_model_fn().eval()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
  def run(self, input_tensor: torch.Tensor):
101
  if self.use_executorch:
@@ -109,7 +135,7 @@ class ModelRunner:
109
 
110
  runner_cls = ModelRunner(PATH_MODEL_CLS, lambda: models.mobilenet_v2(pretrained=True))
111
  runner_seg = ModelRunner(PATH_MODEL_SEG, lambda: models.segmentation.deeplabv3_mobilenet_v3_large(pretrained=True))
112
- runner_det = ModelRunner(PATH_MODEL_DET, lambda: models.detection.ssdlite320_mobilenet_v3_large(pretrained=True))
113
 
114
  import urllib.request
115
  try:
@@ -121,45 +147,100 @@ except Exception:
121
  def predict_classification(image: Image.Image) -> dict:
122
  if image is None:
123
  return {}
124
- tensor = preprocesar_imagen(image, (224, 224))
125
- output = runner_cls.run(tensor)
126
- if isinstance(output, list):
127
- output = output[0]
128
-
129
- probabilities = torch.nn.functional.softmax(output[0], dim=0)
130
- top_prob, top_catid = torch.topk(probabilities, 5)
131
- return {imagenet_classes[int(idx)]: float(prob) for prob, idx in zip(top_prob, top_catid)}
 
 
 
 
 
 
 
132
 
133
  def predict_segmentation(image: Image.Image) -> Image.Image:
134
  if image is None:
135
  return None
136
- tensor = preprocesar_imagen(image, (256, 256))
137
- output = runner_seg.run(tensor)
138
- if isinstance(output, dict):
139
- output_tensor = output["out"]
140
- else:
141
- output_tensor = output
142
- return postprocesar_segmentacion(output_tensor, image)
 
 
 
 
 
 
 
 
 
 
 
143
 
144
  def predict_detection(image: Image.Image) -> Image.Image:
145
  if image is None:
146
  return None
147
- tensor = preprocesar_imagen(image, (320, 320))
148
- output = runner_det.run(tensor)
149
-
150
- if runner_det.use_executorch:
151
- bbox_regression, cls_logits = output
152
- mock_boxes = np.array([[[100, 100, 200, 200]]], dtype=np.float32)
153
- mock_scores = np.array([[0.95]], dtype=np.float32)
154
- mock_labels = np.array([[1]], dtype=np.int64)
155
- return postprocesar_deteccion(mock_boxes, mock_scores, mock_labels, image)
156
- else:
157
- predictions = output[0]
158
- boxes = predictions["boxes"]
159
- scores = predictions["scores"]
160
- labels = predictions["labels"]
161
- boxes_formatted = boxes[:, [1, 0, 3, 2]]
162
- return postprocesar_deteccion(boxes_formatted.unsqueeze(0), scores.unsqueeze(0), labels.unsqueeze(0), image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
  with gr.Blocks(title="Servidor de Inferencia ExecuTorch FP32") as demo:
165
  gr.Markdown("# Servidor de Visión Artificial: ExecuTorch (Float32)")
@@ -187,4 +268,4 @@ with gr.Blocks(title="Servidor de Inferencia ExecuTorch FP32") as demo:
187
  btn_run_det.click(predict_detection, inputs=img_in_det, outputs=img_out_det)
188
 
189
  if __name__ == "__main__":
190
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
15
  EXECUTORCH_AVAILABLE = False
16
  print("[WARNING] ExecuTorch no está disponible. Usando fallback de PyTorch.")
17
 
18
+ try:
19
+ from ultralytics import YOLO
20
+ YOLO_AVAILABLE = True
21
+ except ImportError:
22
+ YOLO_AVAILABLE = False
23
+ print("[WARNING] Ultralytics no está disponible.")
24
+
25
  PATH_MODEL_CLS = "mobilenet_v2.pte"
26
  PATH_MODEL_SEG = "deeplabv3.pte"
27
+ PATH_MODEL_DET = "yolo26.pte"
28
 
29
  IMAGENET_MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32)
30
  IMAGENET_STD = np.array([0.229, 0.224, 0.225], dtype=np.float32)
31
 
32
  COCO_CLASSES = [
33
+ 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat',
34
+ 'traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat',
35
+ 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack',
36
+ 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball',
 
37
  'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket',
38
+ 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
39
+ 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake',
40
+ 'chair', 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop',
41
+ 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink',
42
+ 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier',
43
+ 'toothbrush'
44
  ]
45
 
46
  np.random.seed(42)
47
  COLOR_PALETTE = np.random.randint(0, 255, size=(100, 3), dtype=np.uint8)
48
 
49
+ def preprocesar_imagen(img_pil: Image.Image, size: tuple, normalizar: bool = True) -> torch.Tensor:
50
  if img_pil.mode != "RGB":
51
  img_pil = img_pil.convert("RGB")
52
  img_resized = img_pil.resize(size)
53
  img_np = np.array(img_resized, dtype=np.float32) / 255.0
54
+ if normalizar:
55
+ img_np = (img_np - IMAGENET_MEAN) / IMAGENET_STD
56
+ img_transposed = np.transpose(img_np, (2, 0, 1))
57
  tensor = torch.from_numpy(img_transposed).unsqueeze(0).contiguous()
58
  return tensor
59
 
 
66
  blended = cv2.addWeighted(img_orig_np, 0.6, mask_color, 0.4, 0)
67
  return Image.fromarray(blended)
68
 
69
+ def postprocesar_deteccion(boxes, scores, labels, original_img: Image.Image, threshold: float = 0.25) -> Image.Image:
70
  img_np = np.array(original_img)
71
  h, w, _ = img_np.shape
72
 
73
+ boxes_np = boxes.detach().numpy() if isinstance(boxes, torch.Tensor) else np.array(boxes)
74
+ scores_np = scores.detach().numpy() if isinstance(scores, torch.Tensor) else np.array(scores)
75
+ labels_np = labels.detach().numpy() if isinstance(labels, torch.Tensor) else np.array(labels)
76
 
77
  for box, score, label_idx in zip(boxes_np, scores_np, labels_np):
78
  if score >= threshold:
79
+ xmin, ymin, xmax, ymax = int(box[0]), int(box[1]), int(box[2]), int(box[3])
80
 
81
  xmin = max(0, min(xmin, w - 1))
82
  ymin = max(0, min(ymin, h - 1))
83
  xmax = max(0, min(xmax, w - 1))
84
  ymax = max(0, min(ymax, h - 1))
85
 
86
+ label_text = f"{COCO_CLASSES[int(label_idx) % len(COCO_CLASSES)]}: {score:.2f}"
87
  color = [int(c) for c in COLOR_PALETTE[int(label_idx) % len(COLOR_PALETTE)]]
88
 
89
  cv2.rectangle(img_np, (xmin, ymin), (xmax, ymax), color, 3)
 
97
 
98
  if self.use_executorch:
99
  print(f"[INFO] Cargando modelo ExecuTorch: {pte_path}")
100
+ try:
101
+ self.runtime = Runtime.get()
102
+ self.program = self.runtime.load_program(pte_path)
103
+ self.method = self.program.load_method("forward")
104
+ except Exception as e:
105
+ print(f"[ERROR] Error al cargar modelo ExecuTorch {pte_path}: {e}. Usando fallback.")
106
+ self.use_executorch = False
107
+
108
+ if not self.use_executorch:
109
  print(f"[INFO] Cargando fallback de PyTorch para: {pte_path}")
110
+ try:
111
+ self.model = fallback_model_fn()
112
+ if hasattr(self.model, "eval"):
113
+ try:
114
+ self.model = self.model.eval()
115
+ except Exception:
116
+ pass
117
+ if hasattr(self.model, "model") and hasattr(self.model.model, "eval"):
118
+ try:
119
+ self.model.model.eval()
120
+ except Exception:
121
+ pass
122
+ except Exception as e:
123
+ print(f"[ERROR] Error al cargar fallback: {e}")
124
+ self.model = None
125
 
126
  def run(self, input_tensor: torch.Tensor):
127
  if self.use_executorch:
 
135
 
136
  runner_cls = ModelRunner(PATH_MODEL_CLS, lambda: models.mobilenet_v2(pretrained=True))
137
  runner_seg = ModelRunner(PATH_MODEL_SEG, lambda: models.segmentation.deeplabv3_mobilenet_v3_large(pretrained=True))
138
+ runner_det = ModelRunner(PATH_MODEL_DET, lambda: YOLO("yolo26n.pt") if YOLO_AVAILABLE else None)
139
 
140
  import urllib.request
141
  try:
 
147
  def predict_classification(image: Image.Image) -> dict:
148
  if image is None:
149
  return {}
150
+ try:
151
+ if not (runner_cls.use_executorch or (hasattr(runner_cls, "model") and runner_cls.model is not None)):
152
+ return {"Error": 1.0, "Modelo de clasificacion no cargado": 0.0}
153
+ tensor = preprocesar_imagen(image, (224, 224))
154
+ output = runner_cls.run(tensor)
155
+ if isinstance(output, list):
156
+ output = output[0]
157
+ if isinstance(output, np.ndarray):
158
+ output = torch.from_numpy(output)
159
+
160
+ probabilities = torch.nn.functional.softmax(output[0], dim=0)
161
+ top_prob, top_catid = torch.topk(probabilities, 5)
162
+ return {imagenet_classes[int(idx)]: float(prob) for prob, idx in zip(top_prob, top_catid)}
163
+ except Exception as e:
164
+ return {"Error": 1.0, str(e): 0.0}
165
 
166
  def predict_segmentation(image: Image.Image) -> Image.Image:
167
  if image is None:
168
  return None
169
+ try:
170
+ if not (runner_seg.use_executorch or (hasattr(runner_seg, "model") and runner_seg.model is not None)):
171
+ img_np = np.array(image)
172
+ cv2.putText(img_np, "Modelo de segmentacion no cargado", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
173
+ return Image.fromarray(img_np)
174
+ tensor = preprocesar_imagen(image, (256, 256))
175
+ output = runner_seg.run(tensor)
176
+ if isinstance(output, dict):
177
+ output_tensor = output["out"]
178
+ else:
179
+ output_tensor = output
180
+ if isinstance(output_tensor, np.ndarray):
181
+ output_tensor = torch.from_numpy(output_tensor)
182
+ return postprocesar_segmentacion(output_tensor, image)
183
+ except Exception as e:
184
+ img_np = np.array(image)
185
+ cv2.putText(img_np, f"Error: {str(e)}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
186
+ return Image.fromarray(img_np)
187
 
188
  def predict_detection(image: Image.Image) -> Image.Image:
189
  if image is None:
190
  return None
191
+ try:
192
+ if not (runner_det.use_executorch or (hasattr(runner_det, "model") and runner_det.model is not None)):
193
+ img_np = np.array(image)
194
+ cv2.putText(img_np, "Modelo de deteccion no cargado", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
195
+ return Image.fromarray(img_np)
196
+
197
+ if runner_det.use_executorch:
198
+ tensor = preprocesar_imagen(image, (640, 640), normalizar=False)
199
+ output = runner_det.run(tensor)
200
+ pred = output[0].numpy() if isinstance(output, torch.Tensor) else output[0]
201
+ predictions = pred[0].T if len(pred.shape) == 3 else pred.T
202
+
203
+ boxes = predictions[:, :4]
204
+ scores = predictions[:, 4:]
205
+ max_scores = np.max(scores, axis=1)
206
+ class_ids = np.argmax(scores, axis=1)
207
+
208
+ conf_threshold = 0.25
209
+ keep = max_scores >= conf_threshold
210
+ filtered_boxes = boxes[keep]
211
+ filtered_scores = max_scores[keep]
212
+ filtered_class_ids = class_ids[keep]
213
+
214
+ if len(filtered_boxes) > 0:
215
+ cx, cy, w_box, h_box = filtered_boxes[:, 0], filtered_boxes[:, 1], filtered_boxes[:, 2], filtered_boxes[:, 3]
216
+ orig_h, orig_w = image.size[1], image.size[0]
217
+ scale_x = orig_w / 640.0
218
+ scale_y = orig_h / 640.0
219
+
220
+ x1 = (cx - w_box / 2) * scale_x
221
+ y1 = (cy - h_box / 2) * scale_y
222
+ x2 = (cx + w_box / 2) * scale_x
223
+ y2 = (cy + h_box / 2) * scale_y
224
+
225
+ boxes_xyxy = np.stack([x1, y1, x2, y2], axis=1)
226
+ boxes_xywh = np.stack([x1, y1, w_box * scale_x, h_box * scale_y], axis=1)
227
+
228
+ indices = cv2.dnn.NMSBoxes(boxes_xywh.tolist(), filtered_scores.tolist(), conf_threshold, 0.45)
229
+ if len(indices) > 0:
230
+ indices = np.array(indices).flatten()
231
+ return postprocesar_deteccion(boxes_xyxy[indices], filtered_scores[indices], filtered_class_ids[indices], image, threshold=conf_threshold)
232
+ return image
233
+ else:
234
+ results = runner_det.model(image, verbose=False)
235
+ r = results[0]
236
+ boxes = r.boxes.xyxy.cpu().numpy()
237
+ scores = r.boxes.conf.cpu().numpy()
238
+ labels = r.boxes.cls.cpu().numpy()
239
+ return postprocesar_deteccion(boxes, scores, labels, image, threshold=0.25)
240
+ except Exception as e:
241
+ img_np = np.array(image)
242
+ cv2.putText(img_np, f"Error: {str(e)}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
243
+ return Image.fromarray(img_np)
244
 
245
  with gr.Blocks(title="Servidor de Inferencia ExecuTorch FP32") as demo:
246
  gr.Markdown("# Servidor de Visión Artificial: ExecuTorch (Float32)")
 
268
  btn_run_det.click(predict_detection, inputs=img_in_det, outputs=img_out_det)
269
 
270
  if __name__ == "__main__":
271
+ demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)
yolo26.pte ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b4026534f5ecfdddf2d311baa72fa7c75304ea06d1101424c52b3bae85408255
3
+ size 9872704