Anchor3

article/2025/10/22 23:38:10

这里介绍如何修改 Anchor 的尺寸来提高小目标的检测效果,算法tricks优化小目标检测

修改 Anchor 尺寸

在实际的应用场景中,我们按照 MS COCO 标准中把大小不大于 32x32 或者占原始图片比率不足 0.01 的目标物体定义为一个小目标物体。

在使用 Anchor 的检测算法(以目标检测网络 Faster RCNN 为例)中,如下图所示:算法会按照一定的规则在主干网络的所有输出 Feature Map 上生成不同尺寸的 Anchor,而候选提议框生成层 RPN(RPN 的输出结果和最后生成的预测目标物体框的大小、分类以及定位息息相关)则会预测这些 Anchor 中是否含有目标物以及目标物体框离 Anchor 框的偏移。

为了提高小目标物体的检测效果,我们可以通过修改 Anchor 的尺寸来生成合适的 Anchor。

下面我们详细介绍如何修改 Anchor 的尺寸来提高小目标的检测效果。根据上图所示我们知道 Anchor 生成在主干网络的输出特征图上进行,如果我们选择合适的 Anchor 来 Match 小目标,我们就可以提高小目标物体的分类准确度和定位精准度,从而提高小目标的检测精准度。

下面我们以 Faster RCNN 网络中 Anchor 的生成代码为例,说明如何调节输入参数来对调节 Anchor 的尺寸。

import numpy as np  # 传入anchor的左上角和右下角的坐标,返回anchor的中心坐标和长宽  
def _whctrs(anchor):  """  :param anchor: list,某个anchor的坐标信息[xmin, ymin, xmax, ymax]  :return: anchor的中心点坐标和anchor的长宽  """  w = anchor[2] - anchor[0] + 1  h = anchor[3] - anchor[1] + 1  x_ctr = anchor[0] + 0.5 * (w - 1)  y_ctr = anchor[1] + 0.5 * (h - 1)  return w, h, x_ctr, y_ctr  # 给定一个anchor的中心坐标和长宽,输出各个anchor,即预测窗口,**输出anchor的面积相等,只是宽高比不同**  
def _mkanchors(ws, hs, x_ctr, y_ctr):  """  :param ws: anchor的宽  :param hs: anchor的长  :param x_ctr: anchor中心点x坐标  :param y_ctr: anchor中心点y坐标  :return: numpy array, 生成的符合条件的一组anchor  """  # 将ws和hs数组转置  ws = ws[:, np.newaxis]  hs = hs[:, np.newaxis]  # 生成符合条件的一组anchor  anchors = np.hstack((x_ctr - 0.5 * (ws - 1),  y_ctr - 0.5 * (hs - 1),  x_ctr + 0.5 * (ws - 1),  y_ctr + 0.5 * (hs - 1)))  return anchors  # 将给定的anchor放大scales中指定的倍数  
def _scale_enum(anchor, scales):  """  :param anchor: list,某个anchor的坐标信息[xmin, ymin, xmax, ymax]  :param scales: list,将anchor中的元素放大到scales中指定的倍数  :return: numpy array, 生成的符合条件的一组anchor  """  # 找到anchor的中心坐标  w, h, x_ctr, y_ctr = _whctrs(anchor)  # 将anchor的长宽放大到scales中指定的倍数  ws = w * scales  hs = h * scales  # 根据指定的anchor长宽和中心点坐标信息生成一组anchor  anchors = _mkanchors(ws, hs, x_ctr, y_ctr)  return anchors  # 计算不同长宽尺度下的anchor的坐标  
def _ratio_enum(anchor, ratios):  """  :param anchor: 基准anchor  :param ratios: list, anchor长宽比例尺寸  :return: list, 生成的anchor信息  """  # 获取anchor的中心点坐标和长宽  w, h, x_ctr, y_ctr = _whctrs(anchor)  # 获取anchor的面积  size = w * h  # 在保持面积不变的情况下生成ratios中指定长宽比的anchor长和宽  size_ratios = size / ratios  ws = np.round(np.sqrt(size_ratios))  hs = np.round(ws * ratios)  # 获取指定长宽和中心点坐标的anchors  anchors = _mkanchors(ws, hs, x_ctr, y_ctr)  return anchors  def generate_anchors(base_size=16, ratios=[0.5, 1, 2],  scales=2 ** np.arange(3, 6)):  """  :param base_size: int, 基准anchor尺寸  :param ratios: list, anchor长宽比例尺寸  :param scales: list, anchor边长放大的倍数  :return: list, 生成的anchor信息  """  # 请注意anchor的表示形式有两种,一种是记录左上角和右下角的坐标,一种是记录中心坐标和宽高  # 这里生成一个基准anchor,采用左上角和右下角的坐标表示[0,0,15,15]  # base_anchor = [0,0,15,15]  base_anchor = np.array([1, 1, base_size, base_size]) - 1  # 按照ratios元素信息生成不同长宽比的anchor  ratio_anchors = _ratio_enum(base_anchor, ratios)  # 将ratio_anchors中的每个anchor放大到scales里指定的倍数  anchors = np.vstack([_scale_enum(ratio_anchors[i, :], scales)  for i in range(ratio_anchors.shape[0])])  return anchors  if __name__ == '__main__':  import time  t = time.time()  # 生成anchor  a = generate_anchors(base_size=16, ratios=[0.25, 0.5, 1, 2],  scales=2 ** np.arange(2, 6))  # 打印生成过程所需要的时间  print(time.time() - t)  print(a)  

