Jump to content

Examples - Sample Pipelines

From RidgeRun Developer Wiki

Follow us on: YouTube Twitter LinkedIn Email Share this page

Share This Page

🚧 Documentation under development

The PVA ISP for NVIDIA Jetson guide is currently under active development. Some sections may be incomplete or change without notice.

Questions? Contact RidgeRun or email to support@ridgerun.com.

Preferred Partner Logo 3 Partner Program Banner




Sample Pipelines

Unlike Sample Applications, which focus on how to use the API, this page focuses on how image processing stages can be combined to satisfy different application requirements.

PVA ISP allows developers to build custom processing graphs by selecting only the algorithms required for a particular use case.

This flexibility enables applications to balance:

  • Image quality
  • Processing latency
  • Power consumption
  • Memory usage
  • AI inference requirements

Not all applications require a complete ISP pipeline.

While a full image processing flow may provide the highest image quality, every additional processing stage introduces computational cost and contributes to the overall pipeline latency.

For example:

  • A surveillance camera may benefit from a complete ISP pipeline
  • An AI inference application may only require RGB output
  • A robotics system may prioritize low latency over image appearance
  • An image analysis application may only require histogram statistics

PVA ISP allows developers to build pipelines tailored to the needs of their application.

Library Pipelines

When using the PVA ISP C++ library, developers can construct custom processing graphs by combining individual ISP stages.

The following examples illustrate common pipeline configurations.

Complete ISP Pipeline

The complete ISP pipeline enables all currently supported image processing stages.

Figure. ISP processing pipeline.

Recommended for:

  • Video recording
  • Surveillance systems
  • Multimedia applications
  • Visualization pipelines

Advantages:

  • Highest image quality
  • Full image enhancement pipeline
  • Video-ready output

Considerations:

  • Largest processing graph
  • Highest processing cost
  • Highest latency among supported configurations

This pipeline is already implemented in:

That file can be used directly as the reference implementation for a complete Bayer-to-NV12 flow.

Low-Latency Pipeline

Some applications prioritize response time over image appearance.

In these situations, image enhancement stages can be removed to reduce processing latency.

Figure. ISP processing pipeline.

Recommended for:

  • Robotics
  • Autonomous systems
  • Real-time control applications
  • Low-latency perception systems

Advantages:

  • Reduced latency
  • Smaller processing graph
  • Lower memory usage

Considerations:

  • Reduced image enhancement
  • Output may be less visually appealing

Complete Example

The following file can replace examples/isp_minimal.cpp directly.

Find the file here


/*
 * Copyright (C) 2025-2026 RidgeRun, LLC (http://www.ridgerun.com)
 * All Rights Reserved.
 *
 * The contents of this software are proprietary and confidential to RidgeRun,
 * LLC.  No part of this program may be photocopied, reproduced or translated
 * into another programming language without prior written consent of
 * RidgeRun, LLC.  The user is free to modify the source code after obtaining
 * a software license from RidgeRun.  All source code changes must be provided
 * back to RidgeRun without any encumbrance.
 */

/**
 * @file isp_minimal.cpp
 * @brief Minimal RAW Bayer to RGB16 low-latency ISP example.
 */

#include <getopt.h>

#include <chrono>
#include <cstdio>
#include <cstring>
#include <filesystem>  // NOLINT
#include <iostream>
#include <string>
#include <vector>

#include "core/pipeline.h"
#include "core/stage.h"
#include "examples/isp_example_common.h"
#include "examples/isp_params/isp_params.h"

