Skip to content

Commit

Permalink
picamera2: Add support for ScalerCrops controls
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
naushir authored and davidplowman committed Oct 17, 2024
1 parent 6cbfdc3 commit 6de23fa
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 8 deletions.
2 changes: 1 addition & 1 deletion picamera2/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {}

Expand Down
23 changes: 16 additions & 7 deletions picamera2/picamera2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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"]},
Expand Down Expand Up @@ -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"]},
Expand Down Expand Up @@ -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"]},
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 6de23fa

Please sign in to comment.