@@ -30,17 +30,24 @@ typedef struct _webcam_obj_t {
3030 int frame_count ;
3131 unsigned char * gray_buffer ; // For grayscale
3232 uint16_t * rgb565_buffer ; // For RGB565
33+ int input_width ; // Webcam capture width (from V4L2)
34+ int input_height ; // Webcam capture height (from V4L2)
35+ int output_width ; // Configurable output width (default OUTPUT_WIDTH)
36+ int output_height ; // Configurable output height (default OUTPUT_HEIGHT)
3337} webcam_obj_t ;
3438
35- static void yuyv_to_rgb565_240x240 (unsigned char * yuyv , uint16_t * rgb565 , int in_width , int in_height ) {
36- int crop_size = 480 ;
39+ static void yuyv_to_rgb565 (unsigned char * yuyv , uint16_t * rgb565 , int in_width , int in_height , int out_width , int out_height ) {
40+ // Crop to largest square that fits in the input frame
41+ int crop_size = (in_width < in_height ) ? in_width : in_height ;
3742 int crop_x_offset = (in_width - crop_size ) / 2 ;
3843 int crop_y_offset = (in_height - crop_size ) / 2 ;
39- float x_ratio = (float )crop_size / OUTPUT_WIDTH ;
40- float y_ratio = (float )crop_size / OUTPUT_HEIGHT ;
4144
42- for (int y = 0 ; y < OUTPUT_HEIGHT ; y ++ ) {
43- for (int x = 0 ; x < OUTPUT_WIDTH ; x ++ ) {
45+ // Calculate scaling ratios
46+ float x_ratio = (float )crop_size / out_width ;
47+ float y_ratio = (float )crop_size / out_height ;
48+
49+ for (int y = 0 ; y < out_height ; y ++ ) {
50+ for (int x = 0 ; x < out_width ; x ++ ) {
4451 int src_x = (int )(x * x_ratio ) + crop_x_offset ;
4552 int src_y = (int )(y * y_ratio ) + crop_y_offset ;
4653 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
6572 uint16_t g6 = (g >> 2 ) & 0x3F ;
6673 uint16_t b5 = (b >> 3 ) & 0x1F ;
6774
68- rgb565 [y * OUTPUT_WIDTH + x ] = (r5 << 11 ) | (g6 << 5 ) | b5 ;
75+ rgb565 [y * out_width + x ] = (r5 << 11 ) | (g6 << 5 ) | b5 ;
6976 }
7077 }
7178}
7279
73- static void yuyv_to_grayscale_240x240 (unsigned char * yuyv , unsigned char * gray , int in_width , int in_height ) {
74- int crop_size = 480 ;
80+ static void yuyv_to_grayscale (unsigned char * yuyv , unsigned char * gray , int in_width , int in_height , int out_width , int out_height ) {
81+ // Crop to largest square that fits in the input frame
82+ int crop_size = (in_width < in_height ) ? in_width : in_height ;
7583 int crop_x_offset = (in_width - crop_size ) / 2 ;
7684 int crop_y_offset = (in_height - crop_size ) / 2 ;
77- float x_ratio = (float )crop_size / OUTPUT_WIDTH ;
78- float y_ratio = (float )crop_size / OUTPUT_HEIGHT ;
7985
80- for (int y = 0 ; y < OUTPUT_HEIGHT ; y ++ ) {
81- for (int x = 0 ; x < OUTPUT_WIDTH ; x ++ ) {
86+ // Calculate scaling ratios
87+ float x_ratio = (float )crop_size / out_width ;
88+ float y_ratio = (float )crop_size / out_height ;
89+
90+ for (int y = 0 ; y < out_height ; y ++ ) {
91+ for (int x = 0 ; x < out_width ; x ++ ) {
8292 int src_x = (int )(x * x_ratio ) + crop_x_offset ;
8393 int src_y = (int )(y * y_ratio ) + crop_y_offset ;
8494 int src_index = (src_y * in_width + src_x ) * 2 ;
85- gray [y * OUTPUT_WIDTH + x ] = yuyv [src_index ];
95+ gray [y * out_width + x ] = yuyv [src_index ];
8696 }
8797 }
8898}
@@ -174,8 +184,22 @@ static int init_webcam(webcam_obj_t *self, const char *device) {
174184 }
175185
176186 self -> frame_count = 0 ;
177- self -> gray_buffer = (unsigned char * )malloc (OUTPUT_WIDTH * OUTPUT_HEIGHT * sizeof (unsigned char ));
178- self -> rgb565_buffer = (uint16_t * )malloc (OUTPUT_WIDTH * OUTPUT_HEIGHT * sizeof (uint16_t ));
187+
188+ // Store the input dimensions from V4L2 format
189+ self -> input_width = WIDTH ;
190+ self -> input_height = HEIGHT ;
191+
192+ // Initialize output dimensions with defaults if not already set
193+ if (self -> output_width == 0 ) self -> output_width = OUTPUT_WIDTH ;
194+ if (self -> output_height == 0 ) self -> output_height = OUTPUT_HEIGHT ;
195+
196+ WEBCAM_DEBUG_PRINT ("Webcam initialized: input %dx%d, output %dx%d\n" ,
197+ self -> input_width , self -> input_height ,
198+ self -> output_width , self -> output_height );
199+
200+ // Allocate buffers with configured output dimensions
201+ self -> gray_buffer = (unsigned char * )malloc (self -> output_width * self -> output_height * sizeof (unsigned char ));
202+ self -> rgb565_buffer = (uint16_t * )malloc (self -> output_width * self -> output_height * sizeof (uint16_t ));
179203 if (!self -> gray_buffer || !self -> rgb565_buffer ) {
180204 WEBCAM_DEBUG_PRINT ("Cannot allocate buffers: %s\n" , strerror (errno ));
181205 free (self -> gray_buffer );
@@ -227,36 +251,40 @@ static mp_obj_t capture_frame(mp_obj_t self_in, mp_obj_t format) {
227251 }
228252
229253 if (!self -> gray_buffer ) {
230- self -> gray_buffer = (unsigned char * )malloc (OUTPUT_WIDTH * OUTPUT_HEIGHT * sizeof (unsigned char ));
254+ self -> gray_buffer = (unsigned char * )malloc (self -> output_width * self -> output_height * sizeof (unsigned char ));
231255 if (!self -> gray_buffer ) {
232256 mp_raise_OSError (MP_ENOMEM );
233257 }
234258 }
235259 if (!self -> rgb565_buffer ) {
236- self -> rgb565_buffer = (uint16_t * )malloc (OUTPUT_WIDTH * OUTPUT_HEIGHT * sizeof (uint16_t ));
260+ self -> rgb565_buffer = (uint16_t * )malloc (self -> output_width * self -> output_height * sizeof (uint16_t ));
237261 if (!self -> rgb565_buffer ) {
238262 mp_raise_OSError (MP_ENOMEM );
239263 }
240264 }
241265
242266 const char * fmt = mp_obj_str_get_str (format );
243267 if (strcmp (fmt , "grayscale" ) == 0 ) {
244- yuyv_to_grayscale_240x240 (self -> buffers [buf .index ], self -> gray_buffer , WIDTH , HEIGHT );
268+ yuyv_to_grayscale (self -> buffers [buf .index ], self -> gray_buffer ,
269+ self -> input_width , self -> input_height ,
270+ self -> output_width , self -> output_height );
245271 // char filename[32];
246272 // snprintf(filename, sizeof(filename), "frame_%03d.raw", self->frame_count++);
247- // save_raw(filename, self->gray_buffer, OUTPUT_WIDTH, OUTPUT_HEIGHT );
248- mp_obj_t result = mp_obj_new_memoryview ('b' , OUTPUT_WIDTH * OUTPUT_HEIGHT , self -> gray_buffer );
273+ // save_raw(filename, self->gray_buffer, self->output_width, self->output_height );
274+ mp_obj_t result = mp_obj_new_memoryview ('b' , self -> output_width * self -> output_height , self -> gray_buffer );
249275 res = ioctl (self -> fd , VIDIOC_QBUF , & buf );
250276 if (res < 0 ) {
251277 mp_raise_OSError (- res );
252278 }
253279 return result ;
254280 } else {
255- yuyv_to_rgb565_240x240 (self -> buffers [buf .index ], self -> rgb565_buffer , WIDTH , HEIGHT );
281+ yuyv_to_rgb565 (self -> buffers [buf .index ], self -> rgb565_buffer ,
282+ self -> input_width , self -> input_height ,
283+ self -> output_width , self -> output_height );
256284 // char filename[32];
257285 // snprintf(filename, sizeof(filename), "frame_%03d.rgb565", self->frame_count++);
258- // save_raw_rgb565(filename, self->rgb565_buffer, OUTPUT_WIDTH, OUTPUT_HEIGHT );
259- mp_obj_t result = mp_obj_new_memoryview ('b' , OUTPUT_WIDTH * OUTPUT_HEIGHT * 2 , self -> rgb565_buffer );
286+ // save_raw_rgb565(filename, self->rgb565_buffer, self->output_width, self->output_height );
287+ mp_obj_t result = mp_obj_new_memoryview ('b' , self -> output_width * self -> output_height * 2 , self -> rgb565_buffer );
260288 res = ioctl (self -> fd , VIDIOC_QBUF , & buf );
261289 if (res < 0 ) {
262290 mp_raise_OSError (- res );
@@ -277,6 +305,10 @@ static mp_obj_t webcam_init(size_t n_args, const mp_obj_t *args) {
277305 self -> fd = -1 ;
278306 self -> gray_buffer = NULL ;
279307 self -> rgb565_buffer = NULL ;
308+ self -> input_width = 0 ; // Will be set from V4L2 format in init_webcam
309+ self -> input_height = 0 ; // Will be set from V4L2 format in init_webcam
310+ self -> output_width = 0 ; // Will use default OUTPUT_WIDTH in init_webcam
311+ self -> output_height = 0 ; // Will use default OUTPUT_HEIGHT in init_webcam
280312
281313 int res = init_webcam (self , device );
282314 if (res < 0 ) {
@@ -309,6 +341,65 @@ static mp_obj_t webcam_capture_frame(mp_obj_t self_in, mp_obj_t format) {
309341}
310342MP_DEFINE_CONST_FUN_OBJ_2 (webcam_capture_frame_obj , webcam_capture_frame );
311343
344+ static mp_obj_t webcam_reconfigure (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
345+ // NOTE: This function only changes OUTPUT resolution (what Python receives).
346+ // The INPUT resolution (what the webcam captures from V4L2) remains fixed at 640x480.
347+ // The conversion functions will crop/scale from input to output resolution.
348+ // TODO: Add support for changing input resolution (requires V4L2 reinit)
349+
350+ enum { ARG_self , ARG_output_width , ARG_output_height };
351+ static const mp_arg_t allowed_args [] = {
352+ { MP_QSTR_self , MP_ARG_REQUIRED | MP_ARG_OBJ , {.u_obj = MP_OBJ_NULL } },
353+ { MP_QSTR_output_width , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
354+ { MP_QSTR_output_height , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
355+ };
356+
357+ mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
358+ mp_arg_parse_all (n_args , pos_args , kw_args , MP_ARRAY_SIZE (allowed_args ), allowed_args , args );
359+
360+ webcam_obj_t * self = MP_OBJ_TO_PTR (args [ARG_self ].u_obj );
361+
362+ // Get new dimensions (keep current if not specified)
363+ int new_width = args [ARG_output_width ].u_int ;
364+ int new_height = args [ARG_output_height ].u_int ;
365+
366+ if (new_width == 0 ) new_width = self -> output_width ;
367+ if (new_height == 0 ) new_height = self -> output_height ;
368+
369+ // Validate dimensions
370+ if (new_width <= 0 || new_height <= 0 || new_width > 1920 || new_height > 1920 ) {
371+ mp_raise_ValueError (MP_ERROR_TEXT ("Invalid output dimensions" ));
372+ }
373+
374+ // If dimensions changed, reallocate buffers
375+ if (new_width != self -> output_width || new_height != self -> output_height ) {
376+ // Free old buffers
377+ free (self -> gray_buffer );
378+ free (self -> rgb565_buffer );
379+
380+ // Update dimensions
381+ self -> output_width = new_width ;
382+ self -> output_height = new_height ;
383+
384+ // Allocate new buffers
385+ self -> gray_buffer = (unsigned char * )malloc (self -> output_width * self -> output_height * sizeof (unsigned char ));
386+ self -> rgb565_buffer = (uint16_t * )malloc (self -> output_width * self -> output_height * sizeof (uint16_t ));
387+
388+ if (!self -> gray_buffer || !self -> rgb565_buffer ) {
389+ free (self -> gray_buffer );
390+ free (self -> rgb565_buffer );
391+ self -> gray_buffer = NULL ;
392+ self -> rgb565_buffer = NULL ;
393+ mp_raise_OSError (MP_ENOMEM );
394+ }
395+
396+ WEBCAM_DEBUG_PRINT ("Webcam reconfigured to %dx%d\n" , self -> output_width , self -> output_height );
397+ }
398+
399+ return mp_const_none ;
400+ }
401+ MP_DEFINE_CONST_FUN_OBJ_KW (webcam_reconfigure_obj , 1 , webcam_reconfigure );
402+
312403static const mp_obj_type_t webcam_type = {
313404 { & mp_type_type },
314405 .name = MP_QSTR_Webcam ,
@@ -321,6 +412,7 @@ static const mp_rom_map_elem_t mp_module_webcam_globals_table[] = {
321412 { MP_ROM_QSTR (MP_QSTR_capture_frame ), MP_ROM_PTR (& webcam_capture_frame_obj ) },
322413 { MP_ROM_QSTR (MP_QSTR_deinit ), MP_ROM_PTR (& webcam_deinit_obj ) },
323414 { MP_ROM_QSTR (MP_QSTR_free_buffer ), MP_ROM_PTR (& webcam_free_buffer_obj ) },
415+ { MP_ROM_QSTR (MP_QSTR_reconfigure ), MP_ROM_PTR (& webcam_reconfigure_obj ) },
324416};
325417static MP_DEFINE_CONST_DICT (mp_module_webcam_globals , mp_module_webcam_globals_table ) ;
326418
0 commit comments