From 85d31def6f2636d797bf7992892a9bc655e724f2 Mon Sep 17 00:00:00 2001 From: ABelliqueux Date: Sat, 1 Mar 2025 19:35:25 +0100 Subject: [PATCH] Add exposure control for picam --- frame_opencv.py | 91 ++++++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/frame_opencv.py b/frame_opencv.py index 10906b1..f2e0db6 100644 --- a/frame_opencv.py +++ b/frame_opencv.py @@ -154,9 +154,9 @@ class webcam(): capture_ok = cv2.imwrite(img_path, self.og_frame) return capture_ok - def increment_setting(self, setting:str, value:int=-1): + def increment_setting(self, setting:str, inc:int=-1): # If value has default -1 value, increment setting - if value == -1: + if inc == -1: if setting in self.camera_current_settings: if self.camera_current_settings[setting]['value'] + self.camera_current_settings[setting]['step'] in range(self.camera_current_settings[setting]['min'],self.camera_current_settings[setting]['max']+1): self.camera_current_settings[setting]['value'] += self.camera_current_settings[setting]['step'] @@ -202,7 +202,7 @@ class webcam(): v4l2_ctl_process = subprocess.Popen(cmd.split(' ')) return v4l2_ctl_process - def apply_setting(self, to_set:list=None, inc:bool=False): + def apply_setting(self, to_set:list=None, inc:int=0): cmd = self.build_v4l2_cmd(to_set) self.run_v4l2_ctl(cmd) return self.camera_current_settings @@ -233,7 +233,7 @@ class showmewebcam(webcam): 'video_bitrate': dict(min=25000000, max=25000000, step=10000, default=camera_settings['video_bitrate'], value=camera_settings['video_bitrate']), } - def apply_setting(self, to_set:list=None, inc:bool=False): + def apply_setting(self, to_set:list=None, inc:int=0): self.cmd, self.args = self.build_v4l2_cmd(to_set) self.serialutils.send_serial_cmd(self.serialutils.find_cam_port(), self.cmd.format(*self.args)) return self.camera_current_settings @@ -246,13 +246,16 @@ class picam(): 'white_balance_auto_preset': dict(min=0, max=7, step=1, default=camera_settings['white_balance_auto_preset'], value=camera_settings['white_balance_auto_preset']), 'horizontal_flip': dict(min=0, max=1, step=1, default=camera_settings['hflip'], value=camera_settings['hflip']), 'vertical_flip': dict(min=0, max=1, step=1, default=camera_settings['vflip'], value=camera_settings['vflip']), - 'anti_flicker': dict(min=0, max=2, step=1, default=1, value=1), + 'anti_flicker': dict(min=0, max=20000, step=1000, default=0, value=0), + 'exposure' : dict(min=0, max=50000, step=1000, default=0, value=0) } # Map generic config name to specific picamera setting name self.cam_settings_map = { 'white_balance_auto_preset': 'AwbMode', 'auto_exposure':'AeExposureMode', - 'anti_flicker' : 'AeFlickerMode', + 'exposure':'ExposureTime', + # ~ 'anti_flicker' : 'AeFlickerMode', + 'anti_flicker' : 'AeFlickerPeriod', 'lenspos' : 'LensPosition', } self.has_liveview = True @@ -269,12 +272,12 @@ class picam(): self.liveview_only = False # Pi Cam V3 setup self.Picamera2 = getattr(import_module('picamera2'), 'Picamera2') + self.Metadata = getattr(import_module('picamera2'), 'Metadata') self.Transform = getattr(import_module('libcamera'), 'Transform') # Cam setup self.cam = self.Picamera2() self.picam_config = self.cam.create_video_configuration(main={"format": 'RGB888',"size": (camera_settings['cam_w'], camera_settings['cam_h'])}) - self.picam_config["transform"] = self.Transform(vflip=self.camera_current_settings['vertical_flip']['value'],hflip=self.camera_current_settings['horizontal_flip']['value']) - + self.picam_config["transform"] = self.Transform(vflip=self.camera_current_settings['vertical_flip']['value'],hflip=self.camera_current_settings['horizontal_flip']['value']) self.cam.configure(self.picam_config) # Autofocus, get lens position and switch to manual mode # Set Af mode to Auto then Manual (0). Default is Continuous (2), Auto is 1 @@ -286,18 +289,24 @@ class picam(): self.camera_default_settings = {'AfMode': 0, 'AwbEnable': 1, 'AwbMode': self.camera_current_settings['white_balance_auto_preset']['default'], + # Disable Autoexposure 'AeEnable': 1, + 'ExposureTime': 0, 'AeExposureMode': self.camera_current_settings['auto_exposure']['default'], # Enable flicker avoidance due to mains + # AeFlickerModeEnum { FlickerOff = 0, FlickerManual = 1, FlickerAuto = 2 } 'AeFlickerMode': 1, # Mains 50hz = 10000, 60hz = 8333 # ~ 'AeFlickerPeriod': 8333, - 'AeFlickerPeriod': 10000, + 'AeFlickerPeriod': self.camera_current_settings['anti_flicker']['default'], # Format is (min, max, default) in ms # here: (60fps, 12fps, None) # ~ 'FrameDurationLimits':(16666,83333,None) } self.cam.set_controls(self.camera_default_settings) + # Get current exposure value and store it in settings + metadata = self.Metadata(self.cam.capture_metadata()) + self.camera_current_settings['exposure']['value'] = self.camera_current_settings['exposure']['default'] = metadata.ExposureTime def test_device(self, source): pass @@ -330,12 +339,18 @@ class picam(): capture_ok = cv2.imwrite(img_path, self.og_frame) return capture_ok - def increment_setting(self, setting:str): + def increment_setting(self, setting:str, inc:int=1): if setting in self.camera_current_settings: - if self.camera_current_settings[setting]['value'] + self.camera_current_settings[setting]['step'] in range(self.camera_current_settings[setting]['min'],self.camera_current_settings[setting]['max']+1): - self.camera_current_settings[setting]['value'] += self.camera_current_settings[setting]['step'] - else: - self.camera_current_settings[setting]['value'] = self.camera_current_settings[setting]['min'] + if inc == -1: + if self.camera_current_settings[setting]['value'] - self.camera_current_settings[setting]['step'] in range(self.camera_current_settings[setting]['min'],self.camera_current_settings[setting]['max']+1): + self.camera_current_settings[setting]['value'] -= self.camera_current_settings[setting]['step'] + else: + self.camera_current_settings[setting]['value'] = self.camera_current_settings[setting]['max'] + elif inc == 1: + if self.camera_current_settings[setting]['value'] + self.camera_current_settings[setting]['step'] in range(self.camera_current_settings[setting]['min'],self.camera_current_settings[setting]['max']+1): + self.camera_current_settings[setting]['value'] += self.camera_current_settings[setting]['step'] + else: + self.camera_current_settings[setting]['value'] = self.camera_current_settings[setting]['min'] # Special cases # Autoexposure if setting == 'autoexposure' and self.camera_current_settings['autoexposure']['value'] == 4: @@ -343,20 +358,16 @@ class picam(): else: self.cam.set_controls({'AeEnable': 0}) self.cam.set_controls({"AeExposureMode": self.camera_current_settings['auto_exposure']['value']}) - # Antiflicker - if setting == 'anti_flicker' and self.camera_current_settings['anti_flicker']['value'] == 0: - self.cam.set_controls({'AeFlickerMode': 0}) - elif self.camera_current_settings['anti_flicker']['value'] == 1: - self.cam.set_controls({'AeFlickerMode': 1, 'AeFlickerPeriod':8333}) - else: - self.cam.set_controls({'AeFlickerMode': 1, 'AeFlickerPeriod':10000}) + return True - def apply_setting(self, to_set:list=None, inc:bool=False): + def apply_setting(self, to_set:list=None, inc:int=0): + set_controls = False if to_set is not None: for setting in to_set: - if inc: - self.increment_setting(setting) - self.cam.set_controls({self.cam_settings_map[setting] : self.camera_current_settings[setting]['value']}) + if inc != 0: + set_controls = self.increment_setting(setting, inc) + if set_controls: + self.cam.set_controls({self.cam_settings_map[setting] : self.camera_current_settings[setting]['value']}) def flip_image(self): self.cam.stop() @@ -376,8 +387,11 @@ class picam(): print(_("-Lens pos: {}".format(self.lenspos))) def reset_picture_settings(self): - for setting in self.camera_default_settings: - self.cam.set_controls({setting : self.camera_default_settings[setting]}) + # ~ for setting in self.camera_default_settings: + # ~ self.cam.set_controls({setting : self.camera_default_settings[setting]}) + self.cam.set_controls(self.camera_default_settings) + metadata = self.Metadata(self.cam.capture_metadata()) + self.camera_current_settings['exposure']['value'] = self.camera_current_settings['exposure']['default'] = metadata.ExposureTime def close(self): self.cam.close() @@ -535,14 +549,14 @@ class dslr(): except: print("Configuration error while setting {} to {}".format(setting, select_setting)) - def increment_setting(self, setting:str): + def increment_setting(self, setting:str, inc:int=-1): if setting in self.camera_current_settings: if self.camera_current_settings[setting]['value'] + self.camera_current_settings[setting]['step'] in range(self.camera_current_settings[setting]['min'],self.camera_current_settings[setting]['max']+1): self.camera_current_settings[setting]['value'] += self.camera_current_settings[setting]['step'] else: self.camera_current_settings[setting]['value'] = self.camera_current_settings[setting]['min'] - def apply_setting(self, to_set:list=None, inc:bool=False): + def apply_setting(self, to_set:list=None, inc:int=0): self.camera_current_config = self.gp.check_result(self.gp.gp_camera_get_config(self.cam)) # iterate over the settings dictionary if to_set is None: @@ -568,7 +582,7 @@ class dslr(): self.frame = cv2.flip(self.frame, -1) def focus(self, direction:str='-'): - self.apply_setting(['shutterspeed'], True) + self.apply_setting(['shutterspeed'], 1) def reset_picture_settings(self): self.camera_current_config = self.gp.check_result(self.gp.gp_camera_get_config(self.cam)) @@ -1020,11 +1034,11 @@ def main(args): # Key w / 7 - cycle wb elif (k%256 == 119) or (k%256 == 55) or (k%256 == 183): print(_("White balance mode")) - cam.apply_setting(['white_balance_auto_preset'], True) + cam.apply_setting(['white_balance_auto_preset'], 1) # Key x / 1 - cycle exposure elif (k%256 == 120) or (k%256 == 49) or (k%256 == 177): print(_("Exp. mode")) - cam.apply_setting(['auto_exposure'], True) + cam.apply_setting(['auto_exposure'], 1) # Key f / 3 - flip image elif (k%256 == 102) or (k%256 == 51) or (k%256 == 179): print(_("Flip image")) @@ -1094,9 +1108,16 @@ def main(args): elif (k%256 == 113): print(_("Anti-flicker mode")) cam.apply_setting(['anti_flicker'], True) - # Unused : key S - elif (k%256 == 115): - pass + print(cam.camera_current_settings['anti_flicker']['value']) + # Exposure : key S or maj A + elif (k%256 == 115) or (k%256 == 65): + print(_("Inc. exposure")) + cam.apply_setting(['exposure'], 1) + print(cam.camera_current_settings['exposure']['value']) + elif (k%256 == 90): + print(_("Dec. exposure")) + cam.apply_setting(['exposure'], -1) + print(cam.camera_current_settings['exposure']['value']) # SPACE or numpad 0 pressed elif (k%256 == 32) or (k%256 == 48) or (k%256 == 176): print(_("Capture frame"))