namespace {

struct AppOptions {
  std::string input_path;
  std::string output_path;
  std::string yaml_path;
  std::string graph_path;
  uint32_t image_width = 0;
  uint32_t image_height = 0;
};

void PrintUsage() {
  std::cerr
      << "Usage: ./isp_minimal --image <path> --resolution <width>x<height> "
         "--parameters <yaml> [--output <path>] [--dump-graph <path>]\n";
}

bool ParseArgs(int argc, char* argv[], AppOptions* options) {
  if (!options) return false;

  static option long_options[] = {
      {"image", required_argument, nullptr, 'i'},
      {"resolution", required_argument, nullptr, 'r'},
      {"parameters", required_argument, nullptr, 'p'},
      {"output", required_argument, nullptr, 'o'},
      {"dump-graph", required_argument, nullptr, 'g'},
      {nullptr, 0, nullptr, 0}};

  while (true) {
    const int option =
        getopt_long(argc, argv, "i:r:p:o:g:", long_options, nullptr);
    if (option == -1) break;

    switch (option) {
      case 'i':
        options->input_path = optarg;
        if (!std::filesystem::exists(options->input_path)) {
          std::cerr << "Input image " << options->input_path << " not found\n";
          return false;
        }
        break;
      case 'r':
        if (sscanf(optarg, "%ux%u", &options->image_width,
                   &options->image_height) != 2) {
          std::cerr << "Invalid resolution format\n";
          return false;
        }
        break;
      case 'p':
        options->yaml_path = optarg;
        if (!std::filesystem::exists(options->yaml_path)) {
          std::cerr << "Parameters " << options->yaml_path << " not found\n";
          return false;
        }
        break;
      case 'o':
        options->output_path = optarg;
        break;
      case 'g':
        options->graph_path = optarg;
        break;
      default:
        return false;
    }
  }

  if (options->input_path.empty() || options->yaml_path.empty() ||
      options->image_width == 0 || options->image_height == 0) {
    return false;
  }

  if (options->output_path.empty()) {
    options->output_path = options->input_path + ".rgb16";
  }

  return true;
}

isp::FrameSpec MakeBayerInputSpec(uint32_t width, uint32_t height,
                                  uint32_t bayer_pattern) {
  const isp::FrameFormat bayer_format =
      isp::frameFormatForBayerPattern(bayer_pattern);
  const size_t bayer_size =
      static_cast<size_t>(width) * static_cast<size_t>(height) *
      sizeof(uint16_t);

  return {width, height, bayer_format, isp::FrameDataType::kUInt16, width, 16,
          bayer_size};
}

isp::FrameSpec MakeRgb16OutputSpec(uint32_t width, uint32_t height) {
  const size_t rgb_size =
      static_cast<size_t>(width) * static_cast<size_t>(height) * 3U *
      sizeof(uint16_t);

  return {width, height, isp::FrameFormat::kRGB, isp::FrameDataType::kUInt16,
          width, 16, rgb_size};
}

bool SetStageProperties(isp::Pipeline* pipeline, const ISPParams& params) {
  if (!pipeline) return false;

  if (!pipeline->setStageProperty(
          "black_level0", "optical_black_red",
          static_cast<int64_t>(params.optical_black.red)) ||
      !pipeline->setStageProperty(
          "black_level0", "optical_black_green_r",
          static_cast<int64_t>(params.optical_black.green_r)) ||
      !pipeline->setStageProperty(
          "black_level0", "optical_black_green_b",
          static_cast<int64_t>(params.optical_black.green_b)) ||
      !pipeline->setStageProperty(
          "black_level0", "optical_black_blue",
          static_cast<int64_t>(params.optical_black.blue)) ||
      !pipeline->setStageProperty("black_level0", "manual_gain",
                                  params.manual_wb) ||
      !pipeline->setStageProperty("black_level0", "digital_gain",
                                  static_cast<double>(params.digital_gain))) {
    std::cerr << "Failed to configure Black Level stage properties\n";
    return false;
  }

  if (!pipeline->setStageProperty("white_balance0", "use_manual_gains",
                                  params.manual_wb) ||
      !pipeline->setStageProperty(
          "white_balance0", "manual_gains",
          isp::StagePropertyValue{std::vector<float>(params.wb_gains.begin(),
                                                     params.wb_gains.end())})) {
    std::cerr << "Failed to configure White Balance stage properties\n";
    return false;
  }

  return true;
}

bool BuildPipeline(isp::Pipeline* pipeline, const ISPParams& params,
                   const AppOptions& options) {
  if (!pipeline) return false;

  auto* blc = pipeline->addStage(isp::StageType::kBlackLevel);
  auto* awb = pipeline->addStage(isp::StageType::kWhiteBalance);
  auto* dmc = pipeline->addStage(isp::StageType::kDemosaic);

  if (!blc || !awb || !dmc) {
    std::cerr << "Failed to add stages to pipeline\n";
    return false;
  }

  if (!pipeline->linkMany({blc, awb, dmc}) ||
      (!params.manual_wb && !pipeline->link(blc, "gains", awb, "gains"))) {
    std::cerr << "Failed to link pipeline stages\n";
    return false;
  }

  return SetStageProperties(pipeline, params);
}

bool InitializePipeline(isp::Pipeline* pipeline, const AppOptions& options) {
  if (!pipeline) return false;
  if (!isp_example::WriteGraph(options.graph_path, *pipeline)) return false;

  const auto start = std::chrono::steady_clock::now();

  if (!pipeline->prepare(false)) {
    std::cerr << "Pipeline initialization failed: " << pipeline->lastError()
              << "\n";
    return false;
  }

  if (!isp_example::WriteGraph(options.graph_path, *pipeline)) return false;

  const auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(
                           std::chrono::steady_clock::now() - start)
                           .count();
  std::cout << "Pipeline initialization time: " << elapsed << " us\n";
  return true;
}

}  // namespace

