Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ pnpm-debug.log*
.env
.env.*
!.env.example

# Local working notes
docs/internal/bug-audit-findings.md
docs/internal/feature-and-bug-proposals.md
21 changes: 18 additions & 3 deletions demo/src/docs-annotation-renderer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ describe("docs annotation renderers", () => {
? ["ellipsesEnabled"]
: renderer === "mask-halo"
? ["maskHaloEnabled"]
: renderer === "regions"
? []
: [`${renderer}Enabled`];
: renderer === "percentage-bar"
? ["percentageBarsEnabled"]
: renderer === "regions"
? []
: [`${renderer}Enabled`];

expect(enabled).toEqual(expectedEnabled);
}
Expand Down Expand Up @@ -104,6 +106,19 @@ describe("docs annotation renderers", () => {
"y: detection.rect.y + detection.rect.height / 2 - radiusY,",
);
expect(ellipseSnippet).toContain("alpha: 1,");
const percentageBarSnippet = createDocsAnnotationRendererSnippet(
"percentage-bar",
{
...defaultDemoPresentationSettings,
percentageBarFillAlpha: 0.9,
percentageBarHeight: 12,
},
);
expect(percentageBarSnippet).toContain(
"annotationRenderers.percentageBar({",
);
expect(percentageBarSnippet).toContain("height: 12");
expect(percentageBarSnippet).toContain("alpha: 0.9");
});

it("exposes marker shape and bounding-box position controls", () => {
Expand Down
68 changes: 66 additions & 2 deletions demo/src/docs-annotation-renderer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { MarkerShape, type DetectionFrame } from "supervision";
import {
MarkerShape,
PercentageBarPlacement,
type DetectionFrame,
} from "supervision";
import {
demoMarkerPositionOffsets,
type DemoMarkerPosition,
Expand All @@ -18,6 +22,7 @@ export const docsAnnotationRendererIds = [
"masks",
"mask-halo",
"markers",
"percentage-bar",
"labels",
"polygons",
"polylines",
Expand All @@ -38,7 +43,7 @@ export interface DocsAnnotationRendererControl {
}

export type DocsAnnotationRendererSelectSetting =
"markerPosition" | "markerShape";
"markerPosition" | "markerShape" | "percentageBarPlacement";

export interface DocsAnnotationRendererSelectControl {
readonly key: DocsAnnotationRendererSelectSetting;
Expand Down Expand Up @@ -240,6 +245,43 @@ export const docsAnnotationRenderers: Readonly<
],
title: "Markers",
},
"percentage-bar": {
controls: [
{
key: "percentageBarHeight",
label: "Height",
max: 24,
min: 2,
step: 1,
unit: "pixels",
},
{
key: "percentageBarFillAlpha",
label: "Fill opacity",
max: 1,
min: 0.1,
step: 0.05,
unit: "percent",
},
],
description: "Proportional metric bars anchored to bounding boxes",
selects: [
{
key: "percentageBarPlacement",
label: "Placement",
options: [
{ label: "Top", value: PercentageBarPlacement.Top },
{ label: "Bottom", value: PercentageBarPlacement.Bottom },
{ label: "Inside Top", value: PercentageBarPlacement.InsideTop },
{
label: "Inside Bottom",
value: PercentageBarPlacement.InsideBottom,
},
],
},
],
title: "Percentage Bar",
},
labels: {
controls: [
{
Expand Down Expand Up @@ -363,6 +405,7 @@ export function createDocsAnnotationRendererPresentation(
maskHaloEnabled: renderer === "mask-halo",
masksEnabled: renderer === "masks" || renderer === "polylines",
markersEnabled: renderer === "markers",
percentageBarsEnabled: renderer === "percentage-bar",
...(renderer === "markers"
? {
markerPosition: "bottom-center" as const,
Expand Down Expand Up @@ -502,6 +545,18 @@ export function createDocsAnnotationRendererSnippet(
}),
}),
],
});`;
case "percentage-bar":
return `session.setPresentation({
renderers: [
annotationRenderers.percentageBar({
style: new BasePercentageBarStyle({
fill: { alpha: ${formatNumber(settings.percentageBarFillAlpha)} },
height: ${formatNumber(settings.percentageBarHeight)},
placement: PercentageBarPlacement.${percentageBarPlacementMemberNames[settings.percentageBarPlacement]},
}),
}),
],
});`;
case "labels":
return `session.setPresentation({
Expand Down Expand Up @@ -581,6 +636,15 @@ const markerShapeMemberNames: Readonly<Record<MarkerShape, string>> = {
[MarkerShape.Triangle]: "Triangle",
};

const percentageBarPlacementMemberNames: Readonly<
Record<PercentageBarPlacement, string>
> = {
[PercentageBarPlacement.Top]: "Top",
[PercentageBarPlacement.Bottom]: "Bottom",
[PercentageBarPlacement.InsideTop]: "InsideTop",
[PercentageBarPlacement.InsideBottom]: "InsideBottom",
};

function formatMarkerCoordinate(axis: "x" | "y", position: DemoMarkerPosition) {
const offset = demoMarkerPositionOffsets[position][axis];
const dimension = axis === "x" ? "width" : "height";
Expand Down
34 changes: 34 additions & 0 deletions demo/src/presentation/demo-presentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
BoxStrokeAlignment,
BoxShape,
BaseMarkerStyle,
BasePercentageBarStyle,
BaseFocusStyle,
BaseBoxCornerStyle,
BaseInteractionStyle,
Expand All @@ -16,6 +17,7 @@ import {
LabelPlacement,
MarkerShape,
MaskRenderMode,
PercentageBarPlacement,
annotationRenderers,
type BoxDrawInstruction,
type BoxCornerStyle,
Expand All @@ -32,6 +34,7 @@ import {
type MaskStyle,
type MarkerStyle,
type MediaRendererPresentation,
type PercentageBarStyle,
type PolygonStyle,
type PolylineStyle,
resolveDetectionClassColorStyle,
Expand Down Expand Up @@ -62,8 +65,12 @@ export interface DemoPresentationSettings {
readonly labelsEnabled: boolean;
readonly masksEnabled: boolean;
readonly markersEnabled: boolean;
readonly percentageBarsEnabled: boolean;
readonly polygonsEnabled: boolean;
readonly polylinesEnabled: boolean;
readonly percentageBarHeight: number;
readonly percentageBarFillAlpha: number;
readonly percentageBarPlacement: PercentageBarPlacement;
readonly boxCornerRadius: number;
readonly boxCornerLength: number;
readonly boxCornerStrokeWidth: number;
Expand Down Expand Up @@ -124,6 +131,7 @@ export type DemoPresentationLayerSetting =
| "labelsEnabled"
| "masksEnabled"
| "markersEnabled"
| "percentageBarsEnabled"
| "polygonsEnabled"
| "polylinesEnabled";

Expand All @@ -140,6 +148,7 @@ const demoPresentationLayerSettings: readonly DemoPresentationLayerSetting[] = [
"labelsEnabled",
"masksEnabled",
"markersEnabled",
"percentageBarsEnabled",
"polygonsEnabled",
"polylinesEnabled",
];
Expand Down Expand Up @@ -229,6 +238,10 @@ export const defaultDemoPresentationSettings: DemoPresentationSettings = {
markerSize: 14,
markerStrokeWidth: 2,
markersEnabled: false,
percentageBarsEnabled: false,
percentageBarFillAlpha: 1,
percentageBarHeight: 8,
percentageBarPlacement: PercentageBarPlacement.Top,
polygonFillAlpha: 0.08,
polygonStrokeWidth: 2,
polygonsEnabled: true,
Expand Down Expand Up @@ -261,6 +274,9 @@ export function createDemoPresentation(
const markerStyle = settings.markersEnabled
? createDemoMarkerStyle(settings)
: null;
const percentageBarStyle = settings.percentageBarsEnabled
? createDemoPercentageBarStyle(settings)
: null;
const polygonStyle = settings.polygonsEnabled
? createDemoPolygonStyle(settings)
: null;
Expand All @@ -284,6 +300,7 @@ export function createDemoPresentation(
labelStyle,
maskStyle,
markerStyle,
percentageBarStyle,
polygonStyle,
polylineStyle,
maskHaloStyle,
Expand All @@ -302,6 +319,9 @@ export function createDemoPresentation(
...(markerStyle
? [annotationRenderers.marker({ style: markerStyle })]
: []),
...(percentageBarStyle
? [annotationRenderers.percentageBar({ style: percentageBarStyle })]
: []),
...(polygonStyle
? [annotationRenderers.polygon({ style: polygonStyle })]
: []),
Expand Down Expand Up @@ -621,6 +641,20 @@ function createDemoMaskStyle(settings: DemoPresentationSettings): MaskStyle {
};
}

function createDemoPercentageBarStyle(
settings: DemoPresentationSettings,
): PercentageBarStyle {
return new BasePercentageBarStyle({
fill: (detection) => ({
alpha: settings.percentageBarFillAlpha,
color: resolveClassStyle(detection, settings).stroke,
}),
height: settings.percentageBarHeight,
placement: settings.percentageBarPlacement,
shouldRender: (detection) => passesConfidenceThreshold(detection, settings),
});
}

function createDemoMaskHaloStyle(
settings: DemoPresentationSettings,
): MaskHaloStyle {
Expand Down
2 changes: 2 additions & 0 deletions docs/public/annotation-renderers.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ children:
- ./annotation-renderers/masks.md
- ./annotation-renderers/mask-halo.md
- ./annotation-renderers/markers.md
- ./annotation-renderers/percentage-bar.md
- ./annotation-renderers/labels.md
- ./annotation-renderers/polygons.md
- ./annotation-renderers/polylines.md
Expand Down Expand Up @@ -44,6 +45,7 @@ motion-gated basketball trajectory rather than inventing geometry at runtime.
- [Masks](./annotation-renderers/masks.md)
- [Mask Halo](./annotation-renderers/mask-halo.md)
- [Markers](./annotation-renderers/markers.md)
- [Percentage Bar](./annotation-renderers/percentage-bar.md)
- [Labels](./annotation-renderers/labels.md)
- [Polygons](./annotation-renderers/polygons.md)
- [Polylines](./annotation-renderers/polylines.md)
Expand Down
43 changes: 43 additions & 0 deletions docs/public/annotation-renderers/percentage-bar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
title: Percentage Bar
summary: Visualize confidence scores or custom metrics as proportional progress bars anchored to detections.
---

# Percentage Bar

The percentage-bar annotation renderer draws proportional progress indicators
anchored to a detection's rectangle bounds. It is ideal for visualizing model
confidence scores, tracking probabilities, or custom progression values
directly on detections.

<div class="supervision-layer-playground">
<iframe
data-supervision-playground-src="demo/?embed=annotation-renderer&amp;renderer=percentage-bar"
loading="lazy"
title="Interactive percentage bar visualization playground"
></iframe>
</div>

The playground uses the frozen basketball fixture's existing detection bounds
and confidence scores. Percentage bars are presentation only: they do not change
the source detection, picking, or editing behavior.

## Add the percentage-bar renderer

```ts
session.setPresentation({
renderers: [
annotationRenderers.percentageBar({
style: new BasePercentageBarStyle({
height: 8,
placement: PercentageBarPlacement.Top,
}),
}),
],
});
```

`BasePercentageBarStyle` normalizes detection confidence scores into proportional
progress indicators. Configure `height`, `placement` (such as `Top`, `Bottom`,
`InsideTop`, or `InsideBottom`), `background`, and `fill` colors to customize
the display for your use case.
7 changes: 7 additions & 0 deletions docs/public/api/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export {
BaseBoxStyle,
BaseBoxCornerStyle,
BaseMarkerStyle,
BasePercentageBarStyle,
BaseFocusStyle,
BaseInteractionStyle,
BaseKeypointStyle,
Expand All @@ -25,6 +26,7 @@ export {
KeypointMarkerShape,
MarkerShape,
MarkerSizeSpace,
PercentageBarPlacement,
LabelVisibilityMode,
RegionRendererComposeMode,
RegionRendererCoverageKind,
Expand Down Expand Up @@ -102,6 +104,11 @@ export {
type MarkerDrawInstruction,
type MarkerStyle,
type MarkerStyleContext,
type BasePercentageBarStyleOptions,
type PercentageBarAnnotationRenderer,
type PercentageBarDrawInstruction,
type PercentageBarStyle,
type PercentageBarStyleContext,
type MaskHaloAnnotationRenderer,
MaskRenderMode,
type MaskStrokeStyle,
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export { BaseMaskStyle } from "#styles/mask-style";
export type { BaseMaskStyleOptions } from "#styles/mask-style";
export { BaseMarkerStyle } from "#styles/marker-style";
export type { BaseMarkerStyleOptions } from "#types/marker-style";
export { BasePercentageBarStyle } from "#styles/percentage-bar-style";
export type { BasePercentageBarStyleOptions } from "#types/percentage-bar-style";
export { BasePolygonStyle } from "#styles/polygon-style";
export type { BasePolygonStyleOptions } from "#styles/polygon-style";
export { BasePolylineStyle } from "#styles/polyline-style";
Expand All @@ -117,6 +119,7 @@ export {
type MaskAnnotationRenderer,
type MaskHaloAnnotationRenderer,
type MarkerAnnotationRenderer,
type PercentageBarAnnotationRenderer,
type PolygonAnnotationRenderer,
type PolylineAnnotationRenderer,
RegionRendererComposeMode,
Expand Down Expand Up @@ -247,6 +250,12 @@ export type {
BoxCornerStyle,
BoxCornerStyleContext,
} from "#types/box-corner-style";
export { PercentageBarPlacement } from "#types/percentage-bar-style";
export type {
PercentageBarDrawInstruction,
PercentageBarStyle,
PercentageBarStyleContext,
} from "#types/percentage-bar-style";
export type {
EllipseGeometry,
MarkerGeometry,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ describe("annotation renderer presentation", () => {
mask: annotationRenderers.mask,
maskHalo: annotationRenderers.maskHalo,
marker: annotationRenderers.marker,
percentageBar: annotationRenderers.percentageBar,
polygon: annotationRenderers.polygon,
polyline: annotationRenderers.polyline,
} as const;
Expand Down
Loading
Loading