前言:
anchor是锚的意思,就是固定船的大铁块儿。在目标检测中,anchor box意为预设固定尺寸的参考框。目标检测要解决的问题是图像中哪个位置有什么样的物体,传统算法的解决方法是采用滑窗的方式,遍历整个图像,判断此位置是否有物体,非常低效耗时。
anchor box的概念首先出现在Faster RCNN中,通过一组9个人工预先设置固定尺寸的框,对经过backbone网络提取的特征图进行遍历,为每一个点都设置这9个固定的先验框,对每个框再采用卷积的方式进行分类(是否包含目标)和回归(anchor的坐标偏移量和缩放因子),再通过非极大抑制,去除重叠框,实现对目标物体的定位和分类。不同尺寸和ratio的框代表着能够适应不同尺度的目标物体。
先验框是由人为指定的,由此带来的问题是先验框设置的好坏会影响模型的训练以及收敛。在Faster RCNN中,对物体的定位是通过计算偏移量实现的,码!
def regression_box_shift(p, g):
"""
compute t to transform p to g
:param p: proposal box
:param g: ground truth
:return: t
"""
w_p = p[2] - p[0]
h_p = p[3] - p[1]
w_g = g[2] - g[0]
h_g = g[3] - g[1]
tx = (g[0] - p[0])/w_p
ty = (g[1] - p[1])/h_p
tw = np.log(w_g/w_p)
th = np.log(h_g/h_p)
t = [tx, ty, tw, th]
return t
举个例子来说,如果训练样本中都是尺寸较小的物体,而先验框的尺寸却很大,这意味着在bounding box regression阶段,将需要更多的调整以及更大的调整幅度以使得proposal框更接近ground truth框,从而影响模型的收敛速度。
那么就要求先验框的设置应当能够适应检测样本中的目标尺寸,也就是说,对于检测样本中的不同物体,9个anchor box中总有一个先验框的尺寸很接近物体的尺寸,而不是所有的anchor都偏离目标物体的尺寸。
聚类生成先验框尺寸:
yolov2中率先提出了使用K-means聚类的方法自动生成anchor尺寸,消除了anchor设置的主观性,在使用5个anchor的情况下就能达到Faster RCNN中使用9个anchor的精度,效果很好。
在K-means聚类算法中,主要概念为距离度量函数和聚类中心。对应于anchor聚类,不同的是样本距离度量函数的设置,定义为:
Distance = 1 – IOU
其中Distance为样本间距离,IOU为某个anchor和某ground truth的交并比,计算时将两个box的中心自动对齐,IOU越大,Distance越小,表明两个box尺寸越相近。
算法流程:码!
def kmeans(boxes, k, dist=np.median):
# number of boxes
box_num = len(boxes)
# store cluster center of each box
nearest_id = np.zeros(box_num)
np.random.seed(42)
# initialize the cluster
clusters = boxes[np.random.choice([i for i in range(box_num)], k, replace=False)]
while True:
# store iou distance between each pair of boxes and anchors
distance = []
for i in range(box_num):
ious = compute_iou(boxes[i], clusters)
dis = [1-iou for iou in ious]
distance.append(dis)
distance = np.array(distance)
# calculate box cluster id
new_nearest_id = np.argmin(distance, axis=1)
# break condition
if (new_nearest_id == nearest_id).all():
break
# update clusters using median strategy
for j in range(k):
clusters[j] = dist(boxes[new_nearest_id == j], axis=0)
nearest_id = new_nearest_id
return clusters
其中boxes为ground truth标注框数据,实际上只需传入框的高和宽就可以了。k为聚类中心的个数,即需要多少个anchor先验框。dist为更新聚类中心时的策略,本文使用取中间值。算法流程:初始化每个box的聚类中心id,随机选取k个box初始化聚类中心。开始聚类:计算每个box和k个聚类中心的距离,得到mxk大小的distance数组。计算每个box的聚类中心id,根据id采取中位数的策略更新聚类中心进行迭代,如果新旧id不发生变化则完成聚类。
完整代码:
import numpy as np
from glob import glob
input_dim = 1024
def compute_iou(box, anchors):
# distance = 1 - iou
# dis = []
ious = []
for anchor in anchors:
w_min = np.min([box[0], anchor[0]])
h_min = np.min([box[1], anchor[1]])
intersection = w_min*h_min
union = box[0]*box[1] + anchor[0]*anchor[1]
iou = intersection/(union - intersection)
# dis.append(1 - iou)
ious.append(iou)
return ious
def kmeans(boxes, k, dist=np.median):
# number of boxes
box_num = len(boxes)
# store cluster center of each box
nearest_id = np.zeros(box_num)
np.random.seed(42)
# initialize the cluster
clusters = boxes[np.random.choice([i for i in range(box_num)], k, replace=False)]
while True:
# store iou distance between each pair of boxes and anchors
distance = []
for i in range(box_num):
ious = compute_iou(boxes[i], clusters)
dis = [1-iou for iou in ious]
distance.append(dis)
distance = np.array(distance)
# calculate box cluster id
new_nearest_id = np.argmin(distance, axis=1)
# break condition
if (new_nearest_id == nearest_id).all():
break
# update clusters using median strategy
for j in range(k):
clusters[j] = dist(boxes[new_nearest_id == j], axis=0)
nearest_id = new_nearest_id
return clusters
def load_dataset(path):
# load normalization width and height of boxes
path = path + '/*.txt'
txt_list = glob(path)
data_set = []
for txt in txt_list:
with open(txt, 'r') as f:
lines = f.readlines()
for line in lines:
coordinate = line.split(' ')
w, h = np.array(coordinate[3:5], dtype=np.float64)
data_set.append([w, h])
data_set = np.array(data_set)
return data_set
def main():
txt_path = 'C:\\Users\\XQ\\Desktop\\labels'
data = load_dataset(txt_path)
# number of cluster center
clusters = kmeans(data, 9)
print('cluster center:*************')
print(clusters*input_dim)
accuracy = np.mean([np.max(compute_iou(box, clusters)) for box in data])*100
print('Accuracy(Average iou): %.4f%%' % accuracy)
anchor_ratio = np.around(clusters[:, 0] / clusters[:, 1], decimals=2)
anchor_ratio = list(anchor_ratio)
print('Final anchor_ratio: ', anchor_ratio)
print('Sorted anchor ratio: ', sorted(anchor_ratio))
if __name__ == "__main__":
main()
此代码使用的数据格式为yolo模型的数据标签格式,其它类型的也可以啦,自行转换即可。
输出效果:
输出有四项,第一项为k个聚类中心anchor的尺寸,注意乘上input_dim进行转换。第二项为Accuracy,其实为所有box和它的聚类中心anchor的IOU均值,这个值越大表明k个anchor能够适应的标注框越多,效果越好。 后两项为anchor长宽比的值。
才疏学浅,欢迎指正!
Original: https://blog.csdn.net/joker_xiansen/article/details/120002013
Author: joker_xiansen
Title: 聚类生成anchor框的尺寸和比例
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/550871/
转载文章受原作者版权保护。转载请注明原作者出处!