Technology Sep 03, 2026 · 7 min read

Building TargetV1: A Confidence-Gated Recognition Pipeline (and the Blackwell GPU Bug That Almost Stopped Me)

I Built a Recognition App That Admits When It's Not Sure. Here's What It Took. I've been building a personal image recognition project for a while now, and somewhere along the way it turned into something I actually want to write about. It's called TargetV1, and the short version is: it's a system...

DE
DEV Community
by Tariq Ahmad
Building TargetV1: A Confidence-Gated Recognition Pipeline (and the Blackwell GPU Bug That Almost Stopped Me)

I Built a Recognition App That Admits When It's Not Sure. Here's What It Took.

I've been building a personal image recognition project for a while now, and somewhere along the way it turned into something I actually want to write about. It's called TargetV1, and the short version is: it's a system that tries to identify what's in a photo, and when it isn't sure, it actually says so and goes and checks itself instead of guessing with a straight face.

That sounds simple. It really wasn't.

The first wall: hardware nobody had caught up to yet

I started on a laptop with an RTX 5060. Blackwell architecture, new enough that a lot of the tooling around it hadn't fully caught up yet. First time I tried running anything on the GPU, I got this:

CUDA error: no kernel image is available for execution on the device

Not my code. Genuinely not my fault, for once. My GPU was being detected fine, PyTorch just had no compiled kernels for this specific new hardware. It could see the card, it just couldn't actually do anything with it.

Took me longer than I'd like to admit to figure out the fix was this simple:

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu128

Installing from the cu128 build instead of the default cu126 one gave PyTorch the missing piece. If you're on newer NVIDIA hardware and hit this exact error, that's probably your fix too. Wish someone had told me on day one.

Cloning the repo felt right. It wasn't.

My second mistake, in hindsight, was doing this the way tutorials tell you to: clone the model's repo, download the weights separately, wire it up by hand. This gave me three different headaches at once. Windows paths broke because the code assumed Unix-style folder structures. A couple of downloads silently turned into corrupted HTML pages instead of actual model weights. And the cloned code expected layer names like cls_token, while the weights I'd downloaded used names like embeddings.cls_token. None of it matched.

I ended up scrapping all of that and just using Hugging Face's transformers library instead:

from transformers import AutoImageProcessor, AutoModel
model = AutoModel.from_pretrained("facebook/dinov3-vitb16-pretrain-lvd1689m")

Two lines did what an entire folder of glue code couldn't. If a model's available through transformers, just start there. I wish I had.

Five models, one job each

Here's the part I'm actually proud of. TargetV1 isn't one model trying to do everything. It's five, and each one only does the thing it's actually good at.

DINOv3 turns images into a kind of numeric fingerprint, which I use for similarity search, spotting duplicates, grouping similar photos together. SAM finds every distinct shape or object in an image without knowing or caring what any of them are called, it just says "here's a region." Moondream2, a small vision-language model, looks at the whole picture and describes it in plain English, coming up with its own guesses without any help from me. CLIP takes those guesses and scores how confident it actually is, a real number, not a vague impression. BLIP writes short one-line captions as extra context.

It took me a while to actually learn this, but no single one of these does the whole job well on its own. Ask a big vision-language model to describe a busy photo with twenty animals crammed into it, and it'll confidently name maybe twelve, repeat a couple, and just miss the rest entirely. Give a classifier a fixed list of names to check against and it'll be very precise, but only for whatever you thought to type into that list beforehand. Chaining these together, letting each one stick to its lane, worked a lot better than leaning on any single model.

When it's not sure, it checks Wikipedia

This is honestly the part of the project I care about most. Every object the system finds gets a real confidence score from CLIP. If that score comes back low, say 30% instead of 90%, the system doesn't just shrug and hand you the guess anyway. It takes that guess, searches Wikipedia for it, pulls a reference photo, and compares that against the original using the same similarity math from the DINOv3 step. If it's a strong match, it tells you it's confirmed. If not, it says that too, plainly: "Wikipedia page found, but image similarity too low to confirm."

I wanted every claim the app makes to come with its receipts. Which model said what, how confident it actually was, and if it went and double-checked, what it found and where.

A result I didn't expect, and I'm not going to hide it

Here's something worth being honest about, because it's a real finding and not something I'm going to bury. When I used DINOv3's similarity math to check a guess against a real Wikipedia photo, the numbers came back way lower than I expected, even for guesses that were almost certainly right. A cartoon-style koala compared against an actual photo of a real koala only scored around 13% similarity. Nowhere close to what I'd set as a "confirmed" threshold.

Turns out DINOv3 cares a lot about pose and visual style, not just what category something belongs to. It's answering "does this look structurally like that," not "is this the same kind of animal." A rendered illustration and a real photograph of the same species just look very different to it, structurally speaking.

I think the fix is to swap that particular comparison over to CLIP's similarity space instead, since CLIP was built specifically to bridge that kind of gap between different image styles. Haven't done it yet. It's next on my list.

I'm including this because it makes the project more honest, not because it makes it look impressive. A system that admits its own blind spots is worth more to me than one that pretends it doesn't have any.

Catching blur before it causes bad guesses

One smaller thing I added, but genuinely useful: before trusting a confidence score, the system checks whether the photo itself is even sharp enough to trust in the first place. I used an old, simple trick called Laplacian variance, basically a measure of how much real edge detail exists in an image. Blurry photos barely have any.

Calibrating this took a couple of tries. My first version kept flagging perfectly sharp photos as blurry, and it took me a bit to realize why: product-style photos with a lot of plain white background confuse it, since blank space reads as "no edges, therefore blurry" even when the actual subject is in perfect focus. Restricting the check to the center of the frame fixed most of that.

Where it stands right now

Today, TargetV1 can take a photo and tell you what's in it in plain language, list every distinct object it finds with an actual confidence number attached, tell you whether it double-checked anything low-confidence and what it found when it did, flag whether the photo itself is sharp enough to trust, and show you which other photos in your collection look similar to it.

All of it runs on a personal laptop GPU, using free public models and a free public API. No subscriptions, nothing hidden behind a paywall.

What's still left to do

I'll be honest about the to-do list too. I need to swap that verification math from DINOv3 to CLIP, like I mentioned above. I want to actually test this against a proper dataset instead of just one busy test image I kept throwing at it. And eventually I want to wrap the whole thing in a real interface, something you can just drag a photo into, instead of running it from a terminal every time.

For now though, it works. It's honest about what it doesn't know. And every piece of it got built the hard way, one mistake at a time, on hardware that the tools around it were still catching up to.
Full Code : Project: https://github.com/AIEnthusiast-art/TargetV1

leave questions for you in the comments. It's a great way to spark additional discussion describing personally why you wrote it or why people might find it helpful.

DE
Source

This article was originally published by DEV Community and written by Tariq Ahmad.

Read original article on DEV Community
Back to Discover

Reading List