Centered face crop estimator
A 200 × 240 face at 400, 250 in a 1200 × 900 photo gives a 360-pixel square starting at x 320, y 190, with the face centered and a quarter of margin on every side. Plan the crop before touching the image. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Width
- 1,200
- Height
- 900
- Face x
- 400
- Face y
- 250
- Face width
- 200
- Face height
- 240
- X
- 320
- Y
- 190
- Side
- 360
- Right
- 680
The centered face crop estimator turns a face detection result into a ready-to-use square crop region. You send the image dimensions and the detected face bounding box — left, top, width and height — plus an optional margin, and you get back the exact x, y and side of a square that keeps the face centered and fully inside the image. No pixels are uploaded, decoded or transformed: this is pure, deterministic geometry, the planning step you run before an actual crop.
Why estimate the crop before you crop
Face-aware cropping shows up everywhere: profile pictures, avatar generators, thumbnails for a people directory, smart previews in a photo library. In all of them the expensive part is decoding and re-encoding the image, and the annoying part is discovering afterwards that the framing was wrong — the face clipped at the edge, the square off-center, the margin too tight for a circular mask. This capability separates the cheap decision from the expensive execution.
You already ran a face detector and you have a bounding box; instead of feeding the image straight into an image pipeline and hoping for the best, you ask for the exact crop region first.
How the region is computed
The algorithm is deliberately simple and fully deterministic. First it finds the center of the face bounding box. The side of the square starts as the largest of the face width and height, scaled by one plus twice the margin — so the default margin of 0.25 adds a quarter of the face size on every side, giving a square one and a half times the largest face side, which is a comfortable framing for headshots.
If that square is larger than the image itself, it is shrunk to the image's smallest dimension. The square is then centered on the face center and, when it would stick out over an edge, shifted back inside the image — clamped, never deformed — so the returned region is always a valid in-bounds crop.
Where it fits in a pipeline
The typical flow is: run your face detector of choice, take the best bounding box, call this estimator, then pass the returned region to a real cropper such as the image.crop capability or your own thumbnail worker. Because the estimator never touches image bytes, you can also use it on metadata alone — for example to decide, before downloading anything, whether a detected face near the border will yield a usable square or should fall back to a different layout.
The margin parameter is your one knob: 0 gives a tight square exactly around the largest face side, 0.25 is the default headshot framing, and 0.5 or more leaves room for hair and shoulders.
What you can do with it
Generate square avatars from uploads
Take the face box from your detector and get the exact square to feed your cropping worker, with a margin that survives a circular mask.
Pre-flight a thumbnail pipeline
Validate face boxes and compute crop regions from metadata alone, before paying the cost of decoding and re-encoding a single image.
Audit automated crops
Store the estimated region next to each asset so a badly framed avatar can be traced back to the exact coordinates that produced it.
FAQ
Do I upload the image?
No. Only the image dimensions and the face bounding box are sent — six numbers. No pixel data is transmitted, processed or stored.
What happens if the face box sticks out of the image?
A face box outside the image is almost always a detection or coordinate-mapping bug, and it is reported instead of silently cropped.
What if the square would extend past an edge?
The square is shifted back inside the image while keeping its size, and if it is larger than the image itself it is shrunk to the image's smallest dimension. The returned region is always fully in bounds.
What does the margin do?
It adds breathing room around the face as a fraction of the face's largest side. The default 0.25 yields a square 1.5 times the largest face side; 0 gives a tight crop and 0.5 leaves generous space.
What do I do with the crop region once I have it?
Pass the x, y and side to the tool that actually crops the photo, or store them next to the image so the framing can be checked later. You can also decide, before downloading anything, whether a face near the border will give a usable square.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/image/crop-face-center-estimate \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"width":1200,"height":900,"face_x":400,"face_y":250,"face_width":200,"face_height":240}'const res = await fetch("https://api.kit.forhosting.com/image/crop-face-center-estimate", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"width": 1200,
"height": 900,
"face_x": 400,
"face_y": 250,
"face_width": 200,
"face_height": 240
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/image/crop-face-center-estimate",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"width": 1200,
"height": 900,
"face_x": 400,
"face_y": 250,
"face_width": 200,
"face_height": 240
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/image/crop-face-center-estimate", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"width":1200,"height":900,"face_x":400,"face_y":250,"face_width":200,"face_height":240}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"width":1200,"height":900,"face_x":400,"face_y":250,"face_width":200,"face_height":240}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/image/crop-face-center-estimate", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"width": 1200,
"height": 900,
"face_x": 400,
"face_y": 250,
"face_width": 200,
"face_height": 240
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "image.crop_face_center_estimate",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_mb | 15 |
max_megapixels | 12 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
It runs free in your browser on this page, and via API at $0.002 per call.
Why estimate the crop before you crop
The answer is a plain JSON object with x, y and side that you can hand to any cropping tool, from ImageMagick to a canvas element, or store next to the asset so the decision is auditable. Because the computation is pure arithmetic on six numbers, it costs almost nothing, answers in microseconds, and behaves identically in the browser and on the API, so you can prototype interactively and automate later without re-checking a single result.
How the region is computed
The face box itself is validated strictly: negative origins, non-positive sizes and boxes that extend past the image edges raise an invalid_input error instead of silently producing a broken crop, because a face box outside the image is a detector bug you want to hear about.
Where it fits in a pipeline
Everything runs on our global edge with nothing stored, and the same module powers the free calculator on this page, so the number you see in the browser is byte-for-byte the number the API returns when you automate the call.
Technical questions
What does it cost?
$0.002 per call via API. It is also free to run in your browser on this page, with exactly the same result.
Is the result deterministic?
Yes. It is pure arithmetic: same input, same coordinates, in the browser and on the API, with no randomness, clocks or network involved.