@@ -320,9 +320,9 @@ def _reset_flags(self):
320320 self .camera = None
321321 self .camera_ctrl_info = {}
322322 self .camera_config = {}
323- self .libcamera_config = None
323+ self .libcamera_config = {}
324324 self .streams = []
325- self .stream_map = None
325+ self .stream_map = {}
326326 self .started = False
327327 self .stop_count = 0
328328 self .configure_count = 0
@@ -338,7 +338,7 @@ def _reset_flags(self):
338338 self ._preview_stopped = threading .Event ()
339339 self .camera_properties_ = {}
340340 self .controls = Controls (self )
341- self .sensor_modes_ = None
341+ self .sensor_modes_ = []
342342 self ._title_fields = []
343343 self ._frame_drops = 0
344344
@@ -469,11 +469,11 @@ def _initialize_camera(self):
469469
470470 self .__identify_camera ()
471471
472- # Re-generate the controls list to someting easer to use.
472+ # Re-generate the controls list to something easier to use.
473473 for k , v in self .camera .controls .items ():
474474 self .camera_ctrl_info [k .name ] = (k , v )
475475
476- # Re-generate the properties list to someting easer to use.
476+ # Re-generate the properties list to something easier to use.
477477 for k , v in self .camera .properties .items ():
478478 self .camera_properties_ [k .name ] = utils .convert_from_libcamera_type (v )
479479
@@ -514,7 +514,7 @@ def sensor_modes(self) -> list[dict[str, Any]]:
514514 When called for the first time this will reconfigure the camera
515515 in order to read the modes.
516516 """
517- if self .sensor_modes_ is not None :
517+ if self .sensor_modes_ :
518518 return self .sensor_modes_
519519
520520 raw_config = self .camera .generate_configuration ([libcamera .StreamRole .Raw ])
@@ -600,13 +600,11 @@ def start_preview(self, preview=None, **kwargs) -> None:
600600 preview = Preview .QTGL .value (** kwargs )
601601 else :
602602 preview = Preview .QT .value (** kwargs )
603- if preview is None or preview is False : # i.e. None or False
603+ elif preview is False or preview is None :
604604 preview = Preview .NULL .value (** kwargs )
605605 elif isinstance (preview , Preview ):
606606 preview = preview .value (** kwargs )
607- else :
608- # Assume it's already a preview object.
609- pass
607+ # Assume it's already a preview object.
610608
611609 # The preview windows call the attach_preview method.
612610 self ._preview_stopped .clear ()
@@ -649,11 +647,11 @@ def close(self) -> None:
649647 self ._cm .cleanup (self .camera_idx )
650648 self .is_open = False
651649 self .streams = []
652- self .stream_map = None
650+ self .stream_map = {}
653651 self .camera = None
654652 self .camera_ctrl_info = {}
655653 self .camera_config = {}
656- self .libcamera_config = None
654+ self .libcamera_config = {}
657655 self .preview_configuration = {}
658656 self .still_configuration = {}
659657 self .video_configuration = {}
@@ -886,9 +884,8 @@ def _update_libcamera_stream_config(libcamera_stream_config, stream_config, buff
886884 def _make_libcamera_config (self , camera_config ):
887885 # Make a libcamera configuration object from our Python configuration.
888886
889- # We will create each stream with the "viewfinder" role just to get the stream
890- # configuration objects, and note the positions our named streams will have in
891- # libcamera's stream list.
887+ # We will create each stream with the "viewfinder" role just to get the stream configuration
888+ # objects, and note the positions our named streams will have in libcamera's stream list.
892889 roles = [VIEWFINDER ]
893890 index = 1
894891 self .main_index = 0
@@ -902,8 +899,7 @@ def _make_libcamera_config(self, camera_config):
902899 self .raw_index = index
903900 roles += [RAW ]
904901
905- # Make the libcamera configuration, and then we'll write all our parameters over
906- # the ones it gave us.
902+ # Make the libcamera configuration, and then we'll write all our parameters over the ones it gave us.
907903 libcamera_config = self .camera .generate_configuration (roles )
908904 libcamera_config .orientation = utils .transform_to_orientation (camera_config ["transform" ])
909905 buffer_count = camera_config ["buffer_count" ]
@@ -924,19 +920,17 @@ def _make_libcamera_config(self, camera_config):
924920
925921 # We're always going to set up the sensor config fully.
926922 bit_depth = 0
927- if camera_config ['sensor' ] is not None and 'bit_depth' in camera_config ['sensor' ] and \
928- camera_config ['sensor' ]['bit_depth' ] is not None :
923+ if camera_config ['sensor' ] is not None and camera_config ['sensor' ].get ('bit_depth' ) is not None :
929924 bit_depth = camera_config ['sensor' ]['bit_depth' ]
930- elif 'raw' in camera_config and camera_config [ 'raw' ] is not None and 'format' in camera_config ['raw' ]:
925+ elif camera_config . get ( 'raw' ) is not None and 'format' in camera_config ['raw' ]:
931926 bit_depth = SensorFormat (camera_config ['raw' ]['format' ]).bit_depth
932927 else :
933928 bit_depth = SensorFormat (self .sensor_format ).bit_depth
934929
935930 output_size = None
936- if camera_config ['sensor' ] is not None and 'output_size' in camera_config ['sensor' ] and \
937- camera_config ['sensor' ]['output_size' ] is not None :
931+ if camera_config ['sensor' ] is not None and camera_config ['sensor' ].get ('output_size' ) is not None :
938932 output_size = camera_config ['sensor' ]['output_size' ]
939- elif 'raw' in camera_config and camera_config [ 'raw' ] is not None and 'size' in camera_config ['raw' ]:
933+ elif camera_config . get ( 'raw' ) is not None and 'size' in camera_config ['raw' ]:
940934 output_size = camera_config ['raw' ]['size' ]
941935 else :
942936 output_size = camera_config ['main' ]['size' ]
@@ -969,7 +963,7 @@ def score_format(desired, actual):
969963 @staticmethod
970964 def align_stream (stream_config , optimal = True ) -> None :
971965 if optimal :
972- # Adjust the image size so that all planes are a mutliple of 32/64 bytes wide.
966+ # Adjust the image size so that all planes are a multiple of 32/64 bytes wide.
973967 # This matches the hardware behaviour and means we can be more efficient.
974968 align = 32 if Picamera2 .platform == Platform .Platform .VC4 else 64
975969 if stream_config ["format" ] in ("YUV420" , "YVU420" ):
@@ -984,7 +978,7 @@ def align_stream(stream_config, optimal=True) -> None:
984978 @staticmethod
985979 def align_configuration (config , optimal = True ) -> None :
986980 Picamera2 .align_stream (config ["main" ], optimal = optimal )
987- if "lores" in config and config [ "lores" ] is not None :
981+ if config . get ( "lores" ) is not None :
988982 Picamera2 .align_stream (config ["lores" ], optimal = optimal )
989983 # No point aligning the raw stream, it wouldn't mean anything.
990984
@@ -1039,28 +1033,30 @@ def _update_camera_config(self, camera_config, libcamera_config):
10391033 sensor_config ['output_size' ] = utils .convert_from_libcamera_type (libcamera_config .sensor_config .output_size )
10401034 camera_config ['sensor' ] = sensor_config
10411035
1042- def configure_ (self , camera_config = "preview" ):
1036+ def configure_ (self , camera_config ):
10431037 """Configure the camera system with the given configuration.
10441038
1045- :param camera_config: Configuration, defaults to the 'preview' configuration
1046- :type camera_config: dict, string or CameraConfiguration, optional
1047- :raises RuntimeError: Failed to configure
1039+ :param camera_config: Camera configuration to be set
1040+ :type camera_config: str, dict or CameraConfiguration
1041+ :raises RuntimeError: Failed to configure at runtime
10481042 """
10491043 if self .started :
10501044 raise RuntimeError ("Camera must be stopped before configuring" )
10511045
10521046 initial_config = camera_config
1047+
10531048 if isinstance (camera_config , str ):
10541049 if camera_config == "preview" :
10551050 camera_config = self .preview_configuration
10561051 elif camera_config == "still" :
10571052 camera_config = self .still_configuration
1058- else :
1053+ elif camera_config == "video" :
10591054 camera_config = self .video_configuration
1055+ else :
1056+ _log .warning ("Invalid name for `camera_config` given, assuming default 'preview' configuration" )
1057+ camera_config = self .preview_configuration
10601058 elif isinstance (camera_config , dict ):
10611059 camera_config = camera_config .copy ()
1062- elif camera_config is None :
1063- camera_config = self .create_preview_configuration ()
10641060
10651061 if isinstance (camera_config , CameraConfiguration ):
10661062 if camera_config .raw is not None :
@@ -1077,7 +1073,7 @@ def configure_(self, camera_config="preview"):
10771073 camera_config ['raw' ] = None
10781074
10791075 # Mark ourselves as unconfigured.
1080- self .libcamera_config = None
1076+ self .libcamera_config = {}
10811077 self .camera_config = {}
10821078
10831079 # Check the config and turn it into a libcamera config.
@@ -1122,10 +1118,9 @@ def configure_(self, camera_config="preview"):
11221118 if self .encode_stream_name is not None and self .encode_stream_name not in camera_config :
11231119 raise RuntimeError (f"Encode stream { self .encode_stream_name } was not defined" )
11241120
1125- # Decide whether we are going to keep hold of the last completed request, or
1126- # whether capture requests will always wait for the next frame. If there's only
1127- # one buffer, never hang on to the request because it would stall the pipeline
1128- # instantly.
1121+ # Decide whether we are going to keep hold of the last completed request, or whether
1122+ # capture requests will always wait for the next frame. If there's only one buffer,
1123+ # never hang on to the request because it would stall the pipeline instantly.
11291124 if camera_config ['queue' ] and camera_config ['buffer_count' ] > 1 :
11301125 self ._max_queue_len = 1
11311126 else :
@@ -1134,16 +1129,19 @@ def configure_(self, camera_config="preview"):
11341129 # Allocate all the frame buffers.
11351130 self .streams = [stream_config .stream for stream_config in libcamera_config ]
11361131 self .allocator .allocate (libcamera_config , camera_config .get ("use_case" ))
1132+
11371133 # Mark ourselves as configured.
11381134 self .libcamera_config = libcamera_config
11391135 self .camera_config = camera_config
1136+
11401137 # Fill in the embedded configuration structures if those were used.
11411138 if initial_config == "preview" :
11421139 self .preview_configuration .update (camera_config )
11431140 elif initial_config == "still" :
11441141 self .still_configuration .update (camera_config )
11451142 else :
11461143 self .video_configuration .update (camera_config )
1144+
11471145 # Set the controls directly so as to overwrite whatever is there.
11481146 self .controls = Controls (self , controls = self .camera_config ['controls' ])
11491147 self .configure_count += 1
@@ -1206,7 +1204,7 @@ def start(self, config=None, show_preview=False) -> None:
12061204 self .configure (config )
12071205 if not self .camera_config :
12081206 raise RuntimeError ("Camera has not been configured" )
1209- # By default we will create an event loop is there isn't one running already.
1207+ # By default we will create an event loop if there isn't one running already.
12101208 if show_preview is not None and not self ._event_loop_running :
12111209 self .start_preview (show_preview )
12121210 self .start_ ()
@@ -1269,8 +1267,7 @@ def set_controls(self, controls) -> None:
12691267 self .controls .set_controls (controls )
12701268
12711269 def process_requests (self , display ) -> None :
1272- # This is the function that the event loop, which runs externally to us, must
1273- # call.
1270+ # This is the function that the event loop, which runs externally to us, must call.
12741271 requests = []
12751272 with self ._requestslock :
12761273 requests = self ._requests
@@ -1287,9 +1284,8 @@ def process_requests(self, display) -> None:
12871284 # "job" for us to execute here in order to accomplish what it wanted.
12881285
12891286 with self .lock :
1290- # These new requests all have one "use" recorded, which is the one for
1291- # being in this list. Increase by one, so it cant't get discarded in
1292- # self.functions block.
1287+ # These new requests all have one "use" recorded, which is the one for being in
1288+ # this list. Increase by one, so it cant't get discarded in self.functions block.
12931289 for req in requests :
12941290 req .acquire ()
12951291 self .completed_requests += requests
@@ -2580,7 +2576,7 @@ def wait_for_af_state(self, states):
25802576 return (af_state in states , af_state == controls .AfStateEnum .Focused )
25812577
25822578 # First wait for the scan to start. Once we've seen that, the AF cycle may:
2583- # succeed, fail or could go back to Idle if it is cancelled.
2579+ # succeed, fail or could go back to idle if it is cancelled.
25842580 functions = [partial (wait_for_af_state , self , {controls .AfStateEnum .Scanning }),
25852581 partial (wait_for_af_state , self ,
25862582 {controls .AfStateEnum .Focused , controls .AfStateEnum .Failed , controls .AfStateEnum .Idle })]
0 commit comments