YMmim's picture
Object detection from scratch: Faster R-CNN + YOLO comparison
017c046 verified
Raw
History Blame Contribute Delete
5.09 kB
"""
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
# ํด๋ž˜์Šค๋ณ„ ์ƒ‰์ƒ(20๊ฐœ) โ€” ์‹œ๊ฐ์ ์œผ๋กœ ๊ตฌ๋ถ„๋˜๋„๋ก HSV ๋ถ„ํ• 
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):
# ๋ชจ๋ธ์€ ๋ฆฌ์‚ฌ์ด์ฆˆ๋œ ์ขŒํ‘œ๋ฅผ ์ถœ๋ ฅ โ†’ ์›๋ณธ ํฌ๊ธฐ๋กœ ๋˜๋Œ๋ฆผ(รทscale)
x1, y1, x2, y2 = (box / scale).tolist()
cls_idx = int(label) - 1 # 0=๋ฐฐ๊ฒฝ ์ œ์™ธ
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) # eval ๋ชจ๋“œ โ†’ {boxes, labels, scores}
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()