Skip to content

API Reference

Low-level API documentation for developers extending or integrating the toolkit.

Overview

The core library (triton_sampling.hpp / .cpp) provides reusable functions for:

  • Video metadata parsing
  • Metric computation (brightness, sharpness, entropy, motion)
  • Metric caching (FNV-1a hash-based)
  • Frame sampling

The executables (sample.cpp, calibrate.cpp) are thin wrappers around this library.


Core Data Structures

FileMeta

Video metadata extracted from filename.

struct FileMeta {
    std::string vehicle;     // e.g., "Triton43"
    int         camera;      // e.g., 1
    std::string timestamp;   // ISO 8601, e.g., "20250904T120000Z"
};

Populated by: parse_metadata(path)

FrameRecord

Metrics for a single frame.

struct FrameRecord {
    fs::path    video_path;   // Source video file
    FileMeta    meta;         // Video metadata
    int         frame_idx;    // Frame index (0-based)
    std::string frame_ts;     // Frame timestamp (ISO 8601)
    double      fps;          // Video frame rate
    double      brightness;   // Mean pixel value (0–255)
    double      sharpness;    // Laplacian variance
    double      entropy;      // Shannon entropy (bits)
    double      motion;       // Mean abs difference from previous frame
};

Core Functions

Video Discovery

collect_videos

Find all .mp4 files matching camera index.

std::vector<std::pair<fs::path, FileMeta>> collect_videos(
    const std::string& root_dir,
    int camera,
    int max_videos = -1
);

Parameters:

  • root_dir: Root directory to search (non-recursive, immediate children only)
  • camera: Camera index (matches Cam<N> in filename)
  • max_videos: Limit number of videos (-1 = unlimited)

Returns: Vector of (path, metadata) pairs.

Example:

auto videos = collect_videos("/data/videos", 1, 10);
for (const auto& [path, meta] : videos) {
    std::cout << path << " - " << meta.vehicle << std::endl;
}

Metadata Parsing

parse_metadata

Extract vehicle, camera, timestamp from filename.

FileMeta parse_metadata(const fs::path& path);

Expected format: *<Vehicle>*Cam<N>*<timestamp>*.mp4

Example:

auto meta = parse_metadata("/data/Triton43_20250904T120000Z_Cam1.mp4");
// meta.vehicle = "Triton43"
// meta.camera = 1
// meta.timestamp = "20250904T120000Z"

Note: Returns default values if parsing fails (vehicle="Unknown", camera=0).

Metric Computation

sample_video_metrics

Compute metrics for sampled frames.

std::vector<FrameRecord> sample_video_metrics(
    const fs::path& video_path,
    const FileMeta& meta,
    double sample_fps
);

Parameters:

  • video_path: Path to video file
  • meta: Video metadata (from parse_metadata)
  • sample_fps: Frames per second to examine

Returns: Vector of FrameRecord (one per examined frame).

Algorithm:

  1. Open video with OpenCV
  2. Compute sampling interval: interval = round(fps / sample_fps)
  3. For each frame at indices 0, interval, 2*interval, ...:
    • Compute brightness, sharpness, entropy, motion
  4. Return all records

Example:

auto meta = parse_metadata(video_path);
auto records = sample_video_metrics(video_path, meta, 1.0);
std::cout << "Examined " << records.size() << " frames" << std::endl;

Individual Metric Functions

Brightness:

double compute_brightness(const cv::Mat& frame);

Sharpness:

double compute_sharpness(const cv::Mat& frame);

Entropy:

double compute_entropy(const cv::Mat& frame);

Motion:

double compute_motion(const cv::Mat& curr, const cv::Mat& prev);

All functions accept BGR frames (OpenCV default).

Metric Caching

compute_cache_key

Generate FNV-1a hash for cache lookup.

std::string compute_cache_key(
    const fs::path& video_path,
    double sample_fps
);

Hash input: video_path + file_size + last_modified_time + sample_fps

Returns: Hex string (e.g., "a3b2c1d4e5f6")

Example:

auto key = compute_cache_key("/data/video.mp4", 1.0);
std::cout << "Cache key: " << key << std::endl;

save_cached_metrics

Save metrics to cache file.

void save_cached_metrics(
    const std::string& cache_dir,
    const fs::path& video_path,
    double sample_fps,
    const std::vector<FrameRecord>& records
);

Cache format: JSON file at <cache_dir>/<hash>.json

Example:

save_cached_metrics("./.metric_cache", video_path, 1.0, records);

load_cached_metrics

Load metrics from cache (if exists and valid).

std::optional<std::vector<FrameRecord>> load_cached_metrics(
    const std::string& cache_dir,
    const fs::path& video_path,
    const FileMeta& meta,
    double sample_fps
);

Returns: std::nullopt if cache miss or invalid.

Example:

auto cached = load_cached_metrics("./.metric_cache", video_path, meta, 1.0);
if (cached) {
    std::cout << "Cache hit: " << cached->size() << " records" << std::endl;
} else {
    // Compute metrics
}


Utility Functions

Percentile Computation

float percentile(std::vector<float> v, double p);

Linear interpolation (matches numpy default).

Example:

std::vector<float> data = {1, 2, 3, 4, 5};
float p50 = percentile(data, 50);  // 3.0
float p95 = percentile(data, 95);  // 4.8

Interest Scoring

double interest_score(const FrameRecord& r);

Formula: entropy × log(1 + sharpness) × (1 + motion)