int main(int argc, char* argv[]) {
  AppOptions options;
  if (!ParseArgs(argc, argv, &options)) {
    PrintUsage();
    return 1;
  }

  ISPParams params;
  std::string load_error;
  if (!LoadISPParams(options.yaml_path, &params, &load_error)) {
    std::cerr << "Failed to load ISP params:\n" << load_error << "\n";
    return 1;
  }

  isp::Pipeline pipeline;
  isp_example::ConfigurePipelineLogger(&pipeline);

  if (!BuildPipeline(&pipeline, params, options)) {
    return 1;
  }

  const size_t bayer_size =
      static_cast<size_t>(options.image_width) *
      static_cast<size_t>(options.image_height) * sizeof(uint16_t);
  const size_t rgb_size =
      static_cast<size_t>(options.image_width) *
      static_cast<size_t>(options.image_height) * 3U * sizeof(uint16_t);

  const isp::FrameFormat bayer_format =
      isp::frameFormatForBayerPattern(params.bayer_pattern);
  if (!isp::isBayerFormat(bayer_format)) {
    std::cerr << "Invalid Bayer pattern " << params.bayer_pattern << "\n";
    return 1;
  }

  auto* frame_bayer_in = pipeline.setInput(
      "black_level0",
      MakeBayerInputSpec(options.image_width, options.image_height,
                         params.bayer_pattern));
  auto* frame_rgb_out = pipeline.setOutput(
      "demosaic0",
      MakeRgb16OutputSpec(options.image_width, options.image_height));

  if (!frame_bayer_in || !frame_rgb_out) {
    std::cerr << "Failed to bind boundary frames\n";
    return 1;
  }

  if (!InitializePipeline(&pipeline, options)) {
    return 1;
  }

  std::vector<uint16_t> input;
  if (!isp_example::LoadRawImageU16(options.input_path, bayer_size, &input)) {
    return 1;
  }

  if (!frame_bayer_in->hostPtr) {
    std::cerr << "Failed to retrieve input frame from pipeline\n";
    return 1;
  }

  auto* bayer_input = reinterpret_cast<uint16_t*>(frame_bayer_in->hostPtr);
  std::memcpy(bayer_input, input.data(), bayer_size);

  const auto start = std::chrono::steady_clock::now();
  const uint8_t* output = pipeline.process();
  const auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(
                           std::chrono::steady_clock::now() - start)
                           .count();

  if (!output) {
    std::cerr << "Pipeline execution failed\n";
    return 1;
  }

  std::cout << "Pipeline execution time: " << elapsed << " us\n";
  return isp_example::WriteBinaryFile(options.output_path, output, rgb_size)
             ? 0
             : 1;
}

AI-Oriented Pipeline

For many AI applications, the ISP graph is identical to the low-latency RGB pipeline. The difference is usually what the application does with the RGB output buffer.

Use the same example shown in the previous section.

Typical application flow:

Figure. Application flow.

Image Statistics Pipeline

Histogram generation can also be combined with other ISP stages.

Figure. Histogram ISP processing pipeline.

Recommended for:

  • Exposure tuning
  • White balance tuning
  • Sensor validation
  • Calibration workflows

Complete Example

The following file can replace examples/isp_minimal.cpp directly.

Find the file here

/*
 * Copyright (C) 2025-2026 RidgeRun, LLC (http://www.ridgerun.com)
 * All Rights Reserved.
 *
 * The contents of this software are proprietary and confidential to RidgeRun,
 * LLC.  No part of this program may be photocopied, reproduced or translated
 * into another programming language without prior written consent of
 * RidgeRun, LLC.  The user is free to modify the source code after obtaining
 * a software license from RidgeRun.  All source code changes must be provided
 * back to RidgeRun without any encumbrance.
 */

/**
 * @file isp_minimal.cpp
 * @brief Minimal RAW Bayer to BLC+Histogram ISP example.
 */

#include <getopt.h>

#include <chrono>
#include <cstdio>
#include <cstring>
#include <filesystem>  // NOLINT
#include <iostream>
#include <string>
#include <vector>

#include "core/pipeline.h"
#include "core/stage.h"
#include "examples/isp_example_common.h"
#include "examples/isp_params/isp_params.h"

