YOLOv3
1.1样本制作
前一篇文章已经总结了,要检测识别多目标,需要这几样东西。
1.建议框和真实框的相对位置 2 中心点 3 iou(建议框和真实框) 4 cls(类别)
如果自己做样本,就先用标注软件得到框的左上角和右下角的坐标点,然后算出中心点的坐标和宽高。由于yolov3输入图片大小必须是416 * 416的。所以得到中心点和宽高不能直接resize,而是先应该用cx/size(0),cy/size(1),w/size(0),h/size(1),(cx,cy是中心点的坐标,size(0)是整张图片的宽,size(1)是整张图片的高)然后就可以resize,resize后所有的中心点和宽高都乘以416这样,就得到416*416图片下的位置。然后怎么得到中心点的偏移?,我们就用中心点的位置除以缩放比例,整数部分就是中心点所在网格的位置,小数部分就是中心点相对于网格左上角的偏移。建议框和真实框的位置关系就分别用宽高比表示。这里为什么要加log,因为宽高比是0-1之间的网络返回结果很可能是负数,那么通过log反算回去一定是正数。还有iou我们就通过建议框和真实框的交并比来算。类别就用one-hot.这样就得到了样本。
在这里插入代码片
import torchvision
import numpy as np
import cfg
import os
from PIL import Image
import math
transforms = torchvision.transforms.Compose([
torchvision.transforms.Resize((416, 416)),
torchvision.transforms.ToTensor()
])
def one_hot(cls_num, v):
b = np.zeros(cls_num)
b[v] = 1.
return b
class MyDataset():
def __init__(self):
# super(MyDataset, self).__init__()
with open(cfg.LABEL_FILE_PATH) as f:
self.dataset = f.readlines()
# print(self.dataset)
def __len__(self):
return len(self.dataset)
def __getitem__(self, index):
labels = {}
# dataset[index]index 是第几个图片的标签
# print(self.dataset[index])
line = self.dataset[index]
strs = line.split()
_img_data = Image.open(os.path.join(cfg.IMG_BASE_DIR, strs[0]))
# transforms = torchvision.transforms.ToTensor()
# trans = transforms(_img_data)
# print(trans.size())
img_data = transforms(_img_data)
# img_data.show()
# _boxes = transforms(_img_data)
# 把标签转化成数组格式
_boxes = np.array([float(x) for x in strs[1:]])
# 5个5个的划分变成5个数组就是5个框
boxes = np.split(_boxes, len(_boxes) // 5)
for feature_size, anchor in cfg.ANCHORS_GROUP.items(): # 一个feature_size有三个anchor
# 定13,26,52的特征图形状 一个anchor对应一个类别
labels[feature_size] = np.zeros(shape=(feature_size, feature_size, 3, 5+cfg.CLASS_NUM))
# 计算特征图多对应的偏移量和索引
for box in boxes:
# 这里的box就是上面提到的cx/size(0),cy/size(1),w/size(0),h/size(1)
cls, cx, cy, w, h = box
# print(box)
cx = cx * 416
cy = cy * 416
# print(cx, cy)
w = w * 416
h = h * 416
# print(cx, cy, w, h)
# math.modf 返回两个值小数部分为偏移量,整数部分为索引
cx_offset, cx_index = math.modf(cx * feature_size / cfg.Img_HEIGHT)
cy_offset, cy_index = math.modf(cy * feature_size / cfg.Img_HEIGHT)
for i, d in enumerate(anchor): # 每次循环完有三个anchor
# 返回每个anchor框的面积
# print(anchor)
anchor_area = cfg.ANCHORS_GROUP_AREA[feature_size][i]
# 真实的宽高除以先验框的宽高
p_w, p_h = w / d[0], h / d[1]
# 真实的面积
p_area = w * h
# 真实框在先验宽里面所以iou为小除以大
# 计算置信度(同心框的IOU(交并))
# inter = np.minimum(w, anchor[0]) * np.minimum(h, anchor[1]) # 交集
# conf = inter / (p_area + anchor_area - inter)
iou = min(p_area, anchor_area) / max(p_area, anchor_area) # 这个iou不是为了筛选是为了一个中心点预测三个物体
# print(iou)
# print(d)
# cy_index==h cx_index==w,i为第几个建议框 返回的是iou 中心点和宽高 和类别的置信度 注意shape 为 h w class
# cx_offset, cy_offset拿到特征图每个网格相对于左上角偏移的位置
labels[feature_size][int(cy_index), int(cx_index), i] = np.array(
[cx_offset, cy_offset, np.log(p_w), np.log(p_h), iou, *one_hot(cfg.CLASS_NUM, int(cls))])
return labels[13].astype(np.float32), labels[26].astype(np.float32),labels[52].astype(np.float32), img_data