Example:

for (const auto& r : records) {
    double score = interest_score(r);
    std::cout << "Frame " << r.frame_idx << ": " << score << std::endl;
}


Integration Examples

Custom Sampler

Build a custom sampler using the core library:

#include "triton_sampling.hpp"

int main() {
    // Find videos
    auto videos = collect_videos("/data/videos", 1);

    // Compute metrics for all videos
    std::vector<FrameRecord> all_records;
    for (const auto& [path, meta] : videos) {
        auto records = sample_video_metrics(path, meta, 1.0);
        all_records.insert(all_records.end(), records.begin(), records.end());
    }

    // Apply custom quality filter
    std::vector<FrameRecord> filtered;
    for (const auto& r : all_records) {
        if (r.brightness > 30 && r.sharpness > 20 && r.entropy > 3.0) {
            filtered.push_back(r);
        }
    }

    // Sort by interest score
    std::sort(filtered.begin(), filtered.end(),
        [](const FrameRecord& a, const FrameRecord& b) {
            return interest_score(a) > interest_score(b);
        });

    // Take top N
    filtered.resize(1000);

    // Extract frames (manually or via OpenCV)
    for (const auto& r : filtered) {
        cv::VideoCapture cap(r.video_path.string());
        cap.set(cv::CAP_PROP_POS_FRAMES, r.frame_idx);
        cv::Mat frame;
        cap >> frame;
        cv::imwrite("output_" + std::to_string(r.frame_idx) + ".png", frame);
    }

    return 0;
}

Batch Metric Export

Export metrics to CSV for analysis in R/Python:

#include "triton_sampling.hpp"
#include <fstream>

int main() {
    auto videos = collect_videos("/data/videos", 1);

    std::ofstream csv("metrics.csv");
    csv << "video,frame_idx,brightness,sharpness,entropy,motion\n";

    for (const auto& [path, meta] : videos) {
        auto records = sample_video_metrics(path, meta, 1.0);
        for (const auto& r : records) {
            csv << path.filename() << ","
                << r.frame_idx << ","
                << r.brightness << ","
                << r.sharpness << ","
                << r.entropy << ","
                << r.motion << "\n";
        }
    }

    return 0;
}

Building Against the Library

As a Static Library

Modify CMakeLists.txt to build library:

# Add library target
add_library(triton_sampling STATIC
    src/triton_sampling.cpp
)
target_include_directories(triton_sampling PUBLIC src)
target_link_libraries(triton_sampling PUBLIC ${OpenCV_LIBS})

# Link executables
add_executable(sample src/sample.cpp)
target_link_libraries(sample triton_sampling CLI11::CLI11 nlohmann_json::nlohmann_json)

External Project

Link from another CMake project:

# In your CMakeLists.txt
add_subdirectory(path/to/triton-video-sampling)
target_link_libraries(your_app triton_sampling)

Extending the Tool

Adding a New Metric

  1. Define metric function in triton_sampling.cpp:

    double compute_contrast(const cv::Mat& frame) {
        cv::Mat gray;
        cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);
        cv::Scalar mu, sigma;
        cv::meanStdDev(gray, mu, sigma);
        return sigma[0];  // Standard deviation as contrast
    }
    

  2. Add field to FrameRecord in triton_sampling.hpp:

    struct FrameRecord {
        // ... existing fields ...
        double contrast;
    };
    

  3. Compute in sample_video_metrics:

    rec.contrast = compute_contrast(frame);
    

  4. Update caching (JSON serialization):

    j["records"][i]["contrast"] = rec.contrast;
    

  5. Use in quality gate or interest score:

    if (r.contrast < min_contrast) continue;  // Quality gate
    // or
    score *= (1.0 + r.contrast);  // Interest score
    

Custom Diversity Selection

Replace grid_diversity() in sample.cpp with custom logic:

Example: K-means clustering:

#include <opencv2/ml.hpp>

std::vector<FrameRecord> kmeans_diversity(
    const std::vector<FrameRecord>& candidates,
    int k
) {
    // Build feature matrix (N x D)
    cv::Mat features(candidates.size(), 3, CV_32F);
    for (size_t i = 0; i < candidates.size(); i++) {
        features.at<float>(i, 0) = candidates[i].brightness;
        features.at<float>(i, 1) = std::log1p(candidates[i].sharpness);
        features.at<float>(i, 2) = candidates[i].entropy;
    }

    // K-means
    cv::Mat labels, centers;
    cv::kmeans(features, k, labels,
        cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 100, 0.01),
        3, cv::KMEANS_PP_CENTERS, centers);

    // Select frame nearest each center
    std::vector<FrameRecord> selected;
    for (int c = 0; c < k; c++) {
        double min_dist = 1e9;
        int best_idx = -1;
        for (size_t i = 0; i < candidates.size(); i++) {
            if (labels.at<int>(i) != c) continue;
            double dist = cv::norm(features.row(i), centers.row(c));
            if (dist < min_dist) {
                min_dist = dist;
                best_idx = i;
            }
        }
        if (best_idx >= 0)
            selected.push_back(candidates[best_idx]);
    }

    return selected;
}


Thread Safety

  • Metric computation: Thread-safe (OpenCV operations are reentrant)
  • Caching: Not thread-safe (file I/O without locking)

For multi-threaded use: - Use separate cache directories per thread: --cache-dir /tmp/cache_${thread_id} - Or disable caching: --no-cache


Next Steps