diff --git a/internal_filesystem/lib/mpos/board/linux.py b/internal_filesystem/lib/mpos/board/linux.py index 42298992..2e519259 100644 --- a/internal_filesystem/lib/mpos/board/linux.py +++ b/internal_filesystem/lib/mpos/board/linux.py @@ -126,20 +126,36 @@ SensorManager.init(None) # === CAMERA HARDWARE === -try: - # Try to initialize webcam to verify it's available +def init_cam(width, height, colormode): + try: + # Try to initialize webcam to verify it's available + import webcam + return webcam.init("/dev/video0", width=width, height=height) + except Exception as e: + print(f"Info: webcam initialization failed, camera will not be available: {e}") + +def deinit_cam(cam_obj): import webcam - test_cam = webcam.init("/dev/video0", width=320, height=240) - if test_cam: - webcam.deinit(test_cam) - from mpos import CameraManager - CameraManager.add_camera(CameraManager.Camera( - lens_facing=CameraManager.CameraCharacteristics.LENS_FACING_FRONT, - name="Video4Linux2 Camera", - vendor="ACME" - )) -except Exception as e: - print(f"Info: webcam initialization failed, camera will not be available: {e}") + webcam.deinit(cam_obj) + +def capture_cam(cam_obj, colormode): + import webcam + return webcam.capture_frame(cam_obj, "rgb565" if colormode else "grayscale") + +def apply_cam_settings(cam_obj, prefs): + print("V4L Camera doesn't support settings for now, skipping...") + +from mpos import CameraManager +CameraManager.add_camera(CameraManager.Camera( + lens_facing=CameraManager.CameraCharacteristics.LENS_FACING_FRONT, + name="Video4Linux2 Camera", + vendor="ACME", + init=init_cam, + deinit=deinit_cam, + capture=capture_cam, + apply_settings=apply_cam_settings +)) + print("linux.py finished") diff --git a/internal_filesystem/lib/mpos/board/matouch_esp32_s3_2_8.py b/internal_filesystem/lib/mpos/board/matouch_esp32_s3_2_8.py index 1ffc8a86..fd6eaebb 100644 --- a/internal_filesystem/lib/mpos/board/matouch_esp32_s3_2_8.py +++ b/internal_filesystem/lib/mpos/board/matouch_esp32_s3_2_8.py @@ -109,6 +109,8 @@ sdcard.init(cmd_pin=2,clk_pin=42,d0_pin=41) # LightsManager will not be initialized (functions will return False) # === CAMERA HARDWARE === +from mpos import CameraManager + def init_cam(width, height, colormode): try: from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling @@ -203,7 +205,11 @@ def deinit_cam(cam): except Exception as e: print(f"Indev enable got exception: {e}") -from mpos import CameraManager +def capture_cam(cam_obj, colormode): + return cam_obj.capture() + +def apply_cam_settings(cam_obj, prefs): + return CameraManager.ov_apply_camera_settings(cam_obj, prefs) # MaTouch ESP32-S3 has OV3660 camera (3MP, up to 2048x1536) # Camera pins are available but initialization is handled by the camera driver @@ -213,6 +219,7 @@ CameraManager.add_camera(CameraManager.Camera( vendor="OmniVision", init=init_cam, deinit=deinit_cam, + capture=capture_cam )) print("matouch_esp32_s3_2_8.py finished") diff --git a/internal_filesystem/lib/mpos/camera_manager.py b/internal_filesystem/lib/mpos/camera_manager.py index cb505cbf..90cc37a3 100644 --- a/internal_filesystem/lib/mpos/camera_manager.py +++ b/internal_filesystem/lib/mpos/camera_manager.py @@ -37,7 +37,7 @@ class Camera: Represents a camera device with its characteristics. """ - def __init__(self, lens_facing, name=None, vendor=None, version=None, init=None, deinit=None): + def __init__(self, lens_facing, name=None, vendor=None, version=None, init=None, deinit=None, capture=None, apply_settings=None): """Initialize camera metadata. Args: @@ -52,6 +52,8 @@ class Camera: self.version = version or 1 self.init_function = init self.deinit_function = deinit + self.capture_function = capture + self.apply_settings_function = apply_settings def __repr__(self): facing_names = { @@ -70,6 +72,14 @@ class Camera: if self.deinit_function: return self.deinit_function(cam_obj) + def capture(self, cam_obj, colormode=None): + if self.capture_function: + return self.capture_function(cam_obj, colormode) + + def apply_settings(self, cam_obj, prefs): + if self.apply_settings_function: + return self.apply_settings_function(cam_obj, prefs) + class CameraManager: """ Centralized camera device management service. @@ -237,6 +247,117 @@ class CameraManager: return resolution_map.get((width, height), FrameSize.R240X240) + @staticmethod + def ov_apply_camera_settings(self, cam, prefs): + if not cam or not prefs: + print("ov_apply_camera_settings: Skipping because invalid prefs or cam object") + return + + try: + # Basic image adjustments + brightness = prefs.get_int("brightness") + cam.set_brightness(brightness) + + contrast = prefs.get_int("contrast") + cam.set_contrast(contrast) + + saturation = prefs.get_int("saturation") + cam.set_saturation(saturation) + + # Orientation + hmirror = prefs.get_bool("hmirror") + cam.set_hmirror(hmirror) + + vflip = prefs.get_bool("vflip") + cam.set_vflip(vflip) + + # Special effect + special_effect = prefs.get_int("special_effect") + cam.set_special_effect(special_effect) + + # Exposure control (apply master switch first, then manual value) + exposure_ctrl = prefs.get_bool("exposure_ctrl") + cam.set_exposure_ctrl(exposure_ctrl) + + if not exposure_ctrl: + aec_value = prefs.get_int("aec_value") + cam.set_aec_value(aec_value) + + # Mode-specific default comes from constructor + ae_level = prefs.get_int("ae_level") + cam.set_ae_level(ae_level) + + aec2 = prefs.get_bool("aec2") + cam.set_aec2(aec2) + + # Gain control (apply master switch first, then manual value) + gain_ctrl = prefs.get_bool("gain_ctrl") + cam.set_gain_ctrl(gain_ctrl) + + if not gain_ctrl: + agc_gain = prefs.get_int("agc_gain") + cam.set_agc_gain(agc_gain) + + gainceiling = prefs.get_int("gainceiling") + cam.set_gainceiling(gainceiling) + + # White balance (apply master switch first, then mode) + whitebal = prefs.get_bool("whitebal") + cam.set_whitebal(whitebal) + + if not whitebal: + wb_mode = prefs.get_int("wb_mode") + cam.set_wb_mode(wb_mode) + + awb_gain = prefs.get_bool("awb_gain") + cam.set_awb_gain(awb_gain) + + # Sensor-specific settings (try/except for unsupported sensors) + try: + sharpness = prefs.get_int("sharpness") + cam.set_sharpness(sharpness) + except: + pass # Not supported on OV2640? + + try: + denoise = prefs.get_int("denoise") + cam.set_denoise(denoise) + except: + pass # Not supported on OV2640? + + # Advanced corrections + colorbar = prefs.get_bool("colorbar") + cam.set_colorbar(colorbar) + + dcw = prefs.get_bool("dcw") + cam.set_dcw(dcw) + + bpc = prefs.get_bool("bpc") + cam.set_bpc(bpc) + + wpc = prefs.get_bool("wpc") + cam.set_wpc(wpc) + + # Mode-specific default comes from constructor + raw_gma = prefs.get_bool("raw_gma") + print(f"applying raw_gma: {raw_gma}") + cam.set_raw_gma(raw_gma) + + lenc = prefs.get_bool("lenc") + cam.set_lenc(lenc) + + # JPEG quality (only relevant for JPEG format) + #try: + # quality = prefs.get_int("quality", 85) + # cam.set_quality(quality) + #except: + # pass # Not in JPEG mode + + print("Camera settings applied successfully") + + except Exception as e: + print(f"Error applying camera settings: {e}") + # ============================================================================ # Class method delegation (at module level) diff --git a/internal_filesystem/lib/mpos/ui/camera_activity.py b/internal_filesystem/lib/mpos/ui/camera_activity.py index b260439c..70e6d301 100644 --- a/internal_filesystem/lib/mpos/ui/camera_activity.py +++ b/internal_filesystem/lib/mpos/ui/camera_activity.py @@ -140,18 +140,8 @@ class CameraActivity(Activity): self.cam = CameraManager.get_cameras()[0].init(self.width, self.height, self.colormode) if self.cam: # Apply saved camera settings, only for internal camera for now: - self.apply_camera_settings(self.scanqr_prefs if self.scanqr_mode else self.prefs, self.cam, self.use_webcam) # needs to be done AFTER the camera is initialized - else: - print("camera app: no internal camera found, trying webcam on /dev/video0") - try: - # Initialize webcam with desired resolution directly - print(f"Initializing webcam at {self.width}x{self.height}") - self.cam = webcam.init("/dev/video0", width=self.width, height=self.height) - self.use_webcam = True - except Exception as e: - print(f"camera app: webcam exception: {e}") - # Start refreshing: - if self.cam: + CameraManager.get_cameras()[0].apply_settings(self.cam, self.scanqr_prefs if self.scanqr_mode else self.prefs) # needs to be done AFTER the camera is initialized + # Start refreshing: print("Camera app initialized, continuing...") self.update_preview_image() self.capture_timer = lv.timer_create(self.try_capture, 100, None) @@ -159,9 +149,7 @@ class CameraActivity(Activity): def stop_cam(self): if self.capture_timer: self.capture_timer.delete() - if self.use_webcam: - webcam.deinit(self.cam) - elif self.cam: + if self.cam: CameraManager.get_cameras()[0].deinit(self.cam) self.cam = None if self.image_dsc: # it's important to delete the image when stopping the camera, otherwise LVGL might try to display it and crash @@ -337,11 +325,10 @@ class CameraActivity(Activity): self.startActivity(intent) def try_capture(self, event): + if not self.cam: + return try: - if self.use_webcam and self.cam: - self.current_cam_buffer = webcam.capture_frame(self.cam, "rgb565" if self.colormode else "grayscale") - elif self.cam and self.cam.frame_available(): - self.current_cam_buffer = self.cam.capture() + self.current_cam_buffer = CameraManager.get_cameras()[0].capture(self.cam, self.colormode) except Exception as e: print(f"Camera capture exception: {e}") return @@ -351,8 +338,10 @@ class CameraActivity(Activity): self.image.set_src(self.image_dsc) if self.scanqr_mode: self.qrdecode_one() - if not self.use_webcam and self.cam: + try: self.cam.free_buffer() # After QR decoding, free the old buffer, otherwise the camera doesn't provide a new one + except Exception as e: + pass # some camera API's don't have this def print_qr_buffer(self, buffer): try: @@ -373,127 +362,6 @@ class CameraActivity(Activity): if buffer.startswith(bom): return buffer[3:] return buffer - - - def apply_camera_settings(self, prefs, cam, use_webcam): - """Apply all saved camera settings to the camera. - - Only applies settings when use_webcam is False (ESP32 camera). - Settings are applied in dependency order (master switches before dependent values). - - Args: - cam: Camera object - use_webcam: Boolean indicating if using webcam - """ - if not cam or use_webcam: - print("apply_camera_settings: Skipping (no camera or webcam mode)") - return - - try: - # Basic image adjustments - brightness = prefs.get_int("brightness") - cam.set_brightness(brightness) - - contrast = prefs.get_int("contrast") - cam.set_contrast(contrast) - - saturation = prefs.get_int("saturation") - cam.set_saturation(saturation) - - # Orientation - hmirror = prefs.get_bool("hmirror") - cam.set_hmirror(hmirror) - - vflip = prefs.get_bool("vflip") - cam.set_vflip(vflip) - - # Special effect - special_effect = prefs.get_int("special_effect") - cam.set_special_effect(special_effect) - - # Exposure control (apply master switch first, then manual value) - exposure_ctrl = prefs.get_bool("exposure_ctrl") - cam.set_exposure_ctrl(exposure_ctrl) - - if not exposure_ctrl: - aec_value = prefs.get_int("aec_value") - cam.set_aec_value(aec_value) - - # Mode-specific default comes from constructor - ae_level = prefs.get_int("ae_level") - cam.set_ae_level(ae_level) - - aec2 = prefs.get_bool("aec2") - cam.set_aec2(aec2) - - # Gain control (apply master switch first, then manual value) - gain_ctrl = prefs.get_bool("gain_ctrl") - cam.set_gain_ctrl(gain_ctrl) - - if not gain_ctrl: - agc_gain = prefs.get_int("agc_gain") - cam.set_agc_gain(agc_gain) - - gainceiling = prefs.get_int("gainceiling") - cam.set_gainceiling(gainceiling) - - # White balance (apply master switch first, then mode) - whitebal = prefs.get_bool("whitebal") - cam.set_whitebal(whitebal) - - if not whitebal: - wb_mode = prefs.get_int("wb_mode") - cam.set_wb_mode(wb_mode) - - awb_gain = prefs.get_bool("awb_gain") - cam.set_awb_gain(awb_gain) - - # Sensor-specific settings (try/except for unsupported sensors) - try: - sharpness = prefs.get_int("sharpness") - cam.set_sharpness(sharpness) - except: - pass # Not supported on OV2640? - - try: - denoise = prefs.get_int("denoise") - cam.set_denoise(denoise) - except: - pass # Not supported on OV2640? - - # Advanced corrections - colorbar = prefs.get_bool("colorbar") - cam.set_colorbar(colorbar) - - dcw = prefs.get_bool("dcw") - cam.set_dcw(dcw) - - bpc = prefs.get_bool("bpc") - cam.set_bpc(bpc) - - wpc = prefs.get_bool("wpc") - cam.set_wpc(wpc) - - # Mode-specific default comes from constructor - raw_gma = prefs.get_bool("raw_gma") - print(f"applying raw_gma: {raw_gma}") - cam.set_raw_gma(raw_gma) - - lenc = prefs.get_bool("lenc") - cam.set_lenc(lenc) - - # JPEG quality (only relevant for JPEG format) - #try: - # quality = prefs.get_int("quality", 85) - # cam.set_quality(quality) - #except: - # pass # Not in JPEG mode - - print("Camera settings applied successfully") - - except Exception as e: - print(f"Error applying camera settings: {e}") - """ def zoom_button_click_unused(self, e):