namespace {

constexpr uint32_t kHistogramBins = 256;

struct AppOptions {
  std::string input_path;
  std::string output_path;
  std::string yaml_path;
  std::string graph_path;
  uint32_t image_width = 0;
  uint32_t image_height = 0;
};

void PrintUsage() {
  std::cerr
      << "Usage: ./isp_minimal --image <path> --resolution <width>x<height> "
         "--parameters <yaml> [--output <path>] [--dump-graph <path>]\n";
}

bool ParseArgs(int argc, char* argv[], AppOptions* options) {
  if (!options) return false;

  static option long_options[] = {
      {"image", required_argument, nullptr, 'i'},
      {"resolution", required_argument, nullptr, 'r'},
      {"parameters", required_argument, nullptr, 'p'},
      {"output", required_argument, nullptr, 'o'},
      {"dump-graph", required_argument, nullptr, 'g'},
      {nullptr, 0, nullptr, 0}};

  while (true) {
    const int option =
        getopt_long(argc, argv, "i:r:p:o:g:", long_options, nullptr);
    if (option == -1) break;

    switch (option) {
      case 'i':
        options->input_path = optarg;
        if (!std::filesystem::exists(options->input_path)) {
          std::cerr << "Input image " << options->input_path << " not found\n";
          return false;
        }
        break;
      case 'r':
        if (sscanf(optarg, "%ux%u", &options->image_width,
                   &options->image_height) != 2) {
          std::cerr << "Invalid resolution format\n";
          return false;
        }
        break;
      case 'p':
        options->yaml_path = optarg;
        if (!std::filesystem::exists(options->yaml_path)) {
          std::cerr << "Parameters " << options->yaml_path << " not found\n";
          return false;
        }
        break;
      case 'o':
        options->output_path = optarg;
        break;
      case 'g':
        options->graph_path = optarg;
        break;
      default:
        return false;
    }
  }

  if (options->input_path.empty() || options->yaml_path.empty() ||
      options->image_width == 0 || options->image_height == 0) {
    return false;
  }

  if (options->output_path.empty()) {
    options->output_path = options.input_path + ".hist";
  }

  return true;
}

isp::FrameSpec MakeBayerInputSpec(uint32_t width, uint32_t height,
                                  uint32_t bayer_pattern) {
  const isp::FrameFormat bayer_format =
      isp::frameFormatForBayerPattern(bayer_pattern);
  const size_t bayer_size =
      static_cast<size_t>(width) * static_cast<size_t>(height) *
      sizeof(uint16_t);

  return {width, height, bayer_format, isp::FrameDataType::kUInt16, width, 16,
          bayer_size};
}

isp::FrameSpec MakeHistogramOutputSpec(uint32_t bins) {
  return {bins, 1, isp::FrameFormat::kUnknown, isp::FrameDataType::kUInt32,
          bins, 32, static_cast<size_t>(bins) * sizeof(uint32_t)};
}

std::vector<uint16_t> MakeUnityHistogramMask(uint32_t width, uint32_t height) {
  const size_t mask_size =
      static_cast<size_t>(width / 2U) * static_cast<size_t>(height / 2U);
  return std::vector<uint16_t>(mask_size, 256U);
}

bool SetStageProperties(isp::Pipeline* pipeline, const ISPParams& params,
                        uint32_t width, uint32_t height) {
  if (!pipeline) return false;

  const std::vector<uint16_t> histogram_mask =
      MakeUnityHistogramMask(width, height);

  if (!pipeline->setStageProperty(
          "black_level0", "optical_black_red",
          static_cast<int64_t>(params.optical_black.red)) ||
      !pipeline->setStageProperty(
          "black_level0", "optical_black_green_r",
          static_cast<int64_t>(params.optical_black.green_r)) ||
      !pipeline->setStageProperty(
          "black_level0", "optical_black_green_b",
          static_cast<int64_t>(params.optical_black.green_b)) ||
      !pipeline->setStageProperty(
          "black_level0", "optical_black_blue",
          static_cast<int64_t>(params.optical_black.blue)) ||
      !pipeline->setStageProperty("black_level0", "manual_gain",
                                  params.manual_wb) ||
      !pipeline->setStageProperty("black_level0", "digital_gain",
                                  static_cast<double>(params.digital_gain))) {
    std::cerr << "Failed to configure Black Level stage properties\n";
    return false;
  }

  if (!pipeline->setStageProperty("histogram0", "hist_bins",
                                  static_cast<int64_t>(kHistogramBins)) ||
      !pipeline->setStageProperty("histogram0", "mask_data",
                                  isp::StagePropertyValue{histogram_mask})) {
    std::cerr << "Failed to configure Histogram stage properties\n";
    return false;
  }

  return true;
}

bool BuildPipeline(isp::Pipeline* pipeline, const ISPParams& params,
                   const AppOptions& options) {
  if (!pipeline) return false;

  auto* blc = pipeline->addStage(isp::StageType::kBlackLevel);
  auto* hist = pipeline->addStage(isp::StageType::kHistogram);

  if (!blc || !hist) {
    std::cerr << "Failed to add stages to pipeline\n";
    return false;
  }

  if (!pipeline->link(blc, hist)) {
    std::cerr << "Failed to link pipeline stages\n";
    return false;
  }

  return SetStageProperties(pipeline, params, options.image_width,
                            options.image_height);
}

bool InitializePipeline(isp::Pipeline* pipeline, const AppOptions& options) {
  if (!pipeline) return false;
  if (!isp_example::WriteGraph(options.graph_path, *pipeline)) return false;

  const auto start = std::chrono::steady_clock::now();

  if (!pipeline->prepare(false)) {
    std::cerr << "Pipeline initialization failed: " << pipeline->lastError()
              << "\n";
    return false;
  }

  if (!isp_example::WriteGraph(options.graph_path, *pipeline)) return false;

  const auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(
                           std::chrono::steady_clock::now() - start)
                           .count();
  std::cout << "Pipeline initialization time: " << elapsed << " us\n";
  return true;
}

}  // namespace

