目标检测中Anchor如何映射到原图

Anchor在目标检测中是比较常见的,引入Anchor主要是为了让检测更精准,当然现在有很多Anchor Free的方法也达到了较好的结果,但是最近项目中用的是基于Anchor的方法,但是置于Anchor怎么映射到原图其实网上很多内容比较混乱,我这里把自己的理解记录下来,附加上一些代码用来解释说明。
什么是Anchor?

Anchor中文是锚框的意思,简要来讲就是将生成的特征图某一个点,对应到原图中的某个位置,因为一般情况下,利用深度学习进行目标检测一般需要经过较深的卷积神经网络,而卷积本身就有位置不变性,会将原图中的某块区域映射为特征图上的某个点。在一篇博文中看到有个有意思的说法,但觉得很有道理,图片分类和实例分割分别属于image-level和pix-level,而目标检测则属于region-level,那么anchor其实就是将图像分为了若干的块,原图的正确框(ground-truth)是图中绿色的块,其他框为anchor生成的框,一般是在原图对应区域生成9个框,对应3种尺度和3种宽高比或者高宽比的变换的组合(3*3=9),具体效果如下图:
在这里插入图片描述

这个可看做是用在原图上的效果,密密麻麻的anchor框,但是这些框只是生成的对应特征图面积乘以9得到的,如果是真的在原图上进行每个像素点生成anchor,那势必会有很多。

在这里插入图片描述

anchor和特征图的关系

既然anchor是特征图对应像素点在原图的某个区域,那么是怎样的对应关系呢?现在假设某个图像输入网络的大小是800800,那么经过三个阶段的卷积操作(借鉴ResNet中的stage理解),那么其输出是下采样了8倍,也就是变为了100100,那么特征图每个位置生成9个anchor,那么总共就有90000个框,这单单是一层的anchor,这也就解释了有些模型是密集采样的原因,但是其实90000个框中,有效的框很少,特别是针对一些图像中仅有一个图像的数据。一般需要我们在网络层面通过策略进行剔除无用的框,一般是通过IOU设定阈值进行剔除。比如将 IOU>=0.5当作正,IOU<0.4记为负,0.4=<IOU<0.5忽略掉,当然不同的模型具有不同的阈值。
其实特征图上每个像素点对应的区域为将输入的原图 (800800)分为特征图的大小(100100),则每个区域的中心,就是特征图该像素点的中心(0.5,0.5)位置对应的,当然像素的基本的单位是1,但是在让特征图的像素点映射回原图的过程就是将其中心点*该特征图的步长得到的。
还是这张图,特征图上一个像素点位置的中心,对应到原图中某个区域的中心
在这里插入图片描述

代码示例
# size:在原图上生成区域的大小[32,64,128..]
# ratio:对应宽高/高宽比,其实差别不大[0.5,1,2]
# scale:为对应某个块大小的面积扩增/缩减的比例,[0.7,1,1.5]
#针对某个尺寸 32,生成 [22.4,32,48]的尺寸
# fea_size:所对应阶段特征图的尺寸[64,32,16]
# strides:[8,16,32]
# names:自定义的名字列表
# root:存储数据的文件夹
def getAnchor(size,ratio,scale,fea_size,strides,names,root):
	num_base_anchors=len(ratio)*len(scale) 
	result=[]
	start=time.time()
	for index,s in enumerate(size):
		#每次需要置0
		base_anchors=np.zeros((num_base_anchors,4),dtype=np.float32)
		#np.tile(scale,(2,len(ratio)))scale[1,3]沿y轴扩增2倍,横轴扩增3倍
		#则最后变成了2*9 需要转置
		base_anchors[:,2:]=s*np.tile(scale,(2,len(ratio))).T 
		areas = base_anchors[:, 2] * base_anchors[:, 3]#求每个anchors的面积
		# 在scale的基础上进行ratio的计算 
		# 形成经过scale和ratio 组合后的 9 个坐标 
		# 但此时这个坐标仍然是后两维是宽和长(取决于ratio为长高比还是宽高比)
		# 前两维仍然是0,所以需要以当前位置为中心将其换为(x1,y1,x2,y2)的形式
		base_anchors[:,2]=np.sqrt(areas / np.repeat(ratio,len(scale)))
		base_anchors[:,3]=base_anchors[:,2]*np.repeat(ratio,len(scale))
		# 以本位置为中心 换为(x1,y1,x2,y2)的形式
		base_anchors[:,0]-=0.5*base_anchors[:,2]
		base_anchors[:,1]-=0.5*base_anchors[:,3]
		base_anchors[:,2]-=0.5*base_anchors[:,2]
		base_anchors[:,3]-=0.5*base_anchors[:,3]
		#cur_result=[]#用来存储当前尺寸下的变换结果
		# 此时将其映射到原图,特征图的大小假设是怡512*512为输入,
		# 则在3-7层的特征图输出尺寸为fea_size那个数组,这里需要逐次遍历这个数组
		# 这个需要对数组进行间隔取值,比如64*64大小的,其实就是将原图512*512划分成了
		# 64*64大小的块,每个块的中心坐标加上我们以上base_anchors的偏移量即为我们的所求 
		# 如何进行分块组合 这个需要用到 np.meshgrid 函数
		#for fea,stride in zip([fea_size[index]],[strides[index]]):
		#此步得到将512分块后每个块(x,y)的中心位置(上取整)
		shift_x=(np.arange(0,fea_size[index][0])+0.5)*strides[index]
		shift_y=(np.arange(0,fea_size[index][1])+0.5)*strides[index]
		# 使用meshgrid函数进行广播数组
		# 此时为shift_x为其每一行重复原来的shift_x(行向量 1*fea[0])
		# 此时为shift_y为其每一列重复原来的shift_y(行向量 1*fea[1])
		shift_x,shift_y=np.meshgrid(shift_x,shift_y)
		#下一步进行合并shift_x shift_y,即将其组合为先x轴按步长累加,
		# 然后将按列按步长进行累加
		shift_x=np.reshape(shift_x,[-1])#展为行向量(1,fea[1]*fea[0])
		shift_y=np.reshape(shift_y,[-1])#展为行向量(1,fea[0]*fea[1])
		# 每个位置为 x y的中心位置,
		# 再加上之前的那个在以自身为中心偏移过的base_anchors 
		# 即为特征图上某点到原图的映射
		# shifts:4*(fea[0]*fea[1])
		shifts=np.stack([shift_x,shift_y,shift_x,shift_y],axis=0)
		shifts=np.transpose(shifts)# 转置即为所求
		# 将原图的对应中心和base_anchors进行加和组装
		number_of_anchors = np.shape(base_anchors)[0]
		k = np.shape(shifts)[0]
		#将9个anchors分别和当前块的中心点相加
		shifted_anchors = np.reshape(base_anchors, [1, number_of_anchors, 4]) + np.array(np.reshape(shifts, [k, 1, 4]), np.float32)
		shifted_anchors = np.reshape(shifted_anchors, [k * number_of_anchors, 4])#reshape为所求
		#cur_result.append(shifted_anchors)#将其放进当前结果集中
		result.append(shifted_anchors)#将该尺寸的所有框放在
	print("time:",time.time()-start," len ",sum([i.shape[0] for i in result]))
	return result # list (all_level_num_anchors,4)

希望以上可以帮到您!!!!

  • 7
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值