# Tutorial 3 - Utilizing the CAS and Annotators
In this tutorial you will learn how to use the CAS that contains
the annotations created by the various Annotators.
A central annotation type for RoboKudo is the `ObjectHypothesis`.
It marks an object in the scene and many other annotators rely on this
hypothesis to add their own annotations to it.
This basically allows the system to gather more and more information over time
about the detected objects.
In this tutorial, you will work on an Annotator which uses the CAS to
add your own annotation to an ObjectHypothesis.
Specifically, you will create an annotation based on the **Region Of Interest (ROI)** of an object.
The ROI can be seen as the rectangle around the Objects in the image as seen in the previous tutorial.
Your goal is to calculate its area.
The ROI is available in the `roi` field of an `ObjectHypothesis`. Its value is
an `ImageROI` object with which also has a field `roi` leading to a `Rect` object
with four attributes `x`, `y`, `width` and `height`.
The CAS is accessible in each annotator through its function `self.get_cas()`.
The annotations stored in the CAS are available through its field `annotations`,
which is a normal python list of all annotations.
Example to access the annotations in the CAS:
```python
cas = self.get_cas()
annotations: List[Annotation] = cas.annotations
```
The CAS also provides a helper function to retrieve only annotations of a certain
type. This can be used for example to only get `ObjectHypothesis` annotations:
```python
annotations: List[ObjectHypothesis] = cas.filter_annotations_by_type(
robokudo.types.scene.ObjectHypothesis
)
```
Each `ObjectHypothesis` itself also has such a list of annotations.
This was for example used in the `ClusterColorAnnotator`, where the `SemanticColor`
annotations were added to the list of annotations on an `ObjectHypothesis`.
Like Annotators, annotations are also implemented as simple classes.
- **Task 3-1:** Look for the `annotators` directory and implement the `update` function in
`object_size.py` with the following criteria:
- For each `ObjectHypothesis` in the CAS, calculate the area of the ROI.
- For each `ObjectHypothesis` add a new annotation `Size` containing the ROI
area in its field `value`. You can use the already existing annotation class `robokudo.types.annotation.Size` for that.
- Depending on the total size, also add one size classification out of
`["small", "medium", "large"]` to the `Size` annotation in the `class` field,
by using the following values
- `"small"` objects with a size/area of less than `35000`
- `"medium"` objects with a size/area of more than `35000` and less than `50000`
- `"large"` objects with a size/area of more than `50000`
- At the end of the function return `py_trees.Status.SUCCESS` to signal that
the execution was successful.
:::{admonition} Hint for roi data access
:class: dropdown hint
Access to the roi data of an annotation like this:
```python
annotation.roi.roi.width # Check the text above for other possible values.
```
:::
- **Task 3-2:** Similar to the previous tutorial, add the ObjectSizeAnnotator to the Analysis Engine.
- **Task 3-3:** Restart RoboKudo and send the query of
type **detect** again. Afterwards, scroll down to the details tree next to the output
image and check if your annotations show up for each `ObjectHypothesis`.
:::{important}
Add the `ObjectSizeAnnotator` **after** the `ClusterColorAnnotator` so that the
`ObjectHypothesis` annotations are available in the CAS.
```python
object_detection.add_children(
[
# ...
ClusterColorAnnotator(),
ObjectSizeAnnotator(),
# ...
]
)
```
:::
As an example the output in the details tree could look like this:
![](../img/03-object-size-annotation.png)
:::{admonition} The final code of `object_size.py` could look something like this:
:class: dropdown hint
```python
class ObjectSizeAnnotator(robokudo.annotators.core.BaseAnnotator):
def __init__(self, name="ObjectSizeAnnotator") -> None:
super().__init__(name=name)
self.rk_logger.debug("%s.__init__()" % self.__class__.__name__)
def update(self) -> py_trees.common.Status:
# Get all ObjectHypothesis annotations from the CAS
annotations: List[ObjectHypothesis] = self.get_cas().filter_annotations_by_type(
robokudo.types.scene.ObjectHypothesis
)
for annotation in annotations:
# Create a new Size annotation
size = robokudo.types.annotation.Size()
# Set the size value to be the size of the roi
size.value = annotation.roi.roi.width * annotation.roi.roi.height
# Classify the size
if size.value < 35000:
size.cls = "small"
elif 35000 < size.value < 50000:
size.cls = "medium"
else:
size.cls = "large"
# Add the source annotator information to the annotation (optional)
size.source = self.name
# Add the annotation to the list of annotations on the ObjectHypothesis
annotation.annotations.append(size)
return py_trees.Status.SUCCESS
```
:::