Savant Explained: Pipeline Element Groups

Savant Explained: Pipeline Element Groups

Savant 0.2.5 introduces a new pipeline syntax structure — Element Group (EG). The purpose of an EG is to specify a group of elements conditionally loaded during the pipeline initialization based on a specified expression. E.g., imagine that your pipeline can use YOLOV8 or PeopleNet for people detection, and you want to specify the environment variable DETECTOR, determining which model will be loaded.

Consider visiting our GitHub, and don’t forget to subscribe to our Twitter to receive updates on Savant. Also, we have Discord, where we help users to onboard.

In previous versions of Savant, a developer had two strategies to deal with that:

  1. Load both models and implement conditional processing in runtime.
  2. Have a separate pipeline for every flavor.

Both approaches have cons and thus influenced the creation of the new syntax. The main disadvantage of the first one is that you need to maintain multiple pipeline files with duplications which is error-prone and inefficient. The second utilizes more GPU RAM and CPU power than required, thus inefficient.

Element Group Syntax

The syntax for element groups efficiently solves the above-mentioned problems. You can take a look at the syntax in action in the traffic_meter sample on GitHub:

pipeline:

  elements:
  # regular pyfunc unit definition
  - element: pyfunc
    module: samples.traffic_meter.line_crossing
    class_name: ConditionalDetectorSkip
    kwargs:
      config_path: ${oc.env:PROJECT_PATH}/samples/traffic_meter/line_config.yml

  # variant group: peoplenet detector
  # groups are included in the pipeline.elements alongside regular units or other groups
  - group:
    # enabled if env var DETECTOR==peoplenet
    init_condition:
      expr: ${oc.env:DETECTOR}
      value: peoplenet
    elements:
      # below follows detector unit definition as usual
      - element: nvinfer@detector
        name: peoplenet
        # ... hidden for clarity

  # variant group: yolov8m detector
  # groups are included in the pipeline.elements alongside regular units or other groups
  - group:
    # enabled if env var DETECTOR==yolov8m
    init_condition:
      expr: ${oc.env:DETECTOR}
      value: yolov8m
    elements:
      # below follows detector unit definition as usual
      - element: nvinfer@detector
        name: yolov8m
        # ... hidden for clarity

  # regular nvtracker definition
  - element: nvtracker
    properties:
      ll-lib-file: /opt/nvidia/deepstream/deepstream/lib/libnvds_nvmultiobjecttracker.so
      ll-config-file: ${oc.env:PROJECT_PATH}/samples/traffic_meter/config_tracker_NvDCF_perf.yml
      tracker-width: 960
      tracker-height: 544
      display-tracking-id: 0

  # regular pyfunc unit definition
  - element: pyfunc
    module: samples.traffic_meter.line_crossing
    class_name: LineCrossing
    kwargs:
      config_path: ${oc.env:PROJECT_PATH}/samples/traffic_meter/line_config.yml
      stale_track_del_period: 5000
      target_obj_label: ${parameters.detected_object.label}
      send_stats: True

The first element and the last two in the pipeline are common. In the middle, you may find two groups: the first one includes a single element and initializes when the DETECTOR environment variable is set to peoplenet, the second is when it is set to yolov8m.

The init_condition element of a group is checked only upon the pipeline boot and thus defines a static configuration.

Where To Use

There are different cases where to use element groups:

  1. Multiple pipelines defined in a single manifest: you can switch between different pipelines based on the environment variables.
  2. Disabling certain parts of pipelines based on camera placement, like disabling car-plate recognition if camera placement doesn’t allow doing that.
  3. Implementing various flavors that activate different models based on camera placement conditions, like indoor, outdoor, higher, lower, bird’s-eye, fisheye, etc.