@@ -29,47 +29,27 @@ typedef struct _webcam_obj_t {
2929 void * buffers [NUM_BUFFERS ];
3030 size_t buffer_length ;
3131 int frame_count ;
32- unsigned char * gray_buffer ; // For grayscale
33- uint16_t * rgb565_buffer ; // For RGB565
34- int input_width ; // Webcam capture width (from V4L2)
35- int input_height ; // Webcam capture height (from V4L2)
36- int output_width ; // Configurable output width (default OUTPUT_WIDTH)
37- int output_height ; // Configurable output height (default OUTPUT_HEIGHT)
32+ unsigned char * gray_buffer ; // For grayscale conversion
33+ uint16_t * rgb565_buffer ; // For RGB565 conversion
34+ int width ; // Resolution width
35+ int height ; // Resolution height
3836} webcam_obj_t ;
3937
40- static void yuyv_to_rgb565 (unsigned char * yuyv , uint16_t * rgb565 , int in_width , int in_height , int out_width , int out_height ) {
41- // Crop to largest square that fits in the input frame
42- int crop_size = (in_width < in_height ) ? in_width : in_height ;
43- int crop_x_offset = (in_width - crop_size ) / 2 ;
44- int crop_y_offset = (in_height - crop_size ) / 2 ;
45-
46- // Calculate scaling ratios
47- float x_ratio = (float )crop_size / out_width ;
48- float y_ratio = (float )crop_size / out_height ;
49-
50- for (int y = 0 ; y < out_height ; y ++ ) {
51- for (int x = 0 ; x < out_width ; x ++ ) {
52- int src_x = (int )(x * x_ratio ) + crop_x_offset ;
53- int src_y = (int )(y * y_ratio ) + crop_y_offset ;
54-
55- // YUYV format: Y0 U Y1 V (4 bytes for 2 pixels)
56- // Ensure we're aligned to even pixel boundary
57- int src_x_even = (src_x / 2 ) * 2 ;
58- int src_base_index = (src_y * in_width + src_x_even ) * 2 ;
59-
60- // Extract Y, U, V values
61- int y0 ;
62- if (src_x % 2 == 0 ) {
63- // Even pixel: use Y0
64- y0 = yuyv [src_base_index ];
65- } else {
66- // Odd pixel: use Y1
67- y0 = yuyv [src_base_index + 2 ];
68- }
69- int u = yuyv [src_base_index + 1 ];
70- int v = yuyv [src_base_index + 3 ];
71-
72- // YUV to RGB conversion (ITU-R BT.601)
38+ static void yuyv_to_rgb565 (unsigned char * yuyv , uint16_t * rgb565 , int width , int height ) {
39+ // Convert YUYV to RGB565 without scaling
40+ // YUYV format: Y0 U Y1 V (4 bytes for 2 pixels, chroma shared)
41+
42+ for (int y = 0 ; y < height ; y ++ ) {
43+ for (int x = 0 ; x < width ; x += 2 ) {
44+ // Process 2 pixels at a time (one YUYV quad)
45+ int base_index = (y * width + x ) * 2 ;
46+
47+ int y0 = yuyv [base_index + 0 ];
48+ int u = yuyv [base_index + 1 ];
49+ int y1 = yuyv [base_index + 2 ];
50+ int v = yuyv [base_index + 3 ];
51+
52+ // YUV to RGB conversion (ITU-R BT.601) for first pixel
7353 int c = y0 - 16 ;
7454 int d = u - 128 ;
7555 int e = v - 128 ;
@@ -88,42 +68,36 @@ static void yuyv_to_rgb565(unsigned char *yuyv, uint16_t *rgb565, int in_width,
8868 uint16_t g6 = (g >> 2 ) & 0x3F ;
8969 uint16_t b5 = (b >> 3 ) & 0x1F ;
9070
91- rgb565 [y * out_width + x ] = (r5 << 11 ) | (g6 << 5 ) | b5 ;
71+ rgb565 [y * width + x ] = (r5 << 11 ) | (g6 << 5 ) | b5 ;
72+
73+ // Second pixel (shares U/V with first)
74+ c = y1 - 16 ;
75+
76+ r = (298 * c + 409 * e + 128 ) >> 8 ;
77+ g = (298 * c - 100 * d - 208 * e + 128 ) >> 8 ;
78+ b = (298 * c + 516 * d + 128 ) >> 8 ;
79+
80+ r = r < 0 ? 0 : (r > 255 ? 255 : r );
81+ g = g < 0 ? 0 : (g > 255 ? 255 : g );
82+ b = b < 0 ? 0 : (b > 255 ? 255 : b );
83+
84+ r5 = (r >> 3 ) & 0x1F ;
85+ g6 = (g >> 2 ) & 0x3F ;
86+ b5 = (b >> 3 ) & 0x1F ;
87+
88+ rgb565 [y * width + x + 1 ] = (r5 << 11 ) | (g6 << 5 ) | b5 ;
9289 }
9390 }
9491}
9592
96- static void yuyv_to_grayscale (unsigned char * yuyv , unsigned char * gray , int in_width , int in_height , int out_width , int out_height ) {
97- // Crop to largest square that fits in the input frame
98- int crop_size = (in_width < in_height ) ? in_width : in_height ;
99- int crop_x_offset = (in_width - crop_size ) / 2 ;
100- int crop_y_offset = (in_height - crop_size ) / 2 ;
101-
102- // Calculate scaling ratios
103- float x_ratio = (float )crop_size / out_width ;
104- float y_ratio = (float )crop_size / out_height ;
105-
106- for (int y = 0 ; y < out_height ; y ++ ) {
107- for (int x = 0 ; x < out_width ; x ++ ) {
108- int src_x = (int )(x * x_ratio ) + crop_x_offset ;
109- int src_y = (int )(y * y_ratio ) + crop_y_offset ;
110-
111- // YUYV format: Y0 U Y1 V (4 bytes for 2 pixels)
112- // Ensure we're aligned to even pixel boundary
113- int src_x_even = (src_x / 2 ) * 2 ;
114- int src_base_index = (src_y * in_width + src_x_even ) * 2 ;
115-
116- // Extract Y value
117- unsigned char y_val ;
118- if (src_x % 2 == 0 ) {
119- // Even pixel: use Y0
120- y_val = yuyv [src_base_index ];
121- } else {
122- // Odd pixel: use Y1
123- y_val = yuyv [src_base_index + 2 ];
124- }
125-
126- gray [y * out_width + x ] = y_val ;
93+ static void yuyv_to_grayscale (unsigned char * yuyv , unsigned char * gray , int width , int height ) {
94+ // Extract Y (luminance) values from YUYV without scaling
95+ // YUYV format: Y0 U Y1 V (4 bytes for 2 pixels)
96+
97+ for (int y = 0 ; y < height ; y ++ ) {
98+ for (int x = 0 ; x < width ; x ++ ) {
99+ // Y values are at even indices in YUYV
100+ gray [y * width + x ] = yuyv [(y * width + x ) * 2 ];
127101 }
128102 }
129103}
@@ -223,21 +197,15 @@ static int init_webcam(webcam_obj_t *self, const char *device, int width, int he
223197
224198 self -> frame_count = 0 ;
225199
226- // Store the input dimensions (actual values from V4L2, may be adjusted by driver)
227- self -> input_width = width ;
228- self -> input_height = height ;
229-
230- // Initialize output dimensions with defaults if not already set
231- if (self -> output_width == 0 ) self -> output_width = OUTPUT_WIDTH ;
232- if (self -> output_height == 0 ) self -> output_height = OUTPUT_HEIGHT ;
200+ // Store resolution (actual values from V4L2, may be adjusted by driver)
201+ self -> width = width ;
202+ self -> height = height ;
233203
234- WEBCAM_DEBUG_PRINT ("Webcam initialized: input %dx%d, output %dx%d\n" ,
235- self -> input_width , self -> input_height ,
236- self -> output_width , self -> output_height );
204+ WEBCAM_DEBUG_PRINT ("Webcam initialized: %dx%d\n" , self -> width , self -> height );
237205
238- // Allocate buffers with configured output dimensions
239- self -> gray_buffer = (unsigned char * )malloc (self -> output_width * self -> output_height * sizeof (unsigned char ));
240- self -> rgb565_buffer = (uint16_t * )malloc (self -> output_width * self -> output_height * sizeof (uint16_t ));
206+ // Allocate conversion buffers
207+ self -> gray_buffer = (unsigned char * )malloc (self -> width * self -> height * sizeof (unsigned char ));
208+ self -> rgb565_buffer = (uint16_t * )malloc (self -> width * self -> height * sizeof (uint16_t ));
241209 if (!self -> gray_buffer || !self -> rgb565_buffer ) {
242210 WEBCAM_DEBUG_PRINT ("Cannot allocate buffers: %s\n" , strerror (errno ));
243211 free (self -> gray_buffer );
@@ -288,41 +256,25 @@ static mp_obj_t capture_frame(mp_obj_t self_in, mp_obj_t format) {
288256 mp_raise_OSError (- res );
289257 }
290258
291- if (!self -> gray_buffer ) {
292- self -> gray_buffer = (unsigned char * )malloc (self -> output_width * self -> output_height * sizeof (unsigned char ));
293- if (!self -> gray_buffer ) {
294- mp_raise_OSError (MP_ENOMEM );
295- }
296- }
297- if (!self -> rgb565_buffer ) {
298- self -> rgb565_buffer = (uint16_t * )malloc (self -> output_width * self -> output_height * sizeof (uint16_t ));
299- if (!self -> rgb565_buffer ) {
300- mp_raise_OSError (MP_ENOMEM );
301- }
259+ // Buffers should already be allocated in init_webcam
260+ if (!self -> gray_buffer || !self -> rgb565_buffer ) {
261+ mp_raise_msg (& mp_type_RuntimeError , MP_ERROR_TEXT ("Buffers not allocated" ));
302262 }
303263
304264 const char * fmt = mp_obj_str_get_str (format );
305265 if (strcmp (fmt , "grayscale" ) == 0 ) {
306266 yuyv_to_grayscale (self -> buffers [buf .index ], self -> gray_buffer ,
307- self -> input_width , self -> input_height ,
308- self -> output_width , self -> output_height );
309- // char filename[32];
310- // snprintf(filename, sizeof(filename), "frame_%03d.raw", self->frame_count++);
311- // save_raw(filename, self->gray_buffer, self->output_width, self->output_height);
312- mp_obj_t result = mp_obj_new_memoryview ('b' , self -> output_width * self -> output_height , self -> gray_buffer );
267+ self -> width , self -> height );
268+ mp_obj_t result = mp_obj_new_memoryview ('b' , self -> width * self -> height , self -> gray_buffer );
313269 res = ioctl (self -> fd , VIDIOC_QBUF , & buf );
314270 if (res < 0 ) {
315271 mp_raise_OSError (- res );
316272 }
317273 return result ;
318274 } else {
319275 yuyv_to_rgb565 (self -> buffers [buf .index ], self -> rgb565_buffer ,
320- self -> input_width , self -> input_height ,
321- self -> output_width , self -> output_height );
322- // char filename[32];
323- // snprintf(filename, sizeof(filename), "frame_%03d.rgb565", self->frame_count++);
324- // save_raw_rgb565(filename, self->rgb565_buffer, self->output_width, self->output_height);
325- mp_obj_t result = mp_obj_new_memoryview ('b' , self -> output_width * self -> output_height * 2 , self -> rgb565_buffer );
276+ self -> width , self -> height );
277+ mp_obj_t result = mp_obj_new_memoryview ('b' , self -> width * self -> height * 2 , self -> rgb565_buffer );
326278 res = ioctl (self -> fd , VIDIOC_QBUF , & buf );
327279 if (res < 0 ) {
328280 mp_raise_OSError (- res );
@@ -355,10 +307,8 @@ static mp_obj_t webcam_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k
355307 self -> fd = -1 ;
356308 self -> gray_buffer = NULL ;
357309 self -> rgb565_buffer = NULL ;
358- self -> input_width = 0 ; // Will be set from V4L2 format in init_webcam
359- self -> input_height = 0 ; // Will be set from V4L2 format in init_webcam
360- self -> output_width = 0 ; // Will use default OUTPUT_WIDTH in init_webcam
361- self -> output_height = 0 ; // Will use default OUTPUT_HEIGHT in init_webcam
310+ self -> width = 0 ; // Will be set from V4L2 format in init_webcam
311+ self -> height = 0 ; // Will be set from V4L2 format in init_webcam
362312
363313 int res = init_webcam (self , device , width , height );
364314 if (res < 0 ) {
@@ -399,19 +349,14 @@ static mp_obj_t webcam_reconfigure(size_t n_args, const mp_obj_t *pos_args, mp_m
399349 * duplicating V4L2 setup code.
400350 *
401351 * Parameters:
402- * input_width, input_height: V4L2 capture resolution (optional)
403- * output_width, output_height: Output buffer resolution (optional)
404- *
405- * If not specified, dimensions remain unchanged.
352+ * width, height: Resolution (optional, keeps current if not specified)
406353 */
407354
408- enum { ARG_self , ARG_input_width , ARG_input_height , ARG_output_width , ARG_output_height };
355+ enum { ARG_self , ARG_width , ARG_height };
409356 static const mp_arg_t allowed_args [] = {
410357 { MP_QSTR_self , MP_ARG_REQUIRED | MP_ARG_OBJ , {.u_obj = MP_OBJ_NULL } },
411- { MP_QSTR_input_width , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
412- { MP_QSTR_input_height , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
413- { MP_QSTR_output_width , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
414- { MP_QSTR_output_height , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
358+ { MP_QSTR_width , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
359+ { MP_QSTR_height , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
415360 };
416361
417362 mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
@@ -420,47 +365,32 @@ static mp_obj_t webcam_reconfigure(size_t n_args, const mp_obj_t *pos_args, mp_m
420365 webcam_obj_t * self = MP_OBJ_TO_PTR (args [ARG_self ].u_obj );
421366
422367 // Get new dimensions (keep current if not specified)
423- int new_input_width = args [ARG_input_width ].u_int ;
424- int new_input_height = args [ARG_input_height ].u_int ;
425- int new_output_width = args [ARG_output_width ].u_int ;
426- int new_output_height = args [ARG_output_height ].u_int ;
368+ int new_width = args [ARG_width ].u_int ;
369+ int new_height = args [ARG_height ].u_int ;
427370
428- if (new_input_width == 0 ) new_input_width = self -> input_width ;
429- if (new_input_height == 0 ) new_input_height = self -> input_height ;
430- if (new_output_width == 0 ) new_output_width = self -> output_width ;
431- if (new_output_height == 0 ) new_output_height = self -> output_height ;
371+ if (new_width == 0 ) new_width = self -> width ;
372+ if (new_height == 0 ) new_height = self -> height ;
432373
433374 // Validate dimensions
434- if (new_input_width <= 0 || new_input_height <= 0 || new_input_width > 3840 || new_input_height > 2160 ) {
435- mp_raise_ValueError (MP_ERROR_TEXT ("Invalid input dimensions" ));
436- }
437- if (new_output_width <= 0 || new_output_height <= 0 || new_output_width > 3840 || new_output_height > 2160 ) {
438- mp_raise_ValueError (MP_ERROR_TEXT ("Invalid output dimensions" ));
375+ if (new_width <= 0 || new_height <= 0 || new_width > 3840 || new_height > 2160 ) {
376+ mp_raise_ValueError (MP_ERROR_TEXT ("Invalid dimensions" ));
439377 }
440378
441379 // Check if anything changed
442- if (new_input_width == self -> input_width &&
443- new_input_height == self -> input_height &&
444- new_output_width == self -> output_width &&
445- new_output_height == self -> output_height ) {
380+ if (new_width == self -> width && new_height == self -> height ) {
446381 return mp_const_none ; // Nothing to do
447382 }
448383
449- WEBCAM_DEBUG_PRINT ("Reconfiguring webcam: %dx%d -> %dx%d (input), %dx%d -> %dx%d (output)\n" ,
450- self -> input_width , self -> input_height , new_input_width , new_input_height ,
451- self -> output_width , self -> output_height , new_output_width , new_output_height );
384+ WEBCAM_DEBUG_PRINT ("Reconfiguring webcam: %dx%d -> %dx%d\n" ,
385+ self -> width , self -> height , new_width , new_height );
452386
453387 // Remember device path before deinit (which closes fd)
454388 char device [64 ];
455389 strncpy (device , self -> device , sizeof (device ));
456390
457- // Set desired output dimensions before reinit
458- self -> output_width = new_output_width ;
459- self -> output_height = new_output_height ;
460-
461- // Clean shutdown and reinitialize with new input dimensions
391+ // Clean shutdown and reinitialize with new resolution
462392 deinit_webcam (self );
463- int res = init_webcam (self , device , new_input_width , new_input_height );
393+ int res = init_webcam (self , device , new_width , new_height );
464394
465395 if (res < 0 ) {
466396 mp_raise_OSError (- res );
0 commit comments