You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Work towards changing camera resolution
This commit is contained in:
+116
-24
@@ -30,17 +30,24 @@ typedef struct _webcam_obj_t {
|
||||
int frame_count;
|
||||
unsigned char *gray_buffer; // For grayscale
|
||||
uint16_t *rgb565_buffer; // For RGB565
|
||||
int input_width; // Webcam capture width (from V4L2)
|
||||
int input_height; // Webcam capture height (from V4L2)
|
||||
int output_width; // Configurable output width (default OUTPUT_WIDTH)
|
||||
int output_height; // Configurable output height (default OUTPUT_HEIGHT)
|
||||
} webcam_obj_t;
|
||||
|
||||
static void yuyv_to_rgb565_240x240(unsigned char *yuyv, uint16_t *rgb565, int in_width, int in_height) {
|
||||
int crop_size = 480;
|
||||
static void yuyv_to_rgb565(unsigned char *yuyv, uint16_t *rgb565, int in_width, int in_height, int out_width, int out_height) {
|
||||
// Crop to largest square that fits in the input frame
|
||||
int crop_size = (in_width < in_height) ? in_width : in_height;
|
||||
int crop_x_offset = (in_width - crop_size) / 2;
|
||||
int crop_y_offset = (in_height - crop_size) / 2;
|
||||
float x_ratio = (float)crop_size / OUTPUT_WIDTH;
|
||||
float y_ratio = (float)crop_size / OUTPUT_HEIGHT;
|
||||
|
||||
for (int y = 0; y < OUTPUT_HEIGHT; y++) {
|
||||
for (int x = 0; x < OUTPUT_WIDTH; x++) {
|
||||
// Calculate scaling ratios
|
||||
float x_ratio = (float)crop_size / out_width;
|
||||
float y_ratio = (float)crop_size / out_height;
|
||||
|
||||
for (int y = 0; y < out_height; y++) {
|
||||
for (int x = 0; x < out_width; x++) {
|
||||
int src_x = (int)(x * x_ratio) + crop_x_offset;
|
||||
int src_y = (int)(y * y_ratio) + crop_y_offset;
|
||||
int src_index = (src_y * in_width + src_x) * 2;
|
||||
@@ -65,24 +72,27 @@ static void yuyv_to_rgb565_240x240(unsigned char *yuyv, uint16_t *rgb565, int in
|
||||
uint16_t g6 = (g >> 2) & 0x3F;
|
||||
uint16_t b5 = (b >> 3) & 0x1F;
|
||||
|
||||
rgb565[y * OUTPUT_WIDTH + x] = (r5 << 11) | (g6 << 5) | b5;
|
||||
rgb565[y * out_width + x] = (r5 << 11) | (g6 << 5) | b5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void yuyv_to_grayscale_240x240(unsigned char *yuyv, unsigned char *gray, int in_width, int in_height) {
|
||||
int crop_size = 480;
|
||||
static void yuyv_to_grayscale(unsigned char *yuyv, unsigned char *gray, int in_width, int in_height, int out_width, int out_height) {
|
||||
// Crop to largest square that fits in the input frame
|
||||
int crop_size = (in_width < in_height) ? in_width : in_height;
|
||||
int crop_x_offset = (in_width - crop_size) / 2;
|
||||
int crop_y_offset = (in_height - crop_size) / 2;
|
||||
float x_ratio = (float)crop_size / OUTPUT_WIDTH;
|
||||
float y_ratio = (float)crop_size / OUTPUT_HEIGHT;
|
||||
|
||||
for (int y = 0; y < OUTPUT_HEIGHT; y++) {
|
||||
for (int x = 0; x < OUTPUT_WIDTH; x++) {
|
||||
// Calculate scaling ratios
|
||||
float x_ratio = (float)crop_size / out_width;
|
||||
float y_ratio = (float)crop_size / out_height;
|
||||
|
||||
for (int y = 0; y < out_height; y++) {
|
||||
for (int x = 0; x < out_width; x++) {
|
||||
int src_x = (int)(x * x_ratio) + crop_x_offset;
|
||||
int src_y = (int)(y * y_ratio) + crop_y_offset;
|
||||
int src_index = (src_y * in_width + src_x) * 2;
|
||||
gray[y * OUTPUT_WIDTH + x] = yuyv[src_index];
|
||||
gray[y * out_width + x] = yuyv[src_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,8 +184,22 @@ static int init_webcam(webcam_obj_t *self, const char *device) {
|
||||
}
|
||||
|
||||
self->frame_count = 0;
|
||||
self->gray_buffer = (unsigned char *)malloc(OUTPUT_WIDTH * OUTPUT_HEIGHT * sizeof(unsigned char));
|
||||
self->rgb565_buffer = (uint16_t *)malloc(OUTPUT_WIDTH * OUTPUT_HEIGHT * sizeof(uint16_t));
|
||||
|
||||
// Store the input dimensions from V4L2 format
|
||||
self->input_width = WIDTH;
|
||||
self->input_height = HEIGHT;
|
||||
|
||||
// Initialize output dimensions with defaults if not already set
|
||||
if (self->output_width == 0) self->output_width = OUTPUT_WIDTH;
|
||||
if (self->output_height == 0) self->output_height = OUTPUT_HEIGHT;
|
||||
|
||||
WEBCAM_DEBUG_PRINT("Webcam initialized: input %dx%d, output %dx%d\n",
|
||||
self->input_width, self->input_height,
|
||||
self->output_width, self->output_height);
|
||||
|
||||
// Allocate buffers with configured output dimensions
|
||||
self->gray_buffer = (unsigned char *)malloc(self->output_width * self->output_height * sizeof(unsigned char));
|
||||
self->rgb565_buffer = (uint16_t *)malloc(self->output_width * self->output_height * sizeof(uint16_t));
|
||||
if (!self->gray_buffer || !self->rgb565_buffer) {
|
||||
WEBCAM_DEBUG_PRINT("Cannot allocate buffers: %s\n", strerror(errno));
|
||||
free(self->gray_buffer);
|
||||
@@ -227,13 +251,13 @@ static mp_obj_t capture_frame(mp_obj_t self_in, mp_obj_t format) {
|
||||
}
|
||||
|
||||
if (!self->gray_buffer) {
|
||||
self->gray_buffer = (unsigned char *)malloc(OUTPUT_WIDTH * OUTPUT_HEIGHT * sizeof(unsigned char));
|
||||
self->gray_buffer = (unsigned char *)malloc(self->output_width * self->output_height * sizeof(unsigned char));
|
||||
if (!self->gray_buffer) {
|
||||
mp_raise_OSError(MP_ENOMEM);
|
||||
}
|
||||
}
|
||||
if (!self->rgb565_buffer) {
|
||||
self->rgb565_buffer = (uint16_t *)malloc(OUTPUT_WIDTH * OUTPUT_HEIGHT * sizeof(uint16_t));
|
||||
self->rgb565_buffer = (uint16_t *)malloc(self->output_width * self->output_height * sizeof(uint16_t));
|
||||
if (!self->rgb565_buffer) {
|
||||
mp_raise_OSError(MP_ENOMEM);
|
||||
}
|
||||
@@ -241,22 +265,26 @@ static mp_obj_t capture_frame(mp_obj_t self_in, mp_obj_t format) {
|
||||
|
||||
const char *fmt = mp_obj_str_get_str(format);
|
||||
if (strcmp(fmt, "grayscale") == 0) {
|
||||
yuyv_to_grayscale_240x240(self->buffers[buf.index], self->gray_buffer, WIDTH, HEIGHT);
|
||||
yuyv_to_grayscale(self->buffers[buf.index], self->gray_buffer,
|
||||
self->input_width, self->input_height,
|
||||
self->output_width, self->output_height);
|
||||
// char filename[32];
|
||||
// snprintf(filename, sizeof(filename), "frame_%03d.raw", self->frame_count++);
|
||||
// save_raw(filename, self->gray_buffer, OUTPUT_WIDTH, OUTPUT_HEIGHT);
|
||||
mp_obj_t result = mp_obj_new_memoryview('b', OUTPUT_WIDTH * OUTPUT_HEIGHT, self->gray_buffer);
|
||||
// save_raw(filename, self->gray_buffer, self->output_width, self->output_height);
|
||||
mp_obj_t result = mp_obj_new_memoryview('b', self->output_width * self->output_height, self->gray_buffer);
|
||||
res = ioctl(self->fd, VIDIOC_QBUF, &buf);
|
||||
if (res < 0) {
|
||||
mp_raise_OSError(-res);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
yuyv_to_rgb565_240x240(self->buffers[buf.index], self->rgb565_buffer, WIDTH, HEIGHT);
|
||||
yuyv_to_rgb565(self->buffers[buf.index], self->rgb565_buffer,
|
||||
self->input_width, self->input_height,
|
||||
self->output_width, self->output_height);
|
||||
// char filename[32];
|
||||
// snprintf(filename, sizeof(filename), "frame_%03d.rgb565", self->frame_count++);
|
||||
// save_raw_rgb565(filename, self->rgb565_buffer, OUTPUT_WIDTH, OUTPUT_HEIGHT);
|
||||
mp_obj_t result = mp_obj_new_memoryview('b', OUTPUT_WIDTH * OUTPUT_HEIGHT * 2, self->rgb565_buffer);
|
||||
// save_raw_rgb565(filename, self->rgb565_buffer, self->output_width, self->output_height);
|
||||
mp_obj_t result = mp_obj_new_memoryview('b', self->output_width * self->output_height * 2, self->rgb565_buffer);
|
||||
res = ioctl(self->fd, VIDIOC_QBUF, &buf);
|
||||
if (res < 0) {
|
||||
mp_raise_OSError(-res);
|
||||
@@ -277,6 +305,10 @@ static mp_obj_t webcam_init(size_t n_args, const mp_obj_t *args) {
|
||||
self->fd = -1;
|
||||
self->gray_buffer = NULL;
|
||||
self->rgb565_buffer = NULL;
|
||||
self->input_width = 0; // Will be set from V4L2 format in init_webcam
|
||||
self->input_height = 0; // Will be set from V4L2 format in init_webcam
|
||||
self->output_width = 0; // Will use default OUTPUT_WIDTH in init_webcam
|
||||
self->output_height = 0; // Will use default OUTPUT_HEIGHT in init_webcam
|
||||
|
||||
int res = init_webcam(self, device);
|
||||
if (res < 0) {
|
||||
@@ -309,6 +341,65 @@ static mp_obj_t webcam_capture_frame(mp_obj_t self_in, mp_obj_t format) {
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(webcam_capture_frame_obj, webcam_capture_frame);
|
||||
|
||||
static mp_obj_t webcam_reconfigure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
// NOTE: This function only changes OUTPUT resolution (what Python receives).
|
||||
// The INPUT resolution (what the webcam captures from V4L2) remains fixed at 640x480.
|
||||
// The conversion functions will crop/scale from input to output resolution.
|
||||
// TODO: Add support for changing input resolution (requires V4L2 reinit)
|
||||
|
||||
enum { ARG_self, ARG_output_width, ARG_output_height };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_self, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
{ MP_QSTR_output_width, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_output_height, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
webcam_obj_t *self = MP_OBJ_TO_PTR(args[ARG_self].u_obj);
|
||||
|
||||
// Get new dimensions (keep current if not specified)
|
||||
int new_width = args[ARG_output_width].u_int;
|
||||
int new_height = args[ARG_output_height].u_int;
|
||||
|
||||
if (new_width == 0) new_width = self->output_width;
|
||||
if (new_height == 0) new_height = self->output_height;
|
||||
|
||||
// Validate dimensions
|
||||
if (new_width <= 0 || new_height <= 0 || new_width > 1920 || new_height > 1920) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("Invalid output dimensions"));
|
||||
}
|
||||
|
||||
// If dimensions changed, reallocate buffers
|
||||
if (new_width != self->output_width || new_height != self->output_height) {
|
||||
// Free old buffers
|
||||
free(self->gray_buffer);
|
||||
free(self->rgb565_buffer);
|
||||
|
||||
// Update dimensions
|
||||
self->output_width = new_width;
|
||||
self->output_height = new_height;
|
||||
|
||||
// Allocate new buffers
|
||||
self->gray_buffer = (unsigned char *)malloc(self->output_width * self->output_height * sizeof(unsigned char));
|
||||
self->rgb565_buffer = (uint16_t *)malloc(self->output_width * self->output_height * sizeof(uint16_t));
|
||||
|
||||
if (!self->gray_buffer || !self->rgb565_buffer) {
|
||||
free(self->gray_buffer);
|
||||
free(self->rgb565_buffer);
|
||||
self->gray_buffer = NULL;
|
||||
self->rgb565_buffer = NULL;
|
||||
mp_raise_OSError(MP_ENOMEM);
|
||||
}
|
||||
|
||||
WEBCAM_DEBUG_PRINT("Webcam reconfigured to %dx%d\n", self->output_width, self->output_height);
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(webcam_reconfigure_obj, 1, webcam_reconfigure);
|
||||
|
||||
static const mp_obj_type_t webcam_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_Webcam,
|
||||
@@ -321,6 +412,7 @@ static const mp_rom_map_elem_t mp_module_webcam_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_capture_frame), MP_ROM_PTR(&webcam_capture_frame_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&webcam_deinit_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_free_buffer), MP_ROM_PTR(&webcam_free_buffer_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_reconfigure), MP_ROM_PTR(&webcam_reconfigure_obj) },
|
||||
};
|
||||
static MP_DEFINE_CONST_DICT(mp_module_webcam_globals, mp_module_webcam_globals_table);
|
||||
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
"category": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"entrypoint": "assets/camera_app.py",
|
||||
"classname": "CameraSettingsActivity",
|
||||
"intent_filters": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ except Exception as e:
|
||||
print(f"Info: could not import webcam module: {e}")
|
||||
|
||||
from mpos.apps import Activity
|
||||
from mpos.config import SharedPreferences
|
||||
from mpos.content.intent import Intent
|
||||
import mpos.time
|
||||
|
||||
class CameraApp(Activity):
|
||||
@@ -20,6 +22,9 @@ class CameraApp(Activity):
|
||||
width = 240
|
||||
height = 240
|
||||
|
||||
# Resolution preferences
|
||||
prefs = None
|
||||
|
||||
status_label_text = "No camera found."
|
||||
status_label_text_searching = "Searching QR codes...\n\nHold still and try varying scan distance (10-25cm) and QR size (4-12cm). Ensure proper lighting."
|
||||
status_label_text_found = "Decoding QR..."
|
||||
@@ -42,7 +47,24 @@ class CameraApp(Activity):
|
||||
status_label = None
|
||||
status_label_cont = None
|
||||
|
||||
def load_resolution_preference(self):
|
||||
"""Load resolution preference from SharedPreferences and update width/height."""
|
||||
if not self.prefs:
|
||||
self.prefs = SharedPreferences("com.micropythonos.camera")
|
||||
|
||||
resolution_str = self.prefs.get_string("resolution", "240x240")
|
||||
try:
|
||||
width_str, height_str = resolution_str.split('x')
|
||||
self.width = int(width_str)
|
||||
self.height = int(height_str)
|
||||
print(f"Camera resolution loaded: {self.width}x{self.height}")
|
||||
except Exception as e:
|
||||
print(f"Error parsing resolution '{resolution_str}': {e}, using default 240x240")
|
||||
self.width = 240
|
||||
self.height = 240
|
||||
|
||||
def onCreate(self):
|
||||
self.load_resolution_preference()
|
||||
self.scanqr_mode = self.getIntent().extras.get("scanqr_mode")
|
||||
main_screen = lv.obj()
|
||||
main_screen.set_style_pad_all(0, 0)
|
||||
@@ -56,6 +78,16 @@ class CameraApp(Activity):
|
||||
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(main_screen)
|
||||
settings_button.set_size(60,60)
|
||||
settings_button.align(lv.ALIGN.TOP_LEFT, 0, 0)
|
||||
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.snap_button = lv.button(main_screen)
|
||||
self.snap_button.set_size(60, 60)
|
||||
self.snap_button.align(lv.ALIGN.RIGHT_MID, 0, 0)
|
||||
@@ -103,10 +135,10 @@ class CameraApp(Activity):
|
||||
self.setContentView(main_screen)
|
||||
|
||||
def onResume(self, screen):
|
||||
self.cam = init_internal_cam()
|
||||
self.cam = init_internal_cam(self.width, self.height)
|
||||
if not self.cam:
|
||||
# try again because the manual i2c poweroff leaves it in a bad state
|
||||
self.cam = init_internal_cam()
|
||||
self.cam = init_internal_cam(self.width, self.height)
|
||||
if self.cam:
|
||||
self.image.set_rotation(900) # internal camera is rotated 90 degrees
|
||||
else:
|
||||
@@ -114,6 +146,9 @@ class CameraApp(Activity):
|
||||
try:
|
||||
self.cam = webcam.init("/dev/video0")
|
||||
self.use_webcam = True
|
||||
# Reconfigure webcam to use saved resolution
|
||||
print(f"Reconfiguring webcam to {self.width}x{self.height}")
|
||||
webcam.reconfigure(self.cam, output_width=self.width, output_height=self.height)
|
||||
except Exception as e:
|
||||
print(f"camera app: webcam exception: {e}")
|
||||
if self.cam:
|
||||
@@ -241,7 +276,42 @@ class CameraApp(Activity):
|
||||
self.start_qr_decoding()
|
||||
else:
|
||||
self.stop_qr_decoding()
|
||||
|
||||
|
||||
def open_settings(self):
|
||||
"""Launch the camera settings activity."""
|
||||
intent = Intent(activity_class=CameraSettingsActivity)
|
||||
self.startActivityForResult(intent, self.handle_settings_result)
|
||||
|
||||
def handle_settings_result(self, result):
|
||||
"""Handle result from settings activity."""
|
||||
if result.get("result_code") == True:
|
||||
print("Settings changed, reloading resolution...")
|
||||
# Reload resolution preference
|
||||
self.load_resolution_preference()
|
||||
|
||||
# Recreate image descriptor with new dimensions
|
||||
self.image_dsc["header"]["w"] = self.width
|
||||
self.image_dsc["header"]["h"] = self.height
|
||||
self.image_dsc["header"]["stride"] = self.width * 2
|
||||
self.image_dsc["data_size"] = self.width * self.height * 2
|
||||
|
||||
# Reconfigure camera if active
|
||||
if self.cam:
|
||||
if self.use_webcam:
|
||||
print(f"Reconfiguring webcam to {self.width}x{self.height}")
|
||||
webcam.reconfigure(self.cam, output_width=self.width, output_height=self.height)
|
||||
else:
|
||||
# For internal camera, need to reinitialize
|
||||
print(f"Reinitializing internal camera to {self.width}x{self.height}")
|
||||
if self.capture_timer:
|
||||
self.capture_timer.delete()
|
||||
self.cam.deinit()
|
||||
self.cam = init_internal_cam(self.width, self.height)
|
||||
if self.cam:
|
||||
self.capture_timer = lv.timer_create(self.try_capture, 100, None)
|
||||
|
||||
self.set_image_size()
|
||||
|
||||
def try_capture(self, event):
|
||||
#print("capturing camera frame")
|
||||
try:
|
||||
@@ -262,9 +332,36 @@ class CameraApp(Activity):
|
||||
|
||||
|
||||
# Non-class functions:
|
||||
def init_internal_cam():
|
||||
def init_internal_cam(width=240, height=240):
|
||||
"""Initialize internal camera with specified resolution."""
|
||||
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,
|
||||
(640, 480): FrameSize.VGA,
|
||||
(800, 600): FrameSize.SVGA,
|
||||
(1024, 768): FrameSize.XGA,
|
||||
(1280, 720): FrameSize.HD,
|
||||
(1280, 1024): FrameSize.SXGA,
|
||||
(1600, 1200): FrameSize.UXGA,
|
||||
(1920, 1080): FrameSize.FHD,
|
||||
}
|
||||
|
||||
frame_size = resolution_map.get((width, height), FrameSize.R240X240)
|
||||
print(f"init_internal_cam: Using FrameSize for {width}x{height}")
|
||||
|
||||
cam = Camera(
|
||||
data_pins=[12,13,15,11,14,10,7,2],
|
||||
vsync_pin=6,
|
||||
@@ -277,15 +374,9 @@ def init_internal_cam():
|
||||
powerdown_pin=-1,
|
||||
reset_pin=-1,
|
||||
pixel_format=PixelFormat.RGB565,
|
||||
#pixel_format=PixelFormat.GRAYSCALE,
|
||||
frame_size=FrameSize.R240X240,
|
||||
grab_mode=GrabMode.LATEST
|
||||
frame_size=frame_size,
|
||||
grab_mode=GrabMode.LATEST
|
||||
)
|
||||
#cam.init() automatically done when creating the Camera()
|
||||
#cam.reconfigure(frame_size=FrameSize.HVGA)
|
||||
#frame_size=FrameSize.HVGA, # 480x320
|
||||
#frame_size=FrameSize.QVGA, # 320x240
|
||||
#frame_size=FrameSize.QQVGA # 160x120
|
||||
cam.set_vflip(True)
|
||||
return cam
|
||||
except Exception as e:
|
||||
@@ -311,3 +402,124 @@ def remove_bom(buffer):
|
||||
if buffer.startswith(bom):
|
||||
return buffer[3:]
|
||||
return buffer
|
||||
|
||||
|
||||
class CameraSettingsActivity(Activity):
|
||||
"""Settings activity for camera resolution configuration."""
|
||||
|
||||
# Resolution options for desktop/webcam
|
||||
WEBCAM_RESOLUTIONS = [
|
||||
("160x120", "160x120"),
|
||||
("240x240", "240x240"), # Default
|
||||
("320x240", "320x240"),
|
||||
("480x320", "480x320"),
|
||||
("640x480", "640x480"),
|
||||
("800x600", "800x600"),
|
||||
("1024x768", "1024x768"),
|
||||
("1280x720", "1280x720"),
|
||||
]
|
||||
|
||||
# Resolution options for internal camera (ESP32) - all available FrameSize options
|
||||
ESP32_RESOLUTIONS = [
|
||||
("96x96", "96x96"),
|
||||
("160x120", "160x120"),
|
||||
("128x128", "128x128"),
|
||||
("176x144", "176x144"),
|
||||
("240x176", "240x176"),
|
||||
("240x240", "240x240"), # Default
|
||||
("320x240", "320x240"),
|
||||
("320x320", "320x320"),
|
||||
("400x296", "400x296"),
|
||||
("480x320", "480x320"),
|
||||
("640x480", "640x480"),
|
||||
("800x600", "800x600"),
|
||||
("1024x768", "1024x768"),
|
||||
("1280x720", "1280x720"),
|
||||
("1280x1024", "1280x1024"),
|
||||
("1600x1200", "1600x1200"),
|
||||
("1920x1080", "1920x1080"),
|
||||
]
|
||||
|
||||
dropdown = None
|
||||
current_resolution = None
|
||||
|
||||
def onCreate(self):
|
||||
# Load preferences
|
||||
prefs = SharedPreferences("com.micropythonos.camera")
|
||||
self.current_resolution = prefs.get_string("resolution", "240x240")
|
||||
|
||||
# Create main screen
|
||||
screen = lv.obj()
|
||||
screen.set_size(lv.pct(100), lv.pct(100))
|
||||
screen.set_style_pad_all(10, 0)
|
||||
|
||||
# Title
|
||||
title = lv.label(screen)
|
||||
title.set_text("Camera Settings")
|
||||
title.align(lv.ALIGN.TOP_MID, 0, 10)
|
||||
|
||||
# Resolution label
|
||||
resolution_label = lv.label(screen)
|
||||
resolution_label.set_text("Resolution:")
|
||||
resolution_label.align(lv.ALIGN.TOP_LEFT, 0, 50)
|
||||
|
||||
# Detect if we're on desktop or ESP32 based on available modules
|
||||
try:
|
||||
import webcam
|
||||
resolutions = self.WEBCAM_RESOLUTIONS
|
||||
print("Using webcam resolutions")
|
||||
except:
|
||||
resolutions = self.ESP32_RESOLUTIONS
|
||||
print("Using ESP32 camera resolutions")
|
||||
|
||||
# Create dropdown
|
||||
self.dropdown = lv.dropdown(screen)
|
||||
self.dropdown.set_size(200, 40)
|
||||
self.dropdown.align(lv.ALIGN.TOP_LEFT, 0, 80)
|
||||
|
||||
# Build dropdown options string
|
||||
options_str = "\n".join([label for label, _ in resolutions])
|
||||
self.dropdown.set_options(options_str)
|
||||
|
||||
# Set current selection
|
||||
for idx, (label, value) in enumerate(resolutions):
|
||||
if value == self.current_resolution:
|
||||
self.dropdown.set_selected(idx)
|
||||
break
|
||||
|
||||
# Save button
|
||||
save_button = lv.button(screen)
|
||||
save_button.set_size(100, 50)
|
||||
save_button.align(lv.ALIGN.BOTTOM_MID, -60, -10)
|
||||
save_button.add_event_cb(lambda e: self.save_and_close(resolutions), lv.EVENT.CLICKED, None)
|
||||
save_label = lv.label(save_button)
|
||||
save_label.set_text("Save")
|
||||
save_label.center()
|
||||
|
||||
# Cancel button
|
||||
cancel_button = lv.button(screen)
|
||||
cancel_button.set_size(100, 50)
|
||||
cancel_button.align(lv.ALIGN.BOTTOM_MID, 60, -10)
|
||||
cancel_button.add_event_cb(lambda e: self.finish(), lv.EVENT.CLICKED, None)
|
||||
cancel_label = lv.label(cancel_button)
|
||||
cancel_label.set_text("Cancel")
|
||||
cancel_label.center()
|
||||
|
||||
self.setContentView(screen)
|
||||
|
||||
def save_and_close(self, resolutions):
|
||||
"""Save selected resolution and return result."""
|
||||
selected_idx = self.dropdown.get_selected()
|
||||
_, new_resolution = resolutions[selected_idx]
|
||||
|
||||
# Save to preferences
|
||||
prefs = SharedPreferences("com.micropythonos.camera")
|
||||
editor = prefs.edit()
|
||||
editor.put_string("resolution", new_resolution)
|
||||
editor.commit()
|
||||
|
||||
print(f"Camera resolution saved: {new_resolution}")
|
||||
|
||||
# Return success result
|
||||
self.setResult(True, {"resolution": new_resolution})
|
||||
self.finish()
|
||||
|
||||
@@ -160,7 +160,7 @@ class WiFi(Activity):
|
||||
|
||||
def password_page_result_cb(self, result):
|
||||
print(f"PasswordPage finished, result: {result}")
|
||||
if result.get("result_code"):
|
||||
if result.get("result_code") is True:
|
||||
data = result.get("data")
|
||||
if data:
|
||||
self.start_attempt_connecting(data.get("ssid"), data.get("password"))
|
||||
|
||||
Reference in New Issue
Block a user