| --- |
| license: apache-2.0 |
| language: |
| - es |
| pipeline_tag: image-classification |
| --- |
| |
| # Carritos Detector - Faster R-CNN (ResNet-50-FPN) |
|
|
| Modelo de deteccion de vehiculos entrenado con PyTorch Faster R-CNN + ResNet-50-FPN backbone. |
|
|
| ## Clases |
|
|
| | ID | Clase | Descripcion | |
| |----|-------|-------------| |
| | 1 | Carro | Incluye carros, buses, camiones y combis | |
| | 2 | Mototaxi | Mototaxis | |
| | 3 | Persona | Personas | |
|
|
| ## Metricas |
|
|
| - **mAP@50 (val):** 0.7283 |
| - **Epoca:** 86 |
| - **Clases:** 3 + background |
| - **Input:** 640x640 RGB |
|
|
| ## Uso con torch.hub |
|
|
| ```python |
| import cv2 |
| import torch |
| import albumentations as A |
| import matplotlib.pyplot as plt |
| import matplotlib.patches as patches |
| from albumentations.pytorch import ToTensorV2 |
| from torchvision.models.detection import fasterrcnn_resnet50_fpn |
| from torchvision.models.detection.faster_rcnn import FastRCNNPredictor |
| |
| DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| HF_REPO_ID = "pollitoconpapass/carritos-detectorV2" |
| |
| hf_model_url = f"https://huggingface.co/{HF_REPO_ID}/resolve/main/model.pt" |
| ckpt_hf = torch.hub.load_state_dict_from_url(hf_model_url, map_location=DEVICE) |
| |
| model_hf = fasterrcnn_resnet50_fpn(weights=None) |
| in_features = model_hf.roi_heads.box_predictor.cls_score.in_features |
| model_hf.roi_heads.box_predictor = FastRCNNPredictor(in_features, ckpt_hf["num_classes"]) |
| model_hf.load_state_dict(ckpt_hf["model_state_dict"]) |
| model_hf.to(DEVICE) |
| model_hf.eval() |
| |
| categories_hf = ckpt_hf["categories"] |
| print(f"[INFO] Modelo cargado desde HF (epoca {ckpt_hf['best_epoch']}, mAP@50={ckpt_hf['best_map50']:.4f})") |
| print(f"[INFO] Clases: {categories_hf}") |
| |
| def infer_image(model, image_path, categories, score_threshold=0.5): |
| image_bgr = cv2.imread(str(image_path)) |
| if image_bgr is None: |
| raise FileNotFoundError(f"No se pudo cargar: {image_path}") |
| image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB) |
| |
| transform = A.Compose([ |
| A.Resize(640, 640), |
| A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), |
| ToTensorV2(), |
| ]) |
| tensor = transform(image=image_rgb)["image"].unsqueeze(0).to(DEVICE) |
| |
| with torch.no_grad(): |
| pred = model(tensor)[0] |
| |
| keep = pred["scores"] >= score_threshold |
| boxes = pred["boxes"][keep].cpu().numpy() |
| labels = pred["labels"][keep].cpu().numpy() |
| scores = pred["scores"][keep].cpu().numpy() |
| |
| _, ax = plt.subplots(1, 1, figsize=(10, 8)) |
| ax.imshow(image_rgb) |
| ax.set_title(f"Predicciones (threshold={score_threshold})") |
| ax.axis("off") |
| |
| class_colors = {1: "red", 2: "cyan", 3: "lime"} |
| h_orig, w_orig = image_rgb.shape[:2] |
| sx, sy = w_orig / 640, h_orig / 640 |
| |
| for box, label, score in zip(boxes, labels, scores): |
| color = class_colors.get(label, "white") |
| x1, y1 = box[0] * sx, box[1] * sy |
| w, h = (box[2] - box[0]) * sx, (box[3] - box[1]) * sy |
| rect = patches.Rectangle((x1, y1), w, h, linewidth=2, edgecolor=color, facecolor="none") |
| ax.add_patch(rect) |
| ax.text(x1, y1 - 5, f"{categories.get(label, label)}: {score:.2f}", |
| color=color, fontsize=10, fontweight="bold", |
| bbox=dict(boxstyle="round,pad=0.2", facecolor="black", alpha=0.7)) |
| |
| plt.tight_layout() |
| plt.show() |
| |
| print(f"Detecciones: {len(boxes)}") |
| for label, score in zip(labels, scores): |
| print(f" {categories.get(label, label):12s} conf={score:.3f}") |
| |
| # Cambiar path con imagen propia |
| image_path = "/content/frame_0875.png" |
| infer_image(model_hf, image_path, categories_hf, score_threshold=0.5) |
| |
| ``` |
|
|
| ## Entrenado con |
|
|
| - PyTorch 2.12.1+cu130 |
| - Faster R-CNN ResNet-50-FPN (pre-entrenado en COCO) |
| - Fine-tuning de layer3 + layer4 del backbone |
| - Focal Loss + class weights para desbalance de clases |
| - 30 epocas, batch_size=2, SGD con cosine annealing |