/* * slashdot.c: decode Slashdot captchas * $Id$ * * Copyright: (c) 2004 Sam Hocevar * This program is free software; you can redistribute it and/or * modify it under the terms of the Do What The Fuck You Want To * Public License as published by Banlu Kemiyatorn. See * http://sam.zoy.org/projects/COPYING.WTFPL for more details. */ #include #include #include #include #include #include "config.h" #include "common.h" static void count_objects(struct image *img); static void rotate(struct image *img); static void find_glyphs(struct image *img); /* Global stuff */ struct { int xmin, ymin, xmax, ymax; } objlist[100]; int objects, first, last; char *result; /* Main function */ char *decode_slashdot(struct image *img) { struct image *tmp1, *tmp2; /* Initialise local data */ objects = 0; first = -1; last = -1; /* Slashdot captchas have 7 characters */ result = malloc(8 * sizeof(char)); strcpy(result, " "); /* Clean image a bit */ tmp1 = image_dup(img); filter_detect_lines(tmp1); filter_fill_holes(tmp1); /* Detect small objects to guess image orientation */ tmp2 = image_dup(tmp1); filter_median(tmp2); filter_equalize(tmp2, 200); count_objects(tmp2); /* Invert rotation and find glyphs */ rotate(tmp1); filter_median(tmp1); find_glyphs(tmp1); /* Clean up our mess */ image_free(tmp1); image_free(tmp2); /* aaaaaaa means decoding failed */ if(!strcmp(result, "aaaaaaa")) result[0] = '\0'; return result; } /* The following functions are local */ static void count_objects(struct image *img) { struct image *tmp; int gotblack = 1; int x, y, i; int r, g, b; tmp = image_new(img->width, img->height); for(y = 0; y < img->height; y++) for(x = 0; x < img->width; x++) { getpixel(img, x, y, &r, &g, &b); setpixel(tmp, x, y, r, g, b); } while(gotblack) { gotblack = 0; for(y = 0; y < tmp->height; y++) for(x = 0; x < tmp->width; x++) { getpixel(tmp, x, y, &r, &g, &b); if(r == 0 && g == 0 && b == 0) { gotblack = 1; filter_flood_fill(tmp, x, y, 254 - objects, 0, 0); objects++; } } } //printf("%i objects\n", objects); for(i = 0; i < objects; i++) { objlist[i].ymin = tmp->height; objlist[i].ymax = 0; for(y = 0; y < tmp->height; y++) for(x = 0; x < tmp->width; x++) { getpixel(tmp, x, y, &r, &g, &b); if(r == 255 - i && g == 0 && b == 0) { if(y < objlist[i].ymin) { objlist[i].ymin = y; objlist[i].xmin = x; } if(y > objlist[i].ymax) { objlist[i].ymax = y; objlist[i].xmax = x; } } } //printf("y min-max: %i %i (size %i)\n", objlist[i].ymin, objlist[i].ymax, objlist[i].ymax - objlist[i].ymin + 1); if(objlist[i].ymax - objlist[i].ymin > 18 && objlist[i].ymax - objlist[i].ymin < 27) { if(first == -1) first = i; last = i; filter_flood_fill(tmp, objlist[i].xmin, objlist[i].ymin, 0, 0, 255); } } image_swap(img, tmp); image_free(tmp); } static void rotate(struct image *img) { struct image *tmp; int x, y, xdest, ydest; int r, g, b; //int R, G, B; int X = objlist[first].xmin - objlist[last].xmin; int Y = objlist[first].ymin - objlist[last].ymin; float xtmp, ytmp; float sina = (1.0 * Y) / sqrt(1.0 * X * X + Y * Y); float cosa = (1.0 * X) / sqrt(1.0 * X * X + Y * Y); if(sina * cosa > 0) { sina = -sina; cosa = -cosa; } tmp = image_new(img->width, img->height); for(y = 0; y < img->height; y++) for(x = 0; x < img->width; x++) { xtmp = 1.0 * (x - img->width / 2); ytmp = 1.0 * (y - img->height / 2); xdest = xtmp * cosa - ytmp * sina + 0.5 * img->width; ydest = ytmp * cosa + xtmp * sina + 0.5 * img->height; //R = G = B = 0; getpixel(img, xdest, ydest, &r, &g, &b); //R += r; G += g; B += b; //getpixel(img, xdest+1, ydest, &r, &g, &b); //R += r; G += g; B += b; //getpixel(img, xdest, ydest+1, &r, &g, &b); //R += r; G += g; B += b; //getpixel(img, xdest+1, ydest+1, &r, &g, &b); //R += r; G += g; B += b; //r = R / 4; g = G / 4; b = B / 4; if(r == 255 && g == 0 && b == 255) g = 255; setpixel(tmp, x, y, r, g, b); } image_swap(img, tmp); image_free(tmp); } static void find_glyphs(struct image *img) { static struct font *font = NULL; struct image *tmp; int x, y, i = 0; int r, g, b; int xmin, xmax, ymin, ymax, startx = 0, cur = 0; int distmin, distx, disty, distch; if(!font) { font = font_load_variable("font_slashdot.png", "abcdefgijkmnpqrstvwxyz"); if(!font) exit(1); } tmp = image_new(img->width, img->height); for(y = 0; y < img->height; y++) for(x = 0; x < img->width; x++) { getpixel(img, x, y, &r, &g, &b); setpixel(tmp, x, y, 255, g, 255); } while(cur < 7) { /* Try to find 1st letter */ distmin = INT_MAX; for(i = 0; i < font->size; i++) { int localmin = INT_MAX, localx, localy; xmin = font->glyphs[i].xmin; ymin = font->glyphs[i].ymin; xmax = font->glyphs[i].xmax; ymax = font->glyphs[i].ymax; for(y = -5; y < 5; y++) for(x = startx - 5; x < startx + 5; x++) { int z, t, dist; dist = 0; for(t = 0; t < ymax - ymin; t++) for(z = 0; z < xmax - xmin; z++) { int r2; getgray(font->img, xmin + z, ymin + t, &r); getgray(img, x + z, y + t, &r2); dist += abs(r - r2); } // printf("%i %i -> %i\n", x, y, dist); //dist /= sqrt(xmax - xmin); dist = dist * 128 / font->glyphs[i].count; if(dist < localmin) { localmin = dist; localx = x; localy = y; } } //fprintf(stderr, "%i (%i,%i)\n", localmin, localx - startx, localy); if(localmin < distmin) { distmin = localmin; distx = localx; disty = localy; distch = i; } } /* Draw best glyph in picture (debugging purposes) */ xmin = font->glyphs[distch].xmin; ymin = font->glyphs[distch].ymin; xmax = font->glyphs[distch].xmax; ymax = font->glyphs[distch].ymax; for(y = 0; y < ymax - ymin; y++) for(x = 0; x < xmax - xmin; x++) { getpixel(font->img, xmin + x, ymin + y, &r, &g, &b); if(r > 128) continue; setpixel(tmp, distx + x, disty + y, r, g, b); } startx = distx + xmax - xmin; result[cur++] = font->glyphs[distch].c; } image_swap(img, tmp); image_free(tmp); }