Examples - Sample Pipelines
🚧 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.
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.

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.

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, ¶ms, &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:

Image Statistics Pipeline
Histogram generation can also be combined with other ISP stages.

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, ¶ms, &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:
- Load the YAML parameters
- Build the selected pipeline
- Bind the input and output frames
- Call prepare()
- Load the Bayer RAW image into the input frame
- Call process()
- 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 matchingypoints.decompand-y-pts- Default:null. Comma-separated control points for the input domain of the decompand curve. Together withdecompand-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.
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 theu_refandv_refcalibration vectors to derive gains; when disabled, the auto gains follow the default scene-driven path.white-balance-u-ref- Default:null. FiveUchromaticity 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. FiveVchromaticity reference values used by the auto white-balance gain generator. Together withu_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 thegainsside 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 forR,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 are0=BGGR,1=RGGB,2=GBRG, and3=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 to0, 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 near1follow 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 currentsrc/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 precomputespow(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/videoNindex used when V4L2 control writes are enabled. The implementation resolves theExposureandGainV4L2 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.

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.

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.

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