Section 21.4.1
Purpose
Calculate custom anchors for a YOLO configuration using the bounding-box sizes in your training dataset.
Use 21.2 · Bounding Box Visualizer first to review the approximate number and distribution of box-size groups, and 21.3 · Grid / Stride Visualizer when evaluating detection-layer strides.
Section 21.4.2
Why It Exists
Anchors that reflect your dataset’s box sizes provide a better starting point than poorly matched anchors. The book describes this as an important training step: a network can still train with poor anchors, but they can waste iterations and weaken the result.
This is a lightweight alternative to the standard Darknet anchor calculator. Use whichever calculator consistently produces anchors that perform well in your training workflow.
Section 21.4.3
Input
Use an exported YOLO / Darknet training dataset. All source images must have the same resolution. The script finds the first readable PNG, JPG, or JPEG to determine that resolution, then recursively collects annotations from folders named labels.
class_id center_x center_y width height
0 0.50 0.50 0.25 0.20Width and height are normalized. The calculator converts them to source-image pixel dimensions before clustering. It ignores empty files and skips lines without exactly five values. Five-value lines must be numeric, with valid positive box dimensions.
Anchors depend on box size, not class name or class ID. All collected boxes are considered together.
Use lowercase labels folder names and .txt extensions. The script assumes a common source resolution; it does not verify every image. Choose a positive anchor count no larger than the number of collected boxes.
Section 21.4.4
Output
The program prints the anchors and two clustering diagnostics in the terminal. The book shows this example:
anchors=19,24,67,83
Average IoU: 0.7204
Iterations until convergence: 3Illustrative book output; your dataset and settings determine the actual values.
| Output | Meaning |
|---|---|
anchors=19,24,67,83 | Two width/height pairs: (19, 24) and (67, 83), in pixels at the requested network resolution. Anchors are sorted by increasing width. |
| Average IoU | The mean best-match overlap between each box and its closest anchor shape, assuming shared centers. Values closer to 1 indicate a closer shape fit. |
| Iterations | Clustering iterations performed. Assignments stabilize when each box remains assigned to the same cluster. |
IoU is intersection area divided by union area. The diagnostic is calculated using the unrounded clusters in source-image coordinates; the printed anchors are scaled and rounded for your network.
The script prints results without editing your configuration. Review the anchor count and your configuration’s head/mask arrangement before applying the values. Average IoU describes anchor fit; it is not a model accuracy score.
Section 21.4.5
How To Use
Save Calculate_Anchors_Advanced.py in the main project folder. Set up the environment using 21.1 · Python & Libraries. This utility requires NumPy and OpenCV; its other imports are included with Python.
Linux Terminal
Open a terminal in the project folder and activate the environment:
source .venv/bin/activateInstall dependencies if they are missing:
python -m pip install numpy opencv-pythonCalculate two anchors for a 416 × 416 network:
python Calculate_Anchors_Advanced.py . --net_w 416 --net_h 416 --num_anchors 2Inside the activated environment, python runs the environment’s Python 3 interpreter.
Windows Command Prompt
Open Command Prompt in the project folder and activate the environment:
.venv\Scripts\activate.batInstall dependencies if they are missing:
python -m pip install numpy opencv-pythonCalculate two anchors for a 416 × 416 network:
python Calculate_Anchors_Advanced.py . --net_w 416 --net_h 416 --num_anchors 2Inside the activated environment, python runs the environment’s Python 3 interpreter.
Arguments and calculation
- Dataset path:
.means the current project folder. Replace it with another dataset path if needed; quote paths containing spaces. - --net_w and --net_h: required, positive network dimensions.
- --num_anchors: your selected anchor count; defaults to 9 if omitted. Choose it after reviewing box sizes rather than treating the default as a recommendation.
The calculator uses IoU-based clustering, a fixed random seed of 42, median width/height updates, and a limit of 1,000 iterations. It stops when assignments no longer change or the limit is reached. Empty clusters retain their current dimensions.
If something goes wrong
- No valid image: verify the dataset path and that it contains a readable PNG, JPG, or JPEG.
- No usable boxes: check lowercase
labelsfolders, non-empty.txtfiles, and valid five-value annotations. - Too many anchors requested: use an anchor count between 1 and the number of collected boxes.
- Missing package: activate the project environment and install the missing dependency. The
cv2module is provided byopencv-python.
The source images, annotations, and model configuration are left unchanged. Results appear in the terminal; no desktop plot window is opened.
Section 21.4.6
Code
The program from section 21.4, with PDF wrapping and indentation restored.
Previous cluster assignments start at -1 instead of zero. This ensures the first median update runs, including when only one anchor is requested. The IoU clustering and anchor-scaling method otherwise follow the book.
import os
import sys
import cv2
import numpy as np
import argparse
def get_image_resolution(dataset_path):
for root, dirs, files in os.walk(dataset_path):
for file in files:
if file.lower().endswith((".png", ".jpg", ".jpeg")):
img_path = os.path.join(root, file)
img = cv2.imread(img_path)
if img is not None:
height, width = img.shape[:2]
return width, height
raise ValueError("No valid image file found to determine resolution.")
def extract_wh_from_label(label_path, img_w, img_h):
wh_list = []
with open(label_path, "r") as f:
for line in f:
parts = line.strip().split()
if len(parts) != 5:
continue
_, _, _, w, h = map(float, parts)
abs_w = w * img_w
abs_h = h * img_h
wh_list.append([abs_w, abs_h])
return wh_list
def iou(box, clusters):
x = np.minimum(clusters[:, 0], box[0])
y = np.minimum(clusters[:, 1], box[1])
intersection = x * y
box_area = box[0] * box[1]
cluster_area = clusters[:, 0] * clusters[:, 1]
return intersection / (box_area + cluster_area - intersection + 1e-10)
def avg_iou(boxes, clusters):
return np.mean([np.max(iou(box, clusters)) for box in boxes])
def iou_kmeans(boxes, k, max_iter=1000, tol=1e-6):
np.random.seed(42)
# Initialize clusters by randomly sampling boxes
clusters = boxes[np.random.choice(len(boxes), k, replace=False)]
# Start unassigned so the first median update is never skipped.
last_clusters = np.full((len(boxes),), -1, dtype=int)
for iteration in range(max_iter):
distances = np.zeros((len(boxes), k))
for i in range(k):
cluster = clusters[i]
inter = np.minimum(boxes[:, 0], cluster[0]) * np.minimum(boxes[:, 1], cluster[1])
box_area = boxes[:, 0] * boxes[:, 1]
cluster_area = cluster[0] * cluster[1]
iou_vals = inter / (box_area + cluster_area - inter + 1e-10)
distances[:, i] = 1 - iou_vals
nearest_clusters = np.argmin(distances, axis=1)
if (last_clusters == nearest_clusters).all():
break
for i in range(k):
if np.any(nearest_clusters == i):
clusters[i] = np.median(boxes[nearest_clusters == i], axis=0)
last_clusters = nearest_clusters
return clusters, iteration + 1
def get_all_wh_from_dataset(dataset_path, num_anchors, net_w, net_h):
wh_data = []
img_w, img_h = get_image_resolution(dataset_path)
for root, dirs, files in os.walk(dataset_path):
if os.path.basename(root) == "labels":
for file in files:
if file.endswith(".txt"):
label_path = os.path.join(root, file)
if os.path.getsize(label_path) == 0:
continue
wh = extract_wh_from_label(label_path, img_w, img_h)
wh_data.extend(wh)
if not wh_data:
raise ValueError("No non-empty label files found in the dataset.")
wh_data_np = np.array(wh_data, dtype=np.float32)
clusters, iterations = iou_kmeans(wh_data_np, num_anchors)
scale_w = net_w / img_w
scale_h = net_h / img_h
anchors_scaled = np.round(clusters * [scale_w, scale_h]).astype(int)
anchors_scaled = anchors_scaled[anchors_scaled[:, 0].argsort()]
print("anchors=" + ",".join(f"{w},{h}" for w, h in anchors_scaled))
mean_iou = avg_iou(wh_data_np, clusters)
print(f"Average IoU: {mean_iou:.4f}")
print(f"Iterations until convergence: {iterations}")
return anchors_scaled
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Generate YOLO anchors using IoU-based k-means clustering."
)
parser.add_argument("dataset_path", type=str, help="Path to dataset directory (must contain a 'labels' folder).")
parser.add_argument("--net_w", type=int, required=True, help="Network width (e.g., 416)")
parser.add_argument("--net_h", type=int, required=True, help="Network height (e.g., 416)")
parser.add_argument("--num_anchors", type=int, default=9, help="Number of anchors to generate (default: 9).")
if len(sys.argv) == 1:
parser.print_help(sys.stderr)
sys.exit(1)
args = parser.parse_args()
get_all_wh_from_dataset(args.dataset_path, num_anchors=args.num_anchors, net_w=args.net_w, net_h=args.net_h)
Section 21.4.7
Prompt to Reproduce This Utility with AI
Copy the complete request below into your AI chat. Review and test any generated code before using it.
Replace [your favorite programming language] at the top of the request with the language you want the AI to use. The downloadable code and How To Use instructions on this page remain the book’s Python version, with the initialization correction described in the Code view.
Programming language: [your favorite programming language]
Create a complete program that calculates custom YOLO anchors from an image dataset stored in standard Darknet format. Place the program in the project folder. Recursively search the clip folders beneath it, find every folder named labels, read all non-empty .txt annotation files, and combine bounding-box dimensions from the entire dataset.
Each annotation line contains five whitespace-separated numeric values: class_id, center_x, center_y, width, and height. Width and height are normalized relative to the source image dimensions. Ignore empty files and skip lines that do not contain exactly five values. Assume five-value lines contain valid numeric annotations with positive width and height.
Assume all dataset images have the same resolution. Determine that resolution from the first readable .png, .jpg, or .jpeg image. Convert normalized box widths and heights into source-image pixel dimensions. Do not group by class name or class ID; anchors depend only on box size.
Use IoU-based k-means clustering with random seed 42 and a maximum of 1000 iterations. Initialize the requested number of clusters by sampling boxes without replacement. Compare boxes and anchors by width and height only, assuming they share the same center. Assign each box to the cluster with the highest IoU. Update non-empty clusters using the median width and height of their assigned boxes. Leave empty clusters unchanged. Initialize previous assignments with an unassigned value so that the first median update cannot be skipped, including when only one anchor is requested. Stop when assignments no longer change or the iteration limit is reached.
Accept a positional project or dataset path, required --net_w and --net_h options for the network width and height, and an optional --num_anchors option defaulting to 9.
Scale final cluster widths by network width divided by source image width, and heights by network height divided by source image height. Round to whole pixels and sort by increasing width. Print one Darknet configuration line in this format:
anchors=w1,h1,w2,h2,...
Also print average best-match IoU across all boxes, calculated against the unrounded clusters in source-image coordinates, to four decimal places. Print the number of clustering iterations performed.
Use suitable libraries for image loading, numerical processing, filesystem access, and command-line argument parsing. Include dependency/setup instructions and a terminal example requesting two anchors at a 416 by 416 network resolution. Read the dataset without changing its files. Do not automatically edit a model configuration file.

