Skip to content

Commit 1ab4970

Browse files
Work towards changing camera resolution
1 parent fa80b7c commit 1ab4970

File tree

4 files changed

+346
-37
lines changed

4 files changed

+346
-37
lines changed

c_mpos/src/webcam.c

Lines changed: 116 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}
310342
MP_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+
312403
static 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
};
325417
static MP_DEFINE_CONST_DICT(mp_module_webcam_globals, mp_module_webcam_globals_table);
326418

internal_filesystem/apps/com.micropythonos.camera/META-INF/MANIFEST.JSON

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
"category": "default"
2323
}
2424
]
25+
},
26+
{
27+
"entrypoint": "assets/camera_app.py",
28+
"classname": "CameraSettingsActivity",
29+
"intent_filters": []
2530
}
2631
]
2732
}

0 commit comments

Comments
 (0)