diff --git a/docs/code-examples/annotation-context/example.py b/docs/code-examples/annotation-context/example.py new file mode 100644 index 000000000000..9c02c70931ec --- /dev/null +++ b/docs/code-examples/annotation-context/example.py @@ -0,0 +1,18 @@ +# Annotation context with two classes, using two labeled classes, of which ones defines a color. +rr.log_annotation_context( + "masks", # Applies to all entities below "masks". + [ + rr.AnnotationInfo(id=0, label="Background"), + rr.AnnotationInfo(id=1, label="Person", color=(255, 0, 0)), + ], +) + +# Annotation context with simple keypoints & keypoint connections. +rr.log_annotation_context( + "detections", # Applies to all entities below "detections". + rr.ClassDescription( + info=rr.AnnotationInfo(label="Snake"), + keypoint_annotations=[rr.AnnotationInfo(id=i, color=(0, 255 / 9 * i, 0)) for i in range(10)], + keypoint_connections=[(i, i + 1) for i in range(9)], + ), +) diff --git a/docs/code-examples/annotation-context/example.rs b/docs/code-examples/annotation-context/example.rs new file mode 100644 index 000000000000..111a909f3627 --- /dev/null +++ b/docs/code-examples/annotation-context/example.rs @@ -0,0 +1,56 @@ +// Annotation context with two classes, using two labeled classes, of which ones defines a color. +MsgSender::new("masks") // Applies to all entities below "masks". + .with_timeless(true) + .with_component(&[AnnotationContext { + class_map: [ + ClassDescription { + info: AnnotationInfo { + id: 0, + label: Some(Label("Background".into())), + color: None, + }, + ..Default::default() + }, + ClassDescription { + info: AnnotationInfo { + id: 0, + label: Some(Label("Person".into())), + color: Some(ColorRGBA(0xFF000000)), + }, + ..Default::default() + }, + ] + .into_iter() + .map(|class| (ClassId(class.info.id), class)) + .collect(), + }])? + .send(rec_stream)?; + +// Annotation context with simple keypoints & keypoint connections. +MsgSender::new("detections") // Applies to all entities below "detections". + .with_timeless(true) + .with_component(&[AnnotationContext { + class_map: std::iter::once(( + ClassId(0), + ClassDescription { + info: AnnotationInfo { + id: 0, + label: Some(Label("Snake".into())), + color: None, + }, + keypoint_map: (0..10) + .map(|i| AnnotationInfo { + id: i, + label: None, + color: Some(ColorRGBA::from_rgb(0, (255 / 9 * i) as u8, 0)), + }) + .map(|keypoint| (KeypointId(keypoint.id), keypoint)) + .collect(), + keypoint_connections: (0..9) + .map(|i| (KeypointId(i), KeypointId(i + 1))) + .collect(), + }, + )) + .collect(), + }])? + .send(rec_stream)?; diff --git a/docs/content/concepts/annotation-context.md b/docs/content/concepts/annotation-context.md new file mode 100644 index 000000000000..0cad14e64b18 --- /dev/null +++ b/docs/content/concepts/annotation-context.md @@ -0,0 +1,68 @@ +--- +title: Annotation Context +order: 4 +--- + +## Overview + +Any visualization that assigns an identifier ("Class ID") to an instance or entity can benefit from using Annotations. +By using an Annotation Context, you can associate labels and colors with a given class and then re-use +that class across entities. + + +This is particularly useful for visualizing the output of classifications algorithms +(as demonstrated by the [Detect and Track Objects](https://github.com/rerun-io/rerun/tree/main/examples/python/detect_and_track_objects) example), +but can be used more generally for any kind of reoccurring categorization within a Rerun recording. + +![class_ids](https://static.rerun.io/5508e3fd5b2fdc020eda0bd545ccb97d26a01303_classids.png) + + +### Keypoints & Keypoint Connections + +Rerun allows you to define keypoints *within* a class. +Each keypoint can define its own properties (colors, labels, etc.) that overwrite its parent class. + +A typical example usage of keypoints is annotating the joints of a skeleton within a pose detection: +In that case, the entire detected pose/skeleton is assigned a Class ID and each joint within gets a Keypoint ID. + +To help you more with this (and similar) use-case(s), you can also define connections between keypoints +as part of your annotation class description: +The viewer will draw the connecting lines for all connected keypoints whenever that class is used. +Just as with labels & colors this allows you to use the same connection information on any instance that class in your scene. + +Keypoints are currently only applicable to 2D and 3D points. + +![keypoints](https://static.rerun.io/a8be4dff9cf1d2793d5a5f0d5c4bb058d1430ea8_keypoints.png) + + +### Logging an Annotation Context + +Annotation Context is typically logged as [timeless](timelines.md#timeless-data) data, but can change over time if needed. + +The Annotation Context is defined as a list of Class Descriptions that define how classes are styled +(as well as optional keypoint style & connection). + +Annotation contexts are logged with: + +* Python: [`log_annotation_context`](https://ref.rerun.io/docs/python/latest/common/annotations/#rerun.log_annotation_context) +* Rust: [`AnnotationContext`](https://docs.rs/rerun/latest/rerun/external/re_log_types/component_types/context/struct.AnnotationContext.html) + +code-example: annotation-context + + +## Affected Entities + +Each entity that uses a Class ID component (and optionally Keypoint ID components) will look for +the nearest ancestor that in the [entity path hierarchy](entity-path.md#path-hierarchy-functions) that has an Annotation Context defined. + + +## Segmentation images + +Segmentation images are single channel integer images/tensors where each pixel represents a class id. +By default, Rerun will automatically assign colors to each class id, but by defining an Annotation Context, +you can explicitly determine the color of each class. + +* Python: [`log_segmentation_image`](https://ref.rerun.io/docs/python/latest/common/images/#rerun.log_segmentation_image) +* Rust: Log a [`Tensor`](https://docs.rs/rerun/latest/rerun/components/struct.Tensor.html) with [`TensorDataMeaning::ClassId`](https://docs.rs/rerun/latest/rerun/components/enum.TensorDataMeaning.html#variant.ClassId) + +![segmentation image](https://static.rerun.io/7c47738b791a7faaad8f0221a78c027300d407fc_segmentation_image.png) diff --git a/docs/content/concepts/batches.md b/docs/content/concepts/batches.md index 80ad5d3ca168..f22b70f7b0dc 100644 --- a/docs/content/concepts/batches.md +++ b/docs/content/concepts/batches.md @@ -1,6 +1,6 @@ --- title: Batch Data -order: 4 +order: 5 --- Rerun has built-in support for batch data. Whenever you have a collection of things that all have the same type, rather diff --git a/docs/content/concepts/entity-path.md b/docs/content/concepts/entity-path.md index 47ff7823e8de..d29f8723f0bf 100644 --- a/docs/content/concepts/entity-path.md +++ b/docs/content/concepts/entity-path.md @@ -45,7 +45,7 @@ Path hierarchy plays an important role in a number of different functions within * With the [Transform System](spaces-and-transforms.md) the `transform` component logged to any Entity always describes the relationship between that Entity and its direct parent. - * When resolving the meaning of `class_id` and `keypoint_id` components, Rerun uses the Annotation Context from the nearest ancestor in the hierarchy. + * When resolving the meaning of Class ID and Keypoint ID components, Rerun uses the [Annotation Context](annotation-context.md) from the nearest ancestor in the hierarchy. * When adding data to [Blueprints](../reference/viewer/blueprint.md), it is common to add a path and all of its descendants. * When using the `log_cleared` API, it is possible to mark an entity and all of its descendants as being cleared. * In the future, it will also be possible to use path-hierarchy to set default-values for descendants.