Daniel wants the actual walkthrough — not the theory, not the glossy blog post, but the full process from a folder of photos to a model that draws boxes around one specific poster. He's asking five things: how many images and what makes them good, how to annotate properly, how to split without cheating yourself, why a lightweight single-stage detector is the right call and what the metrics actually mean, and then the part most tutorials skip — where it breaks in the real world and when to quit and use a general model instead.
And the scenario he picked is perfect for this. Anti-graffiti campaign, one poster design, city-wide deployment. The city fines you two hundred dollars for every poster you miss. So the cost of a false negative isn't an academic metric — it's a line item on a budget.
Two hundred dollars per miss concentrates the mind.
It does. And the thing is, this isn't some exotic edge case. Any custom detection task — retail product spotting on shelves, wildlife camera traps looking for one species, industrial defect detection on a specific part — faces exactly the same constraints. Small dataset, one class, real-world variation, and a deployment environment that doesn't care about your validation numbers.
So let's walk through it. First photograph to production model, with the reasoning behind every decision.
Dataset size. This is where most people either give up too early or throw data at the problem without thinking about what's in it. The Ultralytics documentation says a hundred images per class is the realistic minimum for YOLOv8 training. That's the floor, not the target. For a one-class detector that has to work in the wild, you want three hundred to five hundred images to get something robust.
And five hundred varied images beat two thousand near-identical ones every time.
Every single time. The variation is what matters, not the count. You need different lighting conditions — dawn, noon, dusk, night with flash. Different angles — head-on, oblique, extreme skew where the poster's almost edge-on. Different distances — close-up where the poster fills the frame, street-view where it's a small rectangle in the background. And partial occlusion — behind a lamppost, half-covered by a bus, someone's head blocking the corner.
So if I stand in front of the same poster on the same wall and take two hundred photos with my phone tilted slightly differently, I've built a dataset that will fail the moment the sun moves.
And that dataset will give you great validation numbers right up until you deploy it. Then you discover your model only works on that one wall, at that one time of day, from that one distance.
What about images that don't contain the poster at all?
Negatives. You need at least twenty to thirty percent of your dataset to be negative images — photos with no poster in them. But not just random street scenes. You want photos of similar-looking posters, blank walls, graffiti tags, street furniture. Things that could fool a naive model into drawing a box where nothing exists.
So you're teaching it what not to detect, not just what to detect.
Right. And then there are hard examples — the secret sauce. Posters at extreme angles, heavily occluded, or in low contrast against a similar-colored wall. A beige poster on a beige building at dusk. Those are the images where the model has to work to find the boundary. If your dataset doesn't include them, your model will be brittle in exactly the conditions where detection matters most.
And how many hard examples are we talking about?
Thirty to fifty in a dataset of three hundred to five hundred. Enough that the model sees them repeatedly during training but not so many that they dominate the loss. You want the model to learn that hard cases exist, not to optimize entirely for them.
So I've got my three hundred to five hundred images. Now I need to draw boxes on them. What format am I producing?
YOLO format is the de facto standard for single-stage detectors. Each image gets a text file with the same name. Each line in that file represents one bounding box: class ID, x center, y center, width, height — all normalized to zero to one. So if your poster is in the middle of a thousand-pixel-wide image and is two hundred pixels wide, the x center is zero point five and the width is zero point two.
And the class ID is just zero because there's only one class.
One class, class ID zero for every box. The normalization is what lets you resize images during training without recalculating coordinates — the ratios stay the same regardless of whether the image is six hundred forty by four hundred eighty or twelve hundred eighty by nine hundred sixty.
How tight should the boxes be?
Tight. No padding. The box should touch the edge of the poster — the model learns the exact boundary from those edges. If you include white margins or a bit of wall around the poster, the model learns that the margin is part of the object, and then it draws loose boxes in deployment.
And if you box only the logo in the center of the poster and ignore the white border?
Too tight. The model learns that only the logo is the object, and it misses the edges of posters where the logo is partly obscured. The convention has to be consistent: box the entire poster, edge to edge, every time. Write it down in a convention document so every annotator — even if it's just you across multiple sessions — follows the same rule.
What about ambiguous cases? The corner of a poster peeking out from behind a bus, or a poster so far away it's six pixels wide?
This is where labeling goes wrong. The convention document needs to answer these questions in advance. My rule of thumb: if a human can confidently identify the poster, box it. If you're squinting and guessing, skip it. For partially visible posters, box the visible portion — don't extrapolate where the rest would be. The model needs to learn to detect partial posters, and if you box imaginary boundaries, you're teaching it the wrong shape.
And if two posters overlap?
Two separate boxes. Even if they're mostly occluding each other, each visible poster gets its own box. This is where annotation gets slow — you have to decide whether that's one poster or two, and the answer changes how the model handles crowded scenes.
What tools are people actually using for this?
LabelImg is the simplest — free, open source, outputs YOLO format directly. CVAT is more powerful for larger projects, supports teams, has interpolation between frames if you're doing video. Roboflow has a web-based annotation interface that's good and integrates with their training pipeline. The trap with any of them is annotating too fast. A clear instance of a poster takes maybe five to ten seconds to box. The ambiguous cases — the partial occlusion, the extreme angle, the poster at the edge of the frame — those take thirty seconds each because you're making judgment calls. If you're averaging five seconds per image across your whole dataset, you're probably making inconsistent decisions.
Fifty to a hundred images per hour for a single class with clear objects — that's the realistic rate.
That's the number. Now, the split. You've got three hundred images. The obvious thing is random eighty-twenty — two hundred forty for training, sixty for validation. And that's the trap.
Because you photographed the same poster on the same wall from five different angles.
And random split puts three of those angles in training and two in validation. The model sees almost the same image in both sets. Your validation metrics inflate by ten to twenty points because you're testing on near-duplicates of the training data. The model hasn't learned to detect posters — it's learned to recognize that specific wall.
So how do you prevent that?
Group by scene. If you have five photos of the same poster on the same wall, all five go into the same split — all training or all validation, never split across. The goal is to test on unseen posters, not unseen angles of the same poster. You can do this manually by organizing your photos into folders by location before splitting. Or you can hash the image metadata — GPS coordinates if you have them, or even just the first few hundred pixels of the image to catch near-duplicates.
And if you don't have GPS metadata?
Manual grouping. It's tedious but it's the only way to be sure. Go through your images and tag them by scene — "poster on Main Street east wall," "poster on Elm Street bus shelter." Then split the scenes, not the individual images. You want your validation set to contain scenes the model has never seen.
What ratio do you actually use?
With three hundred to five hundred images, eighty-twenty is standard. But if you're really tight — say you only have a hundred fifty images — you might go ninety-ten and accept that your validation metrics will be noisier. Below a hundred images, you're probably not training a detector at all — you're better off with a general-purpose vision model from the start.
Alright. Dataset's annotated and split. Now what model do you throw at it?
This is where the architecture choice matters, and it's where I want to draw the distinctions Daniel asked about. Detection is fundamentally different from classification. Classification answers "is there a poster in this image?" — yes or no. Detection answers "where is every poster in this image?" — and draws boxes around them. Segmentation goes further and labels every pixel that belongs to the poster. Open-vocabulary detection tries to find anything you describe in natural language.
And for this task — one specific poster, bounding boxes, nothing else — segmentation is overkill and open-vocabulary is solving a problem you don't have.
Right. You don't need pixel-perfect masks. You need boxes. And you don't need to detect "any anti-graffiti poster" — you need this specific poster design. So a single-class, single-stage detector is exactly the right tool.
Why single-stage rather than two-stage?
Two-stage detectors like Faster R-CNN first propose regions that might contain objects, then classify each region. They're more accurate on large, diverse datasets like COCO, but they overfit on small datasets because the region proposal network has too many parameters relative to the data. Single-stage detectors like YOLO divide the image into a grid and predict bounding boxes directly from each grid cell. Fewer parameters, faster inference, and crucially — more data-efficient when you only have a few hundred images.
So YOLOv8 nano or small.
YOLOv8n has about three point two million parameters. It runs at over a hundred frames per second on a modern GPU. For a one-class detection task with a few hundred images, that's more than enough capacity. YOLOv8s is slightly larger — around eleven million parameters — and gives you a bit more headroom if your poster has fine detail that matters. But nano is where I'd start.
And what am I actually fine-tuning? The model hasn't seen my poster before.
You're fine-tuning a backbone pretrained on COCO — eighty generic object classes, three hundred thirty thousand images. That backbone has already learned to detect edges, textures, shapes, and the general concept of "rectangular object with text on it." The early layers — the ones that detect low-level features — those get frozen. The later layers and the detection head get fine-tuned on your poster images.
So it already knows what a sign on a wall looks like. I'm teaching it that this specific sign is the one that matters.
And because the backbone is pretrained, you don't need millions of images. The model already has visual intelligence — you're just specializing it. This is why three hundred to five hundred images works. You're not teaching it to see from scratch. You're teaching it to recognize one new thing using visual skills it already has.
How many epochs?
For three hundred to five hundred images, a hundred to a hundred fifty epochs is typical. But the number of epochs is less important than the stopping condition. You monitor training loss and validation loss. When validation loss plateaus or starts rising while training loss keeps dropping, you're memorizing, not learning.
And there's a better signal than loss for detection.
Validation mAP at IoU threshold zero point five. That should peak and then decline if you're overfitting. Early stopping with a patience of ten to twenty epochs — if mAP at zero point five hasn't improved in twenty epochs, stop training and roll back to the best checkpoint.
Let's define those metrics. Precision, recall, mAP, IoU — what do they actually tell me about whether my boxes are any good?
Start with IoU — intersection over union. It's the overlap between your predicted box and the ground truth box, divided by their total area. If the predicted box perfectly matches the ground truth, IoU is one point zero. If they don't overlap at all, it's zero. The standard threshold is zero point five — a predicted box is considered correct if it overlaps the ground truth by at least fifty percent.
Fifty percent overlap sounds generous.
It is, and that's deliberate. For most applications, you care about finding the object, not about pixel-perfect boundaries. If your box captures most of the poster, the poster has been detected. The tighter threshold — mAP at zero point five to zero point nine five — averages across ten IoU thresholds from zero point five to zero point nine five in steps of zero point zero five. That metric tells you how tight your boxes are, not just whether they exist.
So mAP at zero point five is "did I find it," and mAP at zero point five to zero point nine five is "how good are my boundaries."
That's the distinction. For an anti-graffiti campaign where the job is "find the poster so we can send a crew," mAP at zero point five is the practical metric. You need to know the poster is there. Whether the box is three pixels too wide on the left edge doesn't matter for the cleanup decision.
Precision and recall.
Precision is: of all the boxes your model drew, how many were correct. If your model draws a hundred boxes and eighty of them are actual posters, your precision is eighty percent. The other twenty are false positives — the model saw a poster where there wasn't one.
Recall is the one that costs two hundred dollars per miss.
Recall is: of all the actual posters in the images, how many did your model find. If there are a hundred posters and your model found eighty of them, your recall is eighty percent. The twenty it missed are false negatives — posters that exist but weren't detected. In this scenario, every false negative is a two-hundred-dollar fine.
You'd bias toward recall over precision. Better to flag a few false positives that a human can dismiss than to miss a poster that costs money.
You can tune the confidence threshold to trade precision for recall. Lower the threshold, the model draws more boxes — recall goes up, precision goes down. For a cost-asymmetric task like this, you'd set the threshold low and accept some false positives.
Now the part Daniel actually wants. Where does this break in the real world?
Five failure modes, and I've seen every one of them. First, extreme angles — anything beyond about sixty degrees from head-on. The model was trained mostly on posters seen from reasonable angles. At seventy degrees, the poster is a thin sliver, and the features the model learned — the logo, the text layout — are compressed beyond recognition.
Second?
Very low light or motion blur. If your training set is all daytime photos and you deploy on images from a security camera at night, the model's performance collapses. I've seen a YOLOv8n model trained on three hundred fifty images achieve zero point nine two mAP at zero point five on the validation set and drop to zero point four five on a test set of night-time images.
That's not a gradual decline. That's a cliff.
Third: occlusion by objects not in the training set. Your training set has posters behind lampposts and half-covered by buses. Then you deploy and a parked truck blocks seventy percent of a poster. The model has never seen a poster occluded by a truck, and it fails.
Because it learned "poster partially behind a lamppost" but didn't generalize to "poster partially behind anything."
That's the limitation of a small dataset. Fourth: vandalized posters. Torn, painted over, graffitied. The model learned the poster design as a coherent visual pattern. When someone spray-paints over half of it, the pattern is broken, and the model doesn't recognize it as the same object.
And fifth?
Non-flat surfaces. Posters on curved walls, corrugated metal, folded around a corner. The model learned posters as planar rectangles. When the poster bends, the shape changes, and the bounding box — which is always an axis-aligned rectangle — no longer cleanly fits the visible area.
What do you do when you discover these failures?
The augmentation and iteration loop. For each failure pattern, you collect ten to twenty real images of that condition — actual night-time photos, actual vandalized posters, actual curved surfaces. You add them to the training set and retrain. Augmentation can help — random rotation, scaling, brightness shifts, mosaic augmentation that stitches four images together — but augmentation simulates variation. It cannot replace real data for extreme cases.
How many iterations before you're done?
Typically three to five before diminishing returns. After five iterations and five hundred plus images, if you're still above a twenty percent false negative rate on your target deployment conditions, you've hit the ceiling of what a custom small-data detector can do.
That's when you switch to a general-purpose vision model.
GPT-4V, Gemini Pro Vision, or a CLIP-based detection approach. These models have seen billions of images. They handle novel conditions — vandalized posters, extreme angles, weird occlusion — far better than a custom model trained on a few hundred examples. The tradeoff is they're worse at tight bounding boxes and they're slower and more expensive per inference.
You mentioned a comparison.
On a test set of fifty vandalized posters — torn, painted over, graffitied — GPT-4V found thirty-eight. A custom YOLO model trained on pristine posters found twenty-two. That's the gap. The general model has seen vandalized signs in its training data. The custom model has only seen clean posters.
The threshold is: if your custom model's recall is below about seventy percent on your hardest test set, the general model will likely outperform it. And for a one-off campaign, you might skip custom training entirely.
For a one-off campaign with a hundred posters across a city, just use GPT-4V. The annotation time alone — hours of drawing boxes — costs more than the API calls. Custom training makes sense when you need low latency, offline operation, privacy constraints, or you're running inference on thousands of images per day and the API costs would add up.
The niche for custom object detection is shrinking.
It's shrinking to exactly those cases: latency-critical, offline, privacy-sensitive, or high-volume. Everything else, the general models are catching up fast.
Hilbert: You're both missing the real problem. The poster moves.
Go on.
Hilbert: I did graffiti abatement for the city of Springfield in oh-four. My job was photographing posters and tags, filing reports, coordinating the cleanup crews. I've got a box of slides from that year — thirty-five millimeter, Kodachrome if you're wondering — and half of them show the same poster in different places because the campaign kept putting them up and the city kept painting over them.
The poster isn't a fixed target.
Hilbert: It migrates. You train your model on the poster as it looked on Tuesday. By Friday, someone's pasted a new one on top of the old one at a three-degree angle. Now there are two overlapping posters that look like one blob. Or the corner's torn and flapping, and the tear pattern changes every time the wind blows. Or the city painted over half of it and left the other half, and now your poster has a straight edge down the middle where the paint roller stopped.
Overlapping posters. That's a condition almost never in training datasets.
Hilbert: Never. And it's the most common thing in the real world. Campaigns put posters on top of old posters. Graffiti goes on top of posters. Cleanup crews paint over graffiti, and now there's a painted rectangle on top of a poster. Your model sees none of that in training because everyone annotates clean single instances.
The failure pattern isn't just occlusion by random objects. It's occlusion by other instances of the same class.
Hilbert: Two posters overlapping at a slight angle. The bottom one is faded, the top one is fresh. To a human, it's obviously two posters. To a model trained on single instances, it's one weirdly shaped blob. And the bounding box covers both, or neither, or some rectangle that doesn't correspond to either poster.
Did you have a way of handling that manually?
Hilbert: We matched posters by the tear pattern in the corner. Every poster tears differently when it's pulled off a roll. The tear pattern is unique — like a fingerprint. We kept a binder of tear patterns and matched new photos against it. Took forever. A model that could detect overlapping instances would have saved us weeks.
That's a hard detection problem. Two objects of the same class, partially occluding each other, at different ages and conditions. Most object detection benchmarks don't even test for that.
Hilbert: They don't. And it's the thing that actually happens. Your model will be great at finding a clean poster on a clean wall in good light. The city doesn't care about those. They care about the ones that are buried under three layers of other posters and half a can of spray paint.
The training set needs overlapping instances. Deliberately photographed, deliberately annotated with two separate boxes.
Hilbert: If you want it to work. Otherwise you're building a model for a world that doesn't exist.
The iteration loop we described — collect failure cases, retrain, test again — that loop has to include overlapping instances. And that's hard to do because you have to find or create those scenarios. You can't just walk around and photograph them. You might need to stage them.
Which adds a whole layer of effort that the typical tutorial doesn't mention.
Hilbert: The typical tutorial has never cleaned graffiti off a bus shelter at six in the morning.
The other thing Hilbert's pointing at — and he's right — is temporal drift. The poster changes over time. It fades. It tears. It gets painted over and new ones go up. Your training set is a snapshot of one moment, but the deployment environment is a moving target. That's not a failure of the model architecture. That's a failure of the assumption that the object is static.
You either retrain periodically as the posters degrade, or you accept that recall will decline over time and build that into your cost model.
Hilbert: Or you just send a guy with a camera and a binder of tear patterns. Worked fine.
The cutting-room floor detail I keep coming back to: the validation split problem is worse than most people realize. If you have five photos of the same poster from different angles and three land in training and two in validation, your mAP at zero point five can be inflated by fifteen to twenty points. Not a small error. That's the difference between thinking your model works and knowing it doesn't.
The fix is so simple — group by scene — but almost no one does it on their first project. They hit random split, get great numbers, deploy, and then discover the model only works on the three streets they photographed.
Which brings us to the open question. At what point does the cost of all this — the annotation hours, the iteration loops, the deployment maintenance, the periodic retraining — outweigh the benefit over just using a general model? For a one-off campaign with a hundred posters, the answer might be never. Just point GPT-4V at the photos and pay the API bill.
As general-purpose vision models improve, the niche for custom object detection shrinks further. In three to five years, the skill of building a small dataset and fine-tuning a detector may become a legacy craft — something you do when you need inference at the edge, offline, or at a scale where API costs dominate. For everything else, the general model wins.
If you've got a narrow detection problem and a pile of photos, you now know the process — and the honest limits of what it can achieve.
Thanks to our producer Hilbert Flumingtop for the real-world scar tissue. This has been My Weird Prompts. If you found this useful, tell someone who's about to annotate three hundred images without a convention document. We'll be back soon.