[CV Tasks] Faster R-CNN을 활용한 Object Detection 모델 구현
**패스트 캠퍼스 강의를 보고 정리하는거라 틀린개념/방법 다수존재가능합니다..
이번 강의에서는 Faster R-CNN을 이용해서 Truck과 Bus를 탐지하는 모델을 실습해 보았다.
이 실습의 흐름은 다음과 같이 진행했다.
1. Dataset 살펴보기
2. Dataset 구축과 연산을 위한 텐서변환 모듈 작성하기
3. Faster R-CNN 아키텍처 불러오기
4. Object Detector 학습을 위한 코드 작성하기
5. Confidence threshold와 Non-maximum suppression (NMS) 적용하기
6. 탐지 성능 검증을 위한 지표 소개 및 적용
7. Faster R-CNN 모델 테스트해보기
1. Dataset 살펴보기
먼저 이 실습에 필요한 Dataset을 살펴본다. 이번 실습에는 학습/검증을 위한 이미지들과, Bbounding box들의 좌표와 Label이 저장되어있는 df.csv파일이 Dataset이다.
먼저 df.csv 파일을 살펴보았다. 엑셀로 열어보면 이렇게 생긴것을 확인할 수 있다. 이 파일에서 우리가 필요한것을 이미지 파일 이름, Bbounding box의 좌표들(XMin, Xmax, YMin, YMax) , 라벨 이름이다. 다만 조심할 것은 Colab상에서 df.csv를 pandas로 불러온다면 Fig2 처럼 24062 x 21 크기의 Dataframe이라고 해서 이미지 개수가 24062개라고 생각할 수 있는데, 사실을 겹치는 것을 제외하는 unique()로 ImageID를 세어본다면, 학습/검증 폴더에 존재하는 이미지 파일의 개수와 일치하는 15225개인 것을 알 수 있다.. 별거 아닌 거 같지만, 이것 때문에 너무 헷갈려서 좀 헤맸다.. 설명을 내가 못 들은 건가ㅠ
df.csv를 불러와서, df.csv에 이미지 이름.jpg에 해당하는 파일이 이미지폴더에 존재한다면 해당 이미지 파일의 이미지 경로를 변수에 저장해 준다.
data_dir = '/content/drive/MyDrive/fastCamMedicalProj/DATASET/DATASET/Detection'
data_df = pd.read_csv(os.path.join(data_dir, "df.csv"))
index = 0
image_files = data_df['ImageID']
if image_files[index]+'.jpg' in os.listdir('/content/drive/MyDrive/fastCamMedicalProj/DATASET/DATASET/Detection/images'):
image_file = image_files[index]+'.jpg'
image_path = os.path.join("/content/drive/MyDrive/fastCamMedicalProj/DATASET/DATASET/Detection/images/", image_file)
또 변수 image_file에서 .jpg앞의 이미지 파일 이름으로 이미지 파일의 메타데이터를 df.csv에서 불러와 변수에 저장해 준다.
image_id = image_file.split('.')[0]
meta_data = data_df[data_df['ImageID']==image_id]
meta_data에는 다음과 같은 dataframe의 행이 저장된다.
meta_data에서 현재 이미지파일의 Label이름, Bounding box의 좌표값을 추출해 준다. 다만 한 이미지에 여러 개의 객체가 존재해서 Label이름이 여러개 있을 수 있고, Label이름을 문자열로 저장해주지 않기 위해서 배열에 한 이미지에 대한 Label이름을 Bus는 0으로 Truck은 1로 저장해 준다. 또한 추출된 Bounding box좌표값은 이미지크기에 normalize 된(각각 이미지의 가로, 세로 크기로 나눠진) 값들이다. 그리고, 추출된 Bounding box 좌표값들의 순서는 Xmin, Xmax, Ymin, Ymax로 되어있는데, 그 순서를 Xmin, Ymin, Xmax, Ymax로 바꿔준다.
cate_names = meta_data['LabelName'].values
bboxes = meta_data[["XMin", "XMax", "YMin", "YMax"]].values
# 이 좌표값들은 이미지크기에 normalize된 값들이다.
img_h, img_w, img_c = image.shape
CLASS_NAME_TO_ID = {'Bus': 0, 'Truck': 1}
CLASS_ID_TO_NAME = {0: 'Bus', 1: 'Truck'}
class_ids = [CLASS_NAME_TO_ID[cate_name] for cate_name in cate_names]
unnorm_bboxes = bboxes.copy()
#현재 bboxes 배열에서 ["XMin", "XMax", "YMin", "YMax"] 순서인데 그 순서를 ["XMin", "YMin", "XMax", "YMax"]로 바꿔준다.
unnorm_bboxes[:, [1,2]] = unnorm_bboxes[:, [2,1]]
# Xmax, Ymax = Xmax-Xmin, Ymax-Ymin으로 즉, width, height = Xmax-Xmin, Ymax-Ymin
unnorm_bboxes[:, 2:4] -= unnorm_bboxes[:, 0:2]
# Xmin, Ymin -> Xmin+width/2, Ymin+height/2 -> X_cen, Y_cen
unnorm_bboxes[:, 0:2] += (unnorm_bboxes[:, 2:4] / 2)
unnorm_bboxes[:, [0,2]] *= img_w
unnorm_bboxes[:, [1,3]] *= img_h
# 결과적으로 ["XMin", "YMin", "XMax", "YMax"] -> [X_cen, Y_cen, width, height]
그리고 이렇게 변환된 Bounding box좌표값들, 라벨이름, 이미지를 다음코드를 통해서 시각화해 준다면, Fig4 같이 객체에 해당하는 Bounging box가 그려진 이미지를 얻을 수 있다.
BOX_COLOR = {'Bus':(200, 0, 0), 'Truck':(0, 0, 200)}
def visualize_bbox(image, bbox, class_name, color=BOX_COLOR, thickness=2):
x_center, y_center, w, h = bbox
x_min = int(x_center - w/2)
y_min = int(y_center - h/2)
x_max = int(x_center + w/2)
y_max = int(y_center + h/2)
cv2.rectangle(image, (x_min, y_min), (x_max, y_max), color=color[class_name], thickness=thickness)
((text_width, text_height), _) = cv2.getTextSize(class_name, cv2.FONT_HERSHEY_SIMPLEX, 0.35, 1)
cv2.rectangle(image, (x_min, y_min - int(1.3 * text_height)), (x_min + text_width, y_min), color[class_name], -1)
cv2.putText(
image,
text=class_name,
org=(x_min, y_min - int(0.3 * text_height)),
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
fontScale=0.35,
color=TEXT_COLOR,
lineType=cv2.LINE_AA,
)
return image
# bounding box의 위치를 가지고 이미지에 오버래핑해서 시각화해주는 함수
def visualize(image, bboxes, category_ids):
img = image.copy()
for bbox, category_id in zip(bboxes, category_ids):
class_name = CLASS_ID_TO_NAME[category_id]
img = visualize_bbox(img, bbox, class_name)
return img
canvas = visualize(image, unnorm_bboxes, class_ids)
plt.figure(figsize=(6,6))
plt.imshow(canvas)
plt.show()
2. Dataset 구축과 연산을 위한 텐서변환 모듈 작성하기
데이터셋을 살펴보면서 진행했던 코드를 기반으로 Object Detector를 위한 데이터셋을 불러오는 코드를 작성해야 한다.
__getitem()함수에서는 인덱스가 주어지면, 이미지파일의 이름, 이미지, 라벨(bounding box의 좌표, class id(0이나 1))을 반환한다. 또한 transformer가 초기화 시에 parameter로 주어진다면, transformer을 통해서 이미지를 Tensor형으로 변환하고, 사이즈가 제각각인 이미지를 같은 resolution으로 변환해 준다. 이렇게 이미지를 Tensor형으로 변환하게 되면 원래 (H, W, C)인 형태에서 (C, H, W)로 변하기 때문에 이미지의 가로, 세로를 저장하는 변수를 다시 초기화해줘야 한다.
import torch
import numpy as np
class Detection_dataset():
def __init__(self, data_dir, phase, transformer=None):
self.data_dir = data_dir
self.phase = phase
self.data_df = pd.read_csv(os.path.join(self.data_dir, 'df.csv'))
self.image_files = [fn for fn in os.listdir(os.path.join(self.data_dir, phase)) if fn.endswith("jpg")]
# self.image_files = data_df['ImageID']
self.transformer = transformer
def __len__(self, ):
return len(self.image_files)
def __getitem__(self, index):
# input: image
# target: label (box좌표, class_id)
# get_image -> filename(imageId) -> get_label
filename, image = self.get_image(index)
bboxes, class_ids = self.get_label(filename)
img_H, img_W, _ = image.shape
if self.transformer: # 이미지를 tensor 형으로 변환, 이미지를 동일한 resolution으로 변환 등등....
# 원래는 image shape: (H,W,C)인데 tensor형으로 변환하게 된다면 (C,H,W)
image = self.transformer(image)
_, img_H, img_W = image.shape
bboxes[:, [0,2]] *= img_W
bboxes[:, [1,3]] *= img_H
# bboxes = [[Xmin, Ymin, Xmax, Ymax]] -> [0,2]=Xmin, Xmax 그리고 [1,3]=Ymin, Ymax
target = {}
target["boxes"] = torch.Tensor(bboxes).float()
target["labels"] = torch.Tensor(class_ids).long()
return image, target, filename
def get_image(self, index): # 이미지 불러오는 함수
filename = self.image_files[index]
image_path = os.path.join(self.data_dir, self.phase, filename)
image = cv2.imread(image_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
return filename, image
def get_label(self, filename): # label (box좌표, class_id) 불러오는 함수
image_id = filename.split('.')[0]
meta_data = self.data_df[self.data_df['ImageID'] == image_id]
cate_names = meta_data["LabelName"].values
class_ids = np.array([CLASS_NAME_TO_ID[cate_name] for cate_name in cate_names])
bboxes = meta_data[["XMin", "XMax", "YMin", "YMax"]].values
bboxes[:, [1,2]] = bboxes[:, [2,1]]
return bboxes, class_ids
Transformer도 선언하여 이미지를 텐서 화하고, 448x448 사이즈로 변환하여 주고, Normalize를 진행하여 준다.
IMAGE_SIZE = 448
transformer = transforms.Compose([
transforms.ToTensor(),
transforms.Resize(size=(IMAGE_SIZE, IMAGE_SIZE)),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
이렇게 transformer에서 normalize를 진행시켰을 때, 이미지에 차이가 있는지 확인하기 위해서 같은 이미지를 print 해보면 다음과 같이 색감의 차이가 있어 보인다. pixel의 값이 변경되었기 때문에 당연한 결과일 것이다.
데이터셋을 불러오고 transformer으로 변환까지 진행을 해줬으니, dataloader을 만들어줘서 mini batch 단위로 데이터를 로드할 수 있게 해 준다. 이미지와 target값(라벨, bounding box의 좌표)을 묶어서 미니배치 형태로 iterative 하게 받기 위해서 collate_fn을 받는다.. 이렇게 하면 minibatch의 크기만큼 dataloader가 이쁘게 배열에 묶어서 image, target, filename을 반환해 준다.
from torch.utils.data import DataLoader
def collate_fn(batch): # 이미지값과 target값을 묶어서 미니배치 형태로 iterative하게 받기 위해서 collate func를 선언
image_list = []
target_list = []
filename_list = []
for a,b,c in batch:
image_list.append(a)
target_list.append(b)
filename_list.append(c)
return image_list, target_list, filename_list
시험 삼아 trainloader라는 변수를 Dataloader로 선언해 주고 mini batch 크기를 6으로 설정해서 출력해보면 Fig6와 이미지와 target, filename을 이쁘게 묶어서 출력해주는 것을 확인할 수 있다.
이것을 기반으로 dataloader를 선언하는 함수를 짜주고, train이후에 바로 validation을 진행할 수 있도록 iteration문을 짜준다.
def build_dataloader(data_dir, batch_size=4, image_size=448):
transformer = transforms.Compose([
transforms.ToTensor(),
transforms.Resize(size=(image_size, image_size)),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
dataloaders = {}
train_dataset = Detection_dataset(data_dir=data_dir, phase="train", transformer=transformer)
dataloaders["train"] = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, collate_fn=collate_fn)
val_dataset = Detection_dataset(data_dir=data_dir, phase="val", transformer=transformer)
dataloaders["val"] = DataLoader(val_dataset, batch_size=1, shuffle=False, collate_fn=collate_fn)
return dataloaders
data_dir = "/content/drive/MyDrive/fastCamMedicalProj/DATASET/DATASET/Detection/"
dloaders = build_dataloader(data_dir, batch_size=4, image_size=448)
for phase in ["train", "val"]:
for index, batch in enumerate(dloaders[phase]):
images = batch[0]
targets = batch[1]
filenames = batch[2]
print("phase:{}, targets:{}".format(phase, targets))
if index == 0:
break
3. Faster R-CNN 아키텍처 불러오기
Faster R-CNN은 Fig7과 같은 구조를 가지고 있다. Faster R-CNN은 https://gomduribo.tistory.com/18에서 스터디한 내용을 써놓아서 굳이 따로 설명을 하지는 않겠다. 이 실습에서는 torchvision에 구현되어있는 Faser R-CNN을 사용하였는데, 그중에서도 Backbone을 Resnet50을 사용한 fasterrcnn_resnet50_fpn을 사용하였다. 이 모델을 Colab상에서 띄워서 head부분을 관찰해 보면 Fig8과 같다.
fasterrcnn_resnet50_fpn의 head 부분은 우리의 task에 맞춰서 변경해 줘야 한다. 우리의 task에 맞춰 class score를 2개, 그리고 bbox_pred를 8개(2개x좌표점4개)로 변경해 볼 것이다.
def build_model(num_classes):
model = models.detection.fasterrcnn_resnet50_fpn(pretrained=True)
in_features = model.roi_heads.box_predictor.cls_score.in_features
model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)
return model
NUM_CLASSES = 2
model = build_model(num_classes=NUM_CLASSES)
이렇게 코드를 작성하면 Fig9과 같이 model의 head부분이 변경된 것을 확인할 수 있다.
4. Object Detector 학습을 위한 코드 작성하기
먼저 시험 삼아 모델을 학습모드로 전환하고, 데이터셋의 이미지와 target(라벨값, bounding box좌표)를 모델에 넣어주면 어떤 loss를 반환하는지 찍어보았다. 밑에와 같은 코드로 찍어보았다. 그러면 Fig10과 같이 4개의 loss를 배출하는 것을 알 수 있는데, 이것은 Fig7의 Faster R-CNN의 구조에서 확인할 수 있는 4개의 loss와 일치한다.
phase = 'train'
model.train()
for index, batch in enumerate(dloaders[phase]):
images = batch[0]
targets = batch[1]
filenames = batch[2]
images = list(image for image in images)
targets = [{k: v for k, v in t.items()} for t in targets]
loss = model(images, targets)
if index == 0:
break
torchvision에서 제공하는 fast R-CNN 모델인 Resnet50 fpn 모델은 train모드일 경우에 자동으로 loss를 계산해주기 때문에, 굳이 criterion은 넣을 필요는 없다고 한다.
이렇게 한 개의 epoch동안 학습을 하는 함수를 작성해 준다. 그리고 한 개의 epoch가 끝나면 train loss와 validation loss를 반환해서 출력해 준다.
from collections import defaultdict
def train_one_epoch(dataloaders, model, optimizer, device):
# torchvision에서 제공하는 fast R-cnn 모델인 resnet50 fpn 모델은 train모드일 경우에 자동으로 loss계산해 준다.
# 그래서 굳이 criterion은 따로 넣지 않는다.
train_loss = defaultdict(float)
val_loss = defaultdict(float)
model.train()
for phase in ["train", "val"]:
for index, batch in enumerate(dataloaders[phase]):
images = batch[0]
targets = batch[1]
filenames = batch[2]
images = list(image.to(device) for image in images)
targets = [{k: v.to(device) for k, v in t.items()} for t in targets]
with torch.set_grad_enabled(phase == "train"):
loss = model(images, targets)
# 총 4개의 loss값이 있기 때문에 다 더해준다.
total_loss = sum(each_loss for each_loss in loss.values())
if phase == "train":
optimizer.zero_grad()
total_loss.backward()
optimizer.step()
# VERBOSE_FREQ-> 몇번의 주기로 print해줄지
if (index > 0) and (index % VERBOSE_FREQ) == 0:
text = f"{index}/{len(dataloaders[phase])} - "
for k, v in loss.items():
text += f"{k}: {v.item():.4f} "
print(text)
for k, v in loss.items():
train_loss[k] += v.item()
train_loss["total_loss"] += total_loss.item()
else: # validation 모드일때
for k, v in loss.items():
val_loss[k] += v.item()
val_loss["total_loss"] += total_loss.item()
for k in train_loss.keys():
train_loss[k] /= len(dataloaders["train"])
val_loss[k] /= len(dataloaders["val"])
return train_loss, val_loss
위에 같이 한 epoch동안 학습하는 함수를 작성했으니 이제 학습을 진행하면 된다. 밑에 코드와 같이 코드를 짜서 학습을 진행해 주면 된다.
data_dir = "/content/drive/MyDrive/fastCamMedicalProj/DATASET/DATASET/Detection/"
is_cuda = False
NUM_CLASSES = 2
IMAGE_SIZE = 448
BATCH_SIZE = 6
VERBOSE_FREQ = 200
DEVICE = torch.device('cuda' if torch.cuda.is_available and is_cuda else 'cpu')
dataloaders = build_dataloader(data_dir=data_dir, batch_size=BATCH_SIZE, image_size=IMAGE_SIZE)
model = build_model(num_classes=NUM_CLASSES)
model = model.to(DEVICE)
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
num_epochs = 30
train_losses = []
val_losses = []
for epoch in range(num_epochs):
train_loss, val_loss = train_one_epoch(dataloaders, model, optimizer, DEVICE)
train_losses.append(train_loss)
val_losses.append(val_loss)
print(f"epoch:{epoch+1}/{num_epochs} - Train Loss: {train_loss['total_loss']:.4f}, Val Loss: {val_loss['total_loss']:.4f}")
if (epoch+1) % 10 == 0:
save_model(model.state_dict(), f'model_{epoch+1}.pth')

5. Confidence threshold와 Non-maximum suppression(NMS) 적용하기
학습이 끝났으니 모델이 Inference 한 bounding box들에 대해서 NMS기법을 적용해 보았다. 먼저 학습을 진행하서 .pth 형식으로 저장된 가중치 파일을 불러오고 그 파일을 기반으로 inference를 진행하였다. Dataloader를 선언할 때, validation 모드에서 mini batch 크기는 1 이였기 때문에 한 장의 이미지, 라벨, 이미지이름이 존재한다.
import torch
def load_model(ckpt_path, num_classes, device):
checkpoint = torch.load(ckpt_path, map_location=device)
model = build_model(num_classes=num_classes)
model.load_state_dict(checkpoint)
model = model.to(device)
model.eval()
return model
is_cuda = False
NUM_CLASSES = 2
DEVICE = torch.device('cuda' if is_cuda and torch.cuda.is_available() else 'cpu')
data_dir = "/content/drive/MyDrive/fastCamMedicalProj/DATASET/DATASET/Detection/"
dataloaders = build_dataloader(data_dir, batch_size=1)
num_classes = len(CLASS_NAME_TO_ID)
model = load_model(ckpt_path='/content/drive/MyDrive/fastCamMedicalProj/Chap5_Object detection/model_30.pth', num_classes=NUM_CLASSES, device=DEVICE)
for index, batch in enumerate(dataloaders['val']):
images = batch[0]
images = list(image.to(DEVICE) for image in images)
print("imagess: {}".format(images))
# print('images shape: {}'.format(images.shape))
# print("image: {}".format(image))
with torch.no_grad():
prediction = model(images)
if index == 0:
break
이렇게 코드를 짜고, 모델이 Inference 한 결과인 prediction변수의 값을 찍어보면 Fig12처럼 나오는 것을 확인할 수 있다.
그리고 이제 prediction 한 결과에 대해서 NMS를 진행해주는 postprocess 함수를 짜준다. 그리고 이 함수를 기반으로 다시 Inference 함과 동시에 NMS를 진행을 해주는 코드를 밑에와 같이 짜주면 Fig13과 같은 결과를 얻을 수 있다.
from torchvision.ops import nms
from torchvision.utils import make_grid
def postprocess(prediction, conf_thres=0.2, IoU_threshold=0.1):
pred_box = prediction["boxes"].cpu().detach().numpy()
pred_label = prediction["labels"].cpu().detach().numpy()
pred_conf = prediction['scores'].cpu().detach().numpy()
# confidence score기반 filtering
conf_thres = 0.2
valid_index = pred_conf > conf_thres
pred_box = pred_box[valid_index]
pred_label = pred_label[valid_index]
pred_conf = pred_conf[valid_index]
# NMS 진행
# 다만 이번 task에서 사용하는 R-CNN과 다음 task에서 사용하는 YOLO는 anchor box를 사용하지 않기 때문에
# NMS의 효과가 미미할 수 있다.
valid_index = nms(torch.tensor(pred_box.astype(np.float32)), torch.tensor(pred_conf), IoU_threshold)
pred_box = pred_box[valid_index.numpy()]
pred_conf = pred_conf[valid_index.numpy()]
pred_label = pred_label[valid_index.numpy()]
return np.concatenate((pred_box, pred_conf[:, np.newaxis], pred_label[:, np.newaxis]), axis=1)
# 반환값의 format -> [한 이미지에서 detect한 object의 개수, x1, y1, x2, y2, confidence score, class_id]
pred_images = []
pred_labels =[]
for index, (images, _, filenames) in enumerate(dataloaders["val"]):
images = list(image.to(DEVICE) for image in images)
filename = filenames[0]
# images에 들어있는 값들은 normalize된 값이기 때문에 normalize를 풀어주고, C H W를 H W C로 바꿔준다.
image = make_grid(images[0].cpu().detach(), normalize=True).permute(1,2,0).numpy()
image = (image * 255).astype(np.uint8)
# batch 크기가 1이기 때문에 images = [image]이런 꼴이다..
# 마찬가지 [prediction]이렇게 생김
with torch.no_grad():
prediction = model(images)
prediction = postprocess(prediction[0])
prediction[:, 2].clip(min=0, max=image.shape[1])
prediction[:, 3].clip(min=0, max=image.shape[0])
# display하기 위해선 YOLO format으로 바꿔줘야한다.
xc = (prediction[:, 0] + prediction[:, 2])/2
yc = (prediction[:, 1] + prediction[:, 3])/2
w = prediction[:, 2] - prediction[:, 0]
h = prediction[:, 3] - prediction[:, 1]
cls_id = prediction[:, 5]
prediction_yolo = np.stack([xc,yc, w,h, cls_id], axis=1)
pred_images.append(image)
pred_labels.append(prediction_yolo)
if index == 10:
break
6. 탐지 성능 검증을 위한 지표 소개 및 적용
다음으로 COCO api를 통해서 mAP를 측정해서 구축한 모델의 탐지 성능 검증을 해볼 것이다. 다음과 같은 코드로 COCO api를 측정해 보았고, 30 epoch만큼 학습한 우리의 Faster R-CNN모델의 mAP@.50은 0.313 임을 도출할 수 있었다.
import json
from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval
def XminYminXmaxYmax_to_XminYminWH(box):
Xmin = box[:, 0]
Ymin = box[:, 1]
W = box[:, 2] - box[:, 0]
H = box[:, 3] - box[:, 1]
return np.stack((Xmin, Ymin, W, H), axis=1)
annFile = "/content/drive/MyDrive/fastCamMedicalProj/DATASET/DATASET/Detection/val.json"
# detection을 수행할 이미지에 대한 ground truth 값 모두 들어가 있는 json파일
with open(annFile, mode='r') as f:
json_data = json.load(f)
imageToid = json_data["imageToid"]
cocoGt=COCO(annFile)
COCO_anno = []
for index, (images, _, filenames) in enumerate(dataloaders["val"]):
images = list(image.to(DEVICE) for image in images)
filename = filenames[0]
image = make_grid(images[0].cpu().detach(), normalize=True).permute(1,2,0).numpy()
image = (image * 255).astype(np.uint8)
with torch.no_grad():
prediction = model(images)
prediction = postprocess(prediction[0])
prediction[:, 2].clip(min=0, max=image.shape[1])
prediction[:, 3].clip(min=0, max=image.shape[0])
box_xywh = XminYminXmaxYmax_to_XminYminWH(prediction[:, 0:4])
score = prediction[:, 4][:, np.newaxis]
cls_id = prediction[:, 5][:, np.newaxis]
img_id = np.array([imageToid[filename], ] * len(cls_id))[:, np.newaxis]
COCO_anno.append(np.concatenate((img_id, box_xywh, score, cls_id), axis=1))
if index % 50 == 0:
print(f"{index}/{len(dataloaders['val'])} Done.")
COCO_anno = np.concatenate(COCO_anno, axis=0)
cocoDT = cocoGT.loadRes(COCO_anno)
annType = "bbox"
cocoEval = COCOeval(cocoGt,cocoDt,annType)
cocoEval.evaluate()
cocoEval.accumulate()
cocoEval.summarize()
eval_stats = cocoEval.stats
7. Faster R-CNN 모델 테스트해 보기
마지막으로 실습에서 영상에 대해서 구축한 Faster R-CNN모델을 테스트해 보았는데, 강의에서는 Jupyter notebook을 사용하고 난 Colab을 사용해서 그런지 영상을 띄우는 게 잘 안되었다. 그래서 강의에서 소개된 영상 Inference 캡쳐본을 넣겠다..ㅠ