You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
611 lines
25 KiB
Python
611 lines
25 KiB
Python
import lvgl as lv
|
|
import time
|
|
|
|
try:
|
|
import webcam
|
|
except Exception as e:
|
|
print(f"Info: could not import webcam module: {e}")
|
|
|
|
import mpos.time
|
|
from mpos.apps import Activity
|
|
from mpos.content.intent import Intent
|
|
|
|
from camera_settings import CameraSettingsActivity
|
|
|
|
class CameraApp(Activity):
|
|
|
|
PACKAGE = "com.micropythonos.camera"
|
|
CONFIGFILE = "config.json"
|
|
SCANQR_CONFIG = "config_scanqr_mode.json"
|
|
|
|
button_width = 75
|
|
button_height = 50
|
|
|
|
STATUS_NO_CAMERA = "No camera found."
|
|
STATUS_SEARCHING_QR = "Searching QR codes...\n\nHold still and try varying scan distance (10-25cm) and make the QR code big (4-12cm). Ensure proper lighting."
|
|
STATUS_FOUND_QR = "Found QR, trying to decode... hold still..."
|
|
|
|
cam = None
|
|
current_cam_buffer = None # Holds the current memoryview to prevent garba
|
|
width = None
|
|
height = None
|
|
colormode = False
|
|
|
|
image_dsc = None
|
|
scanqr_mode = False
|
|
scanqr_intent = False
|
|
use_webcam = False
|
|
capture_timer = None
|
|
|
|
prefs = None # regular prefs
|
|
scanqr_prefs = None # qr code scanning prefs
|
|
|
|
# Widgets:
|
|
main_screen = None
|
|
image = None
|
|
qr_label = None
|
|
qr_button = None
|
|
snap_button = None
|
|
status_label = None
|
|
status_label_cont = None
|
|
|
|
def onCreate(self):
|
|
self.main_screen = lv.obj()
|
|
self.main_screen.set_style_pad_all(1, 0)
|
|
self.main_screen.set_style_border_width(0, 0)
|
|
self.main_screen.set_size(lv.pct(100), lv.pct(100))
|
|
self.main_screen.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)
|
|
# Initialize LVGL image widget
|
|
self.image = lv.image(self.main_screen)
|
|
self.image.align(lv.ALIGN.LEFT_MID, 0, 0)
|
|
close_button = lv.button(self.main_screen)
|
|
close_button.set_size(self.button_width, self.button_height)
|
|
close_button.align(lv.ALIGN.TOP_RIGHT, 0, 0)
|
|
close_label = lv.label(close_button)
|
|
close_label.set_text(lv.SYMBOL.CLOSE)
|
|
close_label.center()
|
|
close_button.add_event_cb(lambda e: self.finish(),lv.EVENT.CLICKED,None)
|
|
# Settings button
|
|
settings_button = lv.button(self.main_screen)
|
|
settings_button.set_size(self.button_width, self.button_height)
|
|
settings_button.align_to(close_button, lv.ALIGN.OUT_BOTTOM_MID, 0, 10)
|
|
settings_label = lv.label(settings_button)
|
|
settings_label.set_text(lv.SYMBOL.SETTINGS)
|
|
settings_label.center()
|
|
settings_button.add_event_cb(lambda e: self.open_settings(),lv.EVENT.CLICKED,None)
|
|
#self.zoom_button = lv.button(self.main_screen)
|
|
#self.zoom_button.set_size(self.button_width, self.button_height)
|
|
#self.zoom_button.align(lv.ALIGN.RIGHT_MID, 0, self.button_height + 5)
|
|
#self.zoom_button.add_event_cb(self.zoom_button_click,lv.EVENT.CLICKED,None)
|
|
#zoom_label = lv.label(self.zoom_button)
|
|
#zoom_label.set_text("Z")
|
|
#zoom_label.center()
|
|
self.qr_button = lv.button(self.main_screen)
|
|
self.qr_button.set_size(self.button_width, self.button_height)
|
|
self.qr_button.add_flag(lv.obj.FLAG.HIDDEN)
|
|
self.qr_button.align(lv.ALIGN.BOTTOM_RIGHT, 0, 0)
|
|
self.qr_button.add_event_cb(self.qr_button_click,lv.EVENT.CLICKED,None)
|
|
self.qr_label = lv.label(self.qr_button)
|
|
self.qr_label.set_text(lv.SYMBOL.EYE_OPEN)
|
|
self.qr_label.center()
|
|
|
|
self.snap_button = lv.button(self.main_screen)
|
|
self.snap_button.set_size(self.button_width, self.button_height)
|
|
self.snap_button.align_to(self.qr_button, lv.ALIGN.OUT_TOP_MID, 0, -10)
|
|
self.snap_button.add_flag(lv.obj.FLAG.HIDDEN)
|
|
self.snap_button.add_event_cb(self.snap_button_click,lv.EVENT.CLICKED,None)
|
|
snap_label = lv.label(self.snap_button)
|
|
snap_label.set_text(lv.SYMBOL.OK)
|
|
snap_label.center()
|
|
|
|
|
|
self.status_label_cont = lv.obj(self.main_screen)
|
|
width = mpos.ui.pct_of_display_width(70)
|
|
height = mpos.ui.pct_of_display_width(60)
|
|
self.status_label_cont.set_size(width,height)
|
|
center_w = round((mpos.ui.pct_of_display_width(100) - self.button_width - 5 - width)/2)
|
|
center_h = round((mpos.ui.pct_of_display_height(100) - height)/2)
|
|
self.status_label_cont.set_pos(center_w,center_h)
|
|
self.status_label_cont.set_style_bg_color(lv.color_white(), 0)
|
|
self.status_label_cont.set_style_bg_opa(66, 0)
|
|
self.status_label_cont.set_style_border_width(0, 0)
|
|
self.status_label = lv.label(self.status_label_cont)
|
|
self.status_label.set_text(self.STATUS_NO_CAMERA)
|
|
self.status_label.set_long_mode(lv.label.LONG_MODE.WRAP)
|
|
self.status_label.set_width(lv.pct(100))
|
|
self.status_label.center()
|
|
self.setContentView(self.main_screen)
|
|
|
|
def onResume(self, screen):
|
|
self.scanqr_intent = self.getIntent().extras.get("scanqr_intent")
|
|
self.status_label_cont.add_flag(lv.obj.FLAG.HIDDEN)
|
|
if self.scanqr_mode or self.scanqr_intent:
|
|
self.start_qr_decoding()
|
|
if not self.cam and self.scanqr_mode:
|
|
print("No camera found, stopping camera app")
|
|
self.finish()
|
|
else:
|
|
self.load_settings_cached()
|
|
self.start_cam()
|
|
self.qr_button.remove_flag(lv.obj.FLAG.HIDDEN)
|
|
self.snap_button.remove_flag(lv.obj.FLAG.HIDDEN)
|
|
|
|
def onPause(self, screen):
|
|
print("camera app backgrounded, cleaning up...")
|
|
self.stop_cam()
|
|
print("camera app cleanup done.")
|
|
|
|
def start_cam(self):
|
|
# Init camera:
|
|
self.cam = self.init_internal_cam(self.width, self.height)
|
|
if self.cam:
|
|
self.image.set_rotation(900) # internal camera is rotated 90 degrees
|
|
# 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:
|
|
print("Camera app initialized, continuing...")
|
|
self.update_preview_image()
|
|
self.capture_timer = lv.timer_create(self.try_capture, 100, None)
|
|
|
|
def stop_cam(self):
|
|
if self.capture_timer:
|
|
self.capture_timer.delete()
|
|
if self.use_webcam:
|
|
webcam.deinit(self.cam)
|
|
elif self.cam:
|
|
self.cam.deinit()
|
|
# Power off, otherwise it keeps using a lot of current
|
|
try:
|
|
from machine import Pin, I2C
|
|
i2c = I2C(1, scl=Pin(16), sda=Pin(21)) # Adjust pins and frequency
|
|
#devices = i2c.scan()
|
|
#print([hex(addr) for addr in devices]) # finds it on 60 = 0x3C after init
|
|
camera_addr = 0x3C # for OV5640
|
|
reg_addr = 0x3008
|
|
reg_high = (reg_addr >> 8) & 0xFF # 0x30
|
|
reg_low = reg_addr & 0xFF # 0x08
|
|
power_off_command = 0x42 # Power off command
|
|
i2c.writeto(camera_addr, bytes([reg_high, reg_low, power_off_command]))
|
|
except Exception as e:
|
|
print(f"Warning: powering off camera got exception: {e}")
|
|
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
|
|
print("emptying self.current_cam_buffer...")
|
|
self.image_dsc.data = None
|
|
|
|
def load_settings_cached(self):
|
|
from mpos.config import SharedPreferences
|
|
if self.scanqr_mode:
|
|
print("loading scanqr settings...")
|
|
if not self.scanqr_prefs:
|
|
# Merge common and scanqr-specific defaults
|
|
scanqr_defaults = {}
|
|
scanqr_defaults.update(CameraSettingsActivity.COMMON_DEFAULTS)
|
|
scanqr_defaults.update(CameraSettingsActivity.SCANQR_DEFAULTS)
|
|
self.scanqr_prefs = SharedPreferences(
|
|
self.PACKAGE,
|
|
filename=self.SCANQR_CONFIG,
|
|
defaults=scanqr_defaults
|
|
)
|
|
# Defaults come from constructor, no need to pass them here
|
|
self.width = self.scanqr_prefs.get_int("resolution_width")
|
|
self.height = self.scanqr_prefs.get_int("resolution_height")
|
|
self.colormode = self.scanqr_prefs.get_bool("colormode")
|
|
else:
|
|
if not self.prefs:
|
|
# Merge common and normal-specific defaults
|
|
normal_defaults = {}
|
|
normal_defaults.update(CameraSettingsActivity.COMMON_DEFAULTS)
|
|
normal_defaults.update(CameraSettingsActivity.NORMAL_DEFAULTS)
|
|
self.prefs = SharedPreferences(self.PACKAGE, defaults=normal_defaults)
|
|
# Defaults come from constructor, no need to pass them here
|
|
self.width = self.prefs.get_int("resolution_width")
|
|
self.height = self.prefs.get_int("resolution_height")
|
|
self.colormode = self.prefs.get_bool("colormode")
|
|
|
|
def update_preview_image(self):
|
|
self.image_dsc = lv.image_dsc_t({
|
|
"header": {
|
|
"magic": lv.IMAGE_HEADER_MAGIC,
|
|
"w": self.width,
|
|
"h": self.height,
|
|
"stride": self.width * (2 if self.colormode else 1),
|
|
"cf": lv.COLOR_FORMAT.RGB565 if self.colormode else lv.COLOR_FORMAT.L8
|
|
},
|
|
'data_size': self.width * self.height * (2 if self.colormode else 1),
|
|
'data': None # Will be updated per frame
|
|
})
|
|
self.image.set_src(self.image_dsc)
|
|
disp = lv.display_get_default()
|
|
target_h = disp.get_vertical_resolution()
|
|
#target_w = disp.get_horizontal_resolution() - self.button_width - 5 # leave 5px for border
|
|
target_w = target_h # square
|
|
print(f"scaling to size: {target_w}x{target_h}")
|
|
scale_factor_w = round(target_w * 256 / self.width)
|
|
scale_factor_h = round(target_h * 256 / self.height)
|
|
print(f"scale_factors: {scale_factor_w},{scale_factor_h}")
|
|
self.image.set_size(target_w, target_h)
|
|
#self.image.set_scale(max(scale_factor_w,scale_factor_h)) # fills the entire screen but cuts off borders
|
|
self.image.set_scale(min(scale_factor_w,scale_factor_h))
|
|
|
|
def qrdecode_one(self):
|
|
try:
|
|
result = None
|
|
before = time.ticks_ms()
|
|
import qrdecode
|
|
if self.colormode:
|
|
result = qrdecode.qrdecode_rgb565(self.current_cam_buffer, self.width, self.height)
|
|
else:
|
|
result = qrdecode.qrdecode(self.current_cam_buffer, self.width, self.height)
|
|
after = time.ticks_ms()
|
|
print(f"qrdecode took {after-before}ms")
|
|
except ValueError as e:
|
|
print("QR ValueError: ", e)
|
|
self.status_label.set_text(self.STATUS_SEARCHING_QR)
|
|
except TypeError as e:
|
|
print("QR TypeError: ", e)
|
|
self.status_label.set_text(self.STATUS_FOUND_QR)
|
|
except Exception as e:
|
|
print("QR got other error: ", e)
|
|
#result = bytearray("INSERT_TEST_QR_DATA_HERE", "utf-8")
|
|
if result is None:
|
|
return
|
|
result = self.remove_bom(result)
|
|
result = self.print_qr_buffer(result)
|
|
print(f"QR decoding found: {result}")
|
|
self.stop_qr_decoding()
|
|
if self.scanqr_intent:
|
|
self.setResult(True, result)
|
|
self.finish()
|
|
else:
|
|
self.status_label.set_text(result) # in the future, the status_label text should be copy-paste-able
|
|
|
|
def snap_button_click(self, e):
|
|
print("Taking picture...")
|
|
# Would be nice to check that there's enough free space here, and show an error if not...
|
|
import os
|
|
path = "data/images"
|
|
try:
|
|
os.mkdir("data")
|
|
except OSError:
|
|
pass
|
|
try:
|
|
os.mkdir(path)
|
|
except OSError:
|
|
pass
|
|
if self.current_cam_buffer is None:
|
|
print("snap_button_click: won't save empty image")
|
|
return
|
|
# Check enough free space?
|
|
stat = os.statvfs("data/images")
|
|
free_space = stat[0] * stat[3]
|
|
size_needed = len(self.current_cam_buffer)
|
|
print(f"Free space {free_space} and size needed {size_needed}")
|
|
if free_space < size_needed:
|
|
self.status_label.set_text(f"Free storage space is {free_space}, need {size_needed}, not saving...")
|
|
self.status_label_cont.remove_flag(lv.obj.FLAG.HIDDEN)
|
|
return
|
|
colorname = "RGB565" if self.colormode else "GRAY"
|
|
filename=f"{path}/picture_{mpos.time.epoch_seconds()}_{self.width}x{self.height}_{colorname}.raw"
|
|
try:
|
|
with open(filename, 'wb') as f:
|
|
f.write(self.current_cam_buffer) # This takes around 17 seconds to store 921600 bytes, so ~50KB/s, so would be nice to show some progress bar
|
|
report = f"Successfully wrote image to {filename}"
|
|
print(report)
|
|
self.status_label.set_text(report)
|
|
self.status_label_cont.remove_flag(lv.obj.FLAG.HIDDEN)
|
|
except OSError as e:
|
|
print(f"Error writing to file: {e}")
|
|
|
|
def start_qr_decoding(self):
|
|
print("Activating live QR decoding...")
|
|
self.scanqr_mode = True
|
|
oldwidth = self.width
|
|
oldheight = self.height
|
|
oldcolormode = self.colormode
|
|
# Activate QR mode settings
|
|
self.load_settings_cached()
|
|
# Check if it's necessary to restart the camera:
|
|
if not self.cam or self.width != oldwidth or self.height != oldheight or self.colormode != oldcolormode:
|
|
if self.cam:
|
|
self.stop_cam()
|
|
self.start_cam()
|
|
self.qr_label.set_text(lv.SYMBOL.EYE_CLOSE)
|
|
self.status_label_cont.remove_flag(lv.obj.FLAG.HIDDEN)
|
|
self.status_label.set_text(self.STATUS_SEARCHING_QR)
|
|
|
|
def stop_qr_decoding(self):
|
|
print("Deactivating live QR decoding...")
|
|
self.scanqr_mode = False
|
|
self.qr_label.set_text(lv.SYMBOL.EYE_OPEN)
|
|
status_label_text = self.status_label.get_text()
|
|
if status_label_text in (self.STATUS_NO_CAMERA, self.STATUS_SEARCHING_QR, self.STATUS_FOUND_QR): # if it found a QR code, leave it
|
|
self.status_label_cont.add_flag(lv.obj.FLAG.HIDDEN)
|
|
# Check if it's necessary to restart the camera:
|
|
oldwidth = self.width
|
|
oldheight = self.height
|
|
oldcolormode = self.colormode
|
|
# Activate non-QR mode settings
|
|
self.load_settings_cached()
|
|
# Check if it's necessary to restart the camera:
|
|
if self.width != oldwidth or self.height != oldheight or self.colormode != oldcolormode:
|
|
self.stop_cam()
|
|
self.start_cam()
|
|
|
|
def qr_button_click(self, e):
|
|
if not self.scanqr_mode:
|
|
self.start_qr_decoding()
|
|
else:
|
|
self.stop_qr_decoding()
|
|
|
|
def open_settings(self):
|
|
intent = Intent(activity_class=CameraSettingsActivity, extras={"prefs": self.prefs if not self.scanqr_mode else self.scanqr_prefs, "use_webcam": self.use_webcam, "scanqr_mode": self.scanqr_mode})
|
|
self.startActivity(intent)
|
|
|
|
def try_capture(self, event):
|
|
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()
|
|
except Exception as e:
|
|
print(f"Camera capture exception: {e}")
|
|
return
|
|
# Display the image:
|
|
self.image_dsc.data = self.current_cam_buffer
|
|
#self.image.invalidate() # does not work so do this:
|
|
self.image.set_src(self.image_dsc)
|
|
if self.scanqr_mode:
|
|
self.qrdecode_one()
|
|
if not self.use_webcam and self.cam:
|
|
self.cam.free_buffer() # After QR decoding, free the old buffer, otherwise the camera doesn't provide a new one
|
|
|
|
def init_internal_cam(self, width, height):
|
|
"""Initialize internal camera with specified resolution.
|
|
|
|
Automatically retries once if initialization fails (to handle I2C poweroff issue).
|
|
"""
|
|
try:
|
|
from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling
|
|
|
|
# Map resolution to FrameSize enum
|
|
# Format: (width, height): FrameSize
|
|
resolution_map = {
|
|
(96, 96): FrameSize.R96X96,
|
|
(160, 120): FrameSize.QQVGA,
|
|
(128, 128): FrameSize.R128X128,
|
|
(176, 144): FrameSize.QCIF,
|
|
(240, 176): FrameSize.HQVGA,
|
|
(240, 240): FrameSize.R240X240,
|
|
(320, 240): FrameSize.QVGA,
|
|
(320, 320): FrameSize.R320X320,
|
|
(400, 296): FrameSize.CIF,
|
|
(480, 320): FrameSize.HVGA,
|
|
(480, 480): FrameSize.R480X480,
|
|
(640, 480): FrameSize.VGA,
|
|
(640, 640): FrameSize.R640X640,
|
|
(720, 720): FrameSize.R720X720,
|
|
(800, 600): FrameSize.SVGA,
|
|
(800, 800): FrameSize.R800X800,
|
|
(960, 960): FrameSize.R960X960,
|
|
(1024, 768): FrameSize.XGA,
|
|
(1024,1024): FrameSize.R1024X1024,
|
|
(1280, 720): FrameSize.HD,
|
|
(1280, 1024): FrameSize.SXGA,
|
|
(1280, 1280): FrameSize.R1280X1280,
|
|
(1600, 1200): FrameSize.UXGA,
|
|
(1920, 1080): FrameSize.FHD,
|
|
}
|
|
|
|
frame_size = resolution_map.get((width, height), FrameSize.QVGA)
|
|
print(f"init_internal_cam: Using FrameSize for {width}x{height}")
|
|
|
|
# Try to initialize, with one retry for I2C poweroff issue
|
|
max_attempts = 3
|
|
for attempt in range(max_attempts):
|
|
try:
|
|
cam = Camera(
|
|
data_pins=[12,13,15,11,14,10,7,2],
|
|
vsync_pin=6,
|
|
href_pin=4,
|
|
sda_pin=21,
|
|
scl_pin=16,
|
|
pclk_pin=9,
|
|
xclk_pin=8,
|
|
xclk_freq=20000000,
|
|
powerdown_pin=-1,
|
|
reset_pin=-1,
|
|
pixel_format=PixelFormat.RGB565 if self.colormode else PixelFormat.GRAYSCALE,
|
|
frame_size=frame_size,
|
|
#grab_mode=GrabMode.WHEN_EMPTY,
|
|
grab_mode=GrabMode.LATEST,
|
|
fb_count=1
|
|
)
|
|
cam.set_vflip(True)
|
|
return cam
|
|
except Exception as e:
|
|
if attempt < max_attempts-1:
|
|
print(f"init_cam attempt {attempt} failed: {e}, retrying...")
|
|
else:
|
|
print(f"init_cam final exception: {e}")
|
|
return None
|
|
except Exception as e:
|
|
print(f"init_cam exception: {e}")
|
|
return None
|
|
|
|
def print_qr_buffer(self, buffer):
|
|
try:
|
|
# Try to decode buffer as a UTF-8 string
|
|
result = buffer.decode('utf-8')
|
|
# Check if the string is printable (ASCII printable characters)
|
|
if all(32 <= ord(c) <= 126 for c in result):
|
|
return result
|
|
except Exception as e:
|
|
pass
|
|
# If not a valid string or not printable, convert to hex
|
|
hex_str = ' '.join([f'{b:02x}' for b in buffer])
|
|
return hex_str.lower()
|
|
|
|
# Byte-Order-Mark is added sometimes
|
|
def remove_bom(self, buffer):
|
|
bom = b'\xEF\xBB\xBF'
|
|
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):
|
|
print("zooming...")
|
|
if self.use_webcam:
|
|
print("zoom_button_click is not supported for webcam")
|
|
return
|
|
if self.cam:
|
|
startX = self.prefs.get_int("startX", CameraSettingsActivity.startX_default)
|
|
startY = self.prefs.get_int("startX", CameraSettingsActivity.startY_default)
|
|
endX = self.prefs.get_int("startX", CameraSettingsActivity.endX_default)
|
|
endY = self.prefs.get_int("startX", CameraSettingsActivity.endY_default)
|
|
offsetX = self.prefs.get_int("startX", CameraSettingsActivity.offsetX_default)
|
|
offsetY = self.prefs.get_int("startX", CameraSettingsActivity.offsetY_default)
|
|
totalX = self.prefs.get_int("startX", CameraSettingsActivity.totalX_default)
|
|
totalY = self.prefs.get_int("startX", CameraSettingsActivity.totalY_default)
|
|
outputX = self.prefs.get_int("startX", CameraSettingsActivity.outputX_default)
|
|
outputY = self.prefs.get_int("startX", CameraSettingsActivity.outputY_default)
|
|
scale = self.prefs.get_bool("scale", CameraSettingsActivity.scale_default)
|
|
binning = self.prefs.get_bool("binning", CameraSettingsActivity.binning_default)
|
|
result = self.cam.set_res_raw(startX,startY,endX,endY,offsetX,offsetY,totalX,totalY,outputX,outputY,scale,binning)
|
|
print(f"self.cam.set_res_raw returned {result}")
|
|
"""
|