Skip to main content

Annotations & Styling

QuickPose provides high definition annotations at a minimal performance cost.

PointsOverlayMeasuring LineRange Of Motion
Example Example Example 2Example 3

As annotations provide important visual feedback to your users, quickpose defaults to display an annotation for every feature.

There are cases where the body overlay is not automatically drawn:

Exercise GuideInside Box
Example 4Example 4

In the Exercise Guide example, when the user is not standing or performing a move correctly, the body overlay is turned off, providing a visual clue to the user's mistake and does not draw focus from the feedback message "Move arms apart" shown in an overlay.

For Inside Box, developer's have to choose whether to implement the skeleton whilst the user is not in the correct position.

Points

This feature visualizes what landmarks have been found by the underlying ML model.

case showPoints()
case showPoints(style: customStyle) // with a custom style
Model Validation

Use this feature when changing the model complexity, for example for performance tuning, as some model versions don't track the face and hands in the same resolution, so whilst you may get a faster frame rate you may be losing key landmarks you need for your app to work.

Overlays

Whilst overlays are returned in overlay image, they don't provide a numeric result. These are intended to provide visual feedback to the user, and are often included in higher level features such as range of motion or fitness features.

case overlay(feature: Landmarks.Group)
case overlay(feature: Landmarks.Group, style: customStyle) // with a custom style

Overlays accept a landmark group:

case none
case wholeBody
case wholeBodyAndHead
case upperBody
case straightArmsUpperBody
case toWristsUpperBody
case shoulders

case arm(side: Side)
case armToWrist(side: Side)
case armNoElbow(side: Side)
case straightArm(side: Side)
case hand(side: Side)
case leg(side: Side)

case lowerBody
case hips
case elbows
case knees
case legs
case arms
case head

Style Reference

Every feature accepts a Style, which controls how its lines, points, arcs and labels are drawn:

PropertyDefaultEffect
colorwhiteColor of lines, points, arcs and labels
conditionalColorsnoneReplaces color when the feature's measured value is in a given range — see Conditional Styling
relativeLineWidth1.0Line thickness, relative to the default width
relativeFontSize1.0Measurement label size; 0 hides the label
relativeArcSize1.0Radius of range-of-motion angle arcs
lineCaproundShape of line endings and joins: round, butt or square
linePatternsolidsolid, dashed or dotted lines
shadownoneDrop shadow or glow behind the overlay
outlinenoneContrasting border behind lines and points
imageFillnoneImage revealed through the skeleton's lines and points
font (iOS) / typeface (Android)systemFont face for measurement labels
letterSpacing0Label letter spacing in ems (fraction of text size)
cornerRadius0Corner rounding of the Inside Box border
hidden (iOS)falseDraws nothing while still reporting results

See Styling Options for the visual options in action.

Availability

lineCap, linePattern, shadow, outline, imageFill, font/typeface and letterSpacing are available from v1.3.0 (iOS) and quickpose-core 0.22 (Android).

Customizing annotations

To specify a smaller line width set a relativeLineWidth:

let smallerStyle = QuickPose.Style(relativeLineWidth: 0.33)
let feature1: QuickPose.Feature = .overlay(.upperBody, style: smallerStyle)

The style entries are relative to the original drawn size, so this will be 1/3 of the size of the default line width.

To shrink the angle markers of a range of motion measurement override relativeArcSize and relativeFontSize, as the rangeOfMotion renders text on screen.

let smallerStyle = QuickPose.Style(relativeFontSize: 0.33, relativeArcSize: 0.33, relativeLineWidth: 0.33)
let feature1: QuickPose.Feature = .rangeOfMotion(.shoulder(side: .right, clockwiseDirection: false), style: smallerStyle)
Remove all Annotations

If you would like to see no annotations for your feature set the hidden value to true

let hiddenStyle = QuickPose.Style(hidden: true)
let feature1: QuickPose.Feature = .overlay(.upperBody, style: hiddenStyle)