int main(int argc, char* argv[]) {
  AppOptions options;
  if (!ParseArgs(argc, argv, &options)) {
    PrintUsage();
    return 1;
  }

  ISPParams params;
  std::string load_error;
  if (!LoadISPParams(options.yaml_path, &params, &load_error)) {
    std::cerr << "Failed to load ISP params:\n" << load_error << "\n";
    return 1;
  }

  isp::Pipeline pipeline;
  isp_example::ConfigurePipelineLogger(&pipeline);

  if (!BuildPipeline(&pipeline, params, options)) {
    return 1;
  }

  const size_t bayer_size =
      static_cast<size_t>(options.image_width) *
      static_cast<size_t>(options.image_height) * sizeof(uint16_t);
  const size_t hist_size =
      static_cast<size_t>(kHistogramBins) * sizeof(uint32_t);

  const isp::FrameFormat bayer_format =
      isp::frameFormatForBayerPattern(params.bayer_pattern);
  if (!isp::isBayerFormat(bayer_format)) {
    std::cerr << "Invalid Bayer pattern " << params.bayer_pattern << "\n";
    return 1;
  }

  auto* frame_bayer_in = pipeline.setInput(
      "black_level0",
      MakeBayerInputSpec(options.image_width, options.image_height,
                         params.bayer_pattern));
  auto* frame_hist_out =
      pipeline.setOutput("histogram0", MakeHistogramOutputSpec(kHistogramBins));

  if (!frame_bayer_in || !frame_hist_out) {
    std::cerr << "Failed to bind boundary frames\n";
    return 1;
  }

  if (!InitializePipeline(&pipeline, options)) {
    return 1;
  }

  std::vector<uint16_t> input;
  if (!isp_example::LoadRawImageU16(options.input_path, bayer_size, &input)) {
    return 1;
  }

  if (!frame_bayer_in->hostPtr) {
    std::cerr << "Failed to retrieve input frame from pipeline\n";
    return 1;
  }

  auto* bayer_input = reinterpret_cast<uint16_t*>(frame_bayer_in->hostPtr);
  std::memcpy(bayer_input, input.data(), bayer_size);

  const auto start = std::chrono::steady_clock::now();
  const uint8_t* output = pipeline.process();
  const auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(
                           std::chrono::steady_clock::now() - start)
                           .count();

  if (!output) {
    std::cerr << "Pipeline execution failed\n";
    return 1;
  }

  std::cout << "Pipeline execution time: " << elapsed << " us\n";
  return isp_example::WriteBinaryFile(options.output_path, output, hist_size)
             ? 0
             : 1;
}

Preparing and Running a Pipeline

Once a modified isp_minimal.cpp has been prepared, the execution flow is the same for all examples:

  1. Load the YAML parameters
  2. Build the selected pipeline
  3. Bind the input and output frames
  4. Call prepare()
  5. Load the Bayer RAW image into the input frame
  6. Call process()
  7. Write the output buffer to disk

Relationship with isp_minimal.cpp

examples/isp_minimal.cpp is the best reference for a complete image-in to NV12-out implementation.

The examples in this page are intended as direct replacements for that file when building alternative pipelines.


The following details are important when adapting isp_minimal.cpp:

  • params.bayer_pattern determines the Bayer CFA used for the input frame
  • black_level0:gains -> white_balance0:gains is only needed when automatic white balance is enabled
  • demosaic0:scene_key -> global_tone_mapping0:scene_key is required by Global Tone Mapping
  • Histogram pipelines require both hist_bins and mask_data
  • The output buffer size depends on the selected output format
  • pipeline.process() returns a const uint8_t*, so the output is written as raw bytes regardless of whether the logical content is NV12, RGB16, or histogram data

GStreamer Pipelines

PVA ISP can also be integrated as a GStreamer element through the pvaisp plug-in.

This integration method allows Bayer image processing to be incorporated directly into multimedia pipelines without requiring a custom C++ application.

Supported Formats

The current implementation accepts Bayer RAW images as input and produces NV12 output.

Supported Bayer formats:

  • RGGB
  • BGGR
  • GBRG
  • GRBG

Supported bit depths:

  • 8-bit
  • 10-bit
  • 12-bit
  • 14-bit
  • 16-bit

Supported memory types:

  • System Memory
  • CUDA Host Memory (mapped to Device)

Output format:

  • NV12

GStreamer Elements

The PVA ISP GStreamer Plugin contains two elements:

  • pvaisp: the ISP algorithm
  • pvacudaupload: a copy element required for sources that do not accept buffer pools.

pvaisp

