You have a cropped person image from your own detector and you want its 17 body keypoints. Run the pose weights directly on the crop. The detection head and the pose head ship as one network and cannot be separated, and on a tight crop the detection step is nearly free, so nothing is lost by keeping it. What you must add is the part most examples skip: read each keypoint's confidence score, treat low scores as invisible, and shift the surviving coordinates back into full-frame pixels. The steps below give the calls, the thresholds, the coordinate arithmetic and the tests that prove the result.
What a YOLO pose model returns for a crop
The 11-series pose weights, from yolo11n-pose.pt up to yolo11x-pose.pt, form a single unified network: the detection head localizes each person, and the pose head regresses 17 keypoints for that detection. There is no pose-only switch; the detection stage is architecturally integrated. That costs you nothing here, because a crop is a fully valid input. The network only sees the pixels you hand it, and when the person fills the frame, the detector fires immediately.
One call returns a list with one entry per detected person — the same per-frame structure the YOLO demo output breakdown walks through. Per entry you read:
-
keypoints.xy— x and y in input-image pixels, 17 rows per person -
keypoints.xyn— the same points normalized to 0–1 by image size -
keypoints.conf— one confidence score per keypoint -
keypoints.data— x, y and visibility (when the model provides it)
The rows follow the COCO order:
| Index | Body points |
|---|---|
| 0 | nose |
| 1–2 | eyes |
| 3–4 | ears |
| 5–6 | shoulders |
| 7–8 | elbows |
| 9–10 | wrists |
| 11–12 | hips |
| 13–14 | knees |
| 15–16 | ankles |
Run it on the crop: six steps
- Crop with margin. Expand the person box by 10–20% of its width and height on every side before cutting. Border keypoints such as the head top or the ankles keep their context instead of sitting on the image edge.
- Load and call. Pass the crop exactly where you would pass a full frame:
model = YOLO("yolo11n-pose.pt") # or your fine-tuned .pt
results = model("person_crop.jpg") # one cropped person, 420 x 560 px
assert len(results) == 1 # a single-person crop gives one detection
assert results[0].keypoints is not None # None means nobody was detected
xy = results[0].keypoints.xy[0] # (17, 2) pixel coordinates in the crop
conf = results[0].keypoints.conf[0] # (17,) one score per keypoint
- Gate the points. Keep a row only when its score passes your threshold — start at 0.5:
visible = [(i, float(p[0]), float(p[1]))
for i, p in enumerate(xy.tolist()) if conf[i] >= 0.5]
- Name the points. Map each surviving index through the COCO table above; index 0 is the nose, index 9 is a wrist.
- Shift back to the frame. Add the crop origin to each kept point; the arithmetic is in the next section.
- Log what you dropped. Store the rejected indices with their scores. When a downstream check fails later, you will want to know whether the wrist was missing from the crop or merely scored low.
Gate keypoints by confidence, not by presence
The pose head is trained to emit the full 17-point skeleton for every detection, so a limb outside the crop still gets a row — usually with coordinates pulled toward a plausible body position and a low score. The presence of the row means nothing. The score is the visibility flag.
Worked scores from one crop: both shoulders at 0.94 and 0.91, both wrists at 0.11 and 0.08. At a 0.5 threshold the output honestly says 'torso visible, hands not in frame' — exactly what a pick or analytics step needs. Drop the threshold to 0.05 to force all 17 rows through and you have fabricated wrist positions, with every coordinate computed from them inheriting the error.
Set the threshold by cost: 0.5 for pass/fail logic, about 0.3 when rough limb presence is enough and some jitter is tolerable, 0.7 when a wrong wrist position is expensive. Tune on roughly 50 labelled crops from your own line and count how often a kept point lands off the joint — the same labelled-data sizing question as building a custom YOLO training set.
Map crop coordinates back to the full frame
The API reports in the pixel space of the image you passed in: pass a crop, get crop pixels. The network resizes the input internally — the stock weights run at a 640 px input, or whatever input size your own training run used — and rescales its output back, so you never apply a scale factor. You only add the crop origin:
x1, y1 = 412, 380 # crop origin in the 1920 x 1080 frame
crop = frame[y1:y1 + 560, x1:x1 + 420] # 420 x 560 px crop
results = model(crop)
wrist_x = float(results[0].keypoints.xy[0][9][0]) + x1 # 198.4 + 412 = 610.4
wrist_y = float(results[0].keypoints.xy[0][9][1]) + y1 # 311.0 + 380 = 691.0
The wrist at index 9 sits at (198.4, 311.0) in crop pixels and at (610.4, 691.0) in the full frame. Use keypoints.xyn only when you want resolution-independent values: normalized 0–1 across the supplied image, so 0.5 is the middle of the crop, not of the frame.
Input size drives accuracy. A 2,100 px wide crop is downscaled by a factor of about 3.3 (2,100 / 640), so joint-position noise grows by the same factor. Crop to the person plus margin rather than to the whole scene.
Edge cases: no person, two people, clipped limbs
-
No person found.
keypointsis None when the detector fires nothing; the assert in step 2 catches it. If it happens on real crops, loosen the detector's confidence setting or widen the crop margin. - Two people in one crop. The list then holds two entries with two keypoint sets. Take the entry with the higher box score, or better, crop tighter upstream.
- Limbs physically out of view. Scores drop and the skeleton stays honest. Keep the threshold and let downstream logic handle missing points; a declared-missing ankle is worth more than a fabricated one.
- You need presence, not pose. If the real question is 'is a person in this zone', a skeleton is the heavy answer; a depth-based presence check is simpler — see choosing a depth sensor for person detection.
Where this runs on the line
The gating logic is a few lines; hardware follows camera count, crop rate and weight size. If the crop itself comes from an on-device detector, the smart cameras range runs that stage at the camera and hands coordinates or images to the vision controllers that run the pose stage. For one camera feeding a modest crop rate on the nano weights, the entry tier is enough:
- LAN ports
- 3-4 LAN ports
- USB 3 ports
- 2 USB 3 ports
- Light outputs
- 4 built-in channels
- Memory
- 8 GB
Several cameras, the extra-large weights or a tight latency budget want a faster processor and more memory:
- LAN ports
- 5+ LAN ports
- USB 3 ports
- 3-4 USB 3 ports
- PoE
- PoE camera ports
- Light outputs
- 8 built-in channels
When you push crops through in bulk, keep images on the accelerator between stages — the GPU tensor input pattern removes the CPU copy that throttles throughput.
Verify the output before you trust it
- Count. A single-person crop must give one entry and a 17-row coordinate block; more entries means two people, None means the detector found nobody.
- Geometry. On a standing person the nose (index 0) must sit above the hips (indices 11–12). If it does not, your index mapping and the model disagree — stop and re-map before any logic consumes the points.
- Occlusion. Deliberately crop an arm out: its scores must fall below your threshold while visible shoulders stay above 0.9. A hidden limb that still scores high means your threshold is too low for the scene.
- Round trip. Click a joint in a saved frame, read the reported pixel, and confirm the error is a few pixels at the crop's scale.
- Timing. Log per-crop inference time next to your cycle budget before the result feeds line logic, and pin the deployment configuration so it cannot drift — the reliable inference configuration guide lists the settings that tend to move.
Common mistakes
- Reading presence as visibility: the rows are always there, the scores decide.
- Mixing
xynvalues (0–1) into pixel arithmetic when stitching back to the frame. - Cutting the crop tight to the box, which starves the border keypoints of context.
- Iterating results without the None guard, which breaks the moment nobody is detected.
- Lowering the threshold until all 17 rows pass, then building logic on fabricated points.
- Treating the scores as a safety signal (see the warning above).
The decision for each case
- Crop already in hand from your own detector: run the pose weights on it, gate at 0.5, add the crop origin. That is the whole integration.
- Full frames available, no separate detector: do not pre-crop. The unified model runs detection anyway, so a crop stage adds latency and a failure point without saving anything.
- Limbs physically out of view: keep the model and the threshold, report those points as missing, and fix the camera view if the application truly needs them — not the threshold.
- Sizing the controller: one camera at a modest crop rate on the nano weights, take the entry tier above; several cameras or the extra-large weights, take the higher-memory tier above.
If you want the hardware sized against your camera count and cycle budget, send the numbers through the contact page, or continue with the systems guides.
Questions
Can I run a YOLO pose model on a cropped person image alone?
Yes. The crop is a complete, valid input: load the pose weights and pass the crop exactly as you would a full scene, then read keypoints.xy and keypoints.conf. The detection head still executes because it is part of the same network, but on a crop where the person fills the frame it finds its target in one pass and costs almost nothing.
Why do I get 17 keypoints when the legs are outside the crop?
The pose head predicts the full skeleton for every detection, so out-of-frame limbs still receive coordinates, typically snapped toward a plausible body position, with a low confidence score. The score, not the row's existence, is the visibility flag. Filter on it: at a 0.5 threshold those phantom points drop out and downstream logic sees a skeleton with the absent limbs marked as missing.
Which keypoint confidence threshold should I start with?
Start at 0.5 for pass/fail logic. Drop to about 0.3 when rough limb presence is enough and jitter is tolerable; raise toward 0.7 when a wrong wrist position carries a cost, such as a pick point. Calibrate on roughly 50 labelled crops from your own line by counting how often a kept point lands off the joint.
Are keypoint coordinates in crop pixels or original frame pixels?
In the pixel space of whatever image you passed in: a crop returns crop pixels, a full frame returns frame pixels, with the model's internal resize mapped back for you. Keep the crop size consistent across runs if you compare coordinates over time, and add the crop origin once when stitching into frame coordinates. The xyn values run 0 to 1 across the supplied image.
What happens with two people or no person in the crop?
Two detected people give two entries in the results list, each with its own 17-row keypoint block; take the entry with the higher box score or crop tighter upstream. Nobody detected leaves keypoints as None, so guard every access before reading coordinates or scores. Both cases are handled in the same loop with a length check.

