> For the complete documentation index, see [llms.txt](https://docs.eyepop.ai/developer-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.eyepop.ai/developer-documentation/platform/pop/pop-examples.md).

# Examples

Worked Pop pipelines, from a single ability to multi-stage chains

Each example below is a complete Pop. Pass one to a worker session and it runs against every [source](/developer-documentation/platform/sources-and-options.md) you submit. For the attributes these examples use, see [Components](/developer-documentation/platform/pop/pop-components.md) and [Forwarding](/developer-documentation/platform/pop/pop-forwarding.md); for the abilities they name, [Models](/developer-documentation/platform/models.md).

{% tabs %}
{% tab title="Python" %}

```python
from eyepop import EyePopSdk

with EyePopSdk.sync_worker(pop=pop) as endpoint:
    result = endpoint.upload("street.jpg").predict()
```

{% endtab %}

{% tab title="Node" %}

```typescript
import { EyePop } from '@eyepop.ai/eyepop'

const endpoint = await EyePop.workerEndpoint({ pop }).connect()
```

{% endtab %}
{% endtabs %}

### One ability

The simplest Pop runs a single ability over the whole frame.

{% tabs %}
{% tab title="Python" %}

```python
from eyepop.worker.worker_types import Pop, InferenceComponent

pop = Pop(components=[
    InferenceComponent(ability="eyepop.person:latest"),
])
```

{% endtab %}

{% tab title="Node" %}

```typescript
import { PopComponentType } from '@eyepop.ai/eyepop'

const pop = {
    components: [
        { type: PopComponentType.INFERENCE, ability: 'eyepop.person:latest' },
    ],
}
```

{% endtab %}
{% endtabs %}

### Detect, crop, then read

The canonical chain: find vehicles, crop each one, find the plate inside it, crop that, and run OCR on the result. Nothing round-trips to your code between stages.

`orientationTargetAngle` straightens the cropped plate before OCR, which is what makes angled plates readable. `topK` keeps only the best plate per vehicle.

{% tabs %}
{% tab title="Python" %}

```python
from eyepop.worker.worker_types import Pop, InferenceComponent, CropForward

pop = Pop(components=[
    InferenceComponent(
        ability="eyepop.vehicle:latest",
        categoryName="vehicles",
        confidenceThreshold=0.8,
        forward=CropForward(
            includeClasses=["car", "truck"],
            targets=[
                InferenceComponent(
                    ability="eyepop.vehicle.license-plate:latest",
                    topK=1,
                    forward=CropForward(
                        orientationTargetAngle=0.0,
                        boxPadding=0.1,
                        targets=[
                            InferenceComponent(
                                ability="eyepop.text.recognize.landscape:latest",
                                categoryName="license-plate",
                            ),
                        ],
                    ),
                ),
            ],
        ),
    ),
])
```

{% endtab %}

{% tab title="Node" %}

```typescript
import { ForwardOperatorType, PopComponentType } from '@eyepop.ai/eyepop'

const pop = {
    components: [
        {
            type: PopComponentType.INFERENCE,
            ability: 'eyepop.vehicle:latest',
            categoryName: 'vehicles',
            confidenceThreshold: 0.8,
            forward: {
                operator: {
                    type: ForwardOperatorType.CROP,
                    includeClasses: ['car', 'truck'],
                },
                targets: [
                    {
                        type: PopComponentType.INFERENCE,
                        ability: 'eyepop.vehicle.license-plate:latest',
                        topK: 1,
                        forward: {
                            operator: {
                                type: ForwardOperatorType.CROP,
                                crop: { orientationTargetAngle: 0.0, boxPadding: 0.1 },
                            },
                            targets: [
                                {
                                    type: PopComponentType.INFERENCE,
                                    ability: 'eyepop.text.recognize.landscape:latest',
                                    categoryName: 'license-plate',
                                },
                            ],
                        },
                    },
                ],
            },
        },
    ],
}
```

{% endtab %}
{% endtabs %}

### Track people across frames

Tracking hangs off the detector whose objects it should follow. Naming a re-identification ability adds appearance matching, so a person who is briefly occluded keeps their `trackId` instead of becoming a new object.

{% tabs %}
{% tab title="Python" %}

```python
from eyepop.worker.worker_types import (
    Pop, InferenceComponent, TrackingComponent, CropForward, MotionModel,
)

pop = Pop(components=[
    InferenceComponent(
        ability="eyepop.person:latest",
        categoryName="people",
        forward=CropForward(targets=[
            TrackingComponent(
                reidModel="eyepop.person.reid:latest",
                maxAgeSeconds=5.0,
                motionModel=MotionModel.CONSTANT_VELOCITY,
                simThreshold=0.6,
            ),
        ]),
    ),
])
```

{% endtab %}

{% tab title="Node" %}

```typescript
import { ForwardOperatorType, MotionModel, PopComponentType } from '@eyepop.ai/eyepop'

const pop = {
    components: [
        {
            type: PopComponentType.INFERENCE,
            ability: 'eyepop.person:latest',
            categoryName: 'people',
            forward: {
                operator: { type: ForwardOperatorType.CROP },
                targets: [
                    {
                        type: PopComponentType.TRACKING,
                        reidModel: 'eyepop.person.reid:latest',
                        maxAgeSeconds: 5.0,
                        motionModel: MotionModel.CONSTANT_VELOCITY,
                        simThreshold: 0.6,
                    },
                ],
            },
        },
    ],
}
```

{% endtab %}
{% endtabs %}

### Hide a stage the caller never asked for

Facial expression analysis needs a face detector in front of it, but the caller wants expressions attached to people — not a separate list of face boxes. Marking the face detector `hidden` keeps its output out of the response while still feeding the stage below it, and because it produces object detections, the expression results attach to the enclosing person.

{% tabs %}
{% tab title="Python" %}

```python
from eyepop.worker.worker_types import Pop, InferenceComponent, CropForward

pop = Pop(components=[
    InferenceComponent(
        ability="eyepop.person:latest",
        categoryName="people",
        forward=CropForward(targets=[
            InferenceComponent(
                ability="eyepop.person.face.short-range:latest",
                hidden=True,
                forward=CropForward(targets=[
                    InferenceComponent(ability="eyepop.expression:latest"),
                ]),
            ),
        ]),
    ),
])
```

{% endtab %}

{% tab title="Node" %}

```typescript
import { ForwardOperatorType, PopComponentType } from '@eyepop.ai/eyepop'

const pop = {
    components: [
        {
            type: PopComponentType.INFERENCE,
            ability: 'eyepop.person:latest',
            categoryName: 'people',
            forward: {
                operator: { type: ForwardOperatorType.CROP },
                targets: [
                    {
                        type: PopComponentType.INFERENCE,
                        ability: 'eyepop.person.face.short-range:latest',
                        hidden: true,
                        forward: {
                            operator: { type: ForwardOperatorType.CROP },
                            targets: [
                                {
                                    type: PopComponentType.INFERENCE,
                                    ability: 'eyepop.expression:latest',
                                },
                            ],
                        },
                    },
                ],
            },
        },
    ],
}
```

{% endtab %}
{% endtabs %}

### Segment, then trace an outline

A segmentation mask is a per-pixel result. A contour finder turns it into a polygon your application can draw or measure. The finder takes a `full` forward, because it works on the mask the segmenter produced rather than on a new crop.

{% tabs %}
{% tab title="Python" %}

```python
from eyepop.worker.worker_types import (
    Pop, InferenceComponent, ContourFinderComponent, ContourType,
    CropForward, FullForward,
)

pop = Pop(components=[
    InferenceComponent(
        ability="eyepop.person:latest",
        forward=CropForward(targets=[
            InferenceComponent(
                ability="eyepop.person.segment:latest",
                forward=FullForward(targets=[
                    ContourFinderComponent(contourType=ContourType.POLYGON),
                ]),
            ),
        ]),
    ),
])
```

{% endtab %}

{% tab title="Node" %}

```typescript
import { ContourType, ForwardOperatorType, PopComponentType } from '@eyepop.ai/eyepop'

const pop = {
    components: [
        {
            type: PopComponentType.INFERENCE,
            ability: 'eyepop.person:latest',
            forward: {
                operator: { type: ForwardOperatorType.CROP },
                targets: [
                    {
                        type: PopComponentType.INFERENCE,
                        ability: 'eyepop.person.segment:latest',
                        forward: {
                            operator: { type: ForwardOperatorType.FULL },
                            targets: [
                                {
                                    type: PopComponentType.CONTOUR_FINDER,
                                    contourType: ContourType.POLYGON,
                                },
                            ],
                        },
                    },
                ],
            },
        },
    ],
}
```

{% endtab %}
{% endtabs %}

### Count objects inside one mask

When a segmenter returns a single mask covering many touching objects, a component finder splits it into separate sub-objects. `erode` shrinks the mask first, breaking the thin bridges between objects that are really separate — here by a kernel 20% of the mask's own size. See [How `erode` and `dilate` reshape the mask](/developer-documentation/platform/pop/pop-components.md#how-erode-and-dilate-reshape-the-mask) for combining it with `dilate`.

{% tabs %}
{% tab title="Python" %}

```python
from eyepop.worker.worker_types import (
    Pop, InferenceComponent, ComponentFinderComponent, FullForward,
)

pop = Pop(components=[
    InferenceComponent(
        abilityUuid="066ae8f1fc0174138000cb8bcfaaaaaa",
        categoryName="colonies",
        forward=FullForward(targets=[
            ComponentFinderComponent(
                componentClassLabel="colony",
                erode=0.2,
                keepSource=False,
            ),
        ]),
    ),
])
```

{% endtab %}

{% tab title="Node" %}

```typescript
import { ForwardOperatorType, PopComponentType } from '@eyepop.ai/eyepop'

const pop = {
    components: [
        {
            type: PopComponentType.INFERENCE,
            abilityUuid: '066ae8f1fc0174138000cb8bcfaaaaaa',
            categoryName: 'colonies',
            forward: {
                operator: { type: ForwardOperatorType.FULL },
                targets: [
                    {
                        type: PopComponentType.COMPONENT_FINDER,
                        componentClassLabel: 'colony',
                        erode: 0.2,
                        keepSource: false,
                    },
                ],
            },
        },
    ],
}
```

{% endtab %}
{% endtabs %}

### Find anything, then ask about it

An open-vocabulary ability takes its target through `params` rather than a fixed label set, and a vision-language ability downstream answers a question about each crop.

{% tabs %}
{% tab title="Python" %}

```python
from eyepop.worker.worker_types import Pop, InferenceComponent, CropForward

pop = Pop(components=[
    InferenceComponent(
        ability="eyepop.localize-objects:latest",
        categoryName="objects",
        params={"prompts": [{"prompt": "forklift"}]},
        forward=CropForward(targets=[
            InferenceComponent(
                ability="eyepop.image-contents:latest",
                params={"prompts": [{"prompt": "Is the forklift carrying a load?"}]},
            ),
        ]),
    ),
])
```

{% endtab %}

{% tab title="Node" %}

```typescript
import { ForwardOperatorType, PopComponentType } from '@eyepop.ai/eyepop'

const pop = {
    components: [
        {
            type: PopComponentType.INFERENCE,
            ability: 'eyepop.localize-objects:latest',
            categoryName: 'objects',
            params: { prompts: [{ prompt: 'forklift' }] },
            forward: {
                operator: { type: ForwardOperatorType.CROP },
                targets: [
                    {
                        type: PopComponentType.INFERENCE,
                        ability: 'eyepop.image-contents:latest',
                        params: {
                            prompts: [{ prompt: 'Is the forklift carrying a load?' }],
                        },
                    },
                ],
            },
        },
    ],
}
```

{% endtab %}
{% endtabs %}

### Run two analyses side by side

Top-level components run in parallel on the full frame. Use this when a Pop needs two unrelated answers about the same media rather than a chain.

{% tabs %}
{% tab title="Python" %}

```python
from eyepop.worker.worker_types import Pop, InferenceComponent, CropForward

pop = Pop(components=[
    InferenceComponent(
        ability="eyepop.person:latest",
        categoryName="people",
        forward=CropForward(targets=[
            InferenceComponent(ability="eyepop.person.2d-body-points:latest"),
        ]),
    ),
    InferenceComponent(
        ability="eyepop.text:latest",
        categoryName="signage",
    ),
])
```

{% endtab %}

{% tab title="Node" %}

```typescript
import { ForwardOperatorType, PopComponentType } from '@eyepop.ai/eyepop'

const pop = {
    components: [
        {
            type: PopComponentType.INFERENCE,
            ability: 'eyepop.person:latest',
            categoryName: 'people',
            forward: {
                operator: { type: ForwardOperatorType.CROP },
                targets: [
                    {
                        type: PopComponentType.INFERENCE,
                        ability: 'eyepop.person.2d-body-points:latest',
                    },
                ],
            },
        },
        {
            type: PopComponentType.INFERENCE,
            ability: 'eyepop.text:latest',
            categoryName: 'signage',
        },
    ],
}
```

{% endtab %}
{% endtabs %}

### Always return something

`crop_with_full_fallback` runs the downstream stage on each detection when the parent found objects, and on the whole frame when it found none — so media that defeats the first stage still produces an answer.

{% tabs %}
{% tab title="Python" %}

```python
from eyepop.worker.worker_types import Pop, InferenceComponent, CropForward

pop = Pop(components=[
    InferenceComponent(
        ability="eyepop.person:latest",
        forward=CropForward(
            is_full_fallback=True,
            targets=[
                InferenceComponent(
                    ability="eyepop.image-contents:latest",
                    params={"prompts": [{"prompt": "Describe the scene."}]},
                ),
            ],
        ),
    ),
])
```

{% endtab %}

{% tab title="Node" %}

```typescript
import { ForwardOperatorType, PopComponentType } from '@eyepop.ai/eyepop'

const pop = {
    components: [
        {
            type: PopComponentType.INFERENCE,
            ability: 'eyepop.person:latest',
            forward: {
                operator: { type: ForwardOperatorType.CROP_WITH_FULL_FALLBACK },
                targets: [
                    {
                        type: PopComponentType.INFERENCE,
                        ability: 'eyepop.image-contents:latest',
                        params: { prompts: [{ prompt: 'Describe the scene.' }] },
                    },
                ],
            },
        },
    ],
}
```

{% endtab %}
{% endtabs %}

### Next steps

* [Components](/developer-documentation/platform/pop/pop-components.md) — every component type and attribute
* [Forwarding](/developer-documentation/platform/pop/pop-forwarding.md) — how components chain
* [Sources and Options](/developer-documentation/platform/sources-and-options.md) — what you can submit to a Pop, and the settings that shape processing
* [Composable Pops for Python](/developer-documentation/sdks/python/composable-pops.md) and [for Node](/developer-documentation/sdks/node/composable-pops.md) — constructing these Pops in each language
