首页 > 编程知识 正文

目标检测中的anchor

时间:2023-05-05 03:56:44 阅读:247772 作者:2734

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

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

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

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。