The pvaisp supports bayer 10, 12, 14 and 16 and outputs NV12, ready for video encoding and streaming.

  SINK template: 'sink'
    Capabilities:
      video/x-bayer
                 format: { (string)rggb, (string)bggr, (string)gbrg, (string)grbg }
                    bpp: { (int)10, (int)12, (int)14, (int)16 }
                  width: [ 1, 2147483647 ]
                 height: [ 1, 2147483647 ]
              framerate: [ 0/1, 2147483647/1 ]
  
  SRC template: 'src'
    Capabilities:
      video/x-raw
                 format: NV12
                  width: [ 1, 2147483647 ]
                 height: [ 1, 2147483647 ]
              framerate: [ 0/1, 2147483647/1 ]

Regarding the properties, pvaisp is equipped with the below-written properties. Unless noted otherwise, the properties below are configuration inputs. Most of them set stage parameters before processing starts; a few only affect graph wiring, debug output, or GStreamer runtime behaviour.

Pipeline and Runtime
  • params - Default: null. Path to the YAML file that seeds the ISP configuration. In PVA ISP it is the source of the decompand curve, black-level offsets, white-balance settings, histogram tuning, tone-mapping values, auto-exposure tuning, and debug options. The property itself does not change pixels; it feeds the whole pipeline configuration. It is recommended for static and production deployment.
  • use-convolution - Default: false. Enables the branch that inserts the Convolution stage before Global Tone Mapping. In the example pipeline, enabling it keeps the Black Level Correction + Convolution path and routes the scene-key through Convolution; disabling it bypasses that branch and uses the alternate white-balance-gain source with the scene-key coming directly from Demosaic.
Decompanding
  • decompand-input-shift - Default: 0. Right-shifts RAW samples before the piecewise-linear LUT lookup. Higher values discard more low-order bits and effectively move the decompand curve to match the sensor's packed-bit format.
  • decompand-x-pts - Default: null. Comma-separated control points for the output domain of the decompand curve. The Decompand stage builds its VLUT from these points, so changing them changes the inverse-companding curve that linearizes sensor values. A valid curve needs at least two strictly increasing points and matching y points.
  • decompand-y-pts - Default: null. Comma-separated control points for the input domain of the decompand curve. Together with decompand-x-pts, these define the piecewise mapping that the decompand kernel uploads into its VLUT.

For more information, see the Decompand description.

Black Level
  • black-level-channel00 - Default: 0. Optical-black offset for pixels at even row/even column. The Black Level Correction stage subtracts this per-CFA-site bias before later stages see the RAW signal.
  • black-level-channel01 - Default: 0. Optical-black offset for pixels at even row/odd column. It feeds the same black-level correction kernel, but for the second CFA site.
  • black-level-channel10 - Default: 0. Optical-black offset for pixels at odd row/even column. It changes the correction factor and offset used for that CFA site.
  • black-level-channel11 - Default: 0. Optical-black offset for pixels at odd row/odd column. This is the fourth per-site correction value used by the black-level stage.
  • digital-gain-gain - Default: 1. Multiplies the black-level correction factors. In the implementation it scales the fixed-point correction applied after optical-black subtraction, so it acts like a global boost on the RAW-domain signal.

RGGB Bayer block with black level channels A 2 by 2 RGGB Bayer block showing the four black level channel positions. Example Bayer block Black level channels align to the 2x2 repeating pattern. R channel00 G channel01 G channel10 B channel11

For more information, please visit the Black Level stage documentation.

White Balance
  • white-balance-use-calibration - Default: false. Selects the calibration-weighted auto white-balance path. When enabled, the gain-generation stage uses the u_ref and v_ref calibration vectors to derive gains; when disabled, the auto gains follow the default scene-driven path.
  • white-balance-u-ref - Default: null. Five U chromaticity reference values used by the auto white-balance gain generator. These are consumed by the gain stage, not by the Bayer multiplier itself.
  • white-balance-v-ref - Default: null. Five V chromaticity reference values used by the auto white-balance gain generator. Together with u_ref, they shape how the scene is converted into RGB gains.
  • white-balance-manual-wb - Default: false. Switches the final White Balance stage to manual gain mode. When false, White Balance consumes the gains side input produced by the gain-generation stage; when true, it uses the private manual gains below.
  • white-balance-manual-wb-gains - Default: null. Comma-separated manual gains for R,G,B. In the implementation the manual path uses a 3-element gain vector and applies it directly to the Bayer data. If you leave this unset, the stage falls back to unity gains internally.

For more information, please visit the White Balance stage.

