From a960225d6656c40bf89df0085c1bab1f05dd3da8 Mon Sep 17 00:00:00 2001 From: Asadullah Shaikh Date: Wed, 16 Apr 2025 10:17:46 +0530 Subject: [PATCH] deprecate `show_preview` in favor of `preview` with the following valid values: - `None` (default* - starts a `NullPreview`) - `Preview` enum member - a preview object specifically, `bool` type has been deprecated in favor of `Preview.auto()` & `Preview.NO` *currently `show_preview` takes precedence when `preview=None` Signed-off-by: Asadullah Shaikh --- examples/capture_old_request.py | 4 +- examples/imx500/imx500_classification_demo.py | 4 +- .../imx500/imx500_object_detection_demo.py | 4 +- .../imx500/imx500_object_detection_demo_mp.py | 2 +- ...imx500_pose_estimation_higherhrnet_demo.py | 4 +- examples/imx500/imx500_segmentation_demo.py | 4 +- examples/stereo_preview.py | 4 +- examples/still_capture_with_config.py | 4 +- examples/title_bar.py | 4 +- picamera2/picamera2.py | 121 +++++++++--------- tests/crop_test.py | 4 +- tests/egl_leak.py | 4 +- tests/mode_test.py | 4 +- tests/preview_cycle_test.py | 2 +- tests/stride_test.py | 4 +- 15 files changed, 87 insertions(+), 86 deletions(-) diff --git a/examples/capture_old_request.py b/examples/capture_old_request.py index 5e816289..79407d08 100755 --- a/examples/capture_old_request.py +++ b/examples/capture_old_request.py @@ -5,11 +5,11 @@ import time -from picamera2 import Picamera2 +from picamera2 import Picamera2, Preview picam2 = Picamera2() capture_config = picam2.create_still_configuration() -picam2.start(show_preview=True) +picam2.start(preview=Preview.auto()) time.sleep(1) diff --git a/examples/imx500/imx500_classification_demo.py b/examples/imx500/imx500_classification_demo.py index 1742471a..142cb0dc 100755 --- a/examples/imx500/imx500_classification_demo.py +++ b/examples/imx500/imx500_classification_demo.py @@ -6,7 +6,7 @@ import cv2 import numpy as np -from picamera2 import CompletedRequest, MappedArray, Picamera2 +from picamera2 import CompletedRequest, MappedArray, Picamera2, Preview from picamera2.devices import IMX500 from picamera2.devices.imx500 import NetworkIntrinsics from picamera2.devices.imx500.postprocess import softmax @@ -146,7 +146,7 @@ def get_args(): config = picam2.create_preview_configuration(controls={"FrameRate": intrinsics.inference_rate}, buffer_count=12) imx500.show_network_fw_progress_bar() - picam2.start(config, show_preview=True) + picam2.start(config, preview=Preview.auto()) if intrinsics.preserve_aspect_ratio: imx500.set_auto_aspect_ratio() # Register the callback to parse and draw classification results diff --git a/examples/imx500/imx500_object_detection_demo.py b/examples/imx500/imx500_object_detection_demo.py index 8e868cb5..90a3b2c2 100755 --- a/examples/imx500/imx500_object_detection_demo.py +++ b/examples/imx500/imx500_object_detection_demo.py @@ -5,7 +5,7 @@ import cv2 import numpy as np -from picamera2 import MappedArray, Picamera2 +from picamera2 import MappedArray, Picamera2, Preview from picamera2.devices import IMX500 from picamera2.devices.imx500 import (NetworkIntrinsics, postprocess_nanodet_detection) @@ -168,7 +168,7 @@ def get_args(): config = picam2.create_preview_configuration(controls={"FrameRate": intrinsics.inference_rate}, buffer_count=12) imx500.show_network_fw_progress_bar() - picam2.start(config, show_preview=True) + picam2.start(config, preview=Preview.auto()) if intrinsics.preserve_aspect_ratio: imx500.set_auto_aspect_ratio() diff --git a/examples/imx500/imx500_object_detection_demo_mp.py b/examples/imx500/imx500_object_detection_demo_mp.py index 9bc5bd49..cc2796b4 100755 --- a/examples/imx500/imx500_object_detection_demo_mp.py +++ b/examples/imx500/imx500_object_detection_demo_mp.py @@ -173,7 +173,7 @@ def get_args(): config = picam2.create_preview_configuration(main, controls={"FrameRate": intrinsics.inference_rate}, buffer_count=12) imx500.show_network_fw_progress_bar() - picam2.start(config, show_preview=False) + picam2.start(config) if intrinsics.preserve_aspect_ratio: imx500.set_auto_aspect_ratio() diff --git a/examples/imx500/imx500_pose_estimation_higherhrnet_demo.py b/examples/imx500/imx500_pose_estimation_higherhrnet_demo.py index 2b754e46..76206f29 100755 --- a/examples/imx500/imx500_pose_estimation_higherhrnet_demo.py +++ b/examples/imx500/imx500_pose_estimation_higherhrnet_demo.py @@ -4,7 +4,7 @@ import numpy as np -from picamera2 import CompletedRequest, MappedArray, Picamera2 +from picamera2 import CompletedRequest, MappedArray, Picamera2, Preview from picamera2.devices.imx500 import IMX500, NetworkIntrinsics from picamera2.devices.imx500.postprocess import COCODrawer from picamera2.devices.imx500.postprocess_highernet import \ @@ -109,7 +109,7 @@ def get_drawer(): config = picam2.create_preview_configuration(controls={'FrameRate': intrinsics.inference_rate}, buffer_count=12) imx500.show_network_fw_progress_bar() - picam2.start(config, show_preview=True) + picam2.start(config, preview=Preview.auto()) imx500.set_auto_aspect_ratio() picam2.pre_callback = picamera2_pre_callback diff --git a/examples/imx500/imx500_segmentation_demo.py b/examples/imx500/imx500_segmentation_demo.py index 64263508..b967152f 100755 --- a/examples/imx500/imx500_segmentation_demo.py +++ b/examples/imx500/imx500_segmentation_demo.py @@ -5,7 +5,7 @@ import numpy as np -from picamera2 import CompletedRequest, Picamera2 +from picamera2 import CompletedRequest, Picamera2, Preview from picamera2.devices import IMX500 from picamera2.devices.imx500 import NetworkIntrinsics @@ -94,7 +94,7 @@ def get_args(): picam2 = Picamera2(imx500.camera_num) config = picam2.create_preview_configuration(controls={'FrameRate': intrinsics.inference_rate}, buffer_count=12) imx500.show_network_fw_progress_bar() - picam2.start(config, show_preview=True) + picam2.start(config, preview=Preview.auto()) picam2.pre_callback = create_and_draw_masks while True: diff --git a/examples/stereo_preview.py b/examples/stereo_preview.py index 0a9ac70f..14818738 100755 --- a/examples/stereo_preview.py +++ b/examples/stereo_preview.py @@ -3,7 +3,7 @@ import time from threading import Lock -from picamera2 import MappedArray, Picamera2, libcamera +from picamera2 import MappedArray, Picamera2, Preview, libcamera cam2_request = None lock = Lock() @@ -57,7 +57,7 @@ def save_request(request): controls={"ScalerCrop": (0, 0, picam2a.sensor_resolution[0], picam2a.sensor_resolution[1])} ) picam2a.configure(main_config) -picam2a.start_preview(True) +picam2a.start_preview(Preview.auto()) # Configure as half frame normally picam2b = Picamera2(1) diff --git a/examples/still_capture_with_config.py b/examples/still_capture_with_config.py index 586a4312..23092e8e 100755 --- a/examples/still_capture_with_config.py +++ b/examples/still_capture_with_config.py @@ -4,7 +4,7 @@ import time -from picamera2 import Picamera2 +from picamera2 import Picamera2, Preview picam2 = Picamera2() @@ -15,7 +15,7 @@ picam2.still_configuration.enable_raw() picam2.still_configuration.raw.size = picam2.sensor_resolution -picam2.start("preview", show_preview=True) +picam2.start("preview", preview=Preview.auto()) time.sleep(2) picam2.switch_mode_and_capture_file("still", "test_full.jpg") diff --git a/examples/title_bar.py b/examples/title_bar.py index be0af271..d7e807ed 100755 --- a/examples/title_bar.py +++ b/examples/title_bar.py @@ -2,10 +2,10 @@ import time -from picamera2 import Picamera2 +from picamera2 import Picamera2, Preview picam2 = Picamera2() -picam2.start(show_preview=True) +picam2.start(preview=Preview.auto()) time.sleep(0.5) # Or you could do this before starting the camera. diff --git a/picamera2/picamera2.py b/picamera2/picamera2.py index 8d0cc88d..edcd35b8 100644 --- a/picamera2/picamera2.py +++ b/picamera2/picamera2.py @@ -53,6 +53,19 @@ class Preview(Enum): DRM = DrmPreview QT = QtPreview QTGL = QtGlPreview + NO = None + + @staticmethod + def auto(): + # Crude attempt at "autodetection" but which will mostly (?) work. We will + # probably find situations that need fixing, VNC perhaps. + display = os.getenv('DISPLAY') + if display is None: + return Preview.DRM + elif display.startswith(':'): + return Preview.QTGL + else: + return Preview.QT class GlobalCameraInfo(TypedDict): @@ -578,34 +591,26 @@ def start_preview(self, preview=None, **kwargs) -> None: """ Start the given preview which drives the camera processing. - The preview may be either: - None or False - in which case a NullPreview is made, - True - which we hope in future to use to autodetect - a Preview enum value - in which case a preview of that type is made, - or an actual preview object. - - When using the enum form, extra keyword arguments can be supplied that - will be forwarded to the preview class constructor. + preview - a Preview enum or an actual preview object. + Pass Preview.auto() to autodetect. Defaults to NullPreview. + Additional keyword arguments may be supplied which will be + forwarded to the preview class constructor. """ - if self._event_loop_running: - raise RuntimeError("An event loop is already running") + if isinstance(preview, bool): + _log.error("Passing bool to preview parameter is deprecated") + preview = Preview.auto() if preview else None - if preview is True: - # Crude attempt at "autodetection" but which will mostly (?) work. We will - # probably find situations that need fixing, VNC perhaps. - display = os.getenv('DISPLAY') - if display is None: - preview = Preview.DRM.value(**kwargs) - elif display.startswith(':'): - preview = Preview.QTGL.value(**kwargs) - else: - preview = Preview.QT.value(**kwargs) - elif preview is False or preview is None: - preview = Preview.NULL.value(**kwargs) + if preview is None: + preview = NullPreview(**kwargs) elif isinstance(preview, Preview): + if preview is Preview.NO: + return preview = preview.value(**kwargs) # Assume it's already a preview object. + if self._event_loop_running: + raise RuntimeError("An event loop is already running") + # The preview windows call the attach_preview method. self._preview_stopped.clear() preview.start(self) @@ -1183,32 +1188,27 @@ def start_(self): _log.info("Camera started") self.started = True - def start(self, config=None, show_preview=False) -> None: + def start(self, config=None, show_preview=False, preview=None) -> None: """ Start the camera system running. Camera controls may be sent to the camera before it starts running. - The following parameters may be supplied: + config - Camera configuration to be set. Defaults to 'preview' configuration. + Note: if the camera is already configured, this has no effect. - config - if not None this is used to configure the camera. This is just a - convenience so that you don't have to call configure explicitly. - - show_preview - whether to show a preview window. You can pass in the preview - type or True to attempt to autodetect. If left as False you'll get no - visible preview window but the "NULL preview" will still be run. The - value None would mean no event loop runs at all and you would have to - implement your own. + preview - A Preview enum or an actual preview object. + Pass Preview.auto() to autodetect. Defaults to NullPreview. + Note: if an event loop is already running, this parameter has no effect. """ - if not self.camera_config and config is None: - config = "preview" - if config is not None: - self.configure(config) + if preview is None and show_preview is not False: + _log.error("show_preview is deprecated, use preview instead") + preview = Preview.NO if show_preview is None else show_preview + if not self.camera_config: - raise RuntimeError("Camera has not been configured") - # By default we will create an event loop if there isn't one running already. - if show_preview is not None and not self._event_loop_running: - self.start_preview(show_preview) + self.configure(config) + if not self._event_loop_running: + self.start_preview(preview) self.start_() def cancel_all_and_flush(self) -> None: @@ -2394,7 +2394,7 @@ def set_overlay(self, overlay) -> None: def start_and_capture_files(self, name="image{:03d}.jpg", initial_delay=1, preview_mode="preview", capture_mode="still", num_files=1, delay=1, - show_preview=True, exif_data=None) -> None: + show_preview=True, exif_data=None, preview=None) -> None: """This function makes capturing multiple images more convenient. Should only be used in command line line applications (not from a Qt application, for example). @@ -2417,21 +2417,22 @@ def start_and_capture_files(self, name="image{:03d}.jpg", delay - the time delay for every capture after the first (default 1s). - show_preview - whether to show a preview window (default: yes). The preview window only - displays an image by default during the preview phase, so if captures are back-to-back - with delay zero, then there may be no images shown. This parameter only has any - effect if a preview is not already running. If it is, it would have to be stopped first - (with the stop_preview method). + preview - which preview to start (current-default: auto-detect; in-future: NullPreview). + The preview window only displays an image by default during the preview phase, + so if captures are back-to-back with delay zero, then there may be no images shown. + Note: if a preview is already running, this parameter has no effect. exif_data - dictionary containing user defined exif data (based on `piexif`). This will overwrite existing exif information generated by picamera2. """ + _log.warning("FutureWarning: Parameter preview will default to NullPreview in a future release") + if self.started: self.stop() if delay: # Show a preview between captures, so we will switch mode and back for each capture. self.configure(preview_mode) - self.start(show_preview=show_preview) + self.start(show_preview=show_preview, preview=preview) for i in range(num_files): time.sleep(initial_delay if i == 0 else delay) self.switch_mode_and_capture_file(capture_mode, name.format(i), exif_data=exif_data) @@ -2439,12 +2440,12 @@ def start_and_capture_files(self, name="image{:03d}.jpg", # No preview between captures, it's more efficient just to stay in capture mode. if initial_delay: self.configure(preview_mode) - self.start(show_preview=show_preview) + self.start(show_preview=show_preview, preview=preview) time.sleep(initial_delay) self.switch_mode(capture_mode) else: self.configure(capture_mode) - self.start(show_preview=show_preview) + self.start(show_preview=show_preview, preview=preview) for i in range(num_files): self.capture_file(name.format(i), exif_data=exif_data) if i == num_files - 1: @@ -2453,7 +2454,7 @@ def start_and_capture_files(self, name="image{:03d}.jpg", self.stop() def start_and_capture_file(self, name="image.jpg", delay=1, preview_mode="preview", - capture_mode="still", show_preview=True, exif_data=None) -> None: + capture_mode="still", show_preview=True, exif_data=None, preview=None) -> None: """This function makes capturing a single image more convenient. Should only be used in command line line applications (not from a Qt application, for example). @@ -2471,21 +2472,22 @@ def start_and_capture_file(self, name="image.jpg", delay=1, preview_mode="previe capture_mode - the camera mode to use to capture the still images (defaulting to the Picamera2 object's still_configuration field). - show_preview - whether to show a preview window (default: yes). The preview window only - displays an image by default during the preview phase. This parameter only has any - effect if a preview is not already running. If it is, it would have to be stopped first - (with the stop_preview method). + preview - which preview to start (current-default: auto-detect; in-future: NullPreview). + The preview window only displays an image by default during the preview phase. + Note: if a preview is already running, this parameter has no effect. exif_data - dictionary containing user defined exif data (based on `piexif`). This will overwrite existing exif information generated by picamera2. """ + _log.warning("FutureWarning: Parameter preview will default to NullPreview in a future release") + self.start_and_capture_files(name=name, initial_delay=delay, preview_mode=preview_mode, capture_mode=capture_mode, num_files=1, - show_preview=show_preview, + show_preview=show_preview, preview=preview, exif_data=exif_data) def start_and_record_video(self, output, encoder=None, config=None, quality=Quality.MEDIUM, - show_preview=False, duration=0, audio=False) -> None: + show_preview=False, duration=0, audio=False, preview=None) -> None: """This function makes video recording more convenient. Should only be used in command line applications (not from a Qt application, for example). @@ -2507,9 +2509,8 @@ def start_and_record_video(self, output, encoder=None, config=None, quality=Qual quality - an indication of the video quality to use. This will be ignored if the encoder object was created with all its quality parameters (such as bitrate) filled in. - show_preview - whether to show a preview window (default: no). This parameter only has an - effect if a preview is not already running, in which case that preview would need - stopping first (using stop_preview) for any change to take effect. + preview - which preview to start (default: NullPreview). + Note: if a preview is already running, this parameter has no effect. duration - the duration of the video. The function will wait this amount of time before stopping the recording and returning. The default behaviour is to return immediately @@ -2538,7 +2539,7 @@ def start_and_record_video(self, output, encoder=None, config=None, quality=Qual if encoder is None: encoder = H264Encoder() self.start_encoder(encoder=encoder, output=output, quality=quality) - self.start(show_preview=show_preview) + self.start(show_preview=show_preview, preview=preview) if duration: time.sleep(duration) self.stop_recording() diff --git a/tests/crop_test.py b/tests/crop_test.py index 350123f9..c449816a 100644 --- a/tests/crop_test.py +++ b/tests/crop_test.py @@ -4,7 +4,7 @@ import cv2 -from picamera2 import Picamera2, Platform +from picamera2 import Picamera2, Platform, Preview # VC4 platforms do not support different crops for the two outputs. if Picamera2.platform == Platform.VC4: @@ -18,7 +18,7 @@ lores={"size": (320, 320), "format": 'XRGB8888', "preserve_ar": l}, display="main") picam2.configure(cfg) - picam2.start(show_preview=True) + picam2.start(preview=Preview.auto()) for _ in range(50): im = picam2.capture_array("lores") diff --git a/tests/egl_leak.py b/tests/egl_leak.py index bbc3ae15..ecdfa214 100755 --- a/tests/egl_leak.py +++ b/tests/egl_leak.py @@ -5,7 +5,7 @@ import subprocess import time -from picamera2 import Picamera2 +from picamera2 import Picamera2, Preview picam2 = Picamera2() half_res = tuple([s // 2 for s in picam2.sensor_resolution]) @@ -14,7 +14,7 @@ subprocess.check_call(['grep', 'Cma', '/proc/meminfo']) config = picam2.create_preview_configuration({'size': half_res}, raw={'size': half_res}) picam2.configure(config) - picam2.start(show_preview=True) + picam2.start(preview=Preview.auto()) time.sleep(1) picam2.stop_preview() picam2.stop() diff --git a/tests/mode_test.py b/tests/mode_test.py index a040943d..2a797d77 100755 --- a/tests/mode_test.py +++ b/tests/mode_test.py @@ -8,7 +8,7 @@ from libcamera import Transform -from picamera2 import Picamera2 +from picamera2 import Picamera2, Preview from picamera2.sensor_format import SensorFormat Picamera2.set_logging() @@ -42,7 +42,7 @@ def check(raw_config, fps): (set_format.packing == '') == (requested_format.packing == ''), \ f'{picam2.camera_configuration()["raw"]["format"]} != {raw_config["format"]}' picam2.set_controls({"FrameRate": fps}) - picam2.start(show_preview=True) + picam2.start(preview=Preview.auto()) time.sleep(1) # Check we got roughly the right framerate metadata = picam2.capture_metadata() diff --git a/tests/preview_cycle_test.py b/tests/preview_cycle_test.py index 88262a9e..511b72e6 100644 --- a/tests/preview_cycle_test.py +++ b/tests/preview_cycle_test.py @@ -14,7 +14,7 @@ def main(): preview = picam2.create_preview_configuration() picam2.configure(preview) - picam2.start(show_preview=None) + picam2.start(preview=Preview.NO) qtgl1 = time.monotonic() print("QT GL Preview") diff --git a/tests/stride_test.py b/tests/stride_test.py index b1b7e4ea..d6253407 100755 --- a/tests/stride_test.py +++ b/tests/stride_test.py @@ -2,7 +2,7 @@ import time -from picamera2 import MappedArray, Picamera2, libcamera +from picamera2 import MappedArray, Picamera2, Preview, libcamera def pre_callback(request): @@ -35,7 +35,7 @@ def post_callback(request): main={"size": half_size, "stride": stride} ) picam2.configure(main_config) -picam2.start_preview(True) +picam2.start_preview(Preview.auto()) picam2.start() time.sleep(2)