# Holoscan utility scripts

This folder includes the following scripts:

- [`aja_build.sh`](#aja_build)
- [`check_copyright.py`](#check_copyrightpy)
- [`convert_gxf_entities_to_images.py`](#convert_gxf_entities_to_imagespy)
- [`convert_gxf_entities_to_video.py`](#convert_gxf_entities_to_videopy)
- [`convert_video_to_gxf_entities.py`](#convert_video_to_gxf_entitiespy)
- [`download_ngc_data`](#download_ngc_data)
- [`generate_extension_uuids.py`](#generate_extension_uuidspy)
- [`get_cmake_cuda_archs.py`](#get_cmake_cuda_archspy)
- [`graph_surgeon.py`](#graph_surgeonpy)
- [`gxf_entity_codec.py`](#gxf_entity_codecpy)
- [`list_ld_dependencies.sh`](#list_ld_dependenciessh)
- [`scheduler_overhead_benchmark.py`](#scheduler_overhead_benchmarkpy)
- [`video_validation.py`](#video_validationpy)

> Note: these will be included in the SDK installation at `/opt/nvidia/holoscan/bin`

____

## aja_build.sh

Builds the AJA SDK with the proper flags and optionally loads the driver.

## convert_gxf_entities_to_images.py

Takes in the encoded GXF tensor files generated by the `video_stream_recorder` and export raw frames in .png files.

### Prerequisites

```sh
pip install numpy~=1.21 pillow
```

This script depends on `gxf_entity_codec.py` which is located in the same folder.

### Usage

The command below will read the `racerx.gxf_entities` and `racerx.gxf_index` files from the existing racerx dataset under `data/racerx` (which is a 854x480 video with framerate 25fps and 3 channels) and convert them to PNG files.

```sh
python3 scripts/convert_gxf_entities_to_images.py --directory data/racerx --basename racerx
```

Use `--outputdir` to specify the directory where the files will be created.
Use `--outputname` to specify a different output name than the default `tensor` prefix.

____

## check_copyright.py

This script adapts an NVIDIA copyright header checker from the RAFT project. It can be used to automatically update the copyright year and can also optionally run on CI and only check the files that have been modified.

The script `check_copyright.py` file checks for NVIDIA copyright headers in files matching any of the lists of regular expressions in the `files_to_check` list.
There are also regular expressions for the copyright string that is being checked. The `CheckSimple` one is for when there is only a single year and `CheckDouble` is for when the header uses a year range. The `gitutils.py` defines some utilities for interacting with Git that enables the `--git-modified-only` capabilities of the copyright checker.

### Usage

You can call the script from the repository, specifying to check all matching files within the include and src subfolders.

```bash
python scripts/check_copyright.py include src
```

Alternatively, it can be used to check only modified files in a CI script using ([example from RAFT here](https://github.com/rapidsai/raft/blob/4f22957db9a38d81d0cd6ad864507c47d243ad56/ci/checks/style.sh#L29)):

```bash
python ci/checks/copyright.py --git-modified-only
```

There is also a `--update-current-year` option to allow update of the copyright year in all files and ``-exclude`` option to allow user-specified regular expressions for exclusion from the check.

____

## convert_gxf_entities_to_video.py

Takes in the encoded GXF tensor files generated by the `video_stream_recorder` (`.gxf_entities` and `.gxf_index`) and emit the raw video feed.

### Prerequisites

```sh
pip install numpy~=1.21
```

This script depends on `gxf_entity_codec.py` which is located in the same folder.

### Usage

The command below will read the `racerx.gxf_entities` and `racerx.gxf_index` files from the existing racerx dataset under `data/racerx` (which is a 854x480 video with framerate 25fps and 3 channels) and use `ffmpeg` to encode the emitted video stream to a video file, `converted_video.mp4`:

```sh
python3 scripts/convert_gxf_entities_to_video.py --directory data/racerx --basename racerx | ffmpeg -f rawvideo -pix_fmt rgb24 -s 854x480 -r 25 -i - -f mp4 -vcodec libx264 -pix_fmt yuv420p -r 25 -y racerx-medium.mp4
```

____

## convert_video_to_gxf_entities.py

Takes in a raw video feed and emits encoded GXF tensor files entities for playback with the `video_stream_replayer` operator.  The tensors will be saved with metadata indicating that the data should be copied to the GPU on read.

### Prerequisites

```sh
pip install numpy~=1.21
```

This script depends on `gxf_entity_codec.py` which is located in the same folder.

### Usage

Example usage converting the output of a tool like `ffmpeg` to encoded GXF tensors:

```sh
ffmpeg -i video_1920x1080.avi -pix_fmt rgb24 -f rawvideo pipe:1 | python3 scripts/convert_video_to_gxf_entities.py --width 1920 --height 1080 --channels 3 --framerate 30
```

Above command will create two files: `tensor.gxf_entities` and `tensor.gxf_index` from the `video_1920x1080.avi` video file.
Use `--directory` to specify the directory where the files will be created.
Use `--basename` to specify a different output name than the default `tensor`

When working with long videos, it can be desired to limit the disc space usage. In this case, `ffmpeg` can be used to convert only a portion of the video.
Use `-ss` option to set the start time.
Use `-t` option to set the duration or the `-to` option to set the end time.

Examples:

```sh
ffmpeg -ss 00:00:05 -i video_1920x1080.avi -t 00:00:05 -pix_fmt rgb24 -f rawvideo pipe:1 | python3 scripts/convert_video_to_gxf_entities.py --width 1920 --height 1080 --channels 3 --framerate 30
```

```sh
ffmpeg -ss 00:00:05 -i video_1920x1080.avi -to 00:00:10 -pix_fmt rgb24 -f rawvideo pipe:1 | python3 scripts/convert_video_to_gxf_entities.py --width 1920 --height 1080 --channels 3 --framerate 30
```

Above commands will parse the video starting at 00:00:05 and ending at 00:00:10.

____

## ctest_time_comparison.py

This script compares time from different tests to make sure they match expected timing comparison.

### Usage

`python3 ./scripts/ctest_time_comparison.py <filename> "TEST1" "LESS" "TEST2"`
____

## download_ngc_data

Download and unzip datasets from NGC. This can optionally run a script to convert video files to GXF tensor files compatible with the `video_stream_replayer` operator.

## Prerequisites

- `wget` or `curl` to download datasets using curl. Note that as of Jan 2025, download full zip archives from NGC is not supported.
- [NGC CLI](https://ngc.nvidia.com/setup/installers/cli) to download datasets using the `<org>/[team]/<name>:<version>` format (useful if the above fails or for private registries)

## Usage

The example below will download and unzip the RacerX video from NGC:

```sh
./scripts/download_ngc_data --url nvidia/clara-holoscan/holoscan_racerx_video:20231009
```

Use `--help` for more options such as output dir/name or conversion to GXF tensor files.

____

## generate_extension_uuids.py

Provides a set of UUIDs to be used by `GXF_EXT_FACTORY_SET_INFO` and `GXF_EXT_FACTORY_ADD` to declare a new GXF extension.

``` sh
python3 scripts/generate_extension_uuids.py
```

____

## get_cmake_cuda_archs.py

Determines and formats a semicolon-separated list of CUDA architectures suitable for CMake's `CMAKE_CUDA_ARCHITECTURES` variable. It uses `nvcc -code-ls` to find supported architectures and then filters them. By default, it excludes architectures primarily for specific features (e.g., `sm_89` for FP8) or platform-specific (e.g., `sm_XXa`, `sm_XXf`) unless explicitly requested by the user with a specific flag. The filtering also considers minimum architecture requirements and platform compatibility (e.g., removing iGPU architectures on x86_64). The final list includes each selected architecture suffixed with `-real` (for SASS generation). It also adds one `-virtual` architecture entry based on the highest non-specific (no 'a' or 'f' letter suffix) architecture chosen; if no such base architecture is found, no PTX target is added.

### Usage

```sh
python3 scripts/get_cmake_cuda_archs.py <requested_archs> [options]
```

**Positional Arguments:**

- `requested_archs`: Defines the target architectures. Can be:
  - `all`: Use all nvcc supported, platform-compatible, and non-feature-specific architectures.
  - `all-major`: Use only major versions from the `all` selection (e.g., 70, 80, 90).
  - `native`: Passes the string "native" directly through (for CMake to detect).
  - A comma or space-separated list of specific architecture numbers (e.g., `'75 86 90a'`).

**Options:**

- `--nvcc-path <path>`, `-n <path>`: Path to the `nvcc` executable. Defaults to searching PATH, then `/usr/local/cuda/bin/nvcc`.
- `--min-arch <num>`, `-m <num>`: Minimum major CUDA architecture to consider (e.g., `70` for Volta and newer). Set to `0` or omit to disable.
- `--allow-specific-archs`, `-f`: Allow feature-specific architectures (e.g., `sm_89`, `sm_XXa/f`) when they are explicitly listed in `requested_archs`. This flag does not affect the `all` or `all-major` selections, which always exclude these.
- `--verbose`, `-v`: Enable verbose debug logging to stderr.

### Examples

Get all supported architectures for the current platform, with a minimum of sm_70:

```sh
python3 scripts/get_cmake_cuda_archs.py all --min-arch 70
```

Get specific architectures:

```sh
python3 scripts/get_cmake_cuda_archs.py "75 86 90a"
```

____

## graph_surgeon.py

When converting a model from PyTorch to ONNX, it is likely that the input of the model is in the form NCHW (batch, channels, height, width), and needs to be converted to NHWC (batch, height, width, channels). This script performs the conversion and generates a modified model.
Note that this script modifies the names of the output by appending `_old` to the input and output.

### Usage

```bash
python3 scripts/graph_surgeon.py input_model.onnx output_model.onnx
```

____

## gxf_entity_codec.py

Utility classes used by `convert_gxf_entities_to_images.py`, `convert_gxf_entities_to_video.py` and `convert_video_to_gxf_entities.py`.

____

## list_ld_dependencies.sh

This script is used to list the shared library dependencies of the Holoscan SDK and its components. It does this by iterating through a set of directories managed by the SDK and running the `ldd` command on each shared library (`.so` file) found within. It then filters out system libraries and libraries with known paths, and prints out the remaining dependencies.

The script also maintains a map of libraries to their dependencies, as well as a map of dependencies to the libraries that depend on them. This allows the script to print out a list of dependencies for each library, as well as a list of libraries that depend on each dependency.

The script can be run with no arguments to list the dependencies of all libraries in the directories managed by the SDK, or it can be run with a specific library path to list only the dependencies of that library.

### Prerequisites

- The script must be run on a Linux system with the `ldd` command available.

### Usage

baremetal:

  ```sh
  ./scripts/list_ld_dependencies.sh
  ```

container:

  ```sh
  # build container
  ./run launch . --run-cmd ./scripts/list_ld_dependencies.sh -s

  # dev container
  docker run --rm -it \
    --runtime=nvidia \
    --entrypoint=bash \
    -v $(pwd)/scripts/list_ld_dependencies.sh:/list.sh \
    nvcr.io/nvidia/clara-holoscan/holoscan:v3.5.0-dgpu \
    /list.sh
  ```

____

## scheduler_overhead_benchmark.py

Measures scheduling and message-passing overhead for Holoscan operators. This script helps developers understand the performance characteristics of different scheduler configurations and operator granularities.

### Usage

```sh
HOLOSCAN_LOG_LEVEL=ERROR python3 scripts/scheduler_overhead_benchmark.py
```

The script runs benchmarks comparing:

- Greedy vs. Event-Based schedulers
- Operator execution with and without message passing
- Different worker thread configurations

Use `--help` for options such as iteration count and worker thread settings.

Results help determine appropriate operator granularity for your application. See the [Performance Considerations](https://docs.nvidia.com/holoscan/sdk-user-guide/performance_considerations.html) section in the documentation for interpretation guidance.

____

## video_validation.py

This script converts GXF tensor files to frame images to compare each frame with a set of baselines. The difference between them is computed using SSD (Sum of Square difference) for each pixel and an average is reported for a frame.

### Prerequisites

```sh
pip install numpy~=1.21 pillow
```

This script depends on `convert_gxf_entity_to_images.py` which is located in the same folder.

### Usage

See `python3 ./scripts/video_validation.py --help`