Demosaic and Histogram
  • demosaic-bayer-pattern - Default: 0. Selects the Bayer mosaic ordering. This determines how the Demosaic stage reconstructs RGB and also how CFA site indices are assigned in stages that need to know the color layout, such as Histogram and the auto white-balance gain generators. The values are 0=BGGR, 1=RGGB, 2=GBRG, and 3=GRBG.
  • histogram-bit-depth - Default: 16. Logical bit depth used to map decompanded samples into histogram bins. In the histogram implementation it changes the shift used before accumulation, so larger values preserve more of the RAW dynamic range before binning and smaller values compress sooner.
  • histogram-mask - Default: null. Optional path to a float mask used to weight the histogram. In the repo's loader this file is converted into the fixed-point mask uploaded to the Histogram stage. If no mask is supplied, the stage uses a uniform mask, so auto exposure sees an unweighted histogram. It must match the width and height dimensions.
  • histogram-use-mask - Default: true. Enables reading the histogram mask in the Histogram stage. When true, masked pixels contribute with the provided weights; when false, the histogram behaves as an unmasked accumulator.

For more information, please visit the Demosaic stage.

Convolution
  • convolution-kernel - Default: null. Comma-separated 3x3 convolution kernel coefficients. The Convolution stage packs these 9 signed values into the kernel used for the RGB filter, so changing them changes both the filtered image and the scene-key statistics produced for Global Tone Mapping. In the example configs the built-in smoothing kernel is [1, 2, 1, 2, 4, 2, 1, 2, 1].
  • convolution-shift - Default: 0. Output right-shift applied after convolution. Higher values attenuate the filtered result more strongly and help keep the RGB output within range.
Tone Mapping
  • global-tone-mapping-brightness - Default: 0. Target scene brightness for the Reinhard-style global tone-mapping scale. The kernel computes a scale from the scene-key average luminance; if this value is non-positive, the scale collapses to 0, so in practice a positive brightness is what activates the GTM normalization.
  • global-tone-mapping-gtm-scale-min - Default: 0. Lower clamp for the tone-mapping scale. This limits how dark the GTM response can become when the scene-key luminance is large.
  • global-tone-mapping-gtm-scale-max - Default: 1. Upper clamp for the tone-mapping scale. This limits how much GTM can brighten the image when the scene is dark.
  • global-tone-mapping-gtm-temporal-alpha - Default: 1. Temporal adaptation factor for the GTM white-point state. Values near 1 follow the current frame more aggressively, while smaller values blend more of the previous frame into the new white-point estimate.
  • global-tone-mapping-saturation - Default: 0. Documented tone-mapping saturation control. The current src/ implementation does not wire this value into a kernel, so it has no effect on the algorithms that are implemented today.
  • gamma-tone-mapping-gamma - Default: 1. Gamma exponent used to build the gamma LUT. The stage precomputes pow(input, 1/gamma) into a lookup table, so larger gamma values brighten the mid-tones and smaller values darken them.

For more information, please visit the Global Tone Mapping and Gamma Tone Mapping.

Auto Exposure
  • auto-exposure-kp-exposure - Default: 40. Proportional gain for the exposure PID loop. Larger values make exposure respond more aggressively to error in the measured histogram mean.
  • auto-exposure-ki-exposure - Default: 1. Integral gain for the exposure PID loop. This accumulates persistent error so the controller can remove long-term bias.
  • auto-exposure-kd-exposure - Default: 8. Derivative gain for the exposure PID loop. This damps rapid changes in the exposure correction.
  • auto-exposure-kp-gain - Default: 5000000. Proportional gain for the gain PID loop. The gain controller operates on a different numeric range, so its proportional term is much larger than the exposure one.
  • auto-exposure-ki-gain - Default: 10. Integral gain for the gain PID loop. This integrates sustained error in the gain path.
  • auto-exposure-kd-gain - Default: 500. Derivative gain for the gain PID loop. This tempers gain changes between frames.
  • auto-exposure-reference-mean - Default: 120. Target mean luminance used by the PID controllers. The auto-exposure algorithm compares the measured histogram mean against this target to decide whether to brighten or darken the image.
  • auto-exposure-dynamic-range - Default: 255. Maximum linear luminance used to normalize the PID error. The histogram mean is measured in linear units and divided by this range before the PID terms are applied.
  • auto-exposure-min-exposure - Default: 5000. Lower clamp for the exposure PID output. This prevents the controller from driving exposure below the configured hardware or algorithmic floor.
  • auto-exposure-max-exposure - Default: 20000. Upper clamp for the exposure PID output. Once this ceiling is reached, the controller treats exposure as saturated and can rely more on gain.
  • auto-exposure-min-gain - Default: 2000000. Lower clamp for the gain PID output. This bounds the minimum hardware gain the controller is allowed to command.
  • auto-exposure-max-gain - Default: 100000000. Upper clamp for the gain PID output. Once the gain path saturates, the algorithm can fall back to the exposure PID loop.
  • auto-exposure-camera-index - Default: 0. /dev/videoN index used when V4L2 control writes are enabled. The implementation resolves the Exposure and Gain V4L2 controls against this device node.
  • auto-exposure-enable-v4l2 - Default: false. Enables writing the computed exposure and gain back to the camera through V4L2. When false, the algorithm still computes controls, but they remain as pipeline output instead of hardware writes.

