diff --git a/apps/app_full.py b/apps/app_full.py index dc3e6711..3bd49bab 100755 --- a/apps/app_full.py +++ b/apps/app_full.py @@ -1180,6 +1180,7 @@ def toggle_hidden_controls(): "AfWindows", "AfPause", "AfMetering", + "ScalerCrops" } # Main widgets 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 cabfbb16..67e76e30 100644 --- a/picamera2/picamera2.py +++ b/picamera2/picamera2.py @@ -650,7 +650,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) @@ -687,9 +687,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"]}, @@ -721,9 +721,9 @@ 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"]}, @@ -755,9 +755,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"]}, @@ -1116,6 +1116,15 @@ 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) diff --git a/tests/crop_test.py b/tests/crop_test.py new file mode 100644 index 00000000..38e68029 --- /dev/null +++ b/tests/crop_test.py @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +# Test setting the "preserve_ar" stream configuration flag + +import cv2 + +from picamera2 import Picamera2 + +picam2 = Picamera2() + +for m, l in [(False, False), (False, True), (True, False), (True, True)]: + cfg = picam2.create_video_configuration(main={"size": (1920, 1080), "format": 'XRGB8888', "preserve_ar": m}, + lores={"size": (640, 640), "format": 'XRGB8888', "preserve_ar": l}, + display="main") + picam2.configure(cfg) + picam2.start(show_preview=True) + + for _ in range(100): + im = picam2.capture_array("lores") + cv2.imshow("lores", im) + cv2.resizeWindow("lores", (640, 640)) + cv2.waitKey(1) + + picam2.stop() diff --git a/tests/test_list.txt b/tests/test_list.txt index cd2cafaf..b26c6e12 100644 --- a/tests/test_list.txt +++ b/tests/test_list.txt @@ -62,6 +62,7 @@ tests/colour_spaces.py tests/config_with_sensor.py tests/configurations.py tests/context_test.py +tests/crop_test.py tests/display_transform_null.py tests/display_transform_qt.py tests/easy_video2.py