# Zone Counter

{% hint style="info" %}
This API Reference is based on DeGirum Tools version 1.2.0.
{% endhint %}

## Zone Counter Analyzer Module Overview <a href="#zone-counter-analyzer-module-overview" id="zone-counter-analyzer-module-overview"></a>

This module provides a zone counting analyzer (`ZoneCounter`) with support for both traditional list-based zones and named zones (dictionary-based). It features clean separation of concerns and a user-friendly API.

Key Features

* **Named Zones**: Use descriptive names instead of numeric indices
* **Clean Architecture**: Separation of geometry (spatial) and state (temporal) logic
* **Zone Events**: Track entry, exit, occupied, and empty events per zone
* **All ZoneCounter Features**: Supports all triggering methods, tracking, timeouts, etc.
* **Better Maintainability**: Smaller, focused components that are easier to test and extend

Architecture

The implementation separates concerns into focused components:

* **\_ZoneGeometry**: Handles spatial logic - polygon shapes, masks, and triggering
* **\_ZoneState**: Handles temporal logic - object tracking, timeouts, occupancy state
* **ZoneCounter**: Orchestrates the components and generates events

Typical Usage

{% code overflow="wrap" %}

```python
from degirum_tools.analyzers import ZoneCounter

counter = ZoneCounter(
    zones={
        "entrance": entrance_polygon,
        "parking_spot_1": spot1_polygon,
        "exit_area": exit_polygon,
    },
    use_tracking=True,
    timeout_frames=3,  # Hysteresis threshold
    enable_zone_events=True,
)

model.attach_analyzers(counter)
result = model(frame)

# Access named zone counts
print(result.zone_counts)  # {"entrance": {...}, "parking_spot_1": {...}, ...}

# Access zone events
for event in result.zone_events:
    print(f"{event['event_type']} in {event['zone_id']} at {event['timestamp']}")
```

{% endcode %}

Zone Events

When `enable_zone_events=True`, the analyzer generates four types of events:

* **zone\_entry**: Track becomes established in zone (after timeout\_frames + 1 consecutive in-zone detections)
* **zone\_exit**: Track exits zone (hysteresis counter reaches 0)
* **zone\_occupied**: Zone transitions from empty to occupied (when first track becomes established)
* **zone\_empty**: Zone transitions from occupied to empty (after all tracks exit)

Event structure:

{% code overflow="wrap" %}

```yaml
{
    "event_type": str,              # Event type
    "zone_index": int,              # Numeric zone index (0-based)
    "zone_id": str,                 # Zone name/ID
    "timestamp": float,             # Unix timestamp
    "track_id": int | None,         # Track ID (entry/exit) or None (occupied/empty)
    "object_label": str | None,     # Object class (entry/exit) or None (occupied/empty)
    "dwell_time": float | None,     # Duration: in zone (exit), empty/occupied (transitions), None (entry)
    "frame_index": int | None,      # Frame index (if available)
}
```

{% endcode %}

Integration Notes

* Requires detection results with bounding boxes
* Zone events require `use_tracking=True`
* Compatible with ObjectTracker analyzer upstream
* Results structure matches ZoneCounter for easy migration

Configuration Options

* `zones`: Dictionary mapping zone names to polygons
* `class_list`: Optional list of class labels to count
* `triggering_position`: Anchor point or IoPA-based triggering
* `timeout_frames`: Hysteresis threshold for symmetric entry/exit smoothing
* `enable_zone_events`: Generate zone-level events
* `show_overlay`: Visual annotations
* `per_class_display`: Show per-class counts

Hysteresis Smoothing

The analyzer uses symmetric hysteresis for stable zone presence detection:

* **timeout\_frames**: Controls the hysteresis threshold. Objects need `timeout_frames + 1` consecutive in-zone detections to become "established" (counted). The internal counter increments when in zone, decrements when outside or missing, and is clamped to \[0, timeout\_frames + 1]. Objects exit when the counter reaches 0.
* **Symmetric behavior**: The same threshold applies for both entry and exit, providing smooth transitions without flickering. Brief departures don't reset the counter, and brief appearances don't immediately establish presence.
* **Default**: 0 (immediate entry/exit on first detection). Requires `use_tracking=True` if > 0.

## Classes <a href="#classes" id="classes"></a>

## ZoneCounter <a href="#zonecounter" id="zonecounter"></a>

`ZoneCounter`

Bases: `ResultAnalyzerBase`

Analyzer that counts objects inside user-defined polygonal zones.

This analyzer integrates with PySDK inference results to determine whether detected or tracked objects lie within user-defined polygon zones. It supports per-class counting, object tracking with symmetric hysteresis (timeout\_frames) for entry/exit smoothing, zone events, and interactive editing.

