From 0e57b10dbb1a63dceeee15938a30019625f086fd Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 12:14:34 +0200 Subject: [PATCH 01/16] memoryview works sortof but hangs --- c_mpos/src/webcam.c | 6 +++--- .../apps/com.example.camtest/assets/camtest.py | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/c_mpos/src/webcam.c b/c_mpos/src/webcam.c index a8114e7d..cd407f57 100644 --- a/c_mpos/src/webcam.c +++ b/c_mpos/src/webcam.c @@ -163,9 +163,9 @@ static mp_obj_t capture_frame(webcam_obj_t *self) { yuyv_to_grayscale_240x240(self->buffers[buf.index], self->gray_buffer, WIDTH, HEIGHT); - char filename[32]; - snprintf(filename, sizeof(filename), "frame_%03d.raw", self->frame_count++); - save_raw(filename, self->gray_buffer, OUTPUT_WIDTH, 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(0x01, OUTPUT_WIDTH * OUTPUT_HEIGHT, self->gray_buffer); diff --git a/internal_filesystem/apps/com.example.camtest/assets/camtest.py b/internal_filesystem/apps/com.example.camtest/assets/camtest.py index 049d7113..fa01d421 100644 --- a/internal_filesystem/apps/com.example.camtest/assets/camtest.py +++ b/internal_filesystem/apps/com.example.camtest/assets/camtest.py @@ -99,7 +99,9 @@ def try_capture(): if new_cam_buffer and len(new_cam_buffer) == 240 * 240: image_dsc.data = new_cam_buffer # Update image descriptor image.set_src(image_dsc) # Update LVGL image - current_cam_buffer = None # Clear reference to allow GC + #if current_cam_buffer is not None: + # webcam.free_buffer(cam) # Free the old buffer + #current_cam_buffer = new_cam_buffer # Clear reference to allow GC else: print("Invalid buffer size:", len(new_cam_buffer)) @@ -120,8 +122,8 @@ def try_capture_old(): #current_cam_buffer = None # Clear reference to allow GC #image.invalidate() #does not work # Free the previous buffer (if any) after setting new data - if current_cam_buffer is not None and not use_webcam: - cam.free_buffer() # Free the old buffer + #if current_cam_buffer is not None and not use_webcam: + # cam.free_buffer() # Free the old buffer current_cam_buffer = new_cam_buffer # Store new buffer reference From 12b2145d85770d10876df45203c997a5e6ce125c Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 12:26:36 +0200 Subject: [PATCH 02/16] Memview sort of works but hangs when it's being redrawn... --- c_mpos/src/webcam.c | 39 +++++++++++++++++++ .../com.example.camtest/assets/camtest.py | 10 +++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/c_mpos/src/webcam.c b/c_mpos/src/webcam.c index cd407f57..6d666998 100644 --- a/c_mpos/src/webcam.c +++ b/c_mpos/src/webcam.c @@ -149,6 +149,34 @@ static void deinit_webcam(webcam_obj_t *self) { self->fd = -1; } +static mp_obj_t recapture_frame(webcam_obj_t *self) { + struct v4l2_buffer buf = {0}; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + if (ioctl(self->fd, VIDIOC_DQBUF, &buf) < 0) { + mp_raise_OSError(MP_EIO); + } + + if (!self->gray_buffer) { + mp_raise_OSError(MP_ENOMEM); + } + + yuyv_to_grayscale_240x240(self->buffers[buf.index], self->gray_buffer, WIDTH, 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(0x01, OUTPUT_WIDTH * OUTPUT_HEIGHT, self->gray_buffer); + mp_obj_t result = mp_const_none; + + if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) { + mp_raise_OSError(MP_EIO); + } + + return result; +} + static mp_obj_t capture_frame(webcam_obj_t *self) { struct v4l2_buffer buf = {0}; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -212,6 +240,16 @@ static mp_obj_t webcam_capture_frame(mp_obj_t self_in) { } MP_DEFINE_CONST_FUN_OBJ_1(webcam_capture_frame_obj, webcam_capture_frame); +static mp_obj_t webcam_recapture_frame(mp_obj_t self_in) { + webcam_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->fd < 0) { + mp_raise_OSError(MP_EIO); + } + return recapture_frame(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(webcam_recapture_frame_obj, webcam_recapture_frame); + + static const mp_obj_type_t webcam_type = { { &mp_type_type }, .name = MP_QSTR_Webcam, @@ -222,6 +260,7 @@ static const mp_rom_map_elem_t mp_module_webcam_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Webcam), MP_ROM_PTR(&webcam_type) }, { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&webcam_init_obj) }, { MP_ROM_QSTR(MP_QSTR_capture_frame), MP_ROM_PTR(&webcam_capture_frame_obj) }, + { MP_ROM_QSTR(MP_QSTR_recapture_frame), MP_ROM_PTR(&webcam_recapture_frame_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&webcam_deinit_obj) }, }; static MP_DEFINE_CONST_DICT(mp_module_webcam_globals, mp_module_webcam_globals_table); diff --git a/internal_filesystem/apps/com.example.camtest/assets/camtest.py b/internal_filesystem/apps/com.example.camtest/assets/camtest.py index fa01d421..4dd74096 100644 --- a/internal_filesystem/apps/com.example.camtest/assets/camtest.py +++ b/internal_filesystem/apps/com.example.camtest/assets/camtest.py @@ -128,7 +128,7 @@ def try_capture_old(): def build_ui(): - global image, image_dsc,qr_label + global image, image_dsc,qr_label, cam cont = lv.obj(appscreen) cont.set_style_pad_all(0, 0) cont.set_style_border_width(0, 0) @@ -160,6 +160,7 @@ def build_ui(): image.align(lv.ALIGN.LEFT_MID, 0, 0) image.set_rotation(900) # Create image descriptor once + memview = webcam.capture_frame(cam) # Returns memoryview image_dsc = lv.image_dsc_t({ "header": { "magic": lv.IMAGE_HEADER_MAGIC, @@ -170,7 +171,7 @@ def build_ui(): "cf": lv.COLOR_FORMAT.L8 }, 'data_size': width * height, - 'data': None # Will be updated per frame + 'data': memview # Will be updated per frame }) image.set_src(image_dsc) @@ -226,7 +227,10 @@ if cam or use_webcam: while appscreen == lv.screen_active() and keepgoing is True: print(f"capture nr {count}") count += 1 - try_capture() + #try_capture() + webcam.recapture_frame(cam) + #image.invalidate() + #image.set_src(image_dsc) time.sleep_ms(100) # Allow for the MicroPython REPL to still work. Reducing it doesn't seem to affect the on-display FPS. print("App backgrounded, deinitializing camera...") if use_webcam: From 5779ae004dc344211cc39ff377f005e50e86203e Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 12:42:46 +0200 Subject: [PATCH 03/16] doesnt hang but stays black --- .../com.example.camtest/assets/camtest.py | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/internal_filesystem/apps/com.example.camtest/assets/camtest.py b/internal_filesystem/apps/com.example.camtest/assets/camtest.py index 4dd74096..c6686d93 100644 --- a/internal_filesystem/apps/com.example.camtest/assets/camtest.py +++ b/internal_filesystem/apps/com.example.camtest/assets/camtest.py @@ -15,6 +15,7 @@ image = None qr_label = None use_webcam = False +memview = None def print_qr_buffer(buffer): try: @@ -160,7 +161,6 @@ def build_ui(): image.align(lv.ALIGN.LEFT_MID, 0, 0) image.set_rotation(900) # Create image descriptor once - memview = webcam.capture_frame(cam) # Returns memoryview image_dsc = lv.image_dsc_t({ "header": { "magic": lv.IMAGE_HEADER_MAGIC, @@ -171,7 +171,7 @@ def build_ui(): "cf": lv.COLOR_FORMAT.L8 }, 'data_size': width * height, - 'data': memview # Will be updated per frame + 'data': None # Will be updated per frame }) image.set_src(image_dsc) @@ -221,6 +221,11 @@ if not cam: except Exception as e: print(f"camtest.py: webcam exception: {e}") +time.sleep_ms(1000) +memview = webcam.capture_frame(cam) # Returns memoryview +time.sleep_ms(1000) +static_bytes_obj = bytes(memview) + if cam or use_webcam: build_ui() count=0 @@ -228,10 +233,15 @@ if cam or use_webcam: print(f"capture nr {count}") count += 1 #try_capture() - webcam.recapture_frame(cam) - #image.invalidate() + #webcam.recapture_frame(cam) + bytes_obj = bytes(memview) + print(f"got bytes: {len(bytes_obj)}") + image_dsc.data = bytes_obj + #image_dsc.data = static_bytes_obj + time.sleep_ms(200) # Allow for the MicroPython REPL to still work. Reducing it doesn't seem to affect the on-display FPS. + # somehow, everything's fine until I tell LVGL to redraw the image: + image.invalidate() #image.set_src(image_dsc) - time.sleep_ms(100) # Allow for the MicroPython REPL to still work. Reducing it doesn't seem to affect the on-display FPS. print("App backgrounded, deinitializing camera...") if use_webcam: webcam.deinit(cam) # Deinitializes webcam From 95b893002cc6e980a06c7b50c9b28f5a75fe1b7f Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 12:46:33 +0200 Subject: [PATCH 04/16] Still hangs --- c_mpos/src/webcam.c | 2 +- .../apps/com.example.camtest/assets/camtest.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/c_mpos/src/webcam.c b/c_mpos/src/webcam.c index 6d666998..1a91d67f 100644 --- a/c_mpos/src/webcam.c +++ b/c_mpos/src/webcam.c @@ -13,7 +13,7 @@ #define WIDTH 640 #define HEIGHT 480 -#define NUM_BUFFERS 4 +#define NUM_BUFFERS 1 #define OUTPUT_WIDTH 240 #define OUTPUT_HEIGHT 240 diff --git a/internal_filesystem/apps/com.example.camtest/assets/camtest.py b/internal_filesystem/apps/com.example.camtest/assets/camtest.py index c6686d93..746f43d8 100644 --- a/internal_filesystem/apps/com.example.camtest/assets/camtest.py +++ b/internal_filesystem/apps/com.example.camtest/assets/camtest.py @@ -236,12 +236,12 @@ if cam or use_webcam: #webcam.recapture_frame(cam) bytes_obj = bytes(memview) print(f"got bytes: {len(bytes_obj)}") - image_dsc.data = bytes_obj - #image_dsc.data = static_bytes_obj + #image_dsc.data = bytes_obj + image_dsc.data = static_bytes_obj time.sleep_ms(200) # Allow for the MicroPython REPL to still work. Reducing it doesn't seem to affect the on-display FPS. # somehow, everything's fine until I tell LVGL to redraw the image: - image.invalidate() - #image.set_src(image_dsc) + #image.invalidate() + image.set_src(image_dsc) print("App backgrounded, deinitializing camera...") if use_webcam: webcam.deinit(cam) # Deinitializes webcam From e6d160714e2e58dae1036e8892aa463e6fde82bc Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 12:55:31 +0200 Subject: [PATCH 05/16] fix mv type --- c_mpos/src/webcam.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c_mpos/src/webcam.c b/c_mpos/src/webcam.c index 1a91d67f..509c1c06 100644 --- a/c_mpos/src/webcam.c +++ b/c_mpos/src/webcam.c @@ -195,7 +195,7 @@ static mp_obj_t capture_frame(webcam_obj_t *self) { //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(0x01, OUTPUT_WIDTH * OUTPUT_HEIGHT, self->gray_buffer); + mp_obj_t result = mp_obj_new_memoryview('b', OUTPUT_WIDTH * OUTPUT_HEIGHT, self->gray_buffer); if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) { mp_raise_OSError(MP_EIO); From 6fa179dc88bc768b241197a9b5c60b8bd8e9978d Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 13:35:07 +0200 Subject: [PATCH 06/16] task_handler needs to be called from the same thread... --- draft_code/image_tests.py | 42 +++ .../META-INF/MANIFEST.JSON | 13 + .../assets/camtest.py_tries | 288 ++++++++++++++++++ .../assets/camtestnew.py | 57 ++++ internal_filesystem/lib/mpos/apps.py | 1 + internal_filesystem/lib/mpos/ui.py | 2 + internal_filesystem/main.py | 3 +- 7 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 draft_code/image_tests.py create mode 100644 internal_filesystem/apps/com.example.camtestnew/META-INF/MANIFEST.JSON create mode 100644 internal_filesystem/apps/com.example.camtestnew/assets/camtest.py_tries create mode 100644 internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py diff --git a/draft_code/image_tests.py b/draft_code/image_tests.py new file mode 100644 index 00000000..3d21261e --- /dev/null +++ b/draft_code/image_tests.py @@ -0,0 +1,42 @@ +width=240 +height=240 + +import webcam +import time + +cam = webcam.init("/dev/video0") # Initialize webcam with device path +memview = webcam.capture_frame(cam) # Returns memoryview +time.sleep_ms(1000) +static_bytes_obj = bytes(memview) + + +image = lv.image(lv.screen_active()) +image.align(lv.ALIGN.LEFT_MID, 0, 0) +image.set_rotation(900) +# Create image descriptor once +image_dsc = lv.image_dsc_t({ + "header": { + "magic": lv.IMAGE_HEADER_MAGIC, + "w": width, + "h": height, + "stride": width , + "cf": lv.COLOR_FORMAT.L8 + }, + 'data_size': width * height, + 'data': static_bytes_obj # Will be updated per frame +}) +image.set_src(image_dsc) + +for i in range(300): + print(f"iteration {i}") + webcam.recapture_frame(cam) #refresh memview + bytes_obj = bytes(memview) + #print(f"got bytes: {len(bytes_obj)}") + #image_dsc.data = static_bytes_obj + image_dsc.data = bytes_obj + #image.set_src(image_dsc) + image.invalidate() + time.sleep_ms(10) # seems to need more than 0 or 1 ms + +print("cleanup") +webcam.deinit(cam) # Deinitializes webcam diff --git a/internal_filesystem/apps/com.example.camtestnew/META-INF/MANIFEST.JSON b/internal_filesystem/apps/com.example.camtestnew/META-INF/MANIFEST.JSON new file mode 100644 index 00000000..b3e9c5f1 --- /dev/null +++ b/internal_filesystem/apps/com.example.camtestnew/META-INF/MANIFEST.JSON @@ -0,0 +1,13 @@ +{ +"name": "CamTestNew", +"publisher": "ACME Inc", +"short_description": "Simple test of the camera", +"long_description": "A simple test of the camera makes it possible to validate the hardware.", +"icon_url": "http://demo.lnpiggy.com:2121/apps/com.example.camtest_0.0.2.mpk_icon_64x64.png", +"download_url": "http://demo.lnpiggy.com:2121/apps/com.example.camtest_0.0.2.mpk", +"fullname": "com.example.camtest", +"version": "0.0.2", +"entrypoint": "assets/camtestnew.py", +"category": "camera" +} + diff --git a/internal_filesystem/apps/com.example.camtestnew/assets/camtest.py_tries b/internal_filesystem/apps/com.example.camtestnew/assets/camtest.py_tries new file mode 100644 index 00000000..9446c808 --- /dev/null +++ b/internal_filesystem/apps/com.example.camtestnew/assets/camtest.py_tries @@ -0,0 +1,288 @@ +import time +import webcam + +appscreen = lv.screen_active() + +keepgoing = True +keepliveqrdecoding = False +width = 240 +height = 240 + +# Variable to hold the current memoryview to prevent garbage collection +current_cam_buffer = None +image_dsc = None +image = None +qr_label = None +use_webcam = False + + +def print_qr_buffer(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(buffer): + bom = b'\xEF\xBB\xBF' + if buffer.startswith(bom): + return buffer[3:] + return buffer + +def qrdecode_live(): + # Image dimensions + buffer_size = width * height # 240 * 240 = 57600 bytes + while keepgoing and keepliveqrdecoding: + try: + import qrdecode + result = qrdecode.qrdecode(current_cam_buffer, width, height) + result = remove_bom(result) + result = print_qr_buffer(result) + print(f"QR decoding found: {result}") + except Exception as e: + print("QR decode error: ", e) + time.sleep_ms(500) + + +def close_button_click(e): + global keepgoing + print("Close button clicked") + keepgoing = False + + +def snap_button_click(e): + print("Picture taken!") + try: + import os + os.mkdir("data") + os.mkdir("data/com.example.camtest") + except OSError: + pass + if current_cam_buffer is not None: + filename="data/com.example.camtest/capture.raw" + try: + with open(filename, 'wb') as f: + f.write(current_cam_buffer) + print(f"Successfully wrote current_cam_buffer to {filename}") + except OSError as e: + print(f"Error writing to file: {e}") + + +def qr_button_click(e): + global keepliveqrdecoding, qr_label + if not keepliveqrdecoding: + print("Activating live QR decoding...") + keepliveqrdecoding = True + qr_label.set_text(lv.SYMBOL.EYE_CLOSE) + try: + import _thread + _thread.stack_size(12*1024) # 16KB is too much + _thread.start_new_thread(qrdecode_live, ()) + except Exception as e: + print("Could not start live QR decoding thread: ", e) + else: + print("Deactivating live QR decoding...") + keepliveqrdecoding = False + qr_label.set_text(lv.SYMBOL.EYE_OPEN) + +def try_capture(): + global current_cam_buffer, image_dsc, image + use_webcam = True # Set to True for webcam module + if use_webcam: + new_cam_buffer = None + new_cam_buffer = webcam.capture_frame(cam) # Returns bytes + else: + new_cam_buffer = cam.capture() # Returns memoryview for other camera + if new_cam_buffer and len(new_cam_buffer) == 240 * 240: + if current_cam_buffer is not None: + if use_webcam: + #pass + webcam.free_buffer(cam) # Explicitly free webcam buffer + else: + cam.free_buffer() # Free other camera buffer + else: + pass + print("current_cam_buffer is None, not freeing...") + current_cam_buffer = None + current_cam_buffer = new_cam_buffer # Store new buffer reference + # Update image descriptor with new buffer + # Set image source to update LVGL + #image_dsc.data = None # this doesnt help + #image.set_src(None) #this crashes it + oldsrc = image.get_src() + if oldsrc: + oldsrc.delete() + #image_dsc.data = current_cam_buffer + image_dsc = lv.image_dsc_t({ + "header": { + "magic": lv.IMAGE_HEADER_MAGIC, + "w": width, + "h": height, + "stride": width , + #"cf": lv.COLOR_FORMAT.RGB565 + "cf": lv.COLOR_FORMAT.L8 + }, + 'data_size': width * height, + 'data': current_cam_buffer # Will be updated per frame + }) + #image.invalidate() + image.set_src(image_dsc) + else: + print("Invalid buffer size:", len(new_cam_buffer)) + if use_webcam: + pass + #webcam.free_buffer(cam) + else: + cam.free_buffer() + +def try_capture_again(): + global current_cam_buffer, image_dsc, image + new_cam_buffer = webcam.capture_frame(cam) # Returns memoryview + if new_cam_buffer and len(new_cam_buffer) == 240 * 240: + image_dsc.data = new_cam_buffer # Update image descriptor + image.set_src(image_dsc) # Update LVGL image + current_cam_buffer = None # Clear reference to allow GC + else: + print("Invalid buffer size:", len(new_cam_buffer)) + +def try_capture_old(): + global current_cam_buffer, image_dsc, image, use_webcam + if use_webcam: + new_cam_buffer = webcam.capture_frame(cam) + elif cam.frame_available(): + new_cam_buffer = cam.capture() # Returns memoryview + if new_cam_buffer and len(new_cam_buffer): + # print("Invalid buffer size:", len(new_cam_buffer)) + # cam.free_buffer() + # return + # Update image descriptor with new memoryview + image_dsc.data = new_cam_buffer + # Set image source to update LVGL (implicitly invalidates widget) + image.set_src(image_dsc) + #current_cam_buffer = None # Clear reference to allow GC + #image.invalidate() #does not work + # Free the previous buffer (if any) after setting new data + if current_cam_buffer is not None and not use_webcam: + cam.free_buffer() # Free the old buffer + current_cam_buffer = new_cam_buffer # Store new buffer reference + + +def build_ui(): + global image, image_dsc,qr_label + cont = lv.obj(appscreen) + cont.set_style_pad_all(0, 0) + cont.set_style_border_width(0, 0) + cont.set_size(lv.pct(100), lv.pct(100)) + cont.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF) + close_button = lv.button(cont) + close_button.set_size(60,60) + 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(close_button_click,lv.EVENT.CLICKED,None) + snap_button = lv.button(cont) + snap_button.set_size(60, 60) + snap_button.align(lv.ALIGN.RIGHT_MID, 0, 0) + snap_label = lv.label(snap_button) + snap_label.set_text(lv.SYMBOL.OK) + snap_label.center() + snap_button.add_event_cb(snap_button_click,lv.EVENT.CLICKED,None) + qr_button = lv.button(cont) + qr_button.set_size(60, 60) + qr_button.align(lv.ALIGN.BOTTOM_RIGHT, 0, 0) + qr_label = lv.label(qr_button) + qr_label.set_text(lv.SYMBOL.EYE_OPEN) + qr_label.center() + qr_button.add_event_cb(qr_button_click,lv.EVENT.CLICKED,None) + # Initialize LVGL image widget + image = lv.image(cont) + image.align(lv.ALIGN.LEFT_MID, 0, 0) + image.set_rotation(900) + # Create image descriptor once + image_dsc = lv.image_dsc_t({ + "header": { + "magic": lv.IMAGE_HEADER_MAGIC, + "w": width, + "h": height, + "stride": width , + #"cf": lv.COLOR_FORMAT.RGB565 + "cf": lv.COLOR_FORMAT.L8 + }, + 'data_size': width * height, + 'data': None # Will be updated per frame + }) + image.set_src(image_dsc) + + +def init_cam(): + try: + # time.sleep(1) doesn't help + from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling + 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, + pixel_format=PixelFormat.GRAYSCALE, + frame_size=FrameSize.R240X240, + 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: + print(f"init_cam exception: {e}") + return None + + + + + + +cam = init_cam() +if not cam: + print("init cam failed, retrying with webcam...") + try: + cam = webcam.init("/dev/video0") # Initialize webcam with device path + use_webcam = True + except Exception as e: + print(f"camtest.py: webcam exception: {e}") + +if cam or use_webcam: + build_ui() + count=0 + while appscreen == lv.screen_active() and keepgoing is True: + print(f"capture nr {count}") + count += 1 + try_capture() + time.sleep_ms(100) # Allow for the MicroPython REPL to still work. Reducing it doesn't seem to affect the on-display FPS. + print("App backgrounded, deinitializing camera...") + if use_webcam: + webcam.deinit(cam) # Deinitializes webcam + else: + cam.deinit() +else: + print("No camera found, exiting...") + +show_launcher() + + diff --git a/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py b/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py new file mode 100644 index 00000000..f2387051 --- /dev/null +++ b/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py @@ -0,0 +1,57 @@ +# This works with copy-paste +# It also works like this: +# import mpos +# mpos.apps.execute_script("apps/com.example.camtestnew/assets/camtestnew.py", True, False, True) +# import mpos +# mpos.apps.execute_script_new_thread("apps/com.example.camtestnew/assets/camtestnew.py", True, False, True) + + +width=240 +height=240 + +import webcam +import time + +th.disable() + +cam = webcam.init("/dev/video0") # Initialize webcam with device path +memview = webcam.capture_frame(cam) # Returns memoryview +#time.sleep_ms(1000) +#static_bytes_obj = bytes(memview) + + +image = lv.image(lv.screen_active()) +image.align(lv.ALIGN.LEFT_MID, 0, 0) +image.set_rotation(900) +# Create image descriptor once +image_dsc = lv.image_dsc_t({ + "header": { + "magic": lv.IMAGE_HEADER_MAGIC, + "w": width, + "h": height, + "stride": width , + "cf": lv.COLOR_FORMAT.L8 + }, + 'data_size': width * height, + 'data': memview # Will be updated per frame +}) +image.set_src(image_dsc) + +for i in range(600): + #print(f"iteration {i}") + webcam.recapture_frame(cam) #refresh memview + #bytes_obj = bytes(memview) + #print(f"got bytes: {len(bytes_obj)}") + #image_dsc.data = static_bytes_obj + #image_dsc.data = bytes_obj + #image.set_src(image_dsc) + image.invalidate() + lv.task_handler() + time.sleep_ms(5) # seems to need more than 0 or 1 ms, otherwise there's almost never a new image... + lv.tick_inc(5) + +print("cleanup") +webcam.deinit(cam) # Deinitializes webcam + + +th.enable() diff --git a/internal_filesystem/lib/mpos/apps.py b/internal_filesystem/lib/mpos/apps.py index 04f21ecc..e600fe00 100644 --- a/internal_filesystem/lib/mpos/apps.py +++ b/internal_filesystem/lib/mpos/apps.py @@ -35,6 +35,7 @@ def execute_script(script_source, is_file, is_launcher, is_graphical): lv.screen_load(newscreen) script_globals = { 'lv': lv, + 'th': mpos.ui.th, 'NOTIFICATION_BAR_HEIGHT': mpos.ui.NOTIFICATION_BAR_HEIGHT, # for apps that want to leave space for notification bar 'appscreen': newscreen, 'start_app': start_app, # for launcher apps diff --git a/internal_filesystem/lib/mpos/ui.py b/internal_filesystem/lib/mpos/ui.py index ba0857de..dcc9d10d 100644 --- a/internal_filesystem/lib/mpos/ui.py +++ b/internal_filesystem/lib/mpos/ui.py @@ -1,6 +1,8 @@ import lvgl as lv import mpos.apps +th = None + NOTIFICATION_BAR_HEIGHT=24 CLOCK_UPDATE_INTERVAL = 1000 # 10 or even 1 ms doesn't seem to change the framerate but 100ms is enough diff --git a/internal_filesystem/main.py b/internal_filesystem/main.py index 64cd1253..ae47df98 100644 --- a/internal_filesystem/main.py +++ b/internal_filesystem/main.py @@ -1,10 +1,11 @@ import task_handler -th = task_handler.TaskHandler(duration=5) # 5ms is recommended for MicroPython+LVGL on desktop + import mpos.ui mpos.ui.create_rootscreen() mpos.ui.create_notification_bar() mpos.ui.create_drawer(display) +mpos.ui.th = task_handler.TaskHandler(duration=5) # 5ms is recommended for MicroPython+LVGL on desktop try: import freezefs_mount_builtin From e0f557c5a888a3e942a3210378c2f1b04c6f3076 Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 13:39:35 +0200 Subject: [PATCH 07/16] this works --- .../com.example.camtestnew/assets/camtestnew.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py b/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py index f2387051..9646c1de 100644 --- a/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py +++ b/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py @@ -15,7 +15,7 @@ import time th.disable() cam = webcam.init("/dev/video0") # Initialize webcam with device path -memview = webcam.capture_frame(cam) # Returns memoryview +#memview = webcam.capture_frame(cam) # Returns memoryview #time.sleep_ms(1000) #static_bytes_obj = bytes(memview) @@ -33,19 +33,20 @@ image_dsc = lv.image_dsc_t({ "cf": lv.COLOR_FORMAT.L8 }, 'data_size': width * height, - 'data': memview # Will be updated per frame + 'data': None # Will be updated per frame }) image.set_src(image_dsc) for i in range(600): #print(f"iteration {i}") - webcam.recapture_frame(cam) #refresh memview + #webcam.recapture_frame(cam) #refresh memview + memview = webcam.capture_frame(cam) # Returns memoryview #bytes_obj = bytes(memview) #print(f"got bytes: {len(bytes_obj)}") #image_dsc.data = static_bytes_obj - #image_dsc.data = bytes_obj - #image.set_src(image_dsc) - image.invalidate() + image_dsc.data = memview + image.set_src(image_dsc) + #image.invalidate() lv.task_handler() time.sleep_ms(5) # seems to need more than 0 or 1 ms, otherwise there's almost never a new image... lv.tick_inc(5) From f8ba6df5c1380547576453063171298f46005953 Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 13:44:07 +0200 Subject: [PATCH 08/16] still works --- .../assets/camtestnew.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py b/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py index 9646c1de..7e66b725 100644 --- a/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py +++ b/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py @@ -15,6 +15,7 @@ import time th.disable() cam = webcam.init("/dev/video0") # Initialize webcam with device path +memview = None #memview = webcam.capture_frame(cam) # Returns memoryview #time.sleep_ms(1000) #static_bytes_obj = bytes(memview) @@ -33,18 +34,21 @@ image_dsc = lv.image_dsc_t({ "cf": lv.COLOR_FORMAT.L8 }, 'data_size': width * height, - 'data': None # Will be updated per frame + 'data': memview # Will be updated per frame }) image.set_src(image_dsc) -for i in range(600): - #print(f"iteration {i}") - #webcam.recapture_frame(cam) #refresh memview - memview = webcam.capture_frame(cam) # Returns memoryview - #bytes_obj = bytes(memview) +memview = webcam.capture_frame(cam) # Returns memoryview + +for i in range(300): + print(f"iteration {i}") + webcam.recapture_frame(cam) #refresh memview + #memview = + bytes_obj = bytes(memview) #print(f"got bytes: {len(bytes_obj)}") + image_dsc.data = bytes_obj #image_dsc.data = static_bytes_obj - image_dsc.data = memview + #image_dsc.data = webcam.capture_frame(cam) # Returns memoryview image.set_src(image_dsc) #image.invalidate() lv.task_handler() From 771d0415fe45d9bee4bfa74a8fa8d3ebd02fcdcb Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 13:46:54 +0200 Subject: [PATCH 09/16] cleanups --- .../apps/com.example.camtest/assets/camtest.py | 2 +- .../apps/com.example.camtestnew/assets/camtestnew.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal_filesystem/apps/com.example.camtest/assets/camtest.py b/internal_filesystem/apps/com.example.camtest/assets/camtest.py index 746f43d8..53c6b5c6 100644 --- a/internal_filesystem/apps/com.example.camtest/assets/camtest.py +++ b/internal_filesystem/apps/com.example.camtest/assets/camtest.py @@ -221,7 +221,7 @@ if not cam: except Exception as e: print(f"camtest.py: webcam exception: {e}") -time.sleep_ms(1000) +#time.sleep_ms(1000) memview = webcam.capture_frame(cam) # Returns memoryview time.sleep_ms(1000) static_bytes_obj = bytes(memview) diff --git a/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py b/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py index 7e66b725..830fb3f4 100644 --- a/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py +++ b/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py @@ -15,15 +15,15 @@ import time th.disable() cam = webcam.init("/dev/video0") # Initialize webcam with device path -memview = None -#memview = webcam.capture_frame(cam) # Returns memoryview +#memview = None +memview = webcam.capture_frame(cam) # Returns memoryview #time.sleep_ms(1000) #static_bytes_obj = bytes(memview) image = lv.image(lv.screen_active()) image.align(lv.ALIGN.LEFT_MID, 0, 0) -image.set_rotation(900) +#image.set_rotation(900) # Create image descriptor once image_dsc = lv.image_dsc_t({ "header": { @@ -38,15 +38,15 @@ image_dsc = lv.image_dsc_t({ }) image.set_src(image_dsc) -memview = webcam.capture_frame(cam) # Returns memoryview +#memview = webcam.capture_frame(cam) # Returns memoryview for i in range(300): print(f"iteration {i}") webcam.recapture_frame(cam) #refresh memview #memview = - bytes_obj = bytes(memview) + #bytes_obj = bytes(memview) #print(f"got bytes: {len(bytes_obj)}") - image_dsc.data = bytes_obj + #image_dsc.data = bytes_obj #image_dsc.data = static_bytes_obj #image_dsc.data = webcam.capture_frame(cam) # Returns memoryview image.set_src(image_dsc) From 7288501624c6734aedac30ec4fe230559feff157 Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 13:53:06 +0200 Subject: [PATCH 10/16] Finally fix it! --- .../com.example.camtest/assets/camtest.py | 35 +++++++------------ .../assets/camtestnew.py | 8 ++--- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/internal_filesystem/apps/com.example.camtest/assets/camtest.py b/internal_filesystem/apps/com.example.camtest/assets/camtest.py index 53c6b5c6..e1d2e58b 100644 --- a/internal_filesystem/apps/com.example.camtest/assets/camtest.py +++ b/internal_filesystem/apps/com.example.camtest/assets/camtest.py @@ -2,6 +2,7 @@ import time import webcam appscreen = lv.screen_active() +th.disable() keepgoing = True keepliveqrdecoding = False @@ -94,7 +95,7 @@ def qr_button_click(e): qr_label.set_text(lv.SYMBOL.EYE_OPEN) -def try_capture(): +def try_capture_only_webcam(): global current_cam_buffer, image_dsc, image new_cam_buffer = webcam.capture_frame(cam) # Returns memoryview if new_cam_buffer and len(new_cam_buffer) == 240 * 240: @@ -106,7 +107,7 @@ def try_capture(): else: print("Invalid buffer size:", len(new_cam_buffer)) -def try_capture_old(): +def try_capture(): global current_cam_buffer, image_dsc, image, use_webcam if use_webcam: new_cam_buffer = webcam.capture_frame(cam) @@ -123,8 +124,8 @@ def try_capture_old(): #current_cam_buffer = None # Clear reference to allow GC #image.invalidate() #does not work # Free the previous buffer (if any) after setting new data - #if current_cam_buffer is not None and not use_webcam: - # cam.free_buffer() # Free the old buffer + if current_cam_buffer is not None and not use_webcam: + cam.free_buffer() # Free the old buffer current_cam_buffer = new_cam_buffer # Store new buffer reference @@ -178,7 +179,6 @@ def build_ui(): def init_cam(): try: - # time.sleep(1) doesn't help from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling cam = Camera( data_pins=[12,13,15,11,14,10,7,2], @@ -221,27 +221,15 @@ if not cam: except Exception as e: print(f"camtest.py: webcam exception: {e}") -#time.sleep_ms(1000) -memview = webcam.capture_frame(cam) # Returns memoryview -time.sleep_ms(1000) -static_bytes_obj = bytes(memview) - -if cam or use_webcam: +if cam: build_ui() count=0 while appscreen == lv.screen_active() and keepgoing is True: - print(f"capture nr {count}") - count += 1 - #try_capture() - #webcam.recapture_frame(cam) - bytes_obj = bytes(memview) - print(f"got bytes: {len(bytes_obj)}") - #image_dsc.data = bytes_obj - image_dsc.data = static_bytes_obj - time.sleep_ms(200) # Allow for the MicroPython REPL to still work. Reducing it doesn't seem to affect the on-display FPS. - # somehow, everything's fine until I tell LVGL to redraw the image: - #image.invalidate() - image.set_src(image_dsc) + try_capture() + # Task handler needs to be updated from the same thread, otherwise it causes concurrency issues: + lv.task_handler() + time.sleep_ms(5) + lv.tick_inc(5) print("App backgrounded, deinitializing camera...") if use_webcam: webcam.deinit(cam) # Deinitializes webcam @@ -250,6 +238,7 @@ if cam or use_webcam: else: print("No camera found, exiting...") +th.enable() show_launcher() diff --git a/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py b/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py index 830fb3f4..a1d6e6da 100644 --- a/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py +++ b/internal_filesystem/apps/com.example.camtestnew/assets/camtestnew.py @@ -16,7 +16,7 @@ th.disable() cam = webcam.init("/dev/video0") # Initialize webcam with device path #memview = None -memview = webcam.capture_frame(cam) # Returns memoryview +#memview = webcam.capture_frame(cam) # Returns memoryview #time.sleep_ms(1000) #static_bytes_obj = bytes(memview) @@ -34,13 +34,13 @@ image_dsc = lv.image_dsc_t({ "cf": lv.COLOR_FORMAT.L8 }, 'data_size': width * height, - 'data': memview # Will be updated per frame + 'data': None # Will be updated per frame }) image.set_src(image_dsc) #memview = webcam.capture_frame(cam) # Returns memoryview -for i in range(300): +for i in range(100): print(f"iteration {i}") webcam.recapture_frame(cam) #refresh memview #memview = @@ -48,7 +48,7 @@ for i in range(300): #print(f"got bytes: {len(bytes_obj)}") #image_dsc.data = bytes_obj #image_dsc.data = static_bytes_obj - #image_dsc.data = webcam.capture_frame(cam) # Returns memoryview + image_dsc.data = webcam.capture_frame(cam) # Returns memoryview image.set_src(image_dsc) #image.invalidate() lv.task_handler() From ba82e3d4672d1c9bfc6f5b97702a810ea864915d Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 14:08:25 +0200 Subject: [PATCH 11/16] camtest: cleanup --- draft_code/saved_functions.py | 18 +++++---- .../com.example.camtest/assets/camtest.py | 37 ++++++------------- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/draft_code/saved_functions.py b/draft_code/saved_functions.py index 86056951..9164790b 100644 --- a/draft_code/saved_functions.py +++ b/draft_code/saved_functions.py @@ -602,7 +602,7 @@ def log_callback(level, log_str): # Convert log_str to string if it's a bytes object log_str = log_str.decode() if isinstance(log_str, bytes) else log_str # Optional: Print for debugging - print(f"Level: {level}, Log: {log_str}") + #print(f"Level: {level}, Log: {log_str}") # Log message format: "sysmon: 25 FPS (refr_cnt: 8 | redraw_cnt: 1), ..." if "sysmon:" in log_str and "FPS" in log_str: try: @@ -628,14 +628,18 @@ def get_fps(): # Main loop -for _ in range(10): - import time - fps = get_fps() - if fps > 0: # Only print when FPS is updated - print("Current FPS:", fps) - time.sleep(1) +def print_fps(): + for _ in range(100): + import time + fps = get_fps() + if fps > 0: # Only print when FPS is updated + print("Current FPS:", fps) + time.sleep(1) +import _thread +_thread.stack_size(12*1024) +_thread.start_new_thread(print_fps, ()) # crash: diff --git a/internal_filesystem/apps/com.example.camtest/assets/camtest.py b/internal_filesystem/apps/com.example.camtest/assets/camtest.py index e1d2e58b..82b6412d 100644 --- a/internal_filesystem/apps/com.example.camtest/assets/camtest.py +++ b/internal_filesystem/apps/com.example.camtest/assets/camtest.py @@ -61,9 +61,12 @@ def close_button_click(e): def snap_button_click(e): print("Picture taken!") + import os try: - import os os.mkdir("data") + except OSError: + pass + try: os.mkdir("data/com.example.camtest") except OSError: pass @@ -95,18 +98,6 @@ def qr_button_click(e): qr_label.set_text(lv.SYMBOL.EYE_OPEN) -def try_capture_only_webcam(): - global current_cam_buffer, image_dsc, image - new_cam_buffer = webcam.capture_frame(cam) # Returns memoryview - if new_cam_buffer and len(new_cam_buffer) == 240 * 240: - image_dsc.data = new_cam_buffer # Update image descriptor - image.set_src(image_dsc) # Update LVGL image - #if current_cam_buffer is not None: - # webcam.free_buffer(cam) # Free the old buffer - #current_cam_buffer = new_cam_buffer # Clear reference to allow GC - else: - print("Invalid buffer size:", len(new_cam_buffer)) - def try_capture(): global current_cam_buffer, image_dsc, image, use_webcam if use_webcam: @@ -114,19 +105,15 @@ def try_capture(): elif cam.frame_available(): new_cam_buffer = cam.capture() # Returns memoryview if new_cam_buffer and len(new_cam_buffer): - # print("Invalid buffer size:", len(new_cam_buffer)) - # cam.free_buffer() - # return - # Update image descriptor with new memoryview image_dsc.data = new_cam_buffer - # Set image source to update LVGL (implicitly invalidates widget) image.set_src(image_dsc) - #current_cam_buffer = None # Clear reference to allow GC #image.invalidate() #does not work - # Free the previous buffer (if any) after setting new data - if current_cam_buffer is not None and not use_webcam: + if not use_webcam: cam.free_buffer() # Free the old buffer current_cam_buffer = new_cam_buffer # Store new buffer reference + else: + print("No image received from camera, ignoring...") + return def build_ui(): @@ -214,7 +201,7 @@ def init_cam(): cam = init_cam() if not cam: - print("init cam failed, retrying with webcam...") + print("camtest.py: no internal camera found, trying webcam on /dev/video0") try: cam = webcam.init("/dev/video0") # Initialize webcam with device path use_webcam = True @@ -228,9 +215,9 @@ if cam: try_capture() # Task handler needs to be updated from the same thread, otherwise it causes concurrency issues: lv.task_handler() - time.sleep_ms(5) - lv.tick_inc(5) - print("App backgrounded, deinitializing camera...") + time.sleep_ms(1) + lv.tick_inc(1) + print("camtest.py: stopping...") if use_webcam: webcam.deinit(cam) # Deinitializes webcam else: From 4de359c7a236f927f08985da0e1bff8720964713 Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 14:13:31 +0200 Subject: [PATCH 12/16] camtest: simplify --- .../apps/com.example.camtest/assets/camtest.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal_filesystem/apps/com.example.camtest/assets/camtest.py b/internal_filesystem/apps/com.example.camtest/assets/camtest.py index 82b6412d..eaded1fc 100644 --- a/internal_filesystem/apps/com.example.camtest/assets/camtest.py +++ b/internal_filesystem/apps/com.example.camtest/assets/camtest.py @@ -101,16 +101,15 @@ def qr_button_click(e): def try_capture(): global current_cam_buffer, image_dsc, image, use_webcam if use_webcam: - new_cam_buffer = webcam.capture_frame(cam) + current_cam_buffer = webcam.capture_frame(cam) elif cam.frame_available(): - new_cam_buffer = cam.capture() # Returns memoryview - if new_cam_buffer and len(new_cam_buffer): - image_dsc.data = new_cam_buffer + current_cam_buffer = cam.capture() # Returns memoryview + if current_cam_buffer and len(current_cam_buffer): + image_dsc.data = current_cam_buffer + #image.invalidate() # does not work so do this: image.set_src(image_dsc) - #image.invalidate() #does not work if not use_webcam: cam.free_buffer() # Free the old buffer - current_cam_buffer = new_cam_buffer # Store new buffer reference else: print("No image received from camera, ignoring...") return @@ -147,7 +146,8 @@ def build_ui(): # Initialize LVGL image widget image = lv.image(cont) image.align(lv.ALIGN.LEFT_MID, 0, 0) - image.set_rotation(900) + if not use_webcam: + image.set_rotation(900) # Create image descriptor once image_dsc = lv.image_dsc_t({ "header": { From dd4975d50fbd0aa493132c6b1df82a627affb76f Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 14:18:01 +0200 Subject: [PATCH 13/16] webcam.c: cleanups --- c_mpos/src/webcam.c | 41 ++--------------------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/c_mpos/src/webcam.c b/c_mpos/src/webcam.c index 509c1c06..f7f75e0b 100644 --- a/c_mpos/src/webcam.c +++ b/c_mpos/src/webcam.c @@ -17,6 +17,8 @@ #define OUTPUT_WIDTH 240 #define OUTPUT_HEIGHT 240 +#define WEBCAM_DEBUG_PRINT(...) mp_printf(&mp_plat_print, __VA_ARGS__); + // Forward declaration of the webcam type static const mp_obj_type_t webcam_type; @@ -149,34 +151,6 @@ static void deinit_webcam(webcam_obj_t *self) { self->fd = -1; } -static mp_obj_t recapture_frame(webcam_obj_t *self) { - struct v4l2_buffer buf = {0}; - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - if (ioctl(self->fd, VIDIOC_DQBUF, &buf) < 0) { - mp_raise_OSError(MP_EIO); - } - - if (!self->gray_buffer) { - mp_raise_OSError(MP_ENOMEM); - } - - yuyv_to_grayscale_240x240(self->buffers[buf.index], self->gray_buffer, WIDTH, 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(0x01, OUTPUT_WIDTH * OUTPUT_HEIGHT, self->gray_buffer); - mp_obj_t result = mp_const_none; - - if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) { - mp_raise_OSError(MP_EIO); - } - - return result; -} - static mp_obj_t capture_frame(webcam_obj_t *self) { struct v4l2_buffer buf = {0}; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -240,16 +214,6 @@ static mp_obj_t webcam_capture_frame(mp_obj_t self_in) { } MP_DEFINE_CONST_FUN_OBJ_1(webcam_capture_frame_obj, webcam_capture_frame); -static mp_obj_t webcam_recapture_frame(mp_obj_t self_in) { - webcam_obj_t *self = MP_OBJ_TO_PTR(self_in); - if (self->fd < 0) { - mp_raise_OSError(MP_EIO); - } - return recapture_frame(self); -} -MP_DEFINE_CONST_FUN_OBJ_1(webcam_recapture_frame_obj, webcam_recapture_frame); - - static const mp_obj_type_t webcam_type = { { &mp_type_type }, .name = MP_QSTR_Webcam, @@ -260,7 +224,6 @@ static const mp_rom_map_elem_t mp_module_webcam_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Webcam), MP_ROM_PTR(&webcam_type) }, { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&webcam_init_obj) }, { MP_ROM_QSTR(MP_QSTR_capture_frame), MP_ROM_PTR(&webcam_capture_frame_obj) }, - { MP_ROM_QSTR(MP_QSTR_recapture_frame), MP_ROM_PTR(&webcam_recapture_frame_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&webcam_deinit_obj) }, }; static MP_DEFINE_CONST_DICT(mp_module_webcam_globals, mp_module_webcam_globals_table); From 9b6a197e1767ade8efb751df6cbd8913e82b08c9 Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 14:18:29 +0200 Subject: [PATCH 14/16] webcam: fix debug prints --- c_mpos/src/webcam.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/c_mpos/src/webcam.c b/c_mpos/src/webcam.c index f7f75e0b..6228b0f1 100644 --- a/c_mpos/src/webcam.c +++ b/c_mpos/src/webcam.c @@ -17,9 +17,8 @@ #define OUTPUT_WIDTH 240 #define OUTPUT_HEIGHT 240 -#define WEBCAM_DEBUG_PRINT(...) mp_printf(&mp_plat_print, __VA_ARGS__); +#define WEBCAM_DEBUG_PRINT(...) mp_printf(&mp_plat_print, __VA_ARGS__) -// Forward declaration of the webcam type static const mp_obj_type_t webcam_type; typedef struct _webcam_obj_t { @@ -28,7 +27,7 @@ typedef struct _webcam_obj_t { void *buffers[NUM_BUFFERS]; size_t buffer_length; int frame_count; - unsigned char *gray_buffer; // Persistent buffer for memoryview + unsigned char *gray_buffer; } webcam_obj_t; static void yuyv_to_grayscale_240x240(unsigned char *yuyv, unsigned char *gray, int in_width, int in_height) { @@ -48,7 +47,7 @@ static void yuyv_to_grayscale_240x240(unsigned char *yuyv, unsigned char *gray, static void save_raw(const char *filename, unsigned char *data, int width, int height) { FILE *fp = fopen(filename, "wb"); if (!fp) { - fprintf(stderr, "Cannot open file %s: %s\n", filename, strerror(errno)); + WEBCAM_DEBUG_PRINT("Cannot open file %s: %s\n", filename, strerror(errno)); return; } fwrite(data, 1, width * height, fp); @@ -58,7 +57,7 @@ static void save_raw(const char *filename, unsigned char *data, int width, int h static int init_webcam(webcam_obj_t *self, const char *device) { self->fd = open(device, O_RDWR); if (self->fd < 0) { - fprintf(stderr, "Cannot open device: %s\n", strerror(errno)); + WEBCAM_DEBUG_PRINT("Cannot open device: %s\n", strerror(errno)); return -1; } @@ -69,7 +68,7 @@ static int init_webcam(webcam_obj_t *self, const char *device) { fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.field = V4L2_FIELD_ANY; if (ioctl(self->fd, VIDIOC_S_FMT, &fmt) < 0) { - fprintf(stderr, "Cannot set format: %s\n", strerror(errno)); + WEBCAM_DEBUG_PRINT("Cannot set format: %s\n", strerror(errno)); close(self->fd); return -1; } @@ -79,7 +78,7 @@ static int init_webcam(webcam_obj_t *self, const char *device) { req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (ioctl(self->fd, VIDIOC_REQBUFS, &req) < 0) { - fprintf(stderr, "Cannot request buffers: %s\n", strerror(errno)); + WEBCAM_DEBUG_PRINT("Cannot request buffers: %s\n", strerror(errno)); close(self->fd); return -1; } @@ -90,14 +89,14 @@ static int init_webcam(webcam_obj_t *self, const char *device) { buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (ioctl(self->fd, VIDIOC_QUERYBUF, &buf) < 0) { - fprintf(stderr, "Cannot query buffer: %s\n", strerror(errno)); + WEBCAM_DEBUG_PRINT("Cannot query buffer: %s\n", strerror(errno)); close(self->fd); return -1; } self->buffer_length = buf.length; self->buffers[i] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, self->fd, buf.m.offset); if (self->buffers[i] == MAP_FAILED) { - fprintf(stderr, "Cannot map buffer: %s\n", strerror(errno)); + WEBCAM_DEBUG_PRINT("Cannot map buffer: %s\n", strerror(errno)); close(self->fd); return -1; } @@ -109,21 +108,21 @@ static int init_webcam(webcam_obj_t *self, const char *device) { buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) { - fprintf(stderr, "Cannot queue buffer: %s\n", strerror(errno)); + WEBCAM_DEBUG_PRINT("Cannot queue buffer: %s\n", strerror(errno)); return -1; } } enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(self->fd, VIDIOC_STREAMON, &type) < 0) { - fprintf(stderr, "Cannot start streaming: %s\n", strerror(errno)); + WEBCAM_DEBUG_PRINT("Cannot start streaming: %s\n", strerror(errno)); return -1; } self->frame_count = 0; self->gray_buffer = (unsigned char *)malloc(OUTPUT_WIDTH * OUTPUT_HEIGHT); if (!self->gray_buffer) { - fprintf(stderr, "Cannot allocate gray buffer: %s\n", strerror(errno)); + WEBCAM_DEBUG_PRINT("Cannot allocate gray buffer: %s\n", strerror(errno)); close(self->fd); return -1; } @@ -165,10 +164,6 @@ static mp_obj_t capture_frame(webcam_obj_t *self) { yuyv_to_grayscale_240x240(self->buffers[buf.index], self->gray_buffer, WIDTH, 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); if (ioctl(self->fd, VIDIOC_QBUF, &buf) < 0) { @@ -180,7 +175,7 @@ static mp_obj_t capture_frame(webcam_obj_t *self) { static mp_obj_t webcam_init(size_t n_args, const mp_obj_t *args) { mp_arg_check_num(n_args, 0, 0, 1, false); - const char *device = "/dev/video0"; // Default device + const char *device = "/dev/video0"; if (n_args == 1) { device = mp_obj_str_get_str(args[0]); } From e79683033467d595a7383b4de8c298d7c79351d0 Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 14:22:18 +0200 Subject: [PATCH 15/16] webcam: keep aspect ratio --- c_mpos/src/webcam.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/c_mpos/src/webcam.c b/c_mpos/src/webcam.c index 6228b0f1..49351b67 100644 --- a/c_mpos/src/webcam.c +++ b/c_mpos/src/webcam.c @@ -31,15 +31,22 @@ typedef struct _webcam_obj_t { } webcam_obj_t; static void yuyv_to_grayscale_240x240(unsigned char *yuyv, unsigned char *gray, int in_width, int in_height) { - float x_ratio = (float)in_width / OUTPUT_WIDTH; - float y_ratio = (float)in_height / OUTPUT_HEIGHT; + // Crop to 480x480 centered region + int crop_size = 480; + int crop_x_offset = (in_width - crop_size) / 2; // Center the crop: (640 - 480) / 2 = 80 + int crop_y_offset = (in_height - crop_size) / 2; // Center the crop: (480 - 480) / 2 = 0 + + // Downscale ratios from 480x480 to 240x240 + float x_ratio = (float)crop_size / OUTPUT_WIDTH; // 480 / 240 = 2.0 + float y_ratio = (float)crop_size / OUTPUT_HEIGHT; // 480 / 240 = 2.0 for (int y = 0; y < OUTPUT_HEIGHT; y++) { for (int x = 0; x < OUTPUT_WIDTH; x++) { - int src_x = (int)(x * x_ratio); - int src_y = (int)(y * y_ratio); - int src_index = (src_y * in_width + src_x) * 2; - gray[y * OUTPUT_WIDTH + x] = yuyv[src_index]; + // Map output pixel to cropped region + int src_x = (int)(x * x_ratio) + crop_x_offset; // Adjust for crop offset + int src_y = (int)(y * y_ratio) + crop_y_offset; // Adjust for crop offset + int src_index = (src_y * in_width + src_x) * 2; // YUYV uses 2 bytes per pixel + gray[y * OUTPUT_WIDTH + x] = yuyv[src_index]; // Extract Y channel } } } From a47ce6ccae534d4255e7f9766d21e70069249ecd Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Wed, 14 May 2025 14:51:40 +0200 Subject: [PATCH 16/16] comments --- c_mpos/src/webcam.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c_mpos/src/webcam.c b/c_mpos/src/webcam.c index 49351b67..749d39a5 100644 --- a/c_mpos/src/webcam.c +++ b/c_mpos/src/webcam.c @@ -13,7 +13,7 @@ #define WIDTH 640 #define HEIGHT 480 -#define NUM_BUFFERS 1 +#define NUM_BUFFERS 1 # more buffers doesnt seem to help so one is enough #define OUTPUT_WIDTH 240 #define OUTPUT_HEIGHT 240