Your k-means anchor script dies on the first run with ValueError: a must be greater than 0 unless no samples are taken, raised from np.random.choice inside kmeans(). NumPy is not broken and the clustering math is not broken. The message states one specific fact: the box array handed to k-means is empty. txt2boxes() opened an annotation file that exists and parsed zero x_min,y_min,x_max,y_max entries out of it — the typical situation right after annotating a custom dataset with a bounding-box tool. Below you get the mechanics of the error, the three usual causes ranked by how often each is guilty, four tests that find yours in five minutes, and a guard that makes any future zero-box run fail in one clear line.
What the error actually measures
The failing line is the cluster initialisation:
box_number = boxes.shape[0] # number of parsed (width, height) pairs
clusters = boxes[np.random.choice(box_number, k, replace=False)] # init k clusters
np.random.choice(a, size) draws its samples from np.arange(a), the integers 0 to a-1. With a = 0 the population is empty and NumPy raises instead of returning nine distinct samples from nothing; an empty result is accepted only when size itself is 0. The numpy.random.choice reference documents the draw semantics. So the message is a statement about boxes.shape[0]: at the moment of the call it was 0.
Read the traceback against the script and the same fact appears three times. Line 101 calls kmeans.txt2clusters(), line 89 calls self.kmeans(all_boxes, k=self.cluster_number), and line 44 is the sampling call. Conclusion: all_boxes = self.txt2boxes() returned shape (0,). The IoU function, the cluster update loop and the anchor writer never ran. Everything after this point is about why the parser found nothing.
Three causes, ranked by how often each is guilty
The file on disk is 0 bytes
The export step that builds the combined annotation file never ran, crashed halfway, or wrote its output somewhere else, while an empty file of the right name was created where the script looks. An empty file opens without complaint, for line in f never executes, dataSet stays an empty list, and np.array([]) comes back with shape (0,). On a first run this is the most common cause, because the export is the step everyone assumes just worked.
Every line is a path with no boxes
txt2boxes() reads fields 1 onward of each line; field 0 is the image path. A line holding only img_001.jpg is one field, range(1, 1) stays empty, and the line contributes nothing. Path-only lines are legitimate — background images without objects reduce false positives, so they belong in a training set — but a file built only from them parses to zero boxes. The same symptom appears when an exporter loses the object records and writes bare paths for every image.
The script opened a different file than you think
The constructor throws its argument away:
def __init__(self, cluster_number, filename):
self.cluster_number = cluster_number
self.filename = '2007_train.txt' # the filename argument is ignored
Rename your file to vegetables_train.txt, edit filename = 'vegetables_train.txt' at the bottom, run again — and the script still opens 2007_train.txt from the current working directory. Editors and notebook servers often launch scripts from a folder other than the one you saved to, so the file found under that name can be a stale or empty leftover. If the name matched nothing on disk you would see FileNotFoundError: No such file or directory instead; that variant is its own problem, covered in fixing the file-not-found path.
What this error is not
A wrong coordinate format usually fails louder. Darknet-normalized lines — class cx cy w h, values between 0 and 1, no commas — make int(infos[i].split(',')[2]) raise IndexError: list index out of range, and decimal floats raise a different ValueError: invalid literal for int(). The error in your traceback means parsing ran quietly and produced nothing: the file was empty or held no box fields at all. If you do hit the IndexError variant, the CSV bounding-box to YOLO conversion guide covers the field order this script family expects: x_min,y_min,x_max,y_max,class_id, corners in pixels.
Four tests that find your cause in five minutes
- Print what the parser got. In
txt2clusters(), directly afterall_boxes = self.txt2boxes(), addprint(all_boxes.shape).(0,)confirms the empty population; any other shape means the traceback came from a different run or file. - Weigh the file.
os.path.getsize(self.filename)— or the size column in your file manager — settles cause 1 immediately: 0 bytes means the export step is broken. - Count the fields on a real line against the field-count rule below the list. One line tells you whether the file shape is right before the parser runs again.
- Print the exact file read. Put
print(os.path.abspath(self.filename))before the file is opened. If the printed folder is not the one you exported to, cause 3 is yours and the fix is a path, not the data.
All four are one-line edits you delete afterwards; run them in a single session and the guilty cause names itself.
The line img_001.jpg 12,34,180,220,0 90,11,210,150,1 splits into 11 fields: 1 + 5 + 5, so two boxes on the line. A single field is a path-only line — fine per line, fatal for a whole file.
Fix the script and the file
-
Store the filename you pass. Change one line in the constructor to
self.filename = filename. From then on, the assignment in the main block controls which file opens. -
Re-run the annotation export and open the result. Every line must match the 11-field shape above: path first, then
x_min,y_min,x_max,y_max,class_idper object — commas within a box, spaces between boxes, all values in pixels. -
Pin the path while you debug. Set
filenamein the main block to the absolute path of the exported file, forward slashes included, so a moved working directory cannot change what the script opens. -
Guard the script so a future zero-box run fails with words instead of a traceback:
all_boxes = self.txt2boxes() if all_boxes.shape[0] == 0: raise SystemExit('no boxes parsed from ' + self.filename) if all_boxes.shape[0] < self.cluster_number: raise SystemExit('only %d boxes for %d clusters' % (all_boxes.shape[0], self.cluster_number))The second check catches the neighboring error,
Cannot take a larger sample than population when 'replace=False', which appears once the file parses some boxes but fewer thancluster_number. -
Run again. A healthy run prints
K anchors:with ninewidth,heightpairs and anAccuracy:percentage, and writesyolo_anchors.txtnext to the script.
Worked example: 60 annotated images, nine clusters, a 0-byte file
Inputs. cluster_number = 9, the default in the script's main block. 60 images annotated, 2 objects per image on average — the labelling plan for the part mix. Coordinates as x_min,y_min,x_max,y_max in pixels, the format txt2boxes() parses. The NumPy constraint: np.random.choice(n, 9, replace=False) draws nine distinct boxes, so it needs n ≥ 9.
The rule in words. The hard floor is the cluster count: with fewer boxes than clusters, NumPy cannot pick nine distinct starting boxes. For anchors worth training on, each cluster also needs enough members that its median width and height mean something — about 10 boxes per cluster as a working minimum.
The arithmetic.
- Expected boxes with a working export: 60 lines × 2 boxes = 120 boxes.
- Your broken run: 0-byte file, 0 lines, box_number = 0. The call becomes
np.random.choice(0, 9, replace=False), and 0 < 9, so NumPy raises the exact message in your traceback. - After the fix: box_number = 120. 120 ≥ 9, the draw succeeds, and 120 ÷ 9 ≈ 13 boxes per cluster.
- Trust threshold: 10 × 9 = 90 boxes. With 120 ≥ 90, the anchors are usable as printed. At 40 boxes the script would run — 40 ≥ 9 — but 40 < 90, so treat that output as provisional and annotate more.
One more reason for the threshold: the script calls np.random.seed() with no argument, seeding from the entropy pool, so with a thin box population the initial draw — and the final anchors — shift visibly between runs.
Decision. Fix the export, keep nine clusters, re-run. Read the printed Accuracy as a sanity check, not a target: about two-thirds average IoU is typical for nine clusters on a varied custom set, and it should stay near that level when you re-run after adding images. If your count lands below 90 boxes, annotate more first — the custom YOLO dataset sizing guide gives image-count targets per class.
Keep zero-box runs impossible
- Leave the guard in. The two
SystemExitchecks turn a silent 0-byte export into a one-line message that names the file. - Re-run the anchor script after every dataset revision. Anchors describe your box population, and the population changes whenever you add a class or a new part revision; stale anchors cost accuracy quietly.
- Watch the export's decimal separator. A locale-dependent spreadsheet step writes
12,5for 12.5 andint()rejects it withinvalid literal for int()— the same locale parsing trap that breaks calibration files, and the calibration file locale guide shows the fix pattern. - Compare file sizes after each re-export. A combined annotation file that shrinks to 0 bytes is a build failure you can catch before any script runs.
From anchors to a running inspection
With yolo_anchors.txt holding nine sane sizes, training is unblocked and the dataset becomes the real limit on accuracy. When the model is ready, the same inspection has to fit hardware: a bounded part class at a fixed station runs on-device on smart cameras, while several cameras sharing one controller box — with light channels switched per exposure — belong on vision controllers. Filter candidates by resolution, sensor format and interface in the camera finder, browse the other systems guides if a different error is next, or contact the applications team to have the camera-to-controller chain checked before you order.
Questions
Why do I get 'Cannot take a larger sample than population when replace=False' instead?
That is the neighboring failure: the file parsed boxes, but fewer than cluster_number, which is 9 in this script's default. np.random.choice must draw nine distinct boxes without replacement, so the file needs at least nine width-height pairs. Annotate more images rather than lowering the cluster count; anchors fitted to a handful of boxes move every run. The two-check guard in the fix section catches this case alongside the empty file.
Can I train YOLO without generating custom anchors?
Yes. Training starts fine with the shipped default anchors, and if your parts resemble the sizes those defaults came from, detection still works. Custom anchors pay off when your objects differ strongly in scale or aspect ratio from the defaults — small parts on large backgrounds, long thin parts. Re-run the anchor script whenever the part mix changes, and compare the printed average IoU before and after; a clear gain means the new anchors earn their place.
What does the Accuracy percentage after k-means mean?
It is the average best IoU: for every annotated box, the overlap with the closest of the nine final anchors, averaged over all boxes. It measures how well nine fixed rectangles cover your box population, not how well the detector will detect. Around two thirds is a healthy figure for nine clusters on a varied custom dataset, a level the original anchor k-means work also reports; a much lower value points at degenerate boxes or too few of them.
Do background images with no objects break the anchor script?
No. A line holding only an image path contributes zero boxes and parses without error; the script crashes only when every line in the file combined contributes zero boxes. Keep path-only lines for negative samples — they teach the model what the background looks like — but make sure the same file also contains boxed lines. If you built the file solely from background images, the parser sees an empty population and this exact error follows.
Where do the nine anchors go after the script runs?
The script writes yolo_anchors.txt next to itself and prints the same nine width,height pairs. They belong in the anchors line of your YOLO model configuration, matched to the network's input size rather than your raw annotation pixels if the trainer rescales images before the first convolution. Paste them in the printed order, retrain, and keep the anchors file together with the dataset version that produced it.