目标检测中的偏移量是什么,附代码实现
目录
一:回顾
二: 标记类别和偏移量
三:计算偏移量
四:一个例子
所有项目代码+UI界面
一:回顾
上一节课我们了解了目标检测基础中的将真实边界框分配给锚框,在训练集中,我们将每个锚框视为一个训练样本。 为了训练目标检测模型,我们需要每个锚框的类别(class)和偏移量(offset)标签,其中前者是与锚框相关的对象的类别,后者是真实边界框相对于锚框的偏移量。 在预测时,我们为每个图像生成多个锚框,预测所有锚框的类别和偏移量,根据预测的偏移量调整它们的位置以获得预测的边界框,最后只输出符合特定条件的预测边界框。
二: 标记类别和偏移量
现在我们可以为每个锚框标记类别和偏移量了。 假设一个锚框A被分配了一个真实边界框B。 一方面,锚框A的类别将被标记为与B相同。 另一方面,锚框A的偏移量将根据B和A中心坐标的相对位置以及这两个框的相对大小进行标记。 鉴于数据集内不同的框的位置和大小不同,我们可以对那些相对位置和大小应用变换,使其获得分布更均匀且易于拟合的偏移量。 这里介绍一种常见的变换。 给定框A和B,中心坐标分别为(Xa,Xa)和(Xb,Ya),宽度分别为Wa和Wb,高度分别为ℎa和ℎb,可以将A的偏移量标记为:


为什么需要除以一个数或者log,类似于归一化操作?
因为考虑到梯度计算,不能让梯度太大,所以需要设置较小的值。

