Visualization data
Overview
Section titled “Overview”Most Skin Analysis findings come with spatial information — where on the face the metric was measured, and what specifically was detected. Partners use this to draw markers on the user’s photo, generate before/after panels, or feed an AR pipeline.
The API exposes spatial data in three tiers, ordered by detail:
- AOI (Area Of Interest) — a single circle
(x, y, radius)per region. Always present on metrics that have any spatial output. Cheapest to render. - Mask — a binary mask described as a list of drawing operations. Returned only when the
maskscomponent is requested. Captures exact region boundaries. - Advanced (concern) visualization — per-metric feature data: spots, polylines, or polygon-areas with intensities. Returned only when the
<metric>_visualizationopt-in is included in the request.
Tier 3 is the right tier for live AR overlays. Tier 1 is enough for “zoom in on this region” UX. Tier 2 is for diagnostic dashboards and before/after panels where exact region shapes matter.
Coordinate system
Section titled “Coordinate system”All spatial values — AOI centers, mask shape vertices, advanced-visualization x/y arrays — are in the input image’s pixel coordinate system:
- Origin
(0, 0)is the top-left corner. - X axis points right.
- Y axis points down.
- Bottom-right corner is
(W, H)where W, H are the input image’s pixel width and height.
You can paint visualizations directly onto a canvas of the original image without any coordinate transform.
Tier 1 — AOI
Section titled “Tier 1 — AOI”Every numeric finding’s measurement_locations[] includes AOI_x_o, AOI_y_o, AOI_radius_o:
{ "description": "left cheek", "value": 0.21, "AOI_x_o": 412, "AOI_y_o": 580, "AOI_radius_o": 90, "ut_zone": "u-zone"}The AOI is an approximation of the region the score was computed over — a circle that bounds the actual mask data. One simple use-case is zooming in on a specific sub-measurement when the user drills into a metric.
Tier 2 — masks
Section titled “Tier 2 — masks”When the masks component is included in the request, each measurement_locations[] entry gains a mask_shapes array describing the exact region boundary as a small drawing program (ordered ellipse / polygon / polyline operations, plus a mask shape multiplied at the end).
See the dedicated Masks page for the full vocabulary and the buffer-A/buffer-B implementation walkthrough.
Tier 3 — advanced (concern) visualization
Section titled “Tier 3 — advanced (concern) visualization”Each metric that supports it has a paired <metric>_visualization request component. Adding it to your ?components=... query enables the per-feature data inside measurement_locations[].visualization_data[].
?components=acne,acne_normalized_visualization,wrinkles,wrinkles_visualizationThe supported opt-ins (see the API reference for the full list):
acne_visualization wrinkles_visualization melasma_visualizationacne_normalized_visualization uneven_skin_tone_visualizationtexture_visualization redness_visualization skin_sagging_visualizationshine_visualization hyperpigmentation_visualizationfreckles_visualization dark_spots_visualizationpore_dilation_visualization skin_type_visualizationIf both acne_visualization and acne_normalized_visualization are requested, the response will include only the normalized one.
Feature shape
Section titled “Feature shape”visualization_data[] is a list of features. Each feature is one of three types — the type is implicit (no kind field), determined by the metric:
| Feature type | What it draws | Metrics |
|---|---|---|
| Spot | A single point with an intensity. Length is always 1. | acne, freckles, dark spots, hyperpigmentation |
| Polyline | A connected line segment of length-N points (one detected wrinkle, one fold). | wrinkles (and its sub-region variants — see Wrinkles) |
| Polygon area | An area mask delimited by a coordinate path. Intensity is a categorical integer indexing levels — except skin sagging, whose outlines carry a continuous intensity. | redness, melasma, shine, skin type, texture, uneven skin tone, skin sagging, pore dilation |
For most polygon-area metrics the response includes one feature per intensity level, and the levels come from thresholding the same underlying field at increasing cutoffs — so higher levels generally sit inside lower ones. That is what makes layered low-opacity fills read as a gradient. Containment is not guaranteed pixel-for-pixel, though: each level is cleaned up with its own morphological pass, so edges can differ slightly.
Three metrics do not follow the nested-levels model at all:
shine— levels 1–2 come from the diffuse-sheen field, levels 3–4 from the specular-highlight field. They are two independent pairs, not one four-step ramp, so level 3 is not “worse” than level 2 and does not sit inside it.skin_type— a composite stream: levels 1–4 carryshinedata, 5–6pore_dilation, 7–9redness. Three unrelated sources sharing one integer axis.skin_sagging— one continuous-intensity outline per contributing region, not levels.
Feature data fields
Section titled “Feature data fields”Every feature carries:
{ "length": 27, // 1 for spots, N for polylines/areas "x": [288.77, 287.29, /* ... */], // length-N x coords (pixels) "y": [51.61, 51.60, /* ... */], // length-N y coords (pixels) "intensity": 0.24, // number for spots; integer for area categories; array for polylines "mean_intensity": 0.240, // average across the feature "median_intensity": 0.248, // median across the feature "length_weighted_intensity": 0.231 // present on every feature; only meaningful for wrinkles}Intensity shape per type:
- Spot —
intensityis a number; equivalently, an array of length 1. - Polyline —
intensityis an array of length-N (per-point intensities along the line). - Polygon area —
intensityis a single integer (the category index, e.g.1or2).
length_weighted_intensity appears on every feature but is only meaningful for wrinkles — there it ranks findings by both how long and how intense the wrinkle is, useful when surfacing the worst few to a user. On spot and polygon-area features it is computed from a single value and carries no information; ignore it. The same applies to mean_intensity / median_intensity on polygon-area features: with one integer level per feature they simply repeat intensity.
Per-metric support matrix
Section titled “Per-metric support matrix”Six categories:
| Category | What’s available | Metrics |
|---|---|---|
| A — none | No spatial data; the metric is either inverted or a derived combination. value only. | dull skin, skin firmness, radiance, radiance adjusted, redness absolute, dark spots adjusted, hyperpigmentation adjusted, skin age |
| B — AOI only | AOI_* fields per measurement location. | (currently empty) |
| C — AOI + mask | AOI_* + mask_shapes (when masks requested). No advanced viz. | eyes |
| M — mask only | mask_shapes (when masks requested), with no AOI and no score. | makeup, hair |
| D− — advanced (simpler) | The advanced visualization structure but with simpler outputs (relevant finding areas only, no per-feature detail). | skin sagging |
| D — AOI + mask + advanced | All three tiers when respective opt-ins are requested. | acne, dark spots, freckles, hyperpigmentation, melasma, pore dilation, redness, shine, skin type, smoothness, texture, uneven skin tone, wrinkles |
Category M is the odd one. Both metrics carry shapes but nothing numeric attached to them:
makeupreturns nine cosmetic-application areas asmeasurement_locations[]—Lips,Liplines,Eyelines,Eyeshadow,Eyebrows,Nose,Left cheek,Right cheek,Skin— each withvalue: nulland no AOI. They exist only to carrymask_shapes.hairhas nomeasurement_locationsat all; its outline is on the finding’s ownmask_shapes. See Masks → Two places to look.
Without the masks component neither returns anything spatial.
Smoothness footgun: smoothness shares its advanced-visualization data with uneven_skin_tone. To get advanced data for smoothness, request uneven_skin_tone_visualization (the API does not have a separate switch). Both metrics will then carry advanced viz in the response.
Intensity ranges
Section titled “Intensity ranges”intensity means two different things depending on the feature type, and mixing them up is the most common rendering bug:
- Continuous metrics —
intensityis a magnitude. Scaling it into a colour ramp is correct, but the range differs per metric, so parameterize the ramp; don’t assume[0, 1]everywhere. - Categorical (polygon-area) metrics —
intensityis a level index, an integer counting up from1. It is not a normalized score. Feeding it straight into a0..1colour ramp renders every level at full saturation. Map each integer to a colour explicitly instead.
Continuous
| Metric | intensity range | Notes |
|---|---|---|
| Wrinkles | ~[0, 1] per point | intensity is an array — one value per point along the line. |
| Acne | [0, 2500] legacy, [0, 1] normalized | Request acne_normalized_visualization for the normalized scale. |
| Hyperpigmentation / dark spots | [0, 400] | |
| Freckles | [0, 400] | |
| Skin sagging | [0, 1] | The contributing region’s own score, not a level index. |
Categorical — integer level index
| Metric | Levels | Notes |
|---|---|---|
| Redness | 1..3 | Count follows your visualization_thresholds; default [.2, .5, .8]. |
| Melasma | 1..3 | Default [1.5, 6.0, 10.0]. |
| Texture | 1..2 | Default [.2, .6]. |
| Uneven skin tone | 1..2 | Default [2.0, 6.0]. Shared with smoothness. |
| Pore dilation | 1..2 | Default [0.0, 0.5]. |
| Shine | 1..4 | Fixed meanings, two independent pairs: 1/2 low/high diffuse, 3/4 low/high specular. |
| Skin type | 1..9 | Composite: 1..4 shine, 5..6 pore dilation, 7..9 redness. |
For the categorical metrics whose count follows visualization_thresholds, changing that request parameter changes the number of levels you get back — so drive your colour map off the levels actually present in the response rather than hard-coding a count.
Render decision rules
Section titled “Render decision rules”Three patterns to bake into your renderer:
1. Prefer mean_intensity ?? intensity for color decisions. When the metric provides a mean_intensity (most do for area metrics), it gives a more stable color than the raw per-point intensity. Fall back to intensity when mean_intensity is absent.
2. Sort visualization_data ascending by intensity before drawing. For the nested-level metrics, drawing low → high gives correct overpainting — the high-intensity area lands on top of the low. For spot/line metrics the sort is harmless and keeps render order deterministic. For shine and skin_type the sort is still worth keeping for determinism, but don’t read the ordering as severity: their levels come from different source fields (see Feature shape), so give each level its own colour rather than a single ramp.
3. Decimate dense polylines. A wrinkle feature can carry hundreds of points. For canvas / SVG / WebGL rendering, keeping every Nth point above a threshold (every 3rd above 20 is a good default) is visually identical and much cheaper.
For paste-ready code for the three feature types, see Rendering recipes.
Edge cases
Section titled “Edge cases”- Skin sagging
valuecan benullfor males in the lower-face / beard region — beard analysis isn’t reliable, so the API returnsnullrather than guessing. Render skin sagging with an “n/a” treatment in those cases. - Eyeglasses are detected but not compensated. When eyeglasses are present, the API emits status code
314as a warning and processes the rest of the face anyway; the eye-region results are unreliable. There’s no per-region masking-out of the lens area. - Pore dilation works best with ≥1200px face width (status codes
2000/2001). On lower-resolution input, pore-dilation findings will be unreliable or aborted. - Advanced viz adds payload weight. Don’t request
<metric>_visualizationfor metrics whose visualizations you won’t render — it materially increases response size.
Related
Section titled “Related”- Masks — the tier-2 region-boundary format with full shape vocabulary and drawing-syntax walkthrough.
- Sub-area divisions — which face-region split each metric uses for its
measurement_locations[]. - Rendering recipes — paste-ready spot / polyline / area renderers.
- Interpreting results — full response-shape walkthrough; the
value/value_raw/local_warningsfields.