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 (matchesCam<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.
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 filemeta: Video metadata (fromparse_metadata)sample_fps: Frames per second to examine
Returns: Vector of FrameRecord (one per examined frame).
Algorithm:
- Open video with OpenCV
- Compute sampling interval:
interval = round(fps / sample_fps) - For each frame at indices
0, interval, 2*interval, ...:- Compute brightness, sharpness, entropy, motion
- 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:
Sharpness:
Entropy:
Motion:
All functions accept BGR frames (OpenCV default).
Metric Caching¶
compute_cache_key¶
Generate FNV-1a hash for cache lookup.
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:
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¶
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¶
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¶
-
Define metric function in
triton_sampling.cpp: -
Add field to
FrameRecordintriton_sampling.hpp: -
Compute in
sample_video_metrics: -
Update caching (JSON serialization):
-
Use in quality gate or 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¶
- Algorithm: Deep dive into sampling pipeline
- Tuning Guide: Optimize parameters
- Usage: Command-line reference