三:计算偏移量
def offset_anchors(anchors,gtboxs_lisks_anchors,device,eps=1e-6):#gtbox_lisks_anchors是根据anchors分配的gtbox,扩展成anchors数量一样多的矩阵,用于计算偏移量anchors,gtboxs_lisks_anchors = anchors.to(device),gtboxs_lisks_anchors.to(device)num_batch = anchors.shape[0]off_anchors = []center_anchors = corner_to_center(anchors)center_gtboxs = corner_to_center(gtboxs_lisks_anchors)#计算偏移量for i in range(num_batch):offset_x = center_gtboxs[i,:,0] - center_anchors[i,:,0] / center_anchors[i,:,3] offset_y = center_gtboxs[i,:,1] - center_anchors[i,:,1] / center_anchors[i,:,2] offset_h = torch.log(eps + center_gtboxs[i,:,2]/center_anchors[i,:,2]) offset_w = torch.log(eps + center_gtboxs[i,:,3]/center_anchors[i,:,3])off_anchor = torch.stack((offset_x,offset_y,offset_h,offset_w),dim=1)off_anchors.append(off_anchor)off_anchors = torch.cat([*off_anchors],dim=0).reshape(num_batch,-1,4)return off_anchors
如果一个锚框没有被分配真实边界框,我们只需将锚框的类别标记为背景(background)。 背景类别的锚框通常被称为负类锚框,其余的被称为正类锚框。 我们使用真实边界框(labels参数)实现以下multibox_target函数,来标记锚框的类别和偏移量(anchors参数)。 此函数将背景类别的索引设置为零,然后将新类别的整数索引递增一。
注意:除了真实边界框(包括填充)个数小于锚框个数时,false才有-1出现意思是如果没有填充类,也就是当类别个数等于像素点的锚框个数的时候。gtbox_lisks_anchor[_idx]因为_idx是所有从0开始的所有坐标,值就是gtbox,但是如果类别个数小于锚框个数时,就有填充类出现,
其中 gtbox_lisks_anchor[_idx] = gtboxs[i,:,1:][idx_]得到的是真实锚框的坐标(也是锚框经过筛选后与这个真实框比较相似的),取出坐标,与anchors计算偏移量。虽然不知道这个锚框到底跟哪个真是边界框相似,但是可以通过_idx和idx_来找到。
#给锚框分配真实边界框以及标记为此类别,锚框与真实边界框的偏移量,真实的掩码矩阵
def offset_anchor_gtbox(anchors,gtboxs,device,threshold=0.1):anchors,gtboxs = anchors.to(device),gtboxs.to(device)num_batch = anchors.shape[0]#得出上个函数:真实边界框分配给锚框cls_anchors_false,cls_anchors_true = assign_gtbox_to_anchor(anchors, gtboxs, device=device, threshold=threshold)print('cls_anchors_false is :',cls_anchors_false)print('cls_anchors_true is :',cls_anchors_true)mask_anchors = []off_anchors = []#制作掩码矩阵,处理随机制作的真实边界(-1)for i in range(num_batch):
# (cls_anchors_true[i,:]>=0)是放在有填充的无效真实边界框
# .long()转true为1将false转换为0,cls_anchors_true[i,:]只拿出来了一维,升一个维度,repeat后与对应坐标全部置为0或者不变,因为只有true和falsemask_anchor = (cls_anchors_true[i,:]>=0).long().unsqueeze(1).repeat(1,4) #复制4列,行是一个像素点的锚框个数
# print('mask_anchor is ',mask_anchor)#利用cls_anchors_false创建gtbox_lisks_anchorgtbox_lisks_anchor = torch.full(anchors[i,:,:].shape,0,dtype=torch.float,device=device)#建立一个全0的形状为anchor的矩阵_idx = torch.nonzero(cls_anchors_false[i,:].ravel()>=0).ravel()#除了真实边界框(包括填充)个数小于锚框个数时,false才有-1出现idx_ = cls_anchors_false[i,:][(cls_anchors_false[i,:]>=0)]#从cls_anchors_false获取大于0的索引对应的值#赋值给gtboxs_lisk_anchor来建立一个和anchor一样大的,跟anchor中每一个锚框一一对应的真实边界框坐标组成的矩阵,文档有图片print('_idx是_idx is:',_idx)print('idx_是idx_ is:',idx_)gtbox_lisks_anchor[_idx] = gtboxs[i,:,1:][idx_]#计算偏移量,anchors[i,:,:]只取了最后两列,batch去掉了,需要升维,每个函数都是在batch基础上进行的,mask_anchors可以去掉负类边界框#返回的off_anchor是真正的偏移量,已经去除了没有用的框#上面操作的是真实边界框小于锚框。已经去除了一行,这里又用mask_anchor去除了填充类虽然我定义了四个类别,有一个是填充类。off_anchor = offset_anchors(anchors[i,:,:].unsqueeze(0),gtbox_lisks_anchor.unsqueeze(0),device=device)*mask_anchormask_anchors.append(mask_anchor)off_anchors.append(off_anchor)#真实锚框类别,将负类变成0,正类从1开始计数#因为在训练的过程中,模型只能分类从0开始的,例argmaxcls_anchors_true += 1mask_anchors = torch.cat([*mask_anchors],dim=0).reshape(num_batch,-1,4)off_anchors = torch.cat([*off_anchors],dim=0).reshape(num_batch,-1,4)return cls_anchors_true.long(),mask_anchors,off_anchors
四:一个例子
下面通过一个具体的例子来说明锚框标签。 我们已经为加载图像中的狗和猫定义了真实边界框,其中第一个元素是类别(0代表狗,1代表猫),其余四个元素是左上角和右下角的(x,y)轴坐标(范围介于0和1之间)。 我们还构建了五个锚框,用左上角和右下角的坐标进行标记:A0,…,A4(索引从0开始)。 然后我们在图像中绘制这些真实边界框和锚框。
anchors = torch.randn(1,5,4)
gtboex = torch.randn(1,4,5)
gtboex[0,:,0] = torch.tensor([5,7,8,-1])
cls_anchor_true,mask_anchors,off_anchors = offset_anchor_gtbox(anchors,gtboex,device='cpu',threshold=0.1)
print('cls_anchor_true is',cls_anchor_true)
print('mask_anchors is',mask_anchors)
print('off_anchors is',off_anchors)

其中mask_anchor是掩码矩阵用来生成的列表形式的偏移量,对于负分类的锚框或者填充的边界框,就不需要进行方向传播再去计算他们损失量了了。所以要把没用的剔除掉,这需要用到掩码矩阵。
如果锚框检测到的是真实的物体,就置为1,否则置为 0。这样反向传播的时候,值为0就不会进行反向传播。 所以需要掩码矩阵的生成。先筛选大于0的数,然后升维度,然后repeat成与锚框一样的大小,让他们对应相乘得到掩码矩阵。

偏移量是中心坐标

max的用法



W,h的偏移量是nan,说明生成的真实边界框是一条竖线,不过真实的框是不可能这样的

gtbox有五个坐标,第一个是类别,后面四个是偏移量。所以修改只需要修改第0列的就行了

所有项目代码+UI界面
视频,笔记和代码,以及注释都已经上传网盘,放在主页置顶文章
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
