diff --git a/glossa_loop.py b/glossa_loop.py index 8042c3d..eddbd9f 100644 --- a/glossa_loop.py +++ b/glossa_loop.py @@ -335,7 +335,7 @@ def capture_framebuffer(ssh): img = Image.frombytes("RGBA", (1404, 1872), raw) img_rgb = img.convert("RGB") - img_rgb = img_rgb.crop((0, 80, img_rgb.width, img_rgb.height - 200)) + img_rgb = img_rgb.crop((10, 80, img_rgb.width - 10, img_rgb.height - 200)) return img_rgb diff --git a/xovi-ext/glossad/glossad.c b/xovi-ext/glossad/glossad.c index 41a0f1f..7f18537 100644 --- a/xovi-ext/glossad/glossad.c +++ b/xovi-ext/glossad/glossad.c @@ -69,7 +69,10 @@ /* Crop parameters (match Python loop) */ #define CROP_TOP 80 #define CROP_BOTTOM 200 +#define CROP_LEFT 10 +#define CROP_RIGHT 10 #define CROP_HEIGHT (FB_HEIGHT - CROP_TOP - CROP_BOTTOM) +#define CROP_WIDTH (FB_WIDTH - CROP_LEFT - CROP_RIGHT) /* Influence map constants */ #define INFLUENCE_RADIUS 30 /* px around each stroke point */ @@ -92,11 +95,11 @@ static pthread_mutex_t g_inject_lock = PTHREAD_MUTEX_INITIALIZER; static time_t g_safezone_until = 0; static time_t g_last_activity = 0; -/* Stroke influence map: CROP_HEIGHT x FB_WIDTH, uint8. +/* Stroke influence map: CROP_HEIGHT x CROP_WIDTH, uint8. * Tracks known content regions so we can mask them out of the AI crop * and detect only genuinely new handwriting. Zeroed at startup, * cleared on page turn. */ -static unsigned char g_influence[CROP_HEIGHT][FB_WIDTH]; +static unsigned char g_influence[CROP_HEIGHT][CROP_WIDTH]; /* ─── logging ────────────────────────────────────────────────────── */ @@ -251,14 +254,14 @@ static void mark_stroke_region(const float *points, int n_points, int stride) { float px = points[i * stride]; float py = points[i * stride + 1]; - /* Convert to crop-space */ - int cx = (int)(px + 702.0f); /* rm x=0 is center, img x=702 is center */ + /* Convert to crop-space (accounting for side margins) */ + int cx = (int)(px + 702.0f) - CROP_LEFT; int cy = (int)(py - CROP_TOP); /* Bounding box in crop space, clamped */ int x0 = cx - r; if (x0 < 0) x0 = 0; int y0 = cy - r; if (y0 < 0) y0 = 0; - int x1 = cx + r; if (x1 >= FB_WIDTH) x1 = FB_WIDTH - 1; + int x1 = cx + r; if (x1 >= CROP_WIDTH) x1 = CROP_WIDTH - 1; int y1 = cy + r; if (y1 >= CROP_HEIGHT) y1 = CROP_HEIGHT - 1; for (int y = y0; y <= y1; y++) { @@ -316,9 +319,9 @@ static void mark_strokes_from_json(const char *json) { * regions are blanked to white. Used before saving PNG for the AI. */ static void apply_influence_mask(unsigned char *gray_crop) { for (int y = 0; y < CROP_HEIGHT; y++) { - for (int x = 0; x < FB_WIDTH; x++) { + for (int x = 0; x < CROP_WIDTH; x++) { if (g_influence[y][x] > INFLUENCE_THRESH) - gray_crop[y * FB_WIDTH + x] = 255; + gray_crop[y * CROP_WIDTH + x] = 255; } } } @@ -333,10 +336,11 @@ static int detect_new_content(const unsigned char *fb, int *out_bottom) { for (int y = 0; y < CROP_HEIGHT; y++) { int raw_y = y + CROP_TOP; const unsigned char *row_ptr = fb + raw_y * FB_WIDTH * FB_BPP; - for (int x = 0; x < FB_WIDTH; x++) { + for (int x = 0; x < CROP_WIDTH; x++) { if (g_influence[y][x] > INFLUENCE_THRESH) continue; /* known region, skip */ - if (rgba_to_gray(row_ptr + x * FB_BPP) < DARK_THRESHOLD) { + int raw_x = x + CROP_LEFT; + if (rgba_to_gray(row_ptr + raw_x * FB_BPP) < DARK_THRESHOLD) { found = 1; bottom_crop_y = y; /* keep scanning for lowest */ break; /* one dark pixel in this row is enough */ @@ -480,13 +484,13 @@ static int save_cropped_png(const unsigned char *fb, const char *path) { } png_init_io(png, fp); - png_set_IHDR(png, info, FB_WIDTH, CROP_HEIGHT, 8, + png_set_IHDR(png, info, CROP_WIDTH, CROP_HEIGHT, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png, info); /* Write row by row, converting RGBA to grayscale with influence mask */ - unsigned char *row_buf = malloc(FB_WIDTH); + unsigned char *row_buf = malloc(CROP_WIDTH); if (!row_buf) { png_destroy_write_struct(&png, &info); fclose(fp); @@ -496,8 +500,9 @@ static int save_cropped_png(const unsigned char *fb, const char *path) { for (int y = 0; y < CROP_HEIGHT; y++) { int raw_y = y + CROP_TOP; const unsigned char *src = fb + raw_y * FB_WIDTH * FB_BPP; - for (int x = 0; x < FB_WIDTH; x++) { - unsigned char gray = rgba_to_gray(src + x * FB_BPP); + for (int x = 0; x < CROP_WIDTH; x++) { + int raw_x = x + CROP_LEFT; + unsigned char gray = rgba_to_gray(src + raw_x * FB_BPP); /* Blank out known content regions */ if (g_influence[y][x] > INFLUENCE_THRESH) gray = 255; @@ -1357,14 +1362,14 @@ static void run_pipeline(void) { /* 2. Despeckle: build a grayscale crop, remove isolated pixels, * then use it for both detection and the AI PNG. */ - unsigned char *crop_gray = malloc(FB_WIDTH * CROP_HEIGHT); + unsigned char *crop_gray = malloc(CROP_WIDTH * CROP_HEIGHT); if (!crop_gray) { free(fb); return; } for (int y = 0; y < CROP_HEIGHT; y++) { const unsigned char *src = fb + (y + CROP_TOP) * FB_WIDTH * FB_BPP; - for (int x = 0; x < FB_WIDTH; x++) - crop_gray[y * FB_WIDTH + x] = rgba_to_gray(src + x * FB_BPP); + for (int x = 0; x < CROP_WIDTH; x++) + crop_gray[y * CROP_WIDTH + x] = rgba_to_gray(src + (x + CROP_LEFT) * FB_BPP); } - despeckle_crop(crop_gray, FB_WIDTH, CROP_HEIGHT); + despeckle_crop(crop_gray, CROP_WIDTH, CROP_HEIGHT); /* 3. Detect new content outside known regions (using despeckled crop) */ int content_bottom = 0; @@ -1372,9 +1377,9 @@ static void run_pipeline(void) { int found = 0; int bottom_y = -1; for (int y = 0; y < CROP_HEIGHT; y++) { - for (int x = 0; x < FB_WIDTH; x++) { + for (int x = 0; x < CROP_WIDTH; x++) { if (g_influence[y][x] > INFLUENCE_THRESH) continue; - if (crop_gray[y * FB_WIDTH + x] < DARK_THRESHOLD) { + if (crop_gray[y * CROP_WIDTH + x] < DARK_THRESHOLD) { found = 1; bottom_y = y; break; @@ -1404,10 +1409,10 @@ static void run_pipeline(void) { if (!info) { png_destroy_write_struct(&png, NULL); fclose(fp); free(crop_gray); free(fb); return; } if (setjmp(png_jmpbuf(png))) { png_destroy_write_struct(&png, &info); fclose(fp); free(crop_gray); free(fb); return; } png_init_io(png, fp); - png_set_IHDR(png, info, FB_WIDTH, CROP_HEIGHT, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_set_IHDR(png, info, CROP_WIDTH, CROP_HEIGHT, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png, info); for (int y = 0; y < CROP_HEIGHT; y++) - png_write_row(png, crop_gray + y * FB_WIDTH); + png_write_row(png, crop_gray + y * CROP_WIDTH); png_write_end(png, NULL); png_destroy_write_struct(&png, &info); fclose(fp); @@ -1453,7 +1458,7 @@ static void run_pipeline(void) { history_add("assistant", answer); /* 6. Calculate reply Y position */ - int reply_y = content_bottom + 80 + 60; + int reply_y = content_bottom + 40; if (reply_y > 1750) reply_y = 1750; log_msg("Pipeline: reply_y=%d\n", reply_y); @@ -1548,9 +1553,9 @@ int main(int argc, char **argv) { /* Start 5s safe zone */ FILE *sf = fopen(SAFEZONE_PATH, "w"); if (sf) { fputs("1\n", sf); fclose(sf); } - g_safezone_until = time(NULL) + 10; + g_safezone_until = time(NULL) + 4; g_last_activity = time(NULL); - log_msg("Safe zone active for 10s\n"); + log_msg("Safe zone active for 4s\n"); } else { unlink(SAFEZONE_PATH); g_safezone_until = 0;