结果如下:

0.00026607513427734375  
[[ -56.   -8.   71.   23.]  [-120.  -24.  135.   39.]  [-248.  -56.  263.   71.]  [-504. -120.  519.  135.]  [ -38.  -16.   53.   31.]  [ -84.  -40.   99.   55.]  [-176.  -88.  191.  103.]  [-360. -184.  375.  199.]  [ -24.  -24.   39.   39.]  [ -56.  -56.   71.   71.]  [-120. -120.  135.  135.]  [-248. -248.  263.  263.]  [ -14.  -36.   29.   51.]  [ -36.  -80.   51.   95.]  [ -80. -168.   95.  183.]  [-168. -344.  183.  359.]]  

上述 Anchor 函数生成的所有 Anchor,我们可以根据主干网络的网络架构计算出其在原始图像上的感受野大小。进而可以比对原始图片上感受野大小和原始图片上目标标注框大小。而在实际操作过程中,原始图片上目标标注框已经获取,我们需要通过分析这些目标标注框的大小反推 Anchor 生成函数的参数,进而调控生成 Anchor 的尺寸来更好的 Match 小目标物体的尺寸。上面给出的 Anchor 生成函数 generate_anchors 共有三个可调节参数:

  • 第一个参数 base_size 为基准 Anchor 的大小。

  • 第二个参数 ratios=[0.5, 1, 2] 指的是在保持面积不变的情况下,Anchor 框的边长按照 1:2、1:1、2:1 三种比例进行变换得到一组新的 Anchor,如下图所示:

 

  • 第三个参数 scales=2 ** np.arange(3, 6),指的是将各个 Anchor 放大 [8, 16, 32] 倍,得到一组新的 Anchor。如下图所示:

修改 Anchor 数量

根据上述所阐述的生成 Anchor 的尺寸和预测目标物体框的关系可知,如果我们能根据实际应用场景中目标物的大小来设计 Anchor 的尺寸,我们能在一定的程度上提高小目标物体的分类和定位精准度。而在实际应用场景中,我们会碰到一类数据集目标物的大小变化范围比较大且含有大部分的小目标物体,这种情况下,如果我们仅仅通过调节参数值修改 Anchor 的尺寸,可能不足以达到提高所有目标物的分类和定位准确度,我们还需要适当的增加 Anchor 的个数,让 Anchor 更加多尺度的来 Match 不同大小的目标物体。根据上述给出的 Anchor 生成函数可知,修改 anchor ratio 或者 anchor scale 的值的个数可以生成更多数量的 Anchor,即在实际预测过程中,会生成更多的不同尺寸的目标候选框来 Match 更多不同大小的目标物体。下面我们介绍一种在实际应用过程中的普适方法,来详细说明在不同数据集上,如何修改 Anchor 的尺寸和数量让 Anchor 机制生成更加符合实际目标物体大小的 Anchor。第一步:解析并读取目标物标注信息,计算并统计目标物体的坐标信息,代码如下:

