From fa295e7f5756e2d9a44fcb393362f43334bfce8c Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Tue, 13 May 2025 16:12:21 +0200 Subject: [PATCH] camtest: add webcam support --- README.md | 6 +++- c_mpos/src/webcam.c | 26 ++++++++++++++-- .../com.example.camtest/assets/camtest.py | 30 +++++++++++-------- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index afe1ae52..4bbfe3c0 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,11 @@ Building for desktop ==================== Building to run as an app on the Linux desktop or MacOS (untested) is supported. -To do so, make sure you have the necessary dependencies (see https://github.com/lvgl-micropython/) and then run: +To do so, make sure you have the necessary dependencies: +- see https://github.com/lvgl-micropython/ +- sudo apt install libv4l-dev # for webcam.c + +...and then run: ``` ~/sources/PiggyOS/scripts/build_lvgl_micropython.sh unix diff --git a/c_mpos/src/webcam.c b/c_mpos/src/webcam.c index 0e2a30b9..2856e65f 100644 --- a/c_mpos/src/webcam.c +++ b/c_mpos/src/webcam.c @@ -1,4 +1,3 @@ -//#include #include #include #include @@ -17,8 +16,8 @@ // Default webcam device #define VIDEO_DEVICE "/dev/video0" -#define WIDTH 640 -#define HEIGHT 480 +#define WIDTH 240 +#define HEIGHT 240 // Structure to hold webcam state typedef struct { @@ -34,6 +33,27 @@ static void yuyv_to_grayscale(uint8_t *src, uint8_t *dst, size_t width, size_t h } } +// Resize 640x480 grayscale to 240x240 +static void resize_640x480_to_240x240(uint8_t *src, uint8_t *dst) { + const int src_width = 640; + const int src_height = 480; + const int dst_width = 240; + const int dst_height = 240; + + // Scaling factors + float x_ratio = (float)src_width / dst_width; + float y_ratio = (float)src_height / dst_height; + + for (int y = 0; y < dst_height; y++) { + for (int x = 0; x < dst_width; x++) { + // Nearest-neighbor interpolation + int src_x = (int)(x * x_ratio); + int src_y = (int)(y * y_ratio); + dst[y * dst_width + x] = src[src_y * src_width + src_x]; + } + } +} + // Function to capture a grayscale image static mp_obj_t webcam_capture_grayscale(void) { webcam_t cam = { .fd = -1, .buffer = NULL, .buffer_length = 0 }; diff --git a/internal_filesystem/apps/com.example.camtest/assets/camtest.py b/internal_filesystem/apps/com.example.camtest/assets/camtest.py index eabd547d..941de852 100644 --- a/internal_filesystem/apps/com.example.camtest/assets/camtest.py +++ b/internal_filesystem/apps/com.example.camtest/assets/camtest.py @@ -12,6 +12,7 @@ current_cam_buffer = None image_dsc = None image = None qr_label = None +use_webcam = False def print_qr_buffer(buffer): @@ -92,12 +93,12 @@ def qr_button_click(e): def try_capture(): - global current_cam_buffer, image_dsc, image - if cam.frame_available(): - # Get new memoryview from camera + global current_cam_buffer, image_dsc, image, use_webcam + if use_webcam: + new_cam_buffer = webcam.capture_grayscale() + elif cam.frame_available(): new_cam_buffer = cam.capture() # Returns memoryview - # Verify buffer size - #if len(new_cam_buffer) != width * height * 2: + if len(new_cam_buffer): # print("Invalid buffer size:", len(new_cam_buffer)) # cam.free_buffer() # return @@ -107,12 +108,11 @@ def try_capture(): image.set_src(image_dsc) #image.invalidate() #does not work # Free the previous buffer (if any) after setting new data - if current_cam_buffer is not None: + 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) @@ -195,16 +195,22 @@ def init_cam(): cam = init_cam() if not cam: - print("init cam failed, retrying...") - time.sleep(5) - cam = init_cam() + print("init cam failed, retrying with webcam...") + try: + import webcam + current_cam_buffer = webcam.capture_grayscale() + use_webcam = True + except Exception as e: + print(f"camtest.py: webcam exception: {e}") -if cam: +if cam or use_webcam: build_ui() while appscreen == lv.screen_active() and keepgoing is True: 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...") cam.deinit() - show_launcher() +else: + print("No camera found, exiting...") +show_launcher()