| """ |
| Faster R-CNN ๋ฐ๋ฐ๋ฅ ๊ตฌํ โ ์ถ๋ก (inference) ์คํฌ๋ฆฝํธ |
| ===================================================== |
| ํ์ต๋ frcnn.pth ๋ก ์์์ ์ด๋ฏธ์ง์์ ๊ฐ์ฒด๋ฅผ ํ์งํ๊ณ , |
| ๋ฐ์ค + ํด๋์ค๋ช
+ ์ ์๋ฅผ ๊ทธ๋ ค์ ์ ์ฅํ๋ค. |
| |
| ์คํ ์: |
| # ์ด๋ฏธ์ง ํ ์ฅ |
| python infer.py --ckpt frcnn.pth --image test.jpg |
| |
| # ํด๋ ์ ๋ชจ๋ ์ด๋ฏธ์ง |
| python infer.py --ckpt frcnn.pth --image_dir ./samples --out_dir ./results |
| |
| # ์ ์ ์๊ณ๊ฐ ์กฐ์ (๊ธฐ๋ณธ 0.5) |
| python infer.py --ckpt frcnn.pth --image test.jpg --score_thresh 0.7 |
| |
| ์ฃผ์: |
| - train.py, model.py ๋ฑ๊ณผ ๊ฐ์ ํด๋์์ ์คํํ ๊ฒ. |
| - ํ์ต๊ณผ ๋์ผํ ๋ฆฌ์ฌ์ด์ฆ/์ ๊ทํ๋ฅผ ์ ์ฉํด์ผ ๊ฒฐ๊ณผ๊ฐ ์ ์. |
| """ |
|
|
| import os |
| import argparse |
|
|
| import torch |
| from PIL import Image, ImageDraw, ImageFont |
| import torchvision.transforms.functional as F |
|
|
| from model import FasterRCNN |
| from dataset import NUM_CLASSES, VOC_CLASSES |
|
|
|
|
| |
| def _class_colors(): |
| import colorsys |
| colors = [] |
| for i in range(len(VOC_CLASSES)): |
| h = i / len(VOC_CLASSES) |
| r, g, b = colorsys.hsv_to_rgb(h, 0.75, 0.95) |
| colors.append((int(r * 255), int(g * 255), int(b * 255))) |
| return colors |
|
|
| CLASS_COLORS = _class_colors() |
|
|
|
|
| def preprocess(pil_img, min_size=600, max_size=1000): |
| """ํ์ต๊ณผ ๋์ผํ ๋ฆฌ์ฌ์ด์ฆ + ์ ๊ทํ. ์๋ณธ ๋ณต์์ฉ scale๋ ๋ฐํ.""" |
| w, h = pil_img.size |
| short, long = min(w, h), max(w, h) |
| scale = min_size / short |
| if long * scale > max_size: |
| scale = max_size / long |
| new_w, new_h = int(round(w * scale)), int(round(h * scale)) |
|
|
| resized = pil_img.resize((new_w, new_h), Image.BILINEAR) |
| t = F.to_tensor(resized) |
| t = F.normalize(t, mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) |
| return t, scale |
|
|
|
|
| def draw_detections(pil_img, boxes, labels, scores, scale): |
| """ํ์ง ๊ฒฐ๊ณผ๋ฅผ ์๋ณธ ์ด๋ฏธ์ง ์ขํ๋ก ๋๋๋ ค ๋ฐ์ค์ ๋ผ๋ฒจ์ ๊ทธ๋ฆฐ๋ค.""" |
| draw = ImageDraw.Draw(pil_img) |
| try: |
| font = ImageFont.truetype("arial.ttf", 16) |
| except Exception: |
| font = ImageFont.load_default() |
|
|
| for box, label, score in zip(boxes, labels, scores): |
| |
| x1, y1, x2, y2 = (box / scale).tolist() |
| cls_idx = int(label) - 1 |
| if cls_idx < 0 or cls_idx >= len(VOC_CLASSES): |
| continue |
| name = VOC_CLASSES[cls_idx] |
| color = CLASS_COLORS[cls_idx] |
|
|
| |
| draw.rectangle([x1, y1, x2, y2], outline=color, width=3) |
|
|
| |
| text = f"{name} {score:.2f}" |
| tb = draw.textbbox((x1, y1), text, font=font) |
| draw.rectangle([tb[0], tb[1], tb[2], tb[3]], fill=color) |
| draw.text((x1, y1), text, fill="white", font=font) |
|
|
| return pil_img |
|
|
|
|
| @torch.no_grad() |
| def infer_image(model, img_path, device, score_thresh): |
| pil = Image.open(img_path).convert("RGB") |
| tensor, scale = preprocess(pil) |
| tensor = tensor.to(device).unsqueeze(0) |
|
|
| det = model(tensor) |
| keep = det["scores"] >= score_thresh |
| boxes = det["boxes"][keep].cpu() |
| labels = det["labels"][keep].cpu() |
| scores = det["scores"][keep].cpu() |
|
|
| result = draw_detections(pil, boxes, labels, scores, scale) |
| return result, len(boxes) |
|
|
|
|
| def main(): |
| ap = argparse.ArgumentParser() |
| ap.add_argument("--ckpt", required=True, help="ํ์ต๋ ๊ฐ์ค์น (frcnn.pth)") |
| ap.add_argument("--image", help="๋จ์ผ ์ด๋ฏธ์ง ๊ฒฝ๋ก") |
| ap.add_argument("--image_dir", help="์ด๋ฏธ์ง ํด๋ ๊ฒฝ๋ก") |
| ap.add_argument("--out_dir", default="./results", help="๊ฒฐ๊ณผ ์ ์ฅ ํด๋") |
| ap.add_argument("--score_thresh", type=float, default=0.5, |
| help="์ด ์ ์ ์ด์๋ง ํ์") |
| args = ap.parse_args() |
|
|
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| print("device:", device) |
|
|
| model = FasterRCNN(NUM_CLASSES).to(device) |
| model.load_state_dict(torch.load(args.ckpt, map_location=device)) |
| model.eval() |
| print(f"๋ชจ๋ธ ๋ก๋ ์๋ฃ: {args.ckpt}") |
|
|
| os.makedirs(args.out_dir, exist_ok=True) |
|
|
| |
| targets = [] |
| if args.image: |
| targets.append(args.image) |
| if args.image_dir: |
| for fn in os.listdir(args.image_dir): |
| if fn.lower().endswith((".jpg", ".jpeg", ".png", ".bmp")): |
| targets.append(os.path.join(args.image_dir, fn)) |
|
|
| if not targets: |
| print("์ด๋ฏธ์ง๋ฅผ ์ง์ ํ์ธ์: --image ๋๋ --image_dir") |
| return |
|
|
| for path in targets: |
| result, n = infer_image(model, path, device, args.score_thresh) |
| out_path = os.path.join(args.out_dir, "det_" + os.path.basename(path)) |
| result.save(out_path) |
| print(f" {os.path.basename(path)}: {n}๊ฐ ํ์ง โ {out_path}") |
|
|
| print(f"์๋ฃ. ๊ฒฐ๊ณผ๋ {args.out_dir} ํด๋์ ์ ์ฅ๋จ.") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|