You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

271 line
7.6 KiB

  1. /*
  2. * slashdot.c: decode Slashdot captchas
  3. * $Id$
  4. *
  5. * Copyright: (c) 2004 Sam Hocevar <sam@zoy.org>
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the Do What The Fuck You Want To
  8. * Public License as published by Banlu Kemiyatorn. See
  9. * http://sam.zoy.org/projects/COPYING.WTFPL for more details.
  10. */
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <limits.h>
  15. #include <math.h>
  16. #include "config.h"
  17. #include "common.h"
  18. static void count_objects(struct image *img);
  19. static void rotate(struct image *img);
  20. static void find_glyphs(struct image *img);
  21. /* Global stuff */
  22. struct { int xmin, ymin, xmax, ymax; } objlist[100];
  23. int objects, first, last;
  24. char *result;
  25. /* Main function */
  26. char *decode_slashdot(struct image *img)
  27. {
  28. struct image *tmp1, *tmp2;
  29. /* Initialise local data */
  30. objects = 0;
  31. first = -1;
  32. last = -1;
  33. /* Slashdot captchas have 7 characters */
  34. result = malloc(8 * sizeof(char));
  35. strcpy(result, " ");
  36. /* Clean image a bit */
  37. tmp1 = image_dup(img);
  38. filter_detect_lines(tmp1);
  39. filter_fill_holes(tmp1);
  40. /* Detect small objects to guess image orientation */
  41. tmp2 = image_dup(tmp1);
  42. filter_median(tmp2);
  43. filter_equalize(tmp2, 200);
  44. count_objects(tmp2);
  45. /* Invert rotation and find glyphs */
  46. rotate(tmp1);
  47. filter_median(tmp1);
  48. find_glyphs(tmp1);
  49. /* Clean up our mess */
  50. image_free(tmp1);
  51. image_free(tmp2);
  52. /* aaaaaaa means decoding failed */
  53. if(!strcmp(result, "aaaaaaa"))
  54. result[0] = '\0';
  55. return result;
  56. }
  57. /* The following functions are local */
  58. static void count_objects(struct image *img)
  59. {
  60. struct image *tmp;
  61. int gotblack = 1;
  62. int x, y, i;
  63. int r, g, b;
  64. tmp = image_new(img->width, img->height);
  65. for(y = 0; y < img->height; y++)
  66. for(x = 0; x < img->width; x++)
  67. {
  68. getpixel(img, x, y, &r, &g, &b);
  69. setpixel(tmp, x, y, r, g, b);
  70. }
  71. while(gotblack)
  72. {
  73. gotblack = 0;
  74. for(y = 0; y < tmp->height; y++)
  75. for(x = 0; x < tmp->width; x++)
  76. {
  77. getpixel(tmp, x, y, &r, &g, &b);
  78. if(r == 0 && g == 0 && b == 0)
  79. {
  80. gotblack = 1;
  81. filter_flood_fill(tmp, x, y, 254 - objects, 0, 0);
  82. objects++;
  83. }
  84. }
  85. }
  86. //printf("%i objects\n", objects);
  87. for(i = 0; i < objects; i++)
  88. {
  89. objlist[i].ymin = tmp->height;
  90. objlist[i].ymax = 0;
  91. for(y = 0; y < tmp->height; y++)
  92. for(x = 0; x < tmp->width; x++)
  93. {
  94. getpixel(tmp, x, y, &r, &g, &b);
  95. if(r == 255 - i && g == 0 && b == 0)
  96. {
  97. if(y < objlist[i].ymin) { objlist[i].ymin = y; objlist[i].xmin = x; }
  98. if(y > objlist[i].ymax) { objlist[i].ymax = y; objlist[i].xmax = x; }
  99. }
  100. }
  101. //printf("y min-max: %i %i (size %i)\n", objlist[i].ymin, objlist[i].ymax, objlist[i].ymax - objlist[i].ymin + 1);
  102. if(objlist[i].ymax - objlist[i].ymin > 18 && objlist[i].ymax - objlist[i].ymin < 27)
  103. {
  104. if(first == -1)
  105. first = i;
  106. last = i;
  107. filter_flood_fill(tmp, objlist[i].xmin, objlist[i].ymin, 0, 0, 255);
  108. }
  109. }
  110. image_swap(img, tmp);
  111. image_free(tmp);
  112. }
  113. static void rotate(struct image *img)
  114. {
  115. struct image *tmp;
  116. int x, y, xdest, ydest;
  117. int r, g, b;
  118. //int R, G, B;
  119. int X = objlist[first].xmin - objlist[last].xmin;
  120. int Y = objlist[first].ymin - objlist[last].ymin;
  121. float xtmp, ytmp;
  122. float sina = (1.0 * Y) / sqrt(1.0 * X * X + Y * Y);
  123. float cosa = (1.0 * X) / sqrt(1.0 * X * X + Y * Y);
  124. if(sina * cosa > 0)
  125. {
  126. sina = -sina;
  127. cosa = -cosa;
  128. }
  129. tmp = image_new(img->width, img->height);
  130. for(y = 0; y < img->height; y++)
  131. for(x = 0; x < img->width; x++)
  132. {
  133. xtmp = 1.0 * (x - img->width / 2);
  134. ytmp = 1.0 * (y - img->height / 2);
  135. xdest = xtmp * cosa - ytmp * sina + 0.5 * img->width;
  136. ydest = ytmp * cosa + xtmp * sina + 0.5 * img->height;
  137. //R = G = B = 0;
  138. getpixel(img, xdest, ydest, &r, &g, &b);
  139. //R += r; G += g; B += b;
  140. //getpixel(img, xdest+1, ydest, &r, &g, &b);
  141. //R += r; G += g; B += b;
  142. //getpixel(img, xdest, ydest+1, &r, &g, &b);
  143. //R += r; G += g; B += b;
  144. //getpixel(img, xdest+1, ydest+1, &r, &g, &b);
  145. //R += r; G += g; B += b;
  146. //r = R / 4; g = G / 4; b = B / 4;
  147. if(r == 255 && g == 0 && b == 255)
  148. g = 255;
  149. setpixel(tmp, x, y, r, g, b);
  150. }
  151. image_swap(img, tmp);
  152. image_free(tmp);
  153. }
  154. static void find_glyphs(struct image *img)
  155. {
  156. static struct font *font = NULL;
  157. struct image *tmp;
  158. int x, y, i = 0;
  159. int r, g, b;
  160. int xmin, xmax, ymin, ymax, startx = 0, cur = 0;
  161. int distmin, distx, disty, distch;
  162. if(!font)
  163. {
  164. font = font_load_variable("font_slashdot.png", "abcdefgijkmnpqrstvwxyz");
  165. if(!font)
  166. exit(1);
  167. }
  168. tmp = image_new(img->width, img->height);
  169. for(y = 0; y < img->height; y++)
  170. for(x = 0; x < img->width; x++)
  171. {
  172. getpixel(img, x, y, &r, &g, &b);
  173. setpixel(tmp, x, y, 255, g, 255);
  174. }
  175. while(cur < 7)
  176. {
  177. /* Try to find 1st letter */
  178. distmin = INT_MAX;
  179. for(i = 0; i < font->size; i++)
  180. {
  181. int localmin = INT_MAX, localx, localy;
  182. xmin = font->glyphs[i].xmin;
  183. ymin = font->glyphs[i].ymin;
  184. xmax = font->glyphs[i].xmax;
  185. ymax = font->glyphs[i].ymax;
  186. for(y = -5; y < 5; y++)
  187. for(x = startx - 5; x < startx + 5; x++)
  188. {
  189. int z, t, dist;
  190. dist = 0;
  191. for(t = 0; t < ymax - ymin; t++)
  192. for(z = 0; z < xmax - xmin; z++)
  193. {
  194. int r2;
  195. getgray(font->img, xmin + z, ymin + t, &r);
  196. getgray(img, x + z, y + t, &r2);
  197. dist += abs(r - r2);
  198. }
  199. // printf("%i %i -> %i\n", x, y, dist);
  200. //dist /= sqrt(xmax - xmin);
  201. dist = dist * 128 / font->glyphs[i].count;
  202. if(dist < localmin)
  203. {
  204. localmin = dist;
  205. localx = x;
  206. localy = y;
  207. }
  208. }
  209. //fprintf(stderr, "%i (%i,%i)\n", localmin, localx - startx, localy);
  210. if(localmin < distmin)
  211. {
  212. distmin = localmin;
  213. distx = localx;
  214. disty = localy;
  215. distch = i;
  216. }
  217. }
  218. /* Draw best glyph in picture (debugging purposes) */
  219. xmin = font->glyphs[distch].xmin;
  220. ymin = font->glyphs[distch].ymin;
  221. xmax = font->glyphs[distch].xmax;
  222. ymax = font->glyphs[distch].ymax;
  223. for(y = 0; y < ymax - ymin; y++)
  224. for(x = 0; x < xmax - xmin; x++)
  225. {
  226. getpixel(font->img, xmin + x, ymin + y, &r, &g, &b);
  227. if(r > 128) continue;
  228. setpixel(tmp, distx + x, disty + y, r, g, b);
  229. }
  230. startx = distx + xmax - xmin;
  231. result[cur++] = font->glyphs[distch].c;
  232. }
  233. image_swap(img, tmp);
  234. image_free(tmp);
  235. }