参考:
    https://github.com/lars76/kmeans-anchor-boxes

    1. #coding=utf-8
    2. import xml.etree.ElementTree as ET
    3. import numpy as np
    4. import glob
    5. def iou(box, clusters):
    6. """
    7. 计算一个 ground truth 边界盒和 k 个先验框(Anchor)的交并比(IOU)值。
    8. 参数box: 元组或者数据,代表 ground truth 的长宽。
    9. 参数clusters: 形如(k,2)的numpy数组,其中k是聚类Anchor框的个数
    10. 返回:ground truth和每个Anchor框的交并比。
    11. """
    12. x = np.minimum(clusters[:, 0], box[0])
    13. y = np.minimum(clusters[:, 1], box[1])
    14. if np.count_nonzero(x == 0) > 0 or np.count_nonzero(y == 0) > 0:
    15. raise ValueError("Box has no area")
    16. intersection = x * y
    17. box_area = box[0] * box[1]
    18. cluster_area = clusters[:, 0] * clusters[:, 1]
    19. iou_ = intersection / (box_area + cluster_area - intersection)
    20. return iou_
    21. def avg_iou(boxes, clusters):
    22. """
    23. 计算一个ground truth和k个Anchor的交并比的均值。
    24. """
    25. return np.mean([np.max(iou(boxes[i], clusters)) for i in range(boxes.shape[0])])
    26. def kmeans(boxes, k, dist=np.median):
    27. """
    28. 利用IOU值进行K-means聚类
    29. 参数boxes: 形状为(r, 2)的ground truth框,其中r是ground truth的个数
    30. 参数k: Anchor的个数
    31. 参数dist: 距离函数
    32. 返回值:形状为(k, 2)的k个Anchor框
    33. """
    34. # 即是上面提到的r
    35. rows = boxes.shape[0]
    36. # 距离数组,计算每个ground truth和k个Anchor的距离
    37. distances = np.empty((rows, k))
    38. # 上一次每个ground truth"距离"最近的Anchor索引
    39. last_clusters = np.zeros((rows,))
    40. # 设置随机数种子
    41. np.random.seed()
    42. # 初始化聚类中心,k个簇,从r个ground truth随机选k个
    43. clusters = boxes[np.random.choice(rows, k, replace=False)]
    44. # 开始聚类
    45. while True:
    46. # 计算每个ground truth和k个Anchor的距离,用1-IOU(box,anchor)来计算
    47. for row in range(rows):
    48. distances[row] = 1 - iou(boxes[row], clusters)
    49. # 对每个ground truth,选取距离最小的那个Anchor,并存下索引
    50. nearest_clusters = np.argmin(distances, axis=1)
    51. # 如果当前每个ground truth"距离"最近的Anchor索引和上一次一样,聚类结束
    52. if (last_clusters == nearest_clusters).all():
    53. break
    54. # 更新簇中心为簇里面所有的ground truth框的均值
    55. for cluster in range(k):
    56. clusters[cluster] = dist(boxes[nearest_clusters == cluster], axis=0)
    57. # 更新每个ground truth"距离"最近的Anchor索引
    58. last_clusters = nearest_clusters
    59. return clusters
    60. # 加载自己的数据集,只需要所有 labelimg 标注出来的 xml 文件即可
    61. def load_dataset(path):
    62. dataset = []
    63. for xml_file in glob.glob("{}/*xml".format(path)):
    64. tree = ET.parse(xml_file)
    65. # 图片高度
    66. height = int(tree.findtext("./size/height"))
    67. # 图片宽度
    68. width = int(tree.findtext("./size/width"))
    69. for obj in tree.iter("object"):
    70. # 偏移量
    71. xmin = int(obj.findtext("bndbox/xmin")) / width
    72. ymin = int(obj.findtext("bndbox/ymin")) / height
    73. xmax = int(obj.findtext("bndbox/xmax")) / width
    74. ymax = int(obj.findtext("bndbox/ymax")) / height
    75. xmin = np.float64(xmin)
    76. ymin = np.float64(ymin)
    77. xmax = np.float64(xmax)
    78. ymax = np.float64(ymax)
    79. if xmax == xmin or ymax == ymin:
    80. print(xml_file)
    81. # 将Anchor的宽和高放入dateset,运行kmeans获得Anchor
    82. dataset.append([xmax - xmin, ymax - ymin])
    83. return np.array(dataset)
    84. if __name__ == '__main__':
    85. ANNOTATIONS_PATH = "./label_source" #xml文件所在文件夹
    86. CLUSTERS = 9 #聚类数量,anchor数量
    87. INPUTDIM = 416 #输入网络大小
    88. data = load_dataset(ANNOTATIONS_PATH)
    89. out = kmeans(data, k=CLUSTERS)
    90. print('Boxes:')
    91. print(np.array(out)*INPUTDIM)
    92. print("Accuracy: {:.2f}%".format(avg_iou(data, out) * 100))
    93. final_anchors = np.around(out[:, 0] / out[:, 1], decimals=2).tolist()
    94. print("Before Sort Ratios:\n {}".format(final_anchors))
    95. print("After Sort Ratios:\n {}".format(sorted(final_anchors)))

    得到的结果比如

    1. Boxes:
    2. [[131.456 126.464 ]
    3. [159.744 293.57357357]
    4. [ 16.64 28.84266667]
    5. [ 26.624 77.65333333]
    6. [ 48.256 44.37333333]
    7. [ 59.072 106.496 ]
    8. [266.98178313 187.43236036]
    9. [ 82.368 212.16 ]
    10. [346.112 348.33066667]]
    11. Accuracy: 67.29%
    12. Before Sort Ratios:
    13. [1.04, 0.54, 0.58, 0.34, 1.09, 0.55, 1.42, 0.39, 0.99]
    14. After Sort Ratios:
    15. [0.34, 0.39, 0.54, 0.55, 0.58, 0.99, 1.04, 1.09, 1.42]

    After Sort Ratios 从小到大分别是浅层,中层,高层特征图,然后对应上面原始的 Before Sort Ratios
    ,接着去找对应的 Boxes