Backward compatible with old ZoneCounter API (list of zones) while supporting new dict-based named zones interface.

Attributes:

| Name                 | Type | Description                                   |
| -------------------- | ---- | --------------------------------------------- |
| `key_in_zone`        |      | Key for zone presence flags in result objects |
| `key_frames_in_zone` |      | Key for frame counts in result objects        |
| `key_time_in_zone`   |      | Key for time-in-zone in result objects        |
| `key_zone_events`    |      | Key for zone events list in result objects    |

### ZoneCounter Methods <a href="#zonecounter-methods" id="zonecounter-methods"></a>

#### \_\_init\_\_(zones=None, ...) <a href="#init" id="init"></a>

`__init__(zones=None, *, count_polygons=None, class_list=None, per_class_display=False, triggering_position=AnchorPoint.BOTTOM_CENTER, bounding_box_scale=1.0, iopa_threshold=0.0, use_tracking=False, timeout_frames=0, enable_zone_events=False, window_name=None, show_overlay=True, show_inzone_counters=None, annotation_color=None, annotation_line_width=None)`

Initialize ZoneCounter.

Parameters:

| Name                    | Type                                                      | Description                                                                                                                                                                                                                                               | Default         |
| ----------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
| `zones`                 | `Union[Dict[str, ndarray], List[ndarray], ndarray, None]` | Dict mapping zone names to polygons (new style) OR list of polygons (old style backward compat)                                                                                                                                                           | `None`          |
| `class_list`            | `Optional[List[str]]`                                     | List of class labels to count (None = all classes)                                                                                                                                                                                                        | `None`          |
| `per_class_display`     | `bool`                                                    | Show per-class counts separately                                                                                                                                                                                                                          | `False`         |
| `triggering_position`   | `Optional[Union[List[AnchorPoint], AnchorPoint]]`         | Anchor point(s) or None for IoPA                                                                                                                                                                                                                          | `BOTTOM_CENTER` |
| `bounding_box_scale`    | `float`                                                   | Scale factor for bboxes (greater than 0, up to 1)                                                                                                                                                                                                         | `1.0`           |
| `iopa_threshold`        | `Union[float, List[float]]`                               | IoPA threshold (single value or list per zone)                                                                                                                                                                                                            | `0.0`           |
| `use_tracking`          | `bool`                                                    | Enable object tracking                                                                                                                                                                                                                                    | `False`         |
| `timeout_frames`        | `int`                                                     | Hysteresis threshold for zone presence (requires tracking if > 0). Objects need timeout\_frames + 1 consecutive in-zone detections to become established (counted), and the counter decrements when outside/missing. Objects exit when counter reaches 0. | `0`             |
| `enable_zone_events`    | `bool`                                                    | Generate zone-level events (requires tracking)                                                                                                                                                                                                            | `False`         |
| `window_name`           | `Optional[str]`                                           | OpenCV window name for interactive polygon editing (None = disabled)                                                                                                                                                                                      | `None`          |
| `show_overlay`          | `bool`                                                    | Draw zone overlays                                                                                                                                                                                                                                        | `True`          |
| `show_inzone_counters`  | `Optional[str]`                                           | Show presence counters ('time', 'frames', 'all', None)                                                                                                                                                                                                    | `None`          |
| `annotation_color`      | `Optional[tuple]`                                         | RGB color for overlays (None = auto)                                                                                                                                                                                                                      | `None`          |
| `annotation_line_width` | `Optional[int]`                                           | Line width for overlays (None = auto)                                                                                                                                                                                                                     | `None`          |

#### analyze(result) <a href="#analyze" id="analyze"></a>

`analyze(result)`

Analyze inference result and update zone counts and events.

Parameters:

| Name     | Type | Description                 | Default    |
| -------- | ---- | --------------------------- | ---------- |
| `result` |      | Inference result to analyze | *required* |

#### annotate(result, ...) <a href="#annotate" id="annotate"></a>

`annotate(result, image)`

Draw zone overlays and counts on the image.

Parameters:

| Name     | Type      | Description                     | Default    |
| -------- | --------- | ------------------------------- | ---------- |
| `result` |           | Inference result with zone data | *required* |
| `image`  | `ndarray` | Image to annotate               | *required* |

Returns:

| Type      | Description     |
| --------- | --------------- |
| `ndarray` | Annotated image |

#### window\_attach(win\_name) <a href="#window_attach" id="window_attach"></a>

`window_attach(win_name)`

Attach an OpenCV window for interactive polygon editing.

Parameters:

| Name       | Type  | Description                            | Default    |
| ---------- | ----- | -------------------------------------- | ---------- |
| `win_name` | `str` | Name of the OpenCV window to attach to | *required* |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.degirum.com/degirum-tools/analyzers/zone_count.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