The auto-exposure algorithm in this repository works in two stages. It first measures the histogram mean, normalizes it with dynamic_range, and uses the exposure and gain PID loops to move toward reference_mean. Gain is adjusted first; if the gain hits its clamp, the controller enables the exposure loop and continues with exposure updates. When V4L2 is enabled, the resulting values are also written to /dev/videoN.

For more information, visit the Auto Exposure stage.

Debug
  • debug-export-debug-csv - Default: true. Enables the debug CSV export helpers. This affects only the diagnostic files produced by the example/debug tooling; it does not change the ISP output.
  • debug-export-frame-index - Default: 0. Selects the frame index used for the debug CSV exports. It determines which frame is dumped, not how the algorithms process frames.
  • debug-export-scene-key - Default: false. Enables scene-key export in the debug output. This reports the scene-key statistics produced by the Demosaic or Convolution branch, but it does not affect the image pipeline itself.

The information regarding the debugging is available in Debugging Profiling and Tuning.

pvacudaupload

This element supports any format and any resolution.

It is intended to make a copy from system memory to CUDA-mapped host memory.

Offline Bayer Processing

PVA ISP can process Bayer RAW files directly inside a GStreamer pipeline.

GST_PLUGIN_PATH=$PWD:$PWD/builddir gst-launch-1.0 \
  filesrc location=color_checker.raw blocksize=4147200 ! \
  'video/x-bayer,format=bggr,bpp=16,width=1920,height=1080,framerate=1/1' ! \
  pvacudaupload ! \
  pvaispfilter params=pva_isp_params.yaml ! \
  'video/x-raw,format=NV12,width=1920,height=1080,framerate=1/1' ! \
  filesink location=out_nv12.raw

This pipeline is useful for:

  • ISP validation
  • Algorithm tuning
  • Image quality evaluation
  • Offline processing workflows

Live Camera Processing

PVA ISP can process Bayer frames acquired directly from a camera.

GST_PLUGIN_PATH=$PWD:$PWD/builddir:/home/nvidia/lleon/gst-v4l2src/builddir \
gst-launch-1.0 -v \
  rrv4l2src device=/dev/video0 io-mode=userptr ! \
  'video/x-bayer,format=rggb,bpp=10,width=1920,height=1080' ! \
  pvaispfilter params=pva_isp_params.yaml ! \
  'video/x-raw,format=NV12,width=1920,height=1080' ! \
  perf ! fakesink

This pipeline demonstrates real-time Bayer image processing using a live camera source.

Video Encoding Pipeline

The NV12 output generated by PVA ISP can be sent directly to NVIDIA hardware encoders.

Figure. PVA ISP video encoding GStreamer pipeline.

Example:

GST_PLUGIN_PATH=$PWD:$PWD/builddir:/home/nvidia/lleon/gst-v4l2src/builddir \
gst-launch-1.0 -e \
  rrv4l2src device=/dev/video0 io-mode=userptr ! \
  'video/x-bayer,format=rggb,bpp=10,width=1920,height=1080,framerate=30/1' ! \
  pvaispfilter params=pva_isp_params.yaml ! \
  nvv4l2h264enc bitrate=8000000 ! \
  h264parse ! \
  mp4mux ! \
  filesink location=recording.mp4

Typical applications include:

  • Video recording
  • Dash cameras
  • Surveillance systems
  • Industrial inspection

Display Pipeline

The processed NV12 image can be displayed directly.

Figure. PVA ISP Display GStreamer pipeline.

Example:

GST_PLUGIN_PATH=$PWD:$PWD/builddir:/home/nvidia/lleon/gst-v4l2src/builddir \
gst-launch-1.0 \
  rrv4l2src device=/dev/video0 io-mode=userptr ! \
  'video/x-bayer,format=rggb,bpp=10,width=1920,height=1080,framerate=30/1' ! \
  pvaispfilter params=pva_isp_params.yaml ! \
  nvvidconv ! \
  autovideosink

Streaming Pipeline

PVA ISP can be integrated into network streaming applications.

Figure. PVA ISP Streaming GStreamer pipeline.


Example:

GST_PLUGIN_PATH=$PWD:$PWD/builddir:/home/nvidia/lleon/gst-v4l2src/builddir \
gst-launch-1.0 \
  rrv4l2src device=/dev/video0 io-mode=userptr ! \
  'video/x-bayer,format=rggb,bpp=10,width=1920,height=1080,framerate=30/1' ! \
  pvaispfilter params=pva_isp_params.yaml ! \
  nvv4l2h264enc bitrate=4000000 ! \
  h264parse ! \
  rtph264pay pt=96 ! \
  udpsink host=192.168.1.100 port=5000

Typical applications include:

  • Security cameras
  • Traffic monitoring
  • Smart city deployments
  • Remote inspection systems





Cookies help us deliver our services. By using our services, you agree to our use of cookies.