From 6de23fa9eae4f00d9c6f2d642ef0c1213b58446c Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Thu, 8 Aug 2024 15:18:56 +0100 Subject: [PATCH] picamera2: Add support for ScalerCrops controls Note that this change and new functionality is only applicable on Pi 5. The stream configuration dictionary now has a "preserve_ar" key that when set to True, will preserve the aspect ratio of the output by cropping the sensor image appropriately. If set to False, the full field of view of the sensor image is used to scale to the output resolution. This happens separately for the main and lores streams. The default behaviour of this flag is as follows: - For the main stream, it is set to True. This preserves existing behaviour. - For the lowres stream, it is set to False, which then makes the lowres stream crop follows the crop of the main stream. Again, this preservies existing behaviour, also matching VC4. Signed-off-by: Naushir Patuck --- picamera2/configuration.py | 2 +- picamera2/picamera2.py | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/picamera2/configuration.py b/picamera2/configuration.py index 7c6a3f2d..9c01794b 100644 --- a/picamera2/configuration.py +++ b/picamera2/configuration.py @@ -83,7 +83,7 @@ def align(self, optimal=True): class StreamConfiguration(Configuration): - _ALLOWED_FIELDS = ("size", "format", "stride", "framesize") + _ALLOWED_FIELDS = ("size", "format", "stride", "framesize", "preserve_ar") _FIELD_CLASS_MAP = {} _FORWARD_FIELDS = {} diff --git a/picamera2/picamera2.py b/picamera2/picamera2.py index d3117d37..72f57e42 100644 --- a/picamera2/picamera2.py +++ b/picamera2/picamera2.py @@ -655,7 +655,7 @@ def _make_initial_stream_config(stream_config: dict, updates: dict, ignore_list= """ if updates is None: return None - valid = ("format", "size", "stride") + valid = ("format", "size", "stride", "preserve_ar") for key, value in updates.items(): if isinstance(value, SensorFormat): value = str(value) @@ -692,9 +692,9 @@ def create_preview_configuration(self, main={}, lores=None, raw={}, transform=li if not self._is_rpi_camera(): raw = None sensor = None - main = self._make_initial_stream_config({"format": "XBGR8888", "size": (640, 480)}, main) + main = self._make_initial_stream_config({"format": "XBGR8888", "size": (640, 480), "preserve_ar": True}, main) self.align_stream(main, optimal=False) - lores = self._make_initial_stream_config({"format": "YUV420", "size": main["size"]}, lores) + lores = self._make_initial_stream_config({"format": "YUV420", "size": main["size"], "preserve_ar": False}, lores) if lores is not None: self.align_stream(lores, optimal=False) raw = self._make_initial_stream_config({"format": self.sensor_format, "size": main["size"]}, @@ -726,9 +726,10 @@ def create_still_configuration(self, main={}, lores=None, raw={}, transform=libc if not self._is_rpi_camera(): raw = None sensor = None - main = self._make_initial_stream_config({"format": "BGR888", "size": self.sensor_resolution}, main) + main = self._make_initial_stream_config({"format": "BGR888", "size": self.sensor_resolution, "preserve_ar": True}, + main) self.align_stream(main, optimal=False) - lores = self._make_initial_stream_config({"format": "YUV420", "size": main["size"]}, lores) + lores = self._make_initial_stream_config({"format": "YUV420", "size": main["size"], "preserve_ar": False}, lores) if lores is not None: self.align_stream(lores, optimal=False) raw = self._make_initial_stream_config({"format": self.sensor_format, "size": main["size"]}, @@ -760,9 +761,9 @@ def create_video_configuration(self, main={}, lores=None, raw={}, transform=libc if not self._is_rpi_camera(): raw = None sensor = None - main = self._make_initial_stream_config({"format": "XBGR8888", "size": (1280, 720)}, main) + main = self._make_initial_stream_config({"format": "XBGR8888", "size": (1280, 720), "preserve_ar": True}, main) self.align_stream(main, optimal=False) - lores = self._make_initial_stream_config({"format": "YUV420", "size": main["size"]}, lores) + lores = self._make_initial_stream_config({"format": "YUV420", "size": main["size"], "preserve_ar": False}, lores) if lores is not None: self.align_stream(lores, optimal=False) raw = self._make_initial_stream_config({"format": self.sensor_format, "size": main["size"]}, @@ -1121,6 +1122,14 @@ def configure_(self, camera_config="preview") -> None: self.controls = Controls(self, controls=self.camera_config['controls']) self.configure_count += 1 + if "ScalerCrops" in self.camera_controls: + par_crop = self.camera_controls["ScalerCrops"] + full_fov = self.camera_controls["ScalerCrop"][1] + scaler_crops = [par_crop[0] if camera_config["main"]["preserve_ar"] else full_fov] + if self.lores_index >= 0: + scaler_crops.append(par_crop[1] if camera_config["lores"]["preserve_ar"] else scaler_crops[0]) + self.set_controls({"ScalerCrops": scaler_crops}) + def configure(self, camera_config="preview") -> None: """Configure the camera system with the given configuration.""" self.configure_(camera_config)