import os  
import tqdm  
import xml.etree.ElementTree as ET  
import config  def convert_annotation(year, classes, image_name):  """  :param year: str, 数据集版本(voc2012)  :param classes: list, 数据集类别list  :param image_name: str, 标注图片名字  :return: list, 标注框坐标信息和类别信息  """  # 获取图片对应的标注文件路径并打开  xml_file = open(os.path.join(config.PLANE_CUT_DATASET,  'VOC%s/Annotations/%s.xml' % (year, image_name)))  # 使用xml读取三方包解析xml信息  tree = ET.parse(xml_file)  # 遍历根目录下的所有标注信息  b = []  root = tree.getroot()  for obj in root.iter('object'):  # 是否是难检出目标物  difficult = obj.find('difficult').text  # 标注类别名字并过滤掉不在类别list中的标注信息  cls = obj.find('name').text  if cls not in classes or int(difficult) == 1:  continue  # 获取对应的类别id  cls_id = classes.index(cls)  # 获取标注框信息  bbox = obj.find('bndbox')  box_info = (int(bbox.find('xmin').text),   int(bbox.find('ymin').text),   int(bbox.find('xmax').text),   int(bbox.find('ymax').text), cls_id)  # 过滤掉切图产生的空背景标注  if box_info == (1, 1, 1, 1, 0):  continue  else:  b.append(box_info)  return b  def main():  # 设置数据集版本和类别等信息  sets = [('2012', 'train'), ('2012', 'val')]  classes = ["plane"]  wd = os.getcwd()  # 遍历分别为训练集、验证集、测试集生成标注信息文件  for year, image_set in sets:  # 读取数据文件信息  temp_path = 'VOC%s/ImageSets/Main/%s.txt' % (year, image_set)  # 获取图像数据名字  image_names = open(os.path.join(config.PLANE_CUT_DATASET,  temp_path)).read().strip().split()  # 用只读模式打开标注信息记录文件  info_fp = open('%s_%s.txt' % (year, image_set), 'w')  # 遍历写入每个标注文件的标注信息  for image_name in tqdm.tqdm(image_names):  # 解析并读取标注文件信息,并写入文件  idx_info = convert_annotation(year, classes, image_name)  if idx_info:  info_fp.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg'  % (wd, year, image_name))  # 将标注框坐标等信息写入文件  for box_info in idx_info:  info_fp.write(" " + ",".join([str(a) for a in box_info]))  info_fp.write('\n')  info_fp.close()  if __name__ == '__main__':  main()  

第二步:使用 Kmeans 方法对目标物体的统计数据进行聚类,得到每一类的中心位置信息,代码如下:

