From 92938e90fcaa79ce382012e87409c385486f98ed Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Wed, 7 Aug 2024 10:48:02 +0100 Subject: [PATCH] core: Add aspect ratio control for lores stream Add a new command line argument "--lores-par" that when set, preserves the 1:1 aspect ratio of the low res stream. This is only possible on the PiSP platform. The default behaviour is to have this switch disabled, allowing for identical behaviour betwen VC4 and PiSP platforms. This requires the use of the rpi::ScalerCrops vendor control. This switch can also be triggered via the "rpicam-apps.lores.par" key in the postprocessing JSON file. Signed-off-by: Naushir Patuck --- core/options.cpp | 7 +++++-- core/options.hpp | 1 + core/post_processor.cpp | 2 ++ core/rpicam_app.cpp | 38 ++++++++++++++++++++++++++++---------- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/core/options.cpp b/core/options.cpp index 58805253..fdd1ef20 100644 --- a/core/options.cpp +++ b/core/options.cpp @@ -270,9 +270,11 @@ Options::Options() ("tuning-file", value(&tuning_file)->default_value("-"), "Name of camera tuning file to use, omit this option for libcamera default behaviour") ("lores-width", value(&lores_width)->default_value(0), - "Width of low resolution frames (use 0 to omit low resolution stream") + "Width of low resolution frames (use 0 to omit low resolution stream)") ("lores-height", value(&lores_height)->default_value(0), - "Height of low resolution frames (use 0 to omit low resolution stream") + "Height of low resolution frames (use 0 to omit low resolution stream)") + ("lores-par", value(&lores_par)->default_value(false)->implicit_value(true), + "Preserve the 1:1 pixel aspect ratio of the low res image (where possible) by applying a different crop on the stream.") ("mode", value(&mode_string), "Camera mode as W:H:bit-depth:packing, where packing is P (packed) or U (unpacked)") ("viewfinder-mode", value(&viewfinder_mode_string), @@ -695,6 +697,7 @@ void Options::Print() const std::cerr << " tuning-file: " << (tuning_file == "-" ? "(libcamera)" : tuning_file) << std::endl; std::cerr << " lores-width: " << lores_width << std::endl; std::cerr << " lores-height: " << lores_height << std::endl; + std::cerr << " lores-par: " << lores_par << std::endl; if (afMode_index != -1) std::cerr << " autofocus-mode: " << afMode << std::endl; if (afRange_index != -1) diff --git a/core/options.hpp b/core/options.hpp index d6b5cb16..624c14b4 100644 --- a/core/options.hpp +++ b/core/options.hpp @@ -151,6 +151,7 @@ struct Options bool qt_preview; unsigned int lores_width; unsigned int lores_height; + bool lores_par; unsigned int camera; std::string mode_string; Mode mode; diff --git a/core/post_processor.cpp b/core/post_processor.cpp index 5d796faf..638226c1 100644 --- a/core/post_processor.cpp +++ b/core/post_processor.cpp @@ -120,6 +120,7 @@ void PostProcessor::Read(std::string const &filename) unsigned int lores_width = node.get("lores.width"); unsigned int lores_height = node.get("lores.height"); + bool lores_par = node.get("lores.par", app_->GetOptions()->lores_par); std::string lores_format_str = node.get("lores.format", "yuv420"); libcamera::PixelFormat lores_format = libcamera::formats::YUV420; @@ -132,6 +133,7 @@ void PostProcessor::Read(std::string const &filename) app_->GetOptions()->lores_width = lores_width; app_->GetOptions()->lores_height = lores_height; + app_->GetOptions()->lores_par = lores_par; app_->lores_format_ = lores_format; LOG(1, "Postprocessing requested lores: " << lores_width << "x" << lores_height << " " << lores_format); diff --git a/core/rpicam_app.cpp b/core/rpicam_app.cpp index 16232b9e..7c1174fa 100644 --- a/core/rpicam_app.cpp +++ b/core/rpicam_app.cpp @@ -635,17 +635,35 @@ void RPiCamApp::StartCamera() // Build a list of initial controls that we must set in the camera before starting it. // We don't overwrite anything the application may have set before calling us. - if (!controls_.get(controls::ScalerCrop) && options_->roi_width != 0 && options_->roi_height != 0) + if (!controls_.get(controls::ScalerCrop) && !controls_.get(controls::rpi::ScalerCrops)) { - Rectangle sensor_area = camera_->controls().at(&controls::ScalerCrop).max().get(); - int x = options_->roi_x * sensor_area.width; - int y = options_->roi_y * sensor_area.height; - int w = options_->roi_width * sensor_area.width; - int h = options_->roi_height * sensor_area.height; - Rectangle crop(x, y, w, h); - crop.translateBy(sensor_area.topLeft()); - LOG(2, "Using crop " << crop.toString()); - controls_.set(controls::ScalerCrop, crop); + const Rectangle sensor_area = camera_->controls().at(&controls::ScalerCrop).max().get(); + const Rectangle default_crop = camera_->controls().at(&controls::ScalerCrop).def().get(); + std::vector crops; + + if (options_->roi_width != 0 && options_->roi_height != 0) + { + int x = options_->roi_x * sensor_area.width; + int y = options_->roi_y * sensor_area.height; + unsigned int w = options_->roi_width * sensor_area.width; + unsigned int h = options_->roi_height * sensor_area.height; + crops.push_back({ x, y, w, h }); + crops.back().translateBy(sensor_area.topLeft()); + } + else + { + crops.push_back(default_crop); + } + + LOG(2, "Using crop (main) " << crops.back().toString()); + + if (options_->lores_width != 0 && options_->lores_height != 0 && !options_->lores_par) + { + crops.push_back(crops.back()); + LOG(2, "Using crop (lores) " << crops.back().toString()); + } + + controls_.set(controls::rpi::ScalerCrops, libcamera::Span(crops.data(), crops.size())); } if (!controls_.get(controls::AfWindows) && !controls_.get(controls::AfMetering) && options_->afWindow_width != 0 &&