| """Faster R-CNN val集评估 —— 输出 mAP@50 / mAP@50-95 / P / R |
| |
| 用法: |
| python Scripts/eval_fasterrcnn.py |
| """ |
| import torch |
| from torch.utils.data import DataLoader |
| from torchvision.models.detection import fasterrcnn_resnet50_fpn_v2 |
| from torchvision.models.detection.faster_rcnn import FastRCNNPredictor |
| from torchmetrics.detection.mean_ap import MeanAveragePrecision |
| from pathlib import Path |
| from PIL import Image |
| import torchvision.transforms.functional as F |
| from torch.utils.data import Dataset |
|
|
| DATA_ROOT = Path("Data/Detection_dataset") |
| CKPT = Path("Detection_experiments/compare_fasterrcnn/best.pt") |
| NUM_CLASSES = 2 |
| DEVICE = "cuda" if torch.cuda.is_available() else "cpu" |
| CONF_THRESH = 0.5 |
|
|
|
|
| class GoatDataset(Dataset): |
| def __init__(self, split: str): |
| img_dir = DATA_ROOT / "images" / split |
| lbl_dir = DATA_ROOT / "labels" / split |
| self.samples = [] |
| for img_path in sorted(img_dir.glob("*.jpg")): |
| lbl_path = lbl_dir / (img_path.stem + ".txt") |
| if lbl_path.exists(): |
| self.samples.append((img_path, lbl_path)) |
|
|
| def __len__(self): |
| return len(self.samples) |
|
|
| def __getitem__(self, idx): |
| img_path, lbl_path = self.samples[idx] |
| img = Image.open(img_path).convert("RGB") |
| w, h = img.size |
| img_tensor = F.to_tensor(img) |
|
|
| boxes, labels = [], [] |
| for line in lbl_path.read_text().splitlines(): |
| parts = line.strip().split() |
| if len(parts) != 5: |
| continue |
| _, cx, cy, bw, bh = map(float, parts) |
| x1 = (cx - bw / 2) * w |
| y1 = (cy - bh / 2) * h |
| x2 = (cx + bw / 2) * w |
| y2 = (cy + bh / 2) * h |
| boxes.append([x1, y1, x2, y2]) |
| labels.append(1) |
|
|
| target = { |
| "boxes": torch.tensor(boxes, dtype=torch.float32) if boxes else torch.zeros((0, 4), dtype=torch.float32), |
| "labels": torch.tensor(labels, dtype=torch.int64) if labels else torch.zeros(0, dtype=torch.int64), |
| } |
| return img_tensor, target |
|
|
|
|
| def collate_fn(batch): |
| return tuple(zip(*batch)) |
|
|
|
|
| def build_model(): |
| from torchvision.models import ResNet50_Weights |
| model = fasterrcnn_resnet50_fpn_v2(weights=None, weights_backbone=ResNet50_Weights.IMAGENET1K_V2) |
| in_features = model.roi_heads.box_predictor.cls_score.in_features |
| model.roi_heads.box_predictor = FastRCNNPredictor(in_features, NUM_CLASSES) |
| return model |
|
|
|
|
| def main(): |
| model = build_model().to(DEVICE) |
| model.load_state_dict(torch.load(CKPT, map_location=DEVICE)) |
| model.eval() |
|
|
| val_ds = GoatDataset("val") |
| val_loader = DataLoader(val_ds, batch_size=1, shuffle=False, |
| num_workers=2, collate_fn=collate_fn) |
|
|
| metric = MeanAveragePrecision(iou_type="bbox", class_metrics=False) |
|
|
| print(f"Evaluating on {len(val_ds)} val images...") |
| with torch.no_grad(): |
| for imgs, targets in val_loader: |
| imgs = [img.to(DEVICE) for img in imgs] |
| outputs = model(imgs) |
|
|
| preds = [] |
| for out in outputs: |
| keep = out["scores"] >= CONF_THRESH |
| preds.append({ |
| "boxes": out["boxes"][keep].cpu(), |
| "scores": out["scores"][keep].cpu(), |
| "labels": out["labels"][keep].cpu(), |
| }) |
|
|
| gts = [] |
| for t in targets: |
| gts.append({ |
| "boxes": t["boxes"], |
| "labels": t["labels"], |
| }) |
|
|
| metric.update(preds, gts) |
|
|
| result = metric.compute() |
| map50_95 = result["map"].item() |
| map50 = result["map_50"].item() |
| mar = result["mar_100"].item() |
|
|
| print(f"\n{'='*45}") |
| print(f" Faster R-CNN (ImageNet backbone, 30 epochs)") |
| print(f"{'='*45}") |
| print(f" mAP@50 : {map50:.3f}") |
| print(f" mAP@50-95 : {map50_95:.3f}") |
| print(f" mAR@100 : {mar:.3f}") |
| print(f" conf_thresh : {CONF_THRESH}") |
| print(f"{'='*45}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|