Skip to content

Commit 5bf790e

Browse files
Simplify: no scaling
1 parent 31e61e7 commit 5bf790e

File tree

3 files changed

+81
-156
lines changed

3 files changed

+81
-156
lines changed

c_mpos/src/webcam.c

Lines changed: 76 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -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);

internal_filesystem/apps/com.micropythonos.camera/assets/camera_app.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -310,18 +310,12 @@ def handle_settings_result(self, result):
310310
# Reconfigure camera if active
311311
if self.cam:
312312
if self.use_webcam:
313-
print(f"Reconfiguring webcam: input={self.width}x{self.height}, output={self.width}x{self.height}")
314-
# Configure both V4L2 input and output to the same resolution for best quality
315-
webcam.reconfigure(
316-
self.cam,
317-
input_width=self.width,
318-
input_height=self.height,
319-
output_width=self.width,
320-
output_height=self.height
321-
)
313+
print(f"Reconfiguring webcam to {self.width}x{self.height}")
314+
# Reconfigure webcam resolution (input and output are the same)
315+
webcam.reconfigure(self.cam, width=self.width, height=self.height)
322316
# Resume capture timer for webcam
323317
self.capture_timer = lv.timer_create(self.try_capture, 100, None)
324-
print("Webcam reconfigured (V4L2 + output buffers), capture timer resumed")
318+
print("Webcam reconfigured, capture timer resumed")
325319
else:
326320
# For internal camera, need to reinitialize
327321
print(f"Reinitializing internal camera to {self.width}x{self.height}")

internal_filesystem/lib/mpos/board/fri3d_2024.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ def keypad_read_cb(indev, data):
274274
2343 is 3.957
275275
2319 is 3.916
276276
2269 is 3.831
277+
2227 is 3.769
277278
"""
278279
def adc_to_voltage(adc_value):
279280
"""

0 commit comments

Comments
 (0)