import numpy as np  class KMEANS(object):  def __init__(self, cluster_number, filename):  self.cluster_number = cluster_number  self.filename = "2012_train.txt"  # 计算每个标注框和聚类中心的iou值矩阵  def iou(self, boxes, clusters):  """  :param boxes: numpy array, 每个元素为每个标注框宽高  :param clusters: numpy array, 元素个数=聚类中心个数,元素为从boxes中随机选取的元素  :return: float, iou值  """  # 获取标注框个数和聚类中心数目  n = boxes.shape[0]  k = cluster_number  # 计算标注框面积,并让每个元素重复k遍,整理成维度为(n, k)的numpy数组  box_area = boxes[:, 0] * boxes[:, 1]  box_area = box_area.repeat(k)  box_area = np.reshape(box_area, (n, k))  # 计算随机挑选的聚类中心面积, 并将元素复制n遍  cluster_area = clusters[:, 0] * clusters[:, 1]  cluster_area = np.tile(cluster_area, [1, n])  cluster_area = np.reshape(cluster_area, (n, k))  # 构建标注框宽矩阵和聚类中心宽矩阵  box_w_matrix = np.reshape(boxes[:, 0].repeat(k), (n, k))  cluster_w_matrix = np.reshape(np.tile(clusters[:, 0], (1, n)), (n, k))  # 获取两个矩阵中对应元素中较小的值  min_w_matrix = np.minimum(cluster_w_matrix, box_w_matrix)  # 构建标注框高矩阵  box_h_matrix = np.reshape(boxes[:, 1].repeat(k), (n, k))  cluster_h_matrix = np.reshape(np.tile(clusters[:, 1], (1, n)), (n, k))  # 获取最两个矩阵中对应元素中较大的值  min_h_matrix = np.minimum(cluster_h_matrix, box_h_matrix)  # 计算两个矩阵对应元素的内积,即计算聚类中心和每个标注框的相交面积  inter_area = np.multiply(min_w_matrix, min_h_matrix)  # 计算聚类中心和每个标注框的iou值  result = inter_area / (box_area + cluster_area - inter_area)  return result  # 计算iou均值  def avg_iou(self, boxes, clusters):  """  :param boxes: numpy array, 每个元素为每个标注框宽高  :param clusters: numpy array, 每个元素为一个聚类中心  :return: 标注矩形框和聚类中心的iou均值  """  accuracy = np.mean([np.max(self.iou(boxes, clusters), axis=1)])  return accuracy  # 训练kmeans模型  def kmeans(self, boxes, k, dist=np.median):  """  :param boxes: numpy array, 每个元素为每个标注框宽高  :param k: 聚类类别数目  :param dist: 聚类中心距离计算函数  :return: 聚类中心信息  """  # 获取元素个数(标注框数目)  box_number = boxes.shape[0]  # 随机生成一个新的numpy数组,维度为(标注框数目, 聚类数目)  distances = np.empty((box_number, k))  # 生成一个0元素构成的numpy数组  last_nearest = np.zeros((box_number,))  np.random.seed()  # 从box_number中,随机选取大小为k的数据  clusters = boxes[np.random.choice(  box_number, k, replace=False)]  # init k clusters  while True:  # 计算每个元素和聚类中心的"距离"(1-每个标注框和聚类中心框的iou)  distances = 1 - self.iou(boxes, clusters)  # 判断模型是否收敛即聚类结果不再变化  current_nearest = np.argmin(distances, axis=1)  if (last_nearest == current_nearest).all():  break  # 重新计算新的聚类中心  for cluster in range(k):  clusters[cluster] = dist(  # update clusters  boxes[current_nearest == cluster], axis=0)  last_nearest = current_nearest  return clusters  # 将计算出来的kmeans计算出来的anchor结果写入txt文件  def result_txt(self, data):  """  :param data: 聚类中心数据  :return:  """  f = open("yolo_anchors.txt", 'w')  row = np.shape(data)[0]  for i in range(row):  if i == 0:  x_y = "%d,%d" % (data[i][0], data[i][1])  else:  x_y = ", %d,%d" % (data[i][0], data[i][1])  f.write(x_y)  f.close()  # 获取每个标注框的宽高信息  def get_box_info(self):  # 用只读模式打开标注信息统计文件  fp = open(self.filename, 'r')  box_info_list = []  # 遍历文件的每一行(每个文件)获取标注框信息并计算其宽高  for line in fp:  # 按照空格分割每个标注框信息  infos = line.split(" ")  length = len(infos)  # 遍历每个标注框信息并计算其宽和高  for i in range(1, length):  width = int(infos[i].split(",")[2]) - \  int(infos[i].split(",")[0])  height = int(infos[i].split(",")[3]) - \  int(infos[i].split(",")[1])  box_info_list.append([width, height])  # 将标注框信息list转成numpy数组并返回  result = np.array(box_info_list)  fp.close()  return result  # 训练kmeans并获取聚类结果  def get_clusters(self):  # 获取标注信息统计文件中的每个标注框的宽高  all_boxes = self.get_box_info()  # 将标注框宽高信息作为kmeans训练数据,获取其聚类中心  result = self.kmeans(all_boxes, k=self.cluster_number)  # 将聚类中心按照第一列进行排序  result = result[np.lexsort(result.T[0, None])]  # 将聚类中心结果存入txt文件  self.result_txt(result)  print("value of {} anchors:\n {}".format(self.cluster_number, result))  print("Accuracy: {:.2f}%".format(  self.avg_iou(all_boxes, result) * 100))  if __name__ == "__main__":  # 设置聚类中心数目和训练数据文件信息  cluster_number = 9  filename = "2012_train.txt"  # 创建KMEANS类对象  kmeans = KMEANS(cluster_number, filename)  # 调用类成员函数获取聚类中心、聚类中心和标注框iou准确率信息  kmeans.get_clusters()  