However, we don't recommend this approach as annotations give the user feedback that they are visible to the camera, and what is expected of them,

Styling Options

All the visual options combine freely — caps, patterns, shadows, outlines and image fills are independent Style parameters that compose in a single style:

lineCap: .round (default)lineCap: .buttlinePattern: .dashedlinePattern: .dotted
Round capsButt capsDashedDotted
shadowshadow as glowoutlineimageFill
ShadowGlowOutlineImage fill
combined: outline + shadow + dashedimageFill + outlinecustom font
CombinedImage fill with outlineCustom font
  • lineCap — shape of line endings and the joins between limb segments: round (default), butt or square.
  • linePatternsolid, dashed or dotted. Dash and dot lengths scale with the line width, so patterns stay proportionate when you change relativeLineWidth. Dashed overlays work well for showing a target or "ghost" pose alongside a solid live skeleton.
  • shadow — a soft shadow behind lines, points and labels. radius is the blur size in pixels; offsetX/offsetY move the shadow right/down on screen, consistently for front and back cameras. A shadow with no offset in the line's own color renders as a neon glow.
  • outline — a contrasting border behind lines and points, keeping the skeleton visible whatever it is drawn over — a white skeleton on a white wall disappears without one. relativeWidth is relative to the default line width, added on each side.
  • imageFill — the skeleton becomes a mask revealing a supplied image, scaled to fill the camera frame: lines, points and measurement labels all show the image through their shape. Use it for flame effects, gradients or brand textures. It overrides color/conditionalColors, and a wider relativeLineWidth (1.5–2) gives the texture room to read.
  • font / typeface — the face used for measurement labels: any UIFont on iOS, any Typeface on Android (system families, bundled fonts, or files — the image above injects Bungee from Google Fonts). It supplies the face only — label size is always 80 scaled by relativeFontSize, so sizing behaves identically on both platforms.
  • letterSpacing — label letter spacing in ems, the fraction of the text size added between glyphs: 0.1 adds 10% of the text size. Same units on both platforms.
QuickPose.Style(lineCap: .butt)
QuickPose.Style(linePattern: .dashed)
QuickPose.Style(shadow: QuickPose.Style.Shadow(color: UIColor.black, radius: 6, offsetX: 0, offsetY: 4))
QuickPose.Style(color: UIColor.green,
shadow: QuickPose.Style.Shadow(color: UIColor.green, radius: 14, offsetX: 0, offsetY: 0))
QuickPose.Style(outline: QuickPose.Style.Outline(color: UIColor.black, relativeWidth: 0.3))
QuickPose.Style(relativeLineWidth: 1.6, imageFill: UIImage(named: "flames"))
QuickPose.Style(font: UIFont(name: "Bungee-Regular", size: 80), letterSpacing: 0.08)
Readable on any background

Combining a subtle outline with a soft shadow keeps the overlay legible on bright walls, dark gyms and busy backgrounds alike:

let readableStyle = QuickPose.Style(shadow: QuickPose.Style.Shadow(color: UIColor.black, radius: 4, offsetX: 0, offsetY: 2),
outline: QuickPose.Style.Outline(color: UIColor.black, relativeWidth: 0.25))

Conditional Styling

QuickPose allows the annotation color to provide visual feedback on a user's movement.

Target RangeWarning
shoulder-conditional-imageknee-conditional-image

The above gifs demonstrate the two major use cases: displaying visually when the user has entered a target range or has entered a dangerous range. Keep in mind that target or dangerous ranges vary by user and exercise.

For conditional highlighting when the angle goes above 120 degrees use:

let conditionalStyle = QuickPose.Style(conditionalColors: [QuickPose.Style.ConditionalColor(min: 120, max: nil, color: UIColor.green)])
let feature1: QuickPose.Feature = .rangeOfMotion(.shoulder(side: .right, clockwiseDirection: false), style: conditionalStyle)