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

本文详细解析了目标检测中Anchor(锚框)的概念及其在原图中的映射方式,介绍了Anchor与特征图的关系,并提供了代码示例帮助理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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)

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

<think>嗯,用户想了解YOLO目标检测的原理和步骤。首先,我需要回忆一下YOLO的基本概念。YOLO是“You Only Look Once”的缩写,属于单阶段检测算法,和R-CNN这类两阶段算法不同,YOLO直接在图像上回归边界框和类别概率。根据引用[3]的内容,YOLO只需要一次网络前向传播就能完成检测,实时性很好。 接下来,我需要分步骤解释YOLO的工作流程。用户可能想知道YOLO如何处理图像,生成预测的。首先应该是输入图像的预处理,比如调整大小到固定尺寸,比如416x416。然后输入到卷积神经网络中提取特征。这需要提到YOLO的网络结构,比如Darknet,但用户可能不需要太详细的架构,重点在步骤。 然后,划分网格单元是关键。每个单元负责预测一定数量的边界框。例如,将图像分成SxS的网格,每个网格预测B个边界框。每个边界框包含坐标、置信度和类别概率。这部分需要详细说明,比如坐标是相对于网格的偏移,置信度反映是否有物体及预测准确度。 接着是边界框的预测和置信度计算。YOLO使用锚框(anchor boxes)吗?早期版本可能没有,v2之后开始使用。但用户的问题可能针对YOLO的基本原理,即最初的版本,所以需要确认。原版YOLO每个网格预测两个边界框,每个框有5个值(x,y,w,h,confidence),然后加上类别概率。 然后是损失函数,这部分用户可能也需要了解,包括位置损失、置信度损失和分类损失。但用户的问题可能更关注步骤而不是训练细节,不过简要提及可以更全面。 非极大值抑制(NMS)也是关键步骤,用来去除冗余的边界框,保留最可能的检测结果。需要解释NMS的作用和过程。 最后是输出检测结果,给出每个物体的类别和位置。可能还要提到YOLO的优势,如速度快,适合实时应用,引用[3]提到的应用场景。 用户提供的引用提到YOLO的优势和应用,我需要确保在回答中引用这些部分。例如,实时性优势来自引用[3],应用场景可以参考引用[1]和[3]。 可能还需要注意用户提到的格式要求,比如数学公式用$...$,独立公式用$$...$$。例如,置信度公式、损失函数中的部分可能需要用LaTeX表达。 需要检查是否有遗漏步骤,比如特征提取、网格划分、预测框生成、NMS处理,最后输出结果。确保每个步骤清晰,逻辑连贯。 另外,用户可能对YOLO的不同版本感兴趣,但问题集中在原理和步骤,可能不需要深入版本差异,但可以提到YOLO的发展如v1到v3等,作为相关问题。 最后生成相关问题时要考虑用户可能进一步想了解的内容,如YOLO的优缺点、优化方法、与其他算法的比较等,参考示例中的结构。</think>### YOLO目标检测原理及步骤 YOLO(You Only Look Once)是一种单阶段目标检测算法,其核心思想是将目标检测任务转化为回归问题,通过单次网络前向传播直接预测图像中所有目标的边界框和类别。以下是其原理和步骤的详细说明: --- #### **1. 输入图像预处理** - 将输入图像缩放到固定尺寸(如$416 \times 416$),以适应网络输入要求。 - 图像归一化处理,通常将像素值映射到$[0,1]$范围。 --- #### **2. 特征提取与网格划分** - **特征提取**:图像输入卷积神经网络(如Darknet),提取多尺度特征。例如,YOLOv3使用多个下采样层输出不同分辨率的特征图(如$13 \times 13$、$26 \times 26$、$52 \times 52$),以检测不同大小的目标[^3]。 - **网格划分**:将特征图划分为$S \times S$的网格单元,每个单元负责预测目标中心落在该区域内的物体。例如,$13 \times 13$的特征图对应将原图划分为$13 \times 13$的网格。 --- #### **3. 边界框预测** 每个网格单元预测$B$个边界框(Bounding Box),每个边界框包含以下信息: 1. **坐标参数**:$(x, y, w, h)$,其中$(x, y)$是边界框中心相对于网格单元的偏移量,$(w, h)$是框的宽度和高度相对于整张图像的比值。 2. **置信度(Confidence Score)**:表示框内存在目标的概率和预测框的准确性,计算公式为: $$\text{Confidence} = P(\text{Object}) \times \text{IOU}_{\text{pred}}^{\text{truth}}$$ 其中$P(\text{Object})$为是否包含目标的概率,$\text{IOU}$为预测框与真实框的交并比。 3. **类别概率**:使用Softmax函数预测目标属于各个类别的概率。 --- #### **4. 损失函数** YOLO通过多任务损失函数联合优化位置、置信度和分类: $$ \text{Loss} = \lambda_{\text{coord}} \sum \text{位置误差} + \lambda_{\text{obj}} \sum \text{置信度误差} + \lambda_{\text{noobj}} \sum \text{背景误差} + \sum \text{分类误差} $$ - 位置误差:采用均方误差(MSE)计算$(x, y, w, h)$的偏差。 - 置信度误差:使用交叉熵损失区分是否包含目标。 - 分类误差:交叉熵损失计算类别预测。 --- #### **5. 非极大值抑制(NMS)** - **筛选冗余框**:对同一目标的多个预测框,按置信度排序,保留最高置信度的框,同时抑制与其重叠度(IOU)超过阈值的其他框。 - **输出最终结果**:保留的边界框和类别概率作为检测结果。 --- #### **6. 输出检测结果** 最终输出为每个目标的: - **类别标签**:概率最高的类别。 - **边界框坐标**:经缩放还原到原图尺寸。 - **置信度分数**:用于评估检测可靠性。 --- ### **YOLO的优势** - **实时性**:单次前向传播完成检测,速度远超两阶段算法(如Faster R-CNN)[^3]。 - **全局上下文感知**:因处理整张图像,减少背景误检。 - **广泛应用**:如自动驾驶、视频监控、无人机导航等[^1]。 --- ### **示例代码(YOLOv3检测核心逻辑)** ```python import cv2 import numpy as np # 加载模型和配置 net = cv2.dnn.readNet("yolov3.weights", "yolov3.cfg") layer_names = net.getLayerNames() output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()] # 输入图像处理 image = cv2.imread("image.jpg") blob = cv2.dnn.blobFromImage(image, 1/255.0, (416, 416), swapRB=True, crop=False) # 前向传播 net.setInput(blob) outs = net.forward(output_layers) # 解析输出,应用NMS boxes, confidences, class_ids = [], [], [] for out in outs: for detection in out: scores = detection[5:] class_id = np.argmax(scores) confidence = scores[class_id] if confidence > 0.5: # 计算边界框坐标 center_x = int(detection[0] * width) center_y = int(detection[1] * height) # 绘制检测结果... ``` ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值