第三步:根据聚类中心修改 Anchor 生成机制参数。

 whaosoft aiot http://143ai.com  


http://chatgpt.dhexx.cn/article/fMtqnwWo.shtml

相关文章

ios-anchorPoint、position理解

anchorPoint(锚点)是layer的一个属性,下面我们来看看其对view的影响,本文主要通过图片方式展现: 锚点用单位坐标来描述也就是图层的相对坐标。在苹果文档中说明左下是(0,0),右上是(1,1),mac系统…

Anderson‘s pointer analysis

本文是垃圾文章,请直接学习其它资料 南京大学《软件分析》课程08(Pointer Analysis)https://www.cs.cmu.edu/~aldrich/courses/15-819O-13sp/resources/pointer.pdf 指针分析 指针分析是一类特殊的数据流问题,它是其它静态程序分析…

iOS围绕某点缩放或旋转的AnchorPoint的设定

经常会遇到需求,要求手势的缩放或者旋转操作,要求动作变化围绕某一个特定点,或者是两指的中心点,或者是某一个点。 这个问题首先要清晰的知道,iOS各个view的层次关系。特别是,要清除的知道,当前…

彻底理解CALayer的position与anchorPoint

引言 相信初接触到CALayer的人都会遇到以下几个问题: 为什么修改anchorPoint会移动layer的位置? CALayer的position点是哪一点呢? anchorPoint与position有什么关系? 我也迷惑过,找过网上的教程,大部分都是复制粘…

position和anchorPoint

本人录制技术视频地址: https://edu.csdn.net/lecturer/1899 欢迎观看。 一、理论概述 1.简单介绍 CALayer有2个非常重要的属性:position和anchorPoint property CGPoint position; 用来设置CALayer在父层中的位置 以父层的左上角为原点(0, 0) prop…

Ant Design - Anchor

Anchor锚点 此组件的属性有以下几点: 现在给出一份例子 其他属性都很简单就不多说了,主要我遇到的麻烦是 getContainer 属性 锚点是默认body滚动的,所以如果你滚动的区域是body就会看到锚点的小蓝点是会随内容滚动的,但是如果你…

Anchor Point

On default, CCNode’s anchor point is (0, 0), which is at the left-bottom point. CCSprite’s anchor point is (0.5, 0.5), which is at the center. 如果你把一个CCSprite作为child加入到CCNode中,CCNode的anchor point不会对sprite的位置有影响,…

【Cocos2d-x 3.0学习笔记】 AnchorPoint 和Position 关系

先不多说,上两张图片: 解释一下上面图片的意思: 描点就是图片中红点的位置。setAnchorPoint的取值范围0~1,距离设置的是一张图片 setAnchorPoint(Point(0,0))表示在图片左下角, setAnchorPoint(Point(1,1))表示在图片…

iOS开发之layer.frame,layer.anchorPoint,layer.position对frame的影响

最近遇到相关的问题,所以就将这三个属性值,进行了分析和研究,话不多说,直接上代码了,详细的文字描述都在代码中,可以自行查看。 之前还写了一篇文章,也可以同时查看一下: iOS开发之…

anchorPoint

OS开发UI篇—CAlayer层的属性 一、position和anchorPoint 1.简单介绍 CALayer有2个非常重要的属性:position和anchorPoint property CGPoint position; 用来设置CALayer在父层中的位置 以父层的左上角为原点(0, 0) property CGPoint anchorPoint; 称为“定位点”、“…

iOS动画小课堂:定点缩放弹窗(利用锚点anchorPoint进行实现)包含完整demo

文章目录 前言I 基础知识 (CALayer)1.1 anchorPoint1.2 positionII iOS开发中常用的动画(定点缩放弹窗)2.1 核心代码2.2 完整demo源码see also前言 iOS开发中常用的动画(定点缩放弹窗)的应用场景: 会员详情的右侧下拉操作菜单 浏览器的右侧下拉菜单

UIView的bounds、frame、center/position、anchorPoint的关系

视图的frame,bounds和center属性仅仅是存取方法,当操纵视图的frame,实际上是在改变位于视图下方CALayer的frame,不能够独立于图层之外改变视图的frame。 对于视图或者图层来说,frame并不是一个非常清晰的属性&#xff…

彻底理解position与anchorPoint

原文 http://www.cnblogs.com/benbenzhu/p/3615516.html 引言 相信初接触到CALayer的人都会遇到以下几个问题: 为什么修改anchorPoint会移动layer的位置? CALayer的position点是哪一点呢? anchorPoint与position有什么关系? 我也迷惑过&…

彻底弄清 anchorPoint 和 position

最近在研读《iOS Core Animation Advanced Techniques》这一本书,想系统地学习下关于 CALayer、Transition、以及动画等知识点。大家可以在 gitbook 上面找到该书的翻译版本。 传送门 在读到图层几何学这一章的时候,了解到了两个概念:anc…

Cocos2dx学习笔记9:cocos2dx锚点(Anchor Point)

锚点(AnchorPoint)是相对坐标,通常用来定义物体内部的点,在cocos2dx中,一般都是以加载精灵来实现游戏元素的表现,而精灵一般都是对应的一张图片资源。 我们在设置精灵位置的时候,要设置精灵中的锚点来和我们的坐标点相…

Anchorpoints学习笔记:

Anchor Detr学习笔记: 文章目录 Anchor Detr学习笔记:1.首先介绍下什么叫锚点(Anchor point)2.再来介绍下什么叫DETR3.Anchor Detr 1.首先介绍下什么叫锚点(Anchor point) ​   Anchor point就类似一张钉…

数学篇(三)向量的基本运算

1.平面向量 1.1平面向量的加法运算 两个向量,; 向量满足四边形法则; 1.2平面向量的乘法运算 两个向量,; 向量乘表示为 ; 相比于向量加运算,向量乘运算要复杂点,很难看明白向量乘的几何意…

2. 数据类型、向量、向量索引、向量修改、向量运算

b站课程视频链接:https://www.bilibili.com/video/BV19x411X7C6?p1 腾讯课堂(最新,但是要花钱,我花99元😢😢买了,感觉不错):https://ke.qq.com/course/3707827#term_id103855009 &a…

matlab 向量的基本运算

本文主要参考:王沫然编著的MATLAB与科学计算(第2版) 博客文章:点击打开链接 1、向量生成 1.1、直接输入 1.2、 xx0:step:xn 1.3、线性等分向量—linespace 1.4、对数等分向量—logspace 2、向量运算 21、加(减&#x…

向量复习(一):定义、求解、四则运算、点积和叉积

向量复习(一) 1. 向量的定义2. 向量的表示3. 向量的求解4. 向量的四则运算4.1 加法4.2 减法4.3 乘法和除法 5. 点积和叉积5.1 点积5.2 叉积 6. 模的求解7. 附录:代码8. 免责声明 首先,我们先来复习一下二维空间几何求交涉及的向量…