Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/fitz/warp.c @ 3:2c135c81b16c
MERGE: upstream PyMuPDF 1.26.4 with MuPDF 1.26.7
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:44:09 +0200 |
| parents | b50eed0cc0ef |
| children |
comparison
equal
deleted
inserted
replaced
| 0:6015a75abc2d | 3:2c135c81b16c |
|---|---|
| 1 // Copyright (C) 2004-2025 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 #include "mupdf/fitz.h" | |
| 24 | |
| 25 #include "pixmap-imp.h" | |
| 26 | |
| 27 #include <string.h> | |
| 28 | |
| 29 /* Define TIMINGS to get timing information dumped to stdout. */ | |
| 30 #undef TIMINGS | |
| 31 | |
| 32 /* Define WARP_DEBUG to get debugging output (and PNGs saved). Note | |
| 33 * that this will affect timings! */ | |
| 34 #undef WARP_DEBUG | |
| 35 | |
| 36 /* Define WARP_SPEW_DEBUG to get even more debug output (and PNGs). */ | |
| 37 #undef WARP_SPEW_DEBUG | |
| 38 | |
| 39 /* One reference suggested doing histogram equalisation. */ | |
| 40 #define DO_HISTEQ | |
| 41 | |
| 42 /* Define DETECT_DOCUMENT_RGB, and edge detection on RGB documents will | |
| 43 * look for edges in just the R,G,B planes as well as the grey plane. */ | |
| 44 #undef DETECT_DOCUMENT_RGB | |
| 45 | |
| 46 #undef SLOW_INTERPOLATION | |
| 47 #undef SLOW_WARPING | |
| 48 | |
| 49 #ifdef WARP_DEBUG | |
| 50 static void | |
| 51 debug_printf(fz_context *ctx, const char *fmt, ...) | |
| 52 { | |
| 53 char text[1024]; | |
| 54 va_list list; | |
| 55 va_start(list, fmt); | |
| 56 vsnprintf(text, sizeof(text), fmt, list); | |
| 57 va_end(list); | |
| 58 | |
| 59 #ifdef _WIN32 | |
| 60 fz_write_string(ctx, fz_stdods(ctx), text); | |
| 61 #endif | |
| 62 fputs(text, stderr); | |
| 63 } | |
| 64 #else | |
| 65 #define debug_printf(CTX, FMT, ...) do {} while (0) | |
| 66 #endif | |
| 67 | |
| 68 #if defined(TIMINGS) && defined(_WIN32) | |
| 69 | |
| 70 #include "windows.h" | |
| 71 | |
| 72 #define MAX_TIMERS 256 | |
| 73 static struct { | |
| 74 int started; | |
| 75 int stackptr; | |
| 76 int stack[MAX_TIMERS]; | |
| 77 const char *name[MAX_TIMERS]; | |
| 78 LARGE_INTEGER time[MAX_TIMERS]; | |
| 79 } timer; | |
| 80 #define START_TIME() \ | |
| 81 do {\ | |
| 82 int i = timer.stack[timer.stackptr++] = timer.started++;\ | |
| 83 QueryPerformanceCounter(&timer.time[i]);\ | |
| 84 } while (0) | |
| 85 #define END_TIME(NAME) \ | |
| 86 do {\ | |
| 87 LARGE_INTEGER end;\ | |
| 88 int i;\ | |
| 89 QueryPerformanceCounter(&end);\ | |
| 90 i = timer.stack[--timer.stackptr];\ | |
| 91 timer.time[i].QuadPart = end.QuadPart - timer.time[i].QuadPart;\ | |
| 92 timer.name[i] = NAME;\ | |
| 93 } while (0) | |
| 94 #define DUMP_TIMES() \ | |
| 95 do {\ | |
| 96 int i;\ | |
| 97 LARGE_INTEGER freq;\ | |
| 98 QueryPerformanceFrequency(&freq);\ | |
| 99 float f = freq.QuadPart;\ | |
| 100 for (i = 0; i < timer.started; i++)\ | |
| 101 debug_printf(ctx, "%s: %g\n", timer.name[i], (int)1000*timer.time[i].QuadPart/f);\ | |
| 102 } while (0) | |
| 103 #else | |
| 104 #define START_TIME() do {} while(0) | |
| 105 #define END_TIME(NAME) do {} while(0) | |
| 106 #define DUMP_TIMES() do {} while(0) | |
| 107 #endif | |
| 108 | |
| 109 typedef struct | |
| 110 { | |
| 111 int x; | |
| 112 int y; | |
| 113 } fz_ipoint; | |
| 114 | |
| 115 typedef struct | |
| 116 { | |
| 117 int i; | |
| 118 int f; | |
| 119 int di; | |
| 120 int df; | |
| 121 } fz_bresenham_core; | |
| 122 | |
| 123 typedef struct | |
| 124 { | |
| 125 fz_bresenham_core c; | |
| 126 int n; | |
| 127 } fz_bresenham; | |
| 128 | |
| 129 typedef struct | |
| 130 { | |
| 131 fz_bresenham_core x; | |
| 132 fz_bresenham_core y; | |
| 133 int n; | |
| 134 } fz_ipoint_bresenham; | |
| 135 | |
| 136 typedef struct | |
| 137 { | |
| 138 fz_bresenham_core sx; | |
| 139 fz_bresenham_core sy; | |
| 140 fz_bresenham_core ex; | |
| 141 fz_bresenham_core ey; | |
| 142 int n; | |
| 143 } fz_ipoint2_bresenham; | |
| 144 | |
| 145 static inline fz_bresenham_core | |
| 146 init_bresenham_core(int start, int end, int n) | |
| 147 { | |
| 148 fz_bresenham_core b; | |
| 149 int delta = end-start; | |
| 150 | |
| 151 b.di = n == 0 ? 0 : delta/n; | |
| 152 b.df = delta - n*b.di; /* 0 <= b.df < n */ | |
| 153 if (b.df < 0) | |
| 154 b.di--, b.df += n; | |
| 155 /* Starts with bi.i = start, bi.f = n, and then does a half | |
| 156 * step. */ | |
| 157 b.i = start + (b.di>>1); | |
| 158 b.f = n - (((b.di & 1) * n + b.df)>>1); | |
| 159 | |
| 160 return b; | |
| 161 } | |
| 162 | |
| 163 #ifdef CURRENTLY_UNUSED | |
| 164 static inline fz_bresenham | |
| 165 init_bresenham(int start, int end, int n) | |
| 166 { | |
| 167 fz_bresenham b; | |
| 168 | |
| 169 b.c = init_bresenham_core(start, end, n); | |
| 170 b.n = n; | |
| 171 | |
| 172 return b; | |
| 173 } | |
| 174 | |
| 175 static inline void | |
| 176 step(fz_bresenham *b) | |
| 177 { | |
| 178 step_core(&b->c, b->n); | |
| 179 } | |
| 180 #endif | |
| 181 | |
| 182 static inline void | |
| 183 step_core(fz_bresenham_core *b, int n) | |
| 184 { | |
| 185 b->i += b->di; | |
| 186 b->f -= b->df; | |
| 187 if (b->f <= 0) | |
| 188 { | |
| 189 b->f += n; | |
| 190 b->i++; | |
| 191 } | |
| 192 } | |
| 193 | |
| 194 static inline fz_ipoint_bresenham | |
| 195 init_ip_bresenham(fz_ipoint start, fz_ipoint end, int n) | |
| 196 { | |
| 197 fz_ipoint_bresenham b; | |
| 198 | |
| 199 b.x = init_bresenham_core(start.x, end.x, n); | |
| 200 b.y = init_bresenham_core(start.y, end.y, n); | |
| 201 b.n = n; | |
| 202 | |
| 203 return b; | |
| 204 } | |
| 205 | |
| 206 static inline void | |
| 207 step_ip(fz_ipoint_bresenham *b) | |
| 208 { | |
| 209 step_core(&b->x, b->n); | |
| 210 step_core(&b->y, b->n); | |
| 211 } | |
| 212 | |
| 213 static inline fz_ipoint | |
| 214 current_ip(const fz_ipoint_bresenham *b) | |
| 215 { | |
| 216 fz_ipoint ip; | |
| 217 | |
| 218 ip.x = b->x.i; | |
| 219 ip.y = b->y.i; | |
| 220 | |
| 221 return ip; | |
| 222 } | |
| 223 | |
| 224 static inline fz_ipoint2_bresenham | |
| 225 init_ip2_bresenham(fz_ipoint ss, fz_ipoint se, fz_ipoint es, fz_ipoint ee, int n) | |
| 226 { | |
| 227 fz_ipoint2_bresenham b; | |
| 228 | |
| 229 b.sx = init_bresenham_core(ss.x, se.x, n); | |
| 230 b.sy = init_bresenham_core(ss.y, se.y, n); | |
| 231 b.ex = init_bresenham_core(es.x, ee.x, n); | |
| 232 b.ey = init_bresenham_core(es.y, ee.y, n); | |
| 233 b.n = n; | |
| 234 | |
| 235 return b; | |
| 236 } | |
| 237 | |
| 238 static inline void | |
| 239 step_ip2(fz_ipoint2_bresenham *b) | |
| 240 { | |
| 241 step_core(&b->sx, b->n); | |
| 242 step_core(&b->sy, b->n); | |
| 243 step_core(&b->ex, b->n); | |
| 244 step_core(&b->ey, b->n); | |
| 245 } | |
| 246 | |
| 247 static inline fz_ipoint | |
| 248 start_ip(const fz_ipoint2_bresenham *b) | |
| 249 { | |
| 250 fz_ipoint ip; | |
| 251 | |
| 252 ip.x = b->sx.i; | |
| 253 ip.y = b->sy.i; | |
| 254 | |
| 255 return ip; | |
| 256 } | |
| 257 | |
| 258 static fz_forceinline fz_ipoint | |
| 259 end_ip(const fz_ipoint2_bresenham *b) | |
| 260 { | |
| 261 fz_ipoint ip; | |
| 262 | |
| 263 ip.x = b->ex.i; | |
| 264 ip.y = b->ey.i; | |
| 265 | |
| 266 return ip; | |
| 267 } | |
| 268 | |
| 269 static void | |
| 270 interp_n(unsigned char *d, const unsigned char *s0, | |
| 271 const unsigned char *s1, int f, int n) | |
| 272 { | |
| 273 do | |
| 274 { | |
| 275 int a = *s0++; | |
| 276 int b = *s1++ - a; | |
| 277 *d++ = ((a<<8) + b*f + 128)>>8; | |
| 278 } | |
| 279 while (--n); | |
| 280 } | |
| 281 | |
| 282 static void | |
| 283 interp2_n(unsigned char *d, const unsigned char *s0, | |
| 284 const unsigned char *s1, const unsigned char *s2, | |
| 285 int f0, int f1, int n) | |
| 286 { | |
| 287 do | |
| 288 { | |
| 289 int a = *s0++; | |
| 290 int b = *s1++ - a; | |
| 291 int c; | |
| 292 a = (a<<8) + b*f0; | |
| 293 c = (*s2++<<8) - a; | |
| 294 *d++ = ((a<<8) + c*f1 + (1<<15))>>16; | |
| 295 } | |
| 296 while (--n); | |
| 297 } | |
| 298 | |
| 299 static inline void | |
| 300 copy_pixel(unsigned char *d, const fz_pixmap *src, fz_ipoint p) | |
| 301 { | |
| 302 int u = p.x>>8; | |
| 303 int v = p.y>>8; | |
| 304 int fu = p.x & 255; | |
| 305 int fv = p.y & 255; | |
| 306 int n = src->n; | |
| 307 const unsigned char *s; | |
| 308 ptrdiff_t stride = src->stride; | |
| 309 | |
| 310 if (u < 0) | |
| 311 u = 0, fu = 0; | |
| 312 else if (u >= src->w-1) | |
| 313 u = src->w-1, fu = 0; | |
| 314 | |
| 315 if (v < 0) | |
| 316 v = 0, fv = 0; | |
| 317 else if (v >= src->h-1) | |
| 318 v = src->h-1, fv = 0; | |
| 319 | |
| 320 s = &src->samples[u * n + v * stride]; | |
| 321 | |
| 322 #ifdef SLOW_INTERPOLATION | |
| 323 { | |
| 324 int i; | |
| 325 | |
| 326 for (i = 0; i < n; i++) | |
| 327 { | |
| 328 int v0 = s[0]; | |
| 329 int v1 = s[n]; | |
| 330 int v2 = s[stride]; | |
| 331 int v3 = s[stride+n]; | |
| 332 int v01, v23, v; | |
| 333 v01 = (v0<<8) + (v1-v0)*fu; | |
| 334 v23 = (v2<<8) + (v3-v2)*fu; | |
| 335 v = (v01<<8) + (v23-v01)*fv; | |
| 336 assert(v >= 0 && v < (1<<24)-32768); | |
| 337 *d++ = (v + 32768)>>16; | |
| 338 s++; | |
| 339 } | |
| 340 return; | |
| 341 } | |
| 342 #else | |
| 343 if (fu == 0) | |
| 344 { | |
| 345 if (fv == 0) | |
| 346 { | |
| 347 /* Copy single pixel */ | |
| 348 memcpy(d, s, n); | |
| 349 return; | |
| 350 } | |
| 351 /* interpolate y pixels */ | |
| 352 interp_n(d, s, s + stride, fv, n); | |
| 353 return; | |
| 354 } | |
| 355 if (fv == 0) | |
| 356 { | |
| 357 /* interpolate x pixels */ | |
| 358 interp_n(d, s, s+n, fu, n); | |
| 359 return; | |
| 360 } | |
| 361 | |
| 362 if (fu <= fv) | |
| 363 { | |
| 364 /* Top half of the trapezoid. */ | |
| 365 interp2_n(d, s, s+stride, s+stride+n, fv, fu, n); | |
| 366 } | |
| 367 else | |
| 368 { | |
| 369 /* Bottom half of the trapezoid. */ | |
| 370 interp2_n(d, s, s+n, s+stride+n, fu, fv, n); | |
| 371 } | |
| 372 #endif | |
| 373 } | |
| 374 | |
| 375 static void | |
| 376 warp_core(unsigned char *d, int n, int width, int height, int stride, | |
| 377 const fz_ipoint corner[4], const fz_pixmap *src) | |
| 378 { | |
| 379 fz_ipoint2_bresenham row_bres; | |
| 380 int x; | |
| 381 | |
| 382 /* We have a bresenham pair for how to move the start | |
| 383 * and end of the row each y step. */ | |
| 384 row_bres = init_ip2_bresenham(corner[0], corner[3], | |
| 385 corner[1], corner[2], height); | |
| 386 stride -= width * n; | |
| 387 | |
| 388 #ifdef SLOW_WARPING | |
| 389 { | |
| 390 int h; | |
| 391 for (h = 0 ; h < height ; h++) | |
| 392 { | |
| 393 int sx = corner[0].x + (corner[3].x - corner[0].x)*h/height; | |
| 394 int sy = corner[0].y + (corner[3].y - corner[0].y)*h/height; | |
| 395 int ex = corner[1].x + (corner[2].x - corner[1].x)*h/height; | |
| 396 int ey = corner[1].y + (corner[2].y - corner[1].y)*h/height; | |
| 397 for (x = 0; x < width; x++) | |
| 398 { | |
| 399 fz_ipoint p; | |
| 400 p.x = sx + (ex-sx)*x/width; | |
| 401 p.y = sy + (ey-sy)*x/width; | |
| 402 copy_pixel(d, src, p); | |
| 403 d += n; | |
| 404 } | |
| 405 d += stride; | |
| 406 } | |
| 407 } | |
| 408 #else | |
| 409 for (; height > 0; height--) | |
| 410 { | |
| 411 /* We have a bresenham for how to move the | |
| 412 * current pixel across the row. */ | |
| 413 fz_ipoint_bresenham pix_bres; | |
| 414 pix_bres = init_ip_bresenham(start_ip(&row_bres), | |
| 415 end_ip(&row_bres), | |
| 416 width); | |
| 417 for (x = width; x > 0; x--) | |
| 418 { | |
| 419 /* Copy pixel */ | |
| 420 copy_pixel(d, src, current_ip(&pix_bres)); | |
| 421 d += n; | |
| 422 step_ip(&pix_bres); | |
| 423 } | |
| 424 | |
| 425 /* step to the next line. */ | |
| 426 step_ip2(&row_bres); | |
| 427 d += stride; | |
| 428 } | |
| 429 #endif | |
| 430 } | |
| 431 | |
| 432 /* | |
| 433 points are clockwise from NW. | |
| 434 | |
| 435 This performs simple affine warping. | |
| 436 */ | |
| 437 fz_pixmap * | |
| 438 fz_warp_pixmap(fz_context *ctx, fz_pixmap *src, fz_quad points, int width, int height) | |
| 439 { | |
| 440 fz_pixmap *dst; | |
| 441 | |
| 442 if (src == NULL) | |
| 443 return NULL; | |
| 444 | |
| 445 if (width >= (1<<24) || width < 0 || height >= (1<<24) || height < 0) | |
| 446 fz_throw(ctx, FZ_ERROR_LIMIT, "Bad width/height"); | |
| 447 | |
| 448 dst = fz_new_pixmap(ctx, src->colorspace, width, height, | |
| 449 src->seps, src->alpha); | |
| 450 dst->xres = src->xres; | |
| 451 dst->yres = src->yres; | |
| 452 | |
| 453 fz_try(ctx) | |
| 454 { | |
| 455 unsigned char *d = dst->samples; | |
| 456 int n = dst->n; | |
| 457 fz_ipoint corner[4]; | |
| 458 | |
| 459 /* Find the corner texture positions as fixed point */ | |
| 460 corner[0].x = (int)(points.ul.x * 256 + 128); | |
| 461 corner[0].y = (int)(points.ul.y * 256 + 128); | |
| 462 corner[1].x = (int)(points.ur.x * 256 + 128); | |
| 463 corner[1].y = (int)(points.ur.y * 256 + 128); | |
| 464 corner[2].x = (int)(points.ll.x * 256 + 128); | |
| 465 corner[2].y = (int)(points.ll.y * 256 + 128); | |
| 466 corner[3].x = (int)(points.lr.x * 256 + 128); | |
| 467 corner[3].y = (int)(points.lr.y * 256 + 128); | |
| 468 | |
| 469 warp_core(d, n, width, height, width * n, corner, src); | |
| 470 } | |
| 471 fz_catch(ctx) | |
| 472 { | |
| 473 fz_drop_pixmap(ctx, dst); | |
| 474 fz_rethrow(ctx); | |
| 475 } | |
| 476 | |
| 477 return dst; | |
| 478 } | |
| 479 | |
| 480 static float | |
| 481 dist(fz_point a, fz_point b) | |
| 482 { | |
| 483 float x = a.x-b.x; | |
| 484 float y = a.y-b.y; | |
| 485 | |
| 486 return sqrtf(x*x+y*y); | |
| 487 } | |
| 488 | |
| 489 /* Again, affine warping, but this time where the destination width/height | |
| 490 * are chosen automatically. */ | |
| 491 fz_pixmap * | |
| 492 fz_autowarp_pixmap(fz_context *ctx, fz_pixmap *src, fz_quad points) | |
| 493 { | |
| 494 float w0 = dist(points.ur, points.ul); | |
| 495 float w1 = dist(points.ll, points.lr); | |
| 496 float h0 = dist(points.lr, points.ul); | |
| 497 float h1 = dist(points.ll, points.ur); | |
| 498 int w = (w0+w1+0.5)/2; | |
| 499 int h = (h0+h1+0.5)/2; | |
| 500 | |
| 501 return fz_warp_pixmap(ctx, src, points, w, h); | |
| 502 } | |
| 503 | |
| 504 /* | |
| 505 Corner detection: We shall steal the algorithm from the Dropbox | |
| 506 Document Scanner, as described here: | |
| 507 | |
| 508 https://dropbox.tech/machine-learning/fast-and-accurate-document-detection-for-scanning | |
| 509 | |
| 510 A good reference to the steps involved in the canny edge | |
| 511 detection process can be found here: | |
| 512 | |
| 513 https://towardsdatascience.com/canny-edge-detection-step-by-step-in-python-computer-vision-b49c3a2d8123 | |
| 514 | |
| 515 This involves: | |
| 516 * Canny Edge Detection | |
| 517 * * Greyscale conversion | |
| 518 * * Noise reduction | |
| 519 * * Gradient Calculation | |
| 520 * * Non-maximum suppression | |
| 521 * * Double threshold | |
| 522 * * Edge tracking by Hysteresis | |
| 523 * Hough Transform to fix possible edges | |
| 524 * Computing intersections and scoring quads | |
| 525 | |
| 526 We modify the gradient calculation with a simple scale to ensure we fill the range. | |
| 527 */ | |
| 528 | |
| 529 #ifdef DO_HISTEQ | |
| 530 static void | |
| 531 histeq(fz_pixmap *im) | |
| 532 { | |
| 533 uint32_t count[256]; | |
| 534 unsigned char tbl[256]; | |
| 535 int i; | |
| 536 unsigned char *s = im->samples; | |
| 537 int n = im->w*im->h; | |
| 538 int sigma; | |
| 539 int den; | |
| 540 | |
| 541 memset(count, 0, sizeof(count)); | |
| 542 for (i = n; i > 0; i--) | |
| 543 count[*s++]++; | |
| 544 | |
| 545 /* Rather than doing a pure histogram equalisation, we bend | |
| 546 * the table so that 0 always stays as 0, and 255 always stays | |
| 547 * as 255. */ | |
| 548 sigma = (count[0]>>1) - count[0]; | |
| 549 den = sigma + n - (count[255]>>1); | |
| 550 for (i = 0; i < 256; i++) | |
| 551 { | |
| 552 int v = count[i]; | |
| 553 sigma += v - (v>>1); | |
| 554 tbl[i] = (int)(255.0f * sigma / den + 0.5f); | |
| 555 sigma += (v>>1); | |
| 556 } | |
| 557 | |
| 558 s = im->samples; | |
| 559 for (i = n; i > 0; i--) | |
| 560 *s = tbl[*s], s++; | |
| 561 } | |
| 562 #endif | |
| 563 | |
| 564 /* The first functions apply a 5x5 gauss filter to blur the greyscale | |
| 565 * image and remove noise. The gauss filter is a convolution with | |
| 566 * weights: | |
| 567 * | |
| 568 * 2 4 5 4 2 | |
| 569 * 4 9 12 9 4 | |
| 570 * 5 12 15 12 5 | |
| 571 * 4 9 12 9 4 | |
| 572 * 2 4 5 4 2 | |
| 573 * | |
| 574 * As you can see, there are 3 distinct lines of weights within that | |
| 575 * matrix. We walk each row of source pixels once, calculating each of | |
| 576 * those convolutions, storing the result in a rolling buffer of 5x3xw | |
| 577 * entries. Then we sum columns from the buffer to give us the results. | |
| 578 */ | |
| 579 | |
| 580 /* Read across a row of pixels of width w, from s, performing | |
| 581 * the horizontal portion of the convolutions, storing the results | |
| 582 * in 3 lines of a buffer, starting at d, &d[w], &d[2*w]. | |
| 583 * | |
| 584 * d[0*w] uses weights (5 12 15 12 5) | |
| 585 * d[1*w] uses weights (4 9 12 9 4) | |
| 586 * d[2*w] uses weights (2 4 5 4 2) | |
| 587 */ | |
| 588 static void | |
| 589 gauss5row(uint16_t *d, const unsigned char *s, int w) | |
| 590 { | |
| 591 int i; | |
| 592 int s0 = s[0]; | |
| 593 int s1 = s[1]; | |
| 594 int s2 = s[2]; | |
| 595 int s3 = s[3]; | |
| 596 | |
| 597 s += 4; | |
| 598 | |
| 599 d[2*w] = 11*s0 + 4*s1 + 2*s2; | |
| 600 d[1*w] = 25*s0 + 9*s1 + 4*s2; | |
| 601 *d++ = 32*s0 + 12*s1 + 5*s2; | |
| 602 | |
| 603 d[2*w] = 6*s0 + 5*s1 + 4*s2 + 2*s3; | |
| 604 d[1*w] = 13*s0 + 12*s1 + 9*s2 + 4*s3; | |
| 605 *d++ = 17*s0 + 15*s1 + 12*s2 + 5*s3; | |
| 606 | |
| 607 for (i = w - 4; i > 0; i--) | |
| 608 { | |
| 609 int d2 = 2*s0 + 4*s1 + 5*s2 + 4*s3; | |
| 610 int d1 = 4*s0 + 9*s1 + 12*s2 + 9*s3; | |
| 611 int d0 = 5*s0 + 12*s1 + 15*s2 + 12*s3; | |
| 612 s0 = s1; | |
| 613 s1 = s2; | |
| 614 s2 = s3; | |
| 615 s3 = *s++; | |
| 616 d[2*w] = d2 + 2*s3; | |
| 617 d[1*w] = d1 + 4*s3; | |
| 618 *d++ = d0 + 5*s3; | |
| 619 } | |
| 620 | |
| 621 d[2*w] = 2*s0 + 4*s1 + 5*s2 + 6*s3; | |
| 622 d[1*w] = 4*s0 + 9*s1 + 12*s2 + 13*s3; | |
| 623 *d++ = 5*s0 + 12*s1 + 15*s2 + 17*s3; | |
| 624 | |
| 625 d[2*w] = 2*s1 + 4*s2 + 11*s3; | |
| 626 d[1*w] = 4*s1 + 9*s2 + 25*s3; | |
| 627 *d = 5*s1 + 12*s2 + 32*s3; | |
| 628 } | |
| 629 | |
| 630 /* Calculate the results for row y of the image, of width w, by | |
| 631 * summing results from the temporary buffer s, and writing into the | |
| 632 * original pixmap at d. */ | |
| 633 static void | |
| 634 gauss5col(unsigned char *d, const uint16_t *s, int y, int w) | |
| 635 { | |
| 636 const uint16_t *s0, *s1, *s2, *s3, *s4; | |
| 637 y *= 3; | |
| 638 s0 = &s[((y+ 9+2)%15)*w]; | |
| 639 s1 = &s[((y+12+1)%15)*w]; | |
| 640 s2 = &s[( y %15)*w]; | |
| 641 s3 = &s[((y+ 3+1)%15)*w]; | |
| 642 s4 = &s[((y+ 6+2)%15)*w]; | |
| 643 | |
| 644 for (; w > 0; w--) | |
| 645 *d++ = (*s0++ + *s1++ + *s2++ + *s3++ + *s4++ + 79)/159; | |
| 646 } | |
| 647 | |
| 648 static void | |
| 649 gauss5x5(fz_context *ctx, fz_pixmap *src) | |
| 650 { | |
| 651 int w = src->w; | |
| 652 int h = src->h; | |
| 653 uint16_t *buf; | |
| 654 unsigned char *s = src->samples; | |
| 655 int y; | |
| 656 | |
| 657 if (w < 5 || h < 5) | |
| 658 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Pixmap too small"); | |
| 659 | |
| 660 buf = fz_malloc(ctx, w*3*5 * sizeof(uint16_t)); | |
| 661 | |
| 662 gauss5row(&buf[0*3*w], &s[0*w], w); | |
| 663 gauss5row(&buf[1*3*w], &s[1*w], w); | |
| 664 memcpy(&buf[3*3*w], buf, w*3 * sizeof(uint16_t)); /* row -2 */ | |
| 665 memcpy(&buf[4*3*w], buf, w*3 * sizeof(uint16_t)); /* row -1 */ | |
| 666 for (y = 2; y < h; y++) | |
| 667 { | |
| 668 gauss5row(&buf[(y%5)*3*w], &s[2*w], w); | |
| 669 | |
| 670 gauss5col(s, buf, y-2, w); | |
| 671 s += w; | |
| 672 } | |
| 673 for (; y < h+2; y++) | |
| 674 { | |
| 675 memcpy(&buf[(y%5)*3*w], &buf[((y+4)%5)*3*w], 3*w*sizeof(uint16_t)); | |
| 676 gauss5col(s, buf, y-2, w); | |
| 677 s += w; | |
| 678 } | |
| 679 | |
| 680 fz_free(ctx, buf); | |
| 681 } | |
| 682 | |
| 683 #ifdef DETECT_DOCUMENT_RGB | |
| 684 /* Variant of the above that works on a single plane from rgb data */ | |
| 685 static void | |
| 686 gauss5row3(uint16_t *d, const unsigned char *s, int w) | |
| 687 { | |
| 688 int i; | |
| 689 int s0 = s[0]; | |
| 690 int s1 = s[3]; | |
| 691 int s2 = s[6]; | |
| 692 int s3 = s[9]; | |
| 693 | |
| 694 s += 4*3; | |
| 695 | |
| 696 d[2*w] = 11*s0 + 4*s1 + 2*s2; | |
| 697 d[1*w] = 25*s0 + 9*s1 + 4*s2; | |
| 698 *d++ = 32*s0 + 12*s1 + 5*s2; | |
| 699 | |
| 700 d[2*w] = 6*s0 + 5*s1 + 4*s2 + 2*s3; | |
| 701 d[1*w] = 13*s0 + 12*s1 + 9*s2 + 4*s3; | |
| 702 *d++ = 17*s0 + 15*s1 + 12*s2 + 5*s3; | |
| 703 | |
| 704 for (i = w - 4; i > 0; i--) | |
| 705 { | |
| 706 int d2 = 2*s0 + 4*s1 + 5*s2 + 4*s3; | |
| 707 int d1 = 4*s0 + 9*s1 + 12*s2 + 9*s3; | |
| 708 int d0 = 5*s0 + 12*s1 + 15*s2 + 12*s3; | |
| 709 s0 = s1; | |
| 710 s1 = s2; | |
| 711 s2 = s3; | |
| 712 s3 = *s; s += 3; | |
| 713 d[2*w] = d2 + 2*s3; | |
| 714 d[1*w] = d1 + 4*s3; | |
| 715 *d++ = d0 + 5*s3; | |
| 716 } | |
| 717 | |
| 718 d[2*w] = 2*s0 + 4*s1 + 5*s2 + 6*s3; | |
| 719 d[1*w] = 4*s0 + 9*s1 + 12*s2 + 13*s3; | |
| 720 *d++ = 5*s0 + 12*s1 + 15*s2 + 17*s3; | |
| 721 | |
| 722 d[2*w] = 2*s1 + 4*s2 + 11*s3; | |
| 723 d[1*w] = 4*s1 + 9*s2 + 25*s3; | |
| 724 *d = 5*s1 + 12*s2 + 32*s3; | |
| 725 } | |
| 726 | |
| 727 static void | |
| 728 gauss5x5_3(fz_context *ctx, fz_pixmap *dst, const fz_pixmap *src, int comp) | |
| 729 { | |
| 730 int w = src->w; | |
| 731 int h = src->h; | |
| 732 uint16_t *buf; | |
| 733 unsigned char *s = src->samples + comp; | |
| 734 unsigned char *d = dst->samples; | |
| 735 int y; | |
| 736 | |
| 737 if (w < 5 || h < 5) | |
| 738 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Pixmap too small"); | |
| 739 | |
| 740 buf = fz_malloc(ctx, w*3*5 * sizeof(uint16_t)); | |
| 741 | |
| 742 gauss5row3(&buf[0*3*w], s, w); | |
| 743 s += w*3; | |
| 744 gauss5row3(&buf[1*3*w], s, w); | |
| 745 s += w*3; | |
| 746 memcpy(&buf[3*3*w], buf, w*3 * sizeof(uint16_t)); /* row -2 */ | |
| 747 memcpy(&buf[4*3*w], buf, w*3 * sizeof(uint16_t)); /* row -1 */ | |
| 748 for (y = 2; y < h; y++) | |
| 749 { | |
| 750 gauss5row3(&buf[((y*3)%15)*w], s, w); | |
| 751 | |
| 752 gauss5col(d, buf, y-2, w); | |
| 753 s += w*3; | |
| 754 d += w; | |
| 755 } | |
| 756 for (; y < h+2; y++) | |
| 757 { | |
| 758 memcpy(&buf[(y%5)*3*w], &buf[((y+4)%5)*3*w], 3*w*sizeof(uint16_t)); | |
| 759 gauss5col(d, buf, y-2, w); | |
| 760 d += w; | |
| 761 } | |
| 762 | |
| 763 fz_free(ctx, buf); | |
| 764 } | |
| 765 #endif | |
| 766 | |
| 767 /* The next set of functions perform the gradient calculation. | |
| 768 * We convolve with Sobel kernels Kx and Ky respectively: | |
| 769 * | |
| 770 * Kx = -1 0 1 Ky = 1 2 1 | |
| 771 * -2 0 2 0 0 0 | |
| 772 * -1 0 1 -1 -2 -1 | |
| 773 * | |
| 774 * We do this by using a rolling temporary buffer of int16_t's to hold | |
| 775 * 3 pairs of lines of weights scaled by (1 0 1) and (1 2 1). | |
| 776 * | |
| 777 * We can then sum entries from those lines to calculate kx and ky for | |
| 778 * each pixel in the image. | |
| 779 * | |
| 780 * Then by examining the values of x and y, we can figure out the | |
| 781 * "direction" of the edge (horizontal, vertical, or either diagonal), | |
| 782 * and the magnitude of the difference across that edge. These get | |
| 783 * encoded back into the original image storage using the 2 bottom bits | |
| 784 * for direction, and the top 6 bits for magnitude. | |
| 785 */ | |
| 786 | |
| 787 static void | |
| 788 pregradrow(int16_t *d, const unsigned char *s, int w) | |
| 789 { | |
| 790 int i; | |
| 791 unsigned char s0 = *s++; | |
| 792 unsigned char s1 = *s++; | |
| 793 | |
| 794 d[w] = 3*s0 + s1; | |
| 795 *d++ = s1 - s0; | |
| 796 for (i = w-2; i > 0; i--) | |
| 797 { | |
| 798 int s2 = *s++; | |
| 799 d[w] = s0 + 2*s1 + s2; | |
| 800 *d++ = s2 - s0; | |
| 801 s0 = s1; | |
| 802 s1 = s2; | |
| 803 } | |
| 804 d[w] = s0 + 3*s1; | |
| 805 *d = s1 - s0; | |
| 806 } | |
| 807 | |
| 808 static void | |
| 809 pregradcol(unsigned char *d, const int16_t *buf, int y, int w, uint32_t *max) | |
| 810 { | |
| 811 const int16_t *s0 = &buf[((y+2)%3)*w*2]; | |
| 812 const int16_t *s1 = &buf[((y )%3)*w*2]; | |
| 813 const int16_t *s2 = &buf[((y+1)%3)*w*2]; | |
| 814 int i; | |
| 815 | |
| 816 for (i = w; i > 0; i--) | |
| 817 { | |
| 818 uint32_t ax, ay, mag; | |
| 819 int x; | |
| 820 y = s0[w] - s2[w]; | |
| 821 x = *s0++ + 2 * *s1++ + *s2++; | |
| 822 ax = x >= 0 ? x : -x; | |
| 823 ay = y >= 0 ? y : -y; | |
| 824 /* x and y are now both in the range -1020..1020 */ | |
| 825 /* Now we calculate slope and gradient. | |
| 826 * angle = atan2(y, x); | |
| 827 * intensity = hypot(x, y); | |
| 828 * But wait, we don't need that accuracy. We only need | |
| 829 * to distinguish 4 directions... | |
| 830 * | |
| 831 * -22.5 < angle <= 22.5 = 0 | |
| 832 * 22.5 < angle <= 67.5 = 1 | |
| 833 * 67.5 < angle <= 112.5 = 2 | |
| 834 * 115.5 < angle <= 157.5 = 3 | |
| 835 * (and the reflections) | |
| 836 * | |
| 837 * 7 0 1 (x positive right, y positive downwards) | |
| 838 * 6 * 2 | |
| 839 * 5 4 3 | |
| 840 * | |
| 841 * tan(22.5)*65536 = 27146. | |
| 842 * And for magnitude, we consider the magnitude just | |
| 843 * along the 8 directions we've picked. So | |
| 844 * 65536/SQR(2) = 46341. | |
| 845 * We want magnitude in the 0...63 range. | |
| 846 */ | |
| 847 if (ax<<16 < 27146*ay) | |
| 848 { | |
| 849 /* angle = 0 */ | |
| 850 mag = ay<<16; /* 0 to 1020<<16 */ | |
| 851 } | |
| 852 else if (ay<<16 < ax*27146) | |
| 853 { | |
| 854 /* angle = 2 */ | |
| 855 mag = ax<<16; /* 0 to 1020<<16 */ | |
| 856 } | |
| 857 else | |
| 858 { | |
| 859 /* angle = 1 or 3 */ | |
| 860 mag = (46341*(ax+ay)); | |
| 861 } | |
| 862 if (mag > *max) | |
| 863 *max = mag; | |
| 864 } | |
| 865 } | |
| 866 | |
| 867 static uint32_t | |
| 868 pregrad(fz_context *ctx, fz_pixmap *src) | |
| 869 { | |
| 870 int w = src->w; | |
| 871 int h = src->h; | |
| 872 unsigned char *s = src->samples; | |
| 873 int16_t *buf = fz_malloc(ctx, w*2*3*sizeof(int16_t)); | |
| 874 int y; | |
| 875 uint32_t max = 0; | |
| 876 | |
| 877 pregradrow(buf, s, w); /* Line 0 */ | |
| 878 memcpy(&buf[w*2*2], buf, w*2*sizeof(int16_t)); /* Line 1 */ | |
| 879 s += w; | |
| 880 for (y = 1; y < h-1; y++) | |
| 881 { | |
| 882 pregradrow(&buf[(y%3)*w*2], s, w); | |
| 883 pregradcol(s-w, buf, y-1, w, &max); | |
| 884 s += w; | |
| 885 } | |
| 886 memcpy(&buf[((y+1)%3)*w*2], &buf[(y%3)*w*2], w*2*sizeof(int16_t)); /* Line h */ | |
| 887 pregradcol(s-w, buf, h-2, w, &max); | |
| 888 pregradcol(s, buf, h-1, w, &max); | |
| 889 | |
| 890 fz_free(ctx, buf); | |
| 891 | |
| 892 if (max == 0) | |
| 893 return 1; | |
| 894 else | |
| 895 return 0x7FFFFFFFU/max; | |
| 896 } | |
| 897 | |
| 898 | |
| 899 static void | |
| 900 gradrow(int16_t *d, const unsigned char *s, int w) | |
| 901 { | |
| 902 int i; | |
| 903 unsigned char s0 = *s++; | |
| 904 unsigned char s1 = *s++; | |
| 905 | |
| 906 d[w] = 3*s0 + s1; | |
| 907 *d++ = s1 - s0; | |
| 908 for (i = w-2; i > 0; i--) | |
| 909 { | |
| 910 int s2 = *s++; | |
| 911 d[w] = s0 + 2*s1 + s2; | |
| 912 *d++ = s2 - s0; | |
| 913 s0 = s1; | |
| 914 s1 = s2; | |
| 915 } | |
| 916 d[w] = s0 + 3*s1; | |
| 917 *d = s1 - s0; | |
| 918 } | |
| 919 | |
| 920 static void | |
| 921 gradcol(unsigned char *d, const int16_t *buf, int y, int w, int scale) | |
| 922 { | |
| 923 const int16_t *s0 = &buf[((y+2)%3)*w*2]; | |
| 924 const int16_t *s1 = &buf[((y )%3)*w*2]; | |
| 925 const int16_t *s2 = &buf[((y+1)%3)*w*2]; | |
| 926 int i; | |
| 927 | |
| 928 for (i = w; i > 0; i--) | |
| 929 { | |
| 930 uint32_t ax, ay, mag, scaled; | |
| 931 int angle, x; | |
| 932 y = s0[w] - s2[w]; | |
| 933 x = *s0++ + 2 * *s1++ + *s2++; | |
| 934 ax = x >= 0 ? x : -x; | |
| 935 ay = y >= 0 ? y : -y; | |
| 936 /* x and y are now both in the range -1020..1020 */ | |
| 937 /* Now we calculate slope and gradient. | |
| 938 * angle = atan2(y, x); | |
| 939 * intensity = hypot(x, y); | |
| 940 * But wait, we don't need that accuracy. We only need | |
| 941 * to distinguish 4 directions... | |
| 942 * | |
| 943 * -22.5 < angle <= 22.5 = 0 | |
| 944 * 22.5 < angle <= 67.5 = 1 | |
| 945 * 67.5 < angle <= 112.5 = 2 | |
| 946 * 115.5 < angle <= 157.5 = 3 | |
| 947 * (and the reflections) | |
| 948 * | |
| 949 * 7 0 1 (x positive right, y positive downwards) | |
| 950 * 6 * 2 | |
| 951 * 5 4 3 | |
| 952 * | |
| 953 * tan(22.5)*65536 = 27146. | |
| 954 * And for magnitude, we consider the magnitude just | |
| 955 * along the 8 directions we've picked. So | |
| 956 * 65536/SQR(2) = 46341. | |
| 957 * We want magnitude in the 0...63 range. | |
| 958 */ | |
| 959 if (ax<<16 < 27146*ay) | |
| 960 { | |
| 961 angle = 0; | |
| 962 mag = ay<<16; /* 0 to 1020<<16 */ | |
| 963 } | |
| 964 else if (ay<<16 < ax*27146) | |
| 965 { | |
| 966 angle = 2; | |
| 967 mag = ax<<16; /* 0 to 1020<<16 */ | |
| 968 } | |
| 969 else | |
| 970 { | |
| 971 /* 1 or 3 */ | |
| 972 angle = (x^y) >= 0 ? 3 : 1; | |
| 973 mag = (46341*(ax+ay)); | |
| 974 } | |
| 975 scaled = (mag * scale)>>25; | |
| 976 assert(scaled >= 0 && scaled <= 63); | |
| 977 *d++ = (scaled<<2) | angle; | |
| 978 } | |
| 979 } | |
| 980 | |
| 981 static void | |
| 982 grad(fz_context *ctx, fz_pixmap *src, uint32_t scale) | |
| 983 { | |
| 984 int w = src->w; | |
| 985 int h = src->h; | |
| 986 unsigned char *s = src->samples; | |
| 987 int16_t *buf = fz_malloc(ctx, w*2*3*sizeof(int16_t)); | |
| 988 int y; | |
| 989 | |
| 990 gradrow(buf, s, w); /* Line 0 */ | |
| 991 memcpy(&buf[w*2*2], buf, w*2*sizeof(int16_t)); /* Line 1 */ | |
| 992 s += w; | |
| 993 for (y = 1; y < h-1; y++) | |
| 994 { | |
| 995 gradrow(&buf[(y%3)*w*2], s, w); | |
| 996 gradcol(s-w, buf, y-1, w, scale); | |
| 997 s += w; | |
| 998 } | |
| 999 memcpy(&buf[((y+1)%3)*w*2], &buf[(y%3)*w*2], w*2*sizeof(int16_t)); /* Line h */ | |
| 1000 gradcol(s-w, buf, h-2, w, scale); | |
| 1001 gradcol(s, buf, h-1, w, scale); | |
| 1002 | |
| 1003 fz_free(ctx, buf); | |
| 1004 } | |
| 1005 | |
| 1006 #ifdef DETECT_DOCUMENT_RGB | |
| 1007 static void | |
| 1008 combine_grad(fz_pixmap *grey, const fz_pixmap *r, const fz_pixmap *g, const fz_pixmap *b) | |
| 1009 { | |
| 1010 int n; | |
| 1011 unsigned char *sd = grey->samples; | |
| 1012 const unsigned char *sr = r->samples; | |
| 1013 const unsigned char *sg = g->samples; | |
| 1014 const unsigned char *sb = b->samples; | |
| 1015 | |
| 1016 for (n = g->w * g->h; n > 0; n--) | |
| 1017 { | |
| 1018 unsigned char vg = *sg++; | |
| 1019 unsigned char vr = *sr++; | |
| 1020 unsigned char vb = *sb++; | |
| 1021 unsigned char vd = *sd++; | |
| 1022 if (vr > vg) | |
| 1023 vg = vr; | |
| 1024 if (vb > vg) | |
| 1025 vg = vb; | |
| 1026 if (vg > vd) | |
| 1027 sd[-1] = vg; | |
| 1028 } | |
| 1029 } | |
| 1030 #endif | |
| 1031 | |
| 1032 /* Next, we perform Non-Maximum Suppression and Double Thresholding, | |
| 1033 * both in the same phase. | |
| 1034 * | |
| 1035 * We walk the image, looking at the magnitude of the edges. Edges below | |
| 1036 * the 'weak' threshold are discarded. Otherwise, neighbouring pixels in | |
| 1037 * the direction of the edge are considered; if other pixels are stronger | |
| 1038 * then this pixel is discarded. If not, we classify ourself as either | |
| 1039 * 'strong' or 'weak'. | |
| 1040 */ | |
| 1041 #define WEAK_EDGE 64 | |
| 1042 #define STRONG_EDGE 128 | |
| 1043 static void | |
| 1044 nonmax(fz_context *ctx, fz_pixmap *dst, const fz_pixmap *src, int pass) | |
| 1045 { | |
| 1046 int w = src->w; | |
| 1047 int h = src->h; | |
| 1048 const unsigned char *s0 = src->samples; | |
| 1049 const unsigned char *s1 = s0; | |
| 1050 const unsigned char *s2 = s0+w; | |
| 1051 unsigned char *d = dst->samples; | |
| 1052 int x, y; | |
| 1053 /* thresholds are in the 0 to 63 range. | |
| 1054 * WEAK is typically 0.1ish, STRONG 0.3ish | |
| 1055 */ | |
| 1056 int weak = 6 - pass; | |
| 1057 int strong = 12 - pass*2; | |
| 1058 | |
| 1059 /* On entry, pixels have the angle in the bottom 2 bits and the magnitude in the rest. */ | |
| 1060 /* On exit, strong pixels have bit 7 set, weak pixels have bit 6, others are 0. | |
| 1061 * strong and weak pixels have the angle in bits 4 and 5. */ | |
| 1062 | |
| 1063 for (y = h-1; y >= 0;) | |
| 1064 { | |
| 1065 int lastmag; | |
| 1066 int ang = *s1++; | |
| 1067 int mag = ang>>2; | |
| 1068 int q, r; | |
| 1069 | |
| 1070 /* Pixel 0 */ | |
| 1071 if (mag <= weak) | |
| 1072 { | |
| 1073 /* Not even a weak edge. We'll never keep it. */ | |
| 1074 *d++ = 0; | |
| 1075 s0++; | |
| 1076 s2++; | |
| 1077 } | |
| 1078 else | |
| 1079 { | |
| 1080 ang &= 3; | |
| 1081 switch (ang) | |
| 1082 { | |
| 1083 default: | |
| 1084 case 0: | |
| 1085 q = (*s0++)>>2; | |
| 1086 r = (*s2++)>>2; | |
| 1087 break; | |
| 1088 case 1: | |
| 1089 q = (*++s0)>>2; | |
| 1090 r = 0; | |
| 1091 s2++; | |
| 1092 break; | |
| 1093 case 2: | |
| 1094 s0++; | |
| 1095 s2++; | |
| 1096 q = 0; | |
| 1097 r = *s1>>2; | |
| 1098 break; | |
| 1099 case 3: | |
| 1100 q = 0; | |
| 1101 s0++; | |
| 1102 r = (*++s2)>>2; | |
| 1103 break; | |
| 1104 } | |
| 1105 if (mag < q || mag < r) | |
| 1106 { | |
| 1107 /* Neighbouring edges are stronger. | |
| 1108 * Lose this one. */ | |
| 1109 *d++ = 0; | |
| 1110 } | |
| 1111 else if (mag < strong) | |
| 1112 { | |
| 1113 /* Weak edge. */ | |
| 1114 *d++ = WEAK_EDGE | (ang<<4); | |
| 1115 } | |
| 1116 else | |
| 1117 { | |
| 1118 /* Strong edge */ | |
| 1119 *d++ = STRONG_EDGE | (ang<<4); | |
| 1120 } | |
| 1121 } | |
| 1122 lastmag = mag; | |
| 1123 for (x = w-2; x > 0; x--) | |
| 1124 { | |
| 1125 ang = *s1++; | |
| 1126 mag = ang>>2; | |
| 1127 if (mag <= weak) | |
| 1128 { | |
| 1129 /* Not even a weak edge. We'll never keep it. */ | |
| 1130 *d++ = 0; | |
| 1131 s0++; | |
| 1132 s2++; | |
| 1133 } | |
| 1134 else | |
| 1135 { | |
| 1136 ang &= 3; | |
| 1137 switch (ang) | |
| 1138 { | |
| 1139 default: | |
| 1140 case 0: | |
| 1141 q = (*s0++)>>2; | |
| 1142 r = (*s2++)>>2; | |
| 1143 break; | |
| 1144 case 1: | |
| 1145 q = (*++s0)>>2; | |
| 1146 r = ((s2++)[-1])>>2; | |
| 1147 break; | |
| 1148 case 2: | |
| 1149 s0++; | |
| 1150 s2++; | |
| 1151 q = lastmag; | |
| 1152 r = *s1>>2; | |
| 1153 break; | |
| 1154 case 3: | |
| 1155 q = ((s0++)[-1])>>2; | |
| 1156 r = (*++s2)>>2; | |
| 1157 break; | |
| 1158 } | |
| 1159 if (mag < q || mag < r) | |
| 1160 { | |
| 1161 /* Neighbouring edges are stronger. | |
| 1162 * Lose this one. */ | |
| 1163 *d++ = 0; | |
| 1164 } | |
| 1165 else if (mag < strong) | |
| 1166 { | |
| 1167 /* Weak edge. */ | |
| 1168 *d++ = WEAK_EDGE | (ang<<4); | |
| 1169 } | |
| 1170 else | |
| 1171 { | |
| 1172 /* Strong edge */ | |
| 1173 *d++ = STRONG_EDGE | (ang<<4); | |
| 1174 } | |
| 1175 } | |
| 1176 lastmag = mag; | |
| 1177 } | |
| 1178 /* Pixel w-1 */ | |
| 1179 ang = *s1++; | |
| 1180 mag = ang>>2; | |
| 1181 if (mag <= weak) | |
| 1182 { | |
| 1183 /* Not even a weak edge. We'll never keep it. */ | |
| 1184 *d++ = 0; | |
| 1185 s0++; | |
| 1186 s2++; | |
| 1187 lastmag = 0; | |
| 1188 } | |
| 1189 else | |
| 1190 { | |
| 1191 ang &= 3; | |
| 1192 switch (ang) | |
| 1193 { | |
| 1194 default: | |
| 1195 case 0: | |
| 1196 q = (*s0++)>>2; | |
| 1197 r = (*s2++)>>2; | |
| 1198 break; | |
| 1199 case 1: | |
| 1200 q = 0; | |
| 1201 s0++; | |
| 1202 r = ((s2++)[-1])>>2; | |
| 1203 break; | |
| 1204 case 2: | |
| 1205 s0++; | |
| 1206 s2++; | |
| 1207 q = 0; | |
| 1208 r = *s1>>2; | |
| 1209 break; | |
| 1210 case 3: | |
| 1211 q = ((s0++)[-1])>>2; | |
| 1212 r = 0; | |
| 1213 s2++; | |
| 1214 break; | |
| 1215 } | |
| 1216 if (mag < q || mag < r) | |
| 1217 { | |
| 1218 /* Neighbouring edges are stronger. | |
| 1219 * Lose this one. */ | |
| 1220 *d++ = 0; | |
| 1221 } | |
| 1222 else if (mag < strong) | |
| 1223 { | |
| 1224 /* Weak edge. */ | |
| 1225 *d++ = WEAK_EDGE | (ang<<4); | |
| 1226 } | |
| 1227 else | |
| 1228 { | |
| 1229 /* Strong edge */ | |
| 1230 *d++ = STRONG_EDGE | (ang<<4); | |
| 1231 } | |
| 1232 } | |
| 1233 s0 = s1-w; | |
| 1234 if (--y == 0) | |
| 1235 s2 = s1; | |
| 1236 } | |
| 1237 } | |
| 1238 | |
| 1239 /* Next, we have the hysteresis phase. Here we bump any 'weak' pixel | |
| 1240 * that has at least one strong pixel around it up to being a 'strong' | |
| 1241 * pixel. | |
| 1242 * | |
| 1243 * On entry, strong pixels have bit 7 set, weak pixels have bit 6 set, | |
| 1244 * the angle is in bits 4 and 5, everything else is 0. | |
| 1245 * | |
| 1246 * We walk the rows of the image, and for each pixel we set bit 0 to be | |
| 1247 * the logical OR of all bit 7's of itself, and its horizontally | |
| 1248 * neighbouring pixels. | |
| 1249 * | |
| 1250 * Once we have done the first 2 rows like that, we can combine the | |
| 1251 * operation of generating the bottom bits for row i, with the | |
| 1252 * calculation of row i-1. Any given pixel on row i-1 should be promoted | |
| 1253 * to 'strong' if it was 'weak', and if the logical OR of the matching | |
| 1254 * pixels in row i, i-1 or i-2 has bit 0 set. | |
| 1255 * | |
| 1256 * At the end of this process any pixel with bit 7 set is 'strong'. | |
| 1257 * Bits 4 and 5 still have the angle. | |
| 1258 */ | |
| 1259 | |
| 1260 static void | |
| 1261 hysteresis(fz_context *ctx, fz_pixmap *src) | |
| 1262 { | |
| 1263 int w = src->w; | |
| 1264 int h = src->h; | |
| 1265 unsigned char *s0 = src->samples; | |
| 1266 unsigned char *s1 = s0; | |
| 1267 unsigned char *s2 = s0; | |
| 1268 unsigned char v0, v1, v2, r0, r1, r2; | |
| 1269 int x, y; | |
| 1270 | |
| 1271 /* On entry, strong pixels have bit 7 set, weak pixels have bit 6, others are 0. | |
| 1272 * strong and weak pixels have the angle in bits 4 and 5. */ | |
| 1273 | |
| 1274 /* We make the bottom bit in every pixel be 1 iff the pixel | |
| 1275 * or the ones to either side of it are 'strong'. */ | |
| 1276 | |
| 1277 /* First row - just do the bottom bit. */ | |
| 1278 /* Pixel 0 */ | |
| 1279 v0 = *s0++; | |
| 1280 v1 = *s0++; | |
| 1281 s0[-2] = v0 | ((v0 | v1)>>7); | |
| 1282 /* Middle pixels */ | |
| 1283 for (x = w-2; x > 0; x--) | |
| 1284 { | |
| 1285 v2 = *s0++; | |
| 1286 s0[-2] = v1 | ((v0 | v1 | v2)>>7); | |
| 1287 v0 = v1; | |
| 1288 v1 = v2; | |
| 1289 } | |
| 1290 /* Pixel w-1 */ | |
| 1291 s0[-1] = v1 | ((v0 | v1)>>7); | |
| 1292 assert(s0 == src->samples + w); | |
| 1293 | |
| 1294 /* Second row - do the "bottom bit" for the second row, and | |
| 1295 * perform hysteresis on the top row. */ | |
| 1296 /* Pixel 0 */ | |
| 1297 v0 = *s0++; | |
| 1298 v1 = *s0++; | |
| 1299 r0 = v0 | ((v0 | v1)>>7); | |
| 1300 s0[-2] = r0; | |
| 1301 r1 = *s1++; | |
| 1302 if ((r1>>6) & (r0 | r1)) | |
| 1303 s1[-1] |= 128; | |
| 1304 /* Middle pixels */ | |
| 1305 for (x = w-2; x > 0; x--) | |
| 1306 { | |
| 1307 v2 = *s0++; | |
| 1308 r0 = v1 | ((v0 | v1 | v2)>>7); | |
| 1309 s0[-2] = r0; | |
| 1310 r1 = *s1++; | |
| 1311 if ((r1>>6) & (r0 | r1)) | |
| 1312 s1[-1] |= 128; | |
| 1313 v0 = v1; | |
| 1314 v1 = v2; | |
| 1315 } | |
| 1316 /* Pixel w-1 */ | |
| 1317 r0 = v1 | ((v0 | v1)>>7); | |
| 1318 s0[-1] = r0; | |
| 1319 r1 = *s1++; | |
| 1320 if ((r1>>6) & (r0 | r1)) | |
| 1321 s1[-1] |= 128; | |
| 1322 assert(s0 == s1 + w); | |
| 1323 | |
| 1324 /* Now we get into the swing of things. We do the "bottom bit" | |
| 1325 * for row n+1, and do the actual processing for row n. */ | |
| 1326 for (y = h-4; y > 0; y--) | |
| 1327 { | |
| 1328 /* Pixel 0 */ | |
| 1329 v0 = *s0++; | |
| 1330 v1 = *s0++; | |
| 1331 r0 = v0 | ((v0 | v1)>>7); | |
| 1332 s0[-2] = r0; | |
| 1333 r1 = *s1++; | |
| 1334 r2 = *s2++; | |
| 1335 if ((r1>>6) & (r0 | r1 | r2)) | |
| 1336 s1[-1] |= 128; | |
| 1337 /* Middle pixels */ | |
| 1338 for (x = w-2; x > 0; x--) | |
| 1339 { | |
| 1340 v2 = *s0++; | |
| 1341 r0 = v1 | ((v0 | v1 | v2)>>7); | |
| 1342 s0[-2] = r0; | |
| 1343 r1 = *s1++; | |
| 1344 r2 = *s2++; | |
| 1345 if ((r1>>6) & (r0 | r1 | r2)) | |
| 1346 s1[-1] |= 128; | |
| 1347 v0 = v1; | |
| 1348 v1 = v2; | |
| 1349 } | |
| 1350 /* Pixel w-1 */ | |
| 1351 r0 = v1 | ((v0 | v1)>>7); | |
| 1352 s0[-1] = r0; | |
| 1353 r1 = *s1++; | |
| 1354 r2 = *s2++; | |
| 1355 if ((r1>>6) & (r0 | r1 | r2)) | |
| 1356 s1[-1] |= 128; | |
| 1357 assert(s0 == s1 + w); | |
| 1358 assert(s1 == s2 + w); | |
| 1359 } | |
| 1360 | |
| 1361 /* Final 2 rows together */ | |
| 1362 /* Pixel 0 */ | |
| 1363 v0 = *s0++; | |
| 1364 v1 = *s0++; | |
| 1365 r0 = v0 | ((v0 | v1)>>7); | |
| 1366 r1 = *s1++; | |
| 1367 r2 = *s2++; | |
| 1368 if ((r1>>6) & (r0 | r1 | r2)) | |
| 1369 s1[-1] |= 128; | |
| 1370 if ((r0>>6) & (r0 | r1)) | |
| 1371 s0[-2] |= 128; | |
| 1372 /* Middle pixels */ | |
| 1373 for (x = w-2; x > 0; x--) | |
| 1374 { | |
| 1375 v2 = *s0++; | |
| 1376 r0 = v1 | ((v0 | v1 | v2)>>7); | |
| 1377 r1 = *s1++; | |
| 1378 r2 = *s2++; | |
| 1379 if ((r1>>6) & (r0 | r1 | r2)) | |
| 1380 s1[-1] |= 128; | |
| 1381 if ((r0>>6) & (r0 | r1)) | |
| 1382 s0[-2] |= 128; | |
| 1383 v0 = v1; | |
| 1384 v1 = v2; | |
| 1385 } | |
| 1386 /* Pixel w-1 */ | |
| 1387 r0 = v1 | ((v0 | v1)>>7); | |
| 1388 r1 = *s1; | |
| 1389 r2 = *s2; | |
| 1390 if ((r1>>6) & (r0 | r1 | r2)) | |
| 1391 s1[0] |= 128; | |
| 1392 if ((r0>>6) & (r0 | r1)) | |
| 1393 s0[-1] |= 128; | |
| 1394 } | |
| 1395 | |
| 1396 #ifdef WARP_DEBUG | |
| 1397 /* A simple function to keep just bit 7 of the image. This 'cleans' the | |
| 1398 * pixmap so that a grey version can be saved out for visual checking. */ | |
| 1399 static void | |
| 1400 clean(fz_context *ctx, fz_pixmap *src) | |
| 1401 { | |
| 1402 int w = src->w; | |
| 1403 int h = src->h; | |
| 1404 unsigned char *s = src->samples; | |
| 1405 | |
| 1406 for (w = w*h; w > 0; w--) | |
| 1407 *s = *s & 128, s++; | |
| 1408 } | |
| 1409 #endif | |
| 1410 | |
| 1411 #define SINTABLE_SHIFT 14 | |
| 1412 static int16_t sintable[270]; | |
| 1413 #define costable (&sintable[90]) | |
| 1414 | |
| 1415 /* We have collected an array of edge data. | |
| 1416 * For each pixel, we know whether there is a 'strong' edge | |
| 1417 * there, and if so, in which of 4 directions it runs. | |
| 1418 * | |
| 1419 * We want to convert this into hough space. | |
| 1420 * | |
| 1421 * The literature describes points in Hough space as having the | |
| 1422 * form (r, theta). The value of any given point point in hough | |
| 1423 * space is the "strength" of the line described by: | |
| 1424 * x.cos(theta) + y.sin(theta) = r | |
| 1425 * | |
| 1426 * | \ | |
| 1427 * | /\ | |
| 1428 * | /\/\ | |
| 1429 * | r/ \ | |
| 1430 * | / \ | |
| 1431 * | / \ | |
| 1432 * |/theta \ | |
| 1433 * -+-------------- | |
| 1434 * | |
| 1435 * i.e. r is the shortest distance from the origin to the line, | |
| 1436 * and theta gives us the angle of that shortest line. | |
| 1437 * | |
| 1438 * But, we are using angles of theta from the vertical, so we need | |
| 1439 * a different formulation: | |
| 1440 * | |
| 1441 * | \ | |
| 1442 * | /\ | |
| 1443 * | /\/\ | |
| 1444 * | r/ \ t = theta | |
| 1445 * | / \ | |
| 1446 * |t/ \ | |
| 1447 * |/ \ | |
| 1448 * -+-------------- | |
| 1449 * | |
| 1450 * So we're using 90-theta. cos(90-theta) = sin(theta), | |
| 1451 * and sin(90-theta) = cos(theta). | |
| 1452 * | |
| 1453 * So: x.sin(theta) + y.cos(theta) = r (for theta measured | |
| 1454 * clockwise from the y axis). | |
| 1455 * | |
| 1456 * We've been collecting angles according to their position in one | |
| 1457 * of 4 octants: | |
| 1458 * | |
| 1459 * Ang 0 = close to a horizontal edge (-22.5 to 22.5 degrees) | |
| 1460 * Ang 1 = close to diagonal edge (top left to bottom right) (22.5 to 67.5 degrees) | |
| 1461 * Ang 2 = close to a vertical edge (67.5 to 112.5 degrees) | |
| 1462 * Ang 3 = close to diagonal edge (bottom left to top right) (112.5 to 157.5 degrees) | |
| 1463 * | |
| 1464 * The other 4 octants mirror onto these. | |
| 1465 * | |
| 1466 * So, for each point in our (x,y) pixmap we whether we have a strong | |
| 1467 * pixel or not. If we have such a pixel, then we know that an edge | |
| 1468 * passes through that point, and which of those 4 octants it is in. | |
| 1469 * | |
| 1470 * We therefore consider all the possible angles within that octant, | |
| 1471 * and add a 'vote' for each of those lines into our hough transformed | |
| 1472 * space. | |
| 1473 */ | |
| 1474 | |
| 1475 | |
| 1476 static void | |
| 1477 mark_hough(uint32_t *d, int x, int y, int maxlen, int reduce, int ang) | |
| 1478 { | |
| 1479 int theta; | |
| 1480 int stride = (maxlen*2)>>reduce; | |
| 1481 int minang, maxang; | |
| 1482 | |
| 1483 switch (ang) | |
| 1484 { | |
| 1485 /* The angles are really 22.5, 67.5 etc, but we are working in ints | |
| 1486 * and specifying maxang as the first one greater than the one we | |
| 1487 * want to mark. */ | |
| 1488 default: | |
| 1489 case 0: | |
| 1490 /* Vertical boundary. Lines through this boundary | |
| 1491 * go horizontally. So the perpendicular to them | |
| 1492 * is vertical. */ | |
| 1493 minang = 0; maxang = 23; | |
| 1494 break; | |
| 1495 case 1: | |
| 1496 /* NE boundary */ | |
| 1497 minang = 23; maxang = 68; | |
| 1498 break; | |
| 1499 case 2: | |
| 1500 /* Horizontal boundary. */ | |
| 1501 minang = 68; maxang = 113; | |
| 1502 break; | |
| 1503 case 3: | |
| 1504 /* SE boundary */ | |
| 1505 minang = 113; maxang = 158; | |
| 1506 break; | |
| 1507 case 4: | |
| 1508 /* For debugging: */ | |
| 1509 minang = 0; maxang = 180; | |
| 1510 break; | |
| 1511 } | |
| 1512 | |
| 1513 d += minang * stride; | |
| 1514 while (1) | |
| 1515 { | |
| 1516 for (theta = minang; theta < maxang; theta++) | |
| 1517 { | |
| 1518 int p = (x*sintable[theta] + y*costable[theta])>>SINTABLE_SHIFT; | |
| 1519 int v = (maxlen + p)>>reduce; | |
| 1520 | |
| 1521 d[v]++; | |
| 1522 d += stride; | |
| 1523 } | |
| 1524 if (ang != 0) | |
| 1525 break; | |
| 1526 ang = 4; | |
| 1527 minang = 158; | |
| 1528 d += (minang - maxang) * stride; | |
| 1529 maxang = 180; | |
| 1530 } | |
| 1531 } | |
| 1532 | |
| 1533 #ifdef WARP_DEBUG | |
| 1534 static void | |
| 1535 save_hough_debug(fz_context *ctx, uint32_t *hough, int stride) | |
| 1536 { | |
| 1537 uint32_t scale; | |
| 1538 uint32_t maxval; | |
| 1539 uint32_t *p; | |
| 1540 int y; | |
| 1541 fz_pixmap *dst = fz_new_pixmap(ctx, NULL, stride, 180, NULL, 0); | |
| 1542 unsigned char *d = dst->samples; | |
| 1543 /* Make the image of the hough space (for debugging) */ | |
| 1544 maxval = 1; /* Avoid possible division by zero */ | |
| 1545 p = hough; | |
| 1546 for (y = 180*stride; y > 0; y--) | |
| 1547 { | |
| 1548 uint32_t v = *p++; | |
| 1549 if (v > maxval) | |
| 1550 maxval = v; | |
| 1551 } | |
| 1552 | |
| 1553 scale = 0xFFFFFFFFU/maxval; | |
| 1554 p = hough; | |
| 1555 for (y = 180*stride; y > 0; y--) | |
| 1556 { | |
| 1557 *d++ = (scale * *p++)>>24; | |
| 1558 } | |
| 1559 fz_save_pixmap_as_png(ctx, dst, "hough.png"); | |
| 1560 fz_drop_pixmap(ctx, dst); | |
| 1561 } | |
| 1562 #endif | |
| 1563 | |
| 1564 static uint32_t *do_hough(fz_context *ctx, const fz_pixmap *src, int stride, int maxlen, int reduce) | |
| 1565 { | |
| 1566 int w = src->w; | |
| 1567 int h = src->h; | |
| 1568 int x, y; | |
| 1569 const unsigned char *s = src->samples; | |
| 1570 uint32_t *hough = fz_calloc(ctx, sizeof(uint32_t), 180*stride); | |
| 1571 | |
| 1572 START_TIME(); | |
| 1573 /* Construct the hough space representation. */ | |
| 1574 for (y = 0; y < h; y++) | |
| 1575 { | |
| 1576 for (x = 0; x < w; x++) | |
| 1577 { | |
| 1578 unsigned char v = *s++; | |
| 1579 if (v & 128) | |
| 1580 mark_hough(hough, x, y, maxlen, reduce, (v>>4)&3); | |
| 1581 } | |
| 1582 } | |
| 1583 END_TIME("Building hough"); | |
| 1584 | |
| 1585 #ifdef WARP_DEBUG | |
| 1586 save_hough_debug(ctx, hough, stride); | |
| 1587 #endif | |
| 1588 | |
| 1589 return hough; | |
| 1590 } | |
| 1591 | |
| 1592 typedef struct | |
| 1593 { | |
| 1594 int ang; | |
| 1595 int dis; | |
| 1596 int strength; | |
| 1597 int x0; | |
| 1598 int y0; | |
| 1599 int x1; | |
| 1600 int y1; | |
| 1601 } hough_edge_t; | |
| 1602 | |
| 1603 typedef struct | |
| 1604 { | |
| 1605 int e0; | |
| 1606 int e1; | |
| 1607 int score; | |
| 1608 float x; | |
| 1609 float y; | |
| 1610 } hough_point_t; | |
| 1611 | |
| 1612 static int | |
| 1613 intersect(hough_point_t *pt, hough_edge_t *edges, int e0, int e1) | |
| 1614 { | |
| 1615 int x1 = edges[e0].x0; | |
| 1616 int y1 = edges[e0].y0; | |
| 1617 int x2 = edges[e0].x1; | |
| 1618 int y2 = edges[e0].y1; | |
| 1619 int x3 = edges[e1].x0; | |
| 1620 int y3 = edges[e1].y0; | |
| 1621 int x4 = edges[e1].x1; | |
| 1622 int y4 = edges[e1].y1; | |
| 1623 int d = (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4); | |
| 1624 int t = (x1-x3)*(y3-y4)-(y1-y3)*(x3-x4); | |
| 1625 float ft; | |
| 1626 | |
| 1627 if (d < 0) | |
| 1628 { | |
| 1629 if (t < d || t > 0) | |
| 1630 return 0; | |
| 1631 } | |
| 1632 else | |
| 1633 { | |
| 1634 if (t < 0 || t > d) | |
| 1635 return 0; | |
| 1636 } | |
| 1637 | |
| 1638 pt->e0 = e0; | |
| 1639 pt->e1 = e1; | |
| 1640 pt->score = edges[e0].strength + edges[e1].strength; | |
| 1641 ft = t / (float)d; | |
| 1642 pt->x = x1 + ft*(x2-x1); | |
| 1643 pt->y = y1 + ft*(y2-y1); | |
| 1644 | |
| 1645 return 1; | |
| 1646 } | |
| 1647 | |
| 1648 typedef struct | |
| 1649 { | |
| 1650 int w; | |
| 1651 int h; | |
| 1652 int edge[4]; | |
| 1653 int point[4]; | |
| 1654 int best_score; | |
| 1655 int best_edge[4]; | |
| 1656 int best_point[4]; | |
| 1657 } hough_route_t; | |
| 1658 | |
| 1659 /* To test convexity, we use the dot product: | |
| 1660 * B | |
| 1661 * / \ | |
| 1662 * / C | |
| 1663 * A | |
| 1664 * | |
| 1665 * The dot product of vectors u and v is: | |
| 1666 * dot(u,v) = |u|.|v|.cos(theta) | |
| 1667 * Helpfully, cos(theta) > 0 for -90 < theta < 90. | |
| 1668 * So if we can form perp(AB) = the vector given by rotating | |
| 1669 * AB 90 degrees clockwise, we can then look at the sign of: | |
| 1670 * dot(perp(AB),BC) | |
| 1671 * If we do that for each corner of the polygon, and the signs | |
| 1672 * of the results for all of them are the same, we have convexity. | |
| 1673 * | |
| 1674 * If any of the dot products are zero, the edges are parallel | |
| 1675 * (i.e. the same edge). This should never happen. | |
| 1676 * | |
| 1677 * If AB = (x,y), then perp(AB) = (y,-x) | |
| 1678 */ | |
| 1679 static void | |
| 1680 score_corner(hough_point_t *points, hough_route_t *route, int p0, int *score, int *sign) | |
| 1681 { | |
| 1682 float x0, x1, x2, y0, y1, y2, ux, uy, vx, vy, dot; | |
| 1683 int s, len; | |
| 1684 float costheta; | |
| 1685 | |
| 1686 /* If we've already decided this is duff, then bale. */ | |
| 1687 if (*score < 0) | |
| 1688 return; | |
| 1689 | |
| 1690 x0 = points[route->point[p0]].x; | |
| 1691 y0 = points[route->point[p0]].y; | |
| 1692 x1 = points[route->point[(p0+1)&3]].x; | |
| 1693 y1 = points[route->point[(p0+1)&3]].y; | |
| 1694 x2 = points[route->point[(p0+2)&3]].x; | |
| 1695 y2 = points[route->point[(p0+2)&3]].y; | |
| 1696 | |
| 1697 ux = y1-y0; /* u = Perp(p0 to p1)*/ | |
| 1698 uy = x0-x1; | |
| 1699 vx = x2-x1; /* v = p1 to p2 */ | |
| 1700 vy = y2-y1; | |
| 1701 | |
| 1702 dot = ux*vx + uy*vy; | |
| 1703 if (dot == 0) | |
| 1704 { | |
| 1705 *score = -1; | |
| 1706 return; | |
| 1707 } | |
| 1708 | |
| 1709 s = (dot > 0) ? 1 : -1; | |
| 1710 if (*sign == 0) | |
| 1711 *sign = s; | |
| 1712 else if (*sign != s) | |
| 1713 { | |
| 1714 *score = -1; | |
| 1715 return; | |
| 1716 } | |
| 1717 | |
| 1718 len = sqrt(ux*ux + uy*uy) * sqrt(vx*vx + vy*vy); | |
| 1719 costheta = dot / (float)len; | |
| 1720 if (costheta < 0) | |
| 1721 costheta = -costheta; | |
| 1722 if (costheta < 0.7) | |
| 1723 { | |
| 1724 *score = -1; | |
| 1725 return; | |
| 1726 } | |
| 1727 costheta *= costheta; | |
| 1728 | |
| 1729 *score += points[route->point[(p0+1)%3]].score * costheta; | |
| 1730 } | |
| 1731 | |
| 1732 /* route points to 8 ints: | |
| 1733 * 2*i = edge number | |
| 1734 * 2*i+1 = point number | |
| 1735 */ | |
| 1736 static int | |
| 1737 score_route(hough_point_t *points, hough_route_t *route) | |
| 1738 { | |
| 1739 int score = 0; | |
| 1740 int sign = 0; | |
| 1741 | |
| 1742 score_corner(points, route, 0, &score, &sign); | |
| 1743 score_corner(points, route, 1, &score, &sign); | |
| 1744 score_corner(points, route, 2, &score, &sign); | |
| 1745 score_corner(points, route, 3, &score, &sign); | |
| 1746 | |
| 1747 return score; | |
| 1748 } | |
| 1749 | |
| 1750 static float | |
| 1751 score_by_area(const hough_point_t *points, const hough_route_t *route) | |
| 1752 { | |
| 1753 float double_area_of_quad = | |
| 1754 points[route->point[0]].x * points[route->point[1]].y + | |
| 1755 points[route->point[1]].x * points[route->point[2]].y + | |
| 1756 points[route->point[2]].x * points[route->point[3]].y + | |
| 1757 points[route->point[3]].x * points[route->point[0]].y - | |
| 1758 points[route->point[1]].x * points[route->point[0]].y - | |
| 1759 points[route->point[2]].x * points[route->point[1]].y - | |
| 1760 points[route->point[3]].x * points[route->point[2]].y - | |
| 1761 points[route->point[0]].x * points[route->point[3]].y; | |
| 1762 float double_area = route->w * (float)route->h * 2; | |
| 1763 if (double_area_of_quad < 0) | |
| 1764 double_area_of_quad = -double_area_of_quad; | |
| 1765 /* Anything larger than a quarter of the screen is acceptable. */ | |
| 1766 if (double_area_of_quad*4 > double_area) | |
| 1767 return 1; | |
| 1768 /* Anything smaller than a 16th of the screen is unacceptable in all circumstances. */ | |
| 1769 if (double_area_of_quad*16 < double_area) | |
| 1770 return 0; | |
| 1771 | |
| 1772 /* Otherwise, scale the score down by how much it's less than a quarter of the screen. */ | |
| 1773 return (double_area_of_quad*4)/double_area; | |
| 1774 } | |
| 1775 | |
| 1776 /* The first n+1 edges of the route are filled in, as are the first n | |
| 1777 * points. | |
| 1778 * 2*i = edge number | |
| 1779 * 2*i+1 = point number | |
| 1780 */ | |
| 1781 static void | |
| 1782 find_route(fz_context *ctx, hough_point_t *points, int num_points, hough_route_t *route, int n) | |
| 1783 { | |
| 1784 int i; | |
| 1785 | |
| 1786 for (i = 0; i < num_points; i++) | |
| 1787 { | |
| 1788 /* If this point continues our route (e0 == route->edge[n]) | |
| 1789 * then the next point in the route might be e1. */ | |
| 1790 int e0 = points[i].e0; | |
| 1791 int e1 = points[i].e1; | |
| 1792 if (e0 == route->edge[n]) | |
| 1793 { | |
| 1794 } | |
| 1795 else if (e1 == route->edge[n]) | |
| 1796 { | |
| 1797 int t = e0; e0 = e1; e1 = t; | |
| 1798 } | |
| 1799 else | |
| 1800 continue; /* Doesn't fit. Keep searching. */ | |
| 1801 | |
| 1802 /* If we've found 3 points already, then this is the | |
| 1803 * fourth. */ | |
| 1804 if (n == 3) | |
| 1805 { | |
| 1806 int score = 0; | |
| 1807 | |
| 1808 /* If we haven't looped back to our first edge, | |
| 1809 * no good. Keep searching. */ | |
| 1810 if (route->edge[0] != e1) | |
| 1811 continue; | |
| 1812 route->point[3] = i; | |
| 1813 | |
| 1814 score = score_route(points, route); | |
| 1815 | |
| 1816 if (score > 0) | |
| 1817 score *= score_by_area(points, route); | |
| 1818 | |
| 1819 #ifdef WARP_DEBUG | |
| 1820 debug_printf(ctx, "Found route: (point=%d %d %d %d) (edge %d %d %d %d) score=%d\n", | |
| 1821 route->point[0], route->point[1], route->point[2], route->point[3], | |
| 1822 route->edge[0], route->edge[1], route->edge[2], route->edge[3], | |
| 1823 score); | |
| 1824 #endif | |
| 1825 | |
| 1826 /* We want our route to be convex */ | |
| 1827 if (score < 0) | |
| 1828 continue; | |
| 1829 | |
| 1830 /* Score the route */ | |
| 1831 if (route->best_score < score) | |
| 1832 { | |
| 1833 route->best_score = score; | |
| 1834 memcpy(route->best_edge, route->edge, sizeof(*route->edge)*4); | |
| 1835 memcpy(route->best_point, route->point, sizeof(*route->point)*4); | |
| 1836 } | |
| 1837 } | |
| 1838 else | |
| 1839 { | |
| 1840 int j; | |
| 1841 for (j = 0; j < n && route->edge[j] != e1; j++); | |
| 1842 /* If we're about to loop back to any of the | |
| 1843 * previous edges, keep searching. */ | |
| 1844 if (j != n) | |
| 1845 continue; | |
| 1846 | |
| 1847 /* Possible. Extend the route, and recurse. */ | |
| 1848 route->point[n] = i; | |
| 1849 route->edge[n+1] = e1; | |
| 1850 find_route(ctx, points, num_points, route, n+1); | |
| 1851 } | |
| 1852 } | |
| 1853 } | |
| 1854 | |
| 1855 #define MAX_EDGES 32 | |
| 1856 #define BLOT_ANG 10 | |
| 1857 #define BLOT_DIS 10 | |
| 1858 static int | |
| 1859 make_hough(fz_context *ctx, const fz_pixmap *src, fz_quad *corners) | |
| 1860 { | |
| 1861 int w = src->w; | |
| 1862 int h = src->h; | |
| 1863 int maxlen = (int)(sqrtf(w*w + h*h) + 0.5); | |
| 1864 uint32_t *hough; | |
| 1865 int x, y; | |
| 1866 int reduce; | |
| 1867 int stride; | |
| 1868 hough_edge_t edge[MAX_EDGES]; | |
| 1869 hough_point_t points[MAX_EDGES * MAX_EDGES]; | |
| 1870 hough_route_t route; | |
| 1871 int num_edges, num_points; | |
| 1872 | |
| 1873 /* costable could (should) be statically inited. */ | |
| 1874 { | |
| 1875 int i; | |
| 1876 for (i = 0; i < 270; i++) | |
| 1877 { | |
| 1878 float theta = i*M_PI/180; | |
| 1879 sintable[i] = (int16_t)((1<<SINTABLE_SHIFT)*sinf(theta) + 0.5f); | |
| 1880 } | |
| 1881 } | |
| 1882 | |
| 1883 /* Figure out a suitable scale for the data. */ | |
| 1884 reduce = 0; | |
| 1885 while ((maxlen*2>>reduce) > 720 && reduce < 16) | |
| 1886 reduce++; | |
| 1887 | |
| 1888 stride = (maxlen*2)>>reduce; | |
| 1889 hough = do_hough(ctx, src, stride, maxlen, reduce); | |
| 1890 | |
| 1891 /* We want to find the top n edges that aren't too close to | |
| 1892 * one another. */ | |
| 1893 for (x = 0; x < MAX_EDGES; x++) | |
| 1894 { | |
| 1895 int ang, dis; | |
| 1896 int minang, maxang, mindis, maxdis; | |
| 1897 int where = 0; | |
| 1898 uint32_t *p = hough; | |
| 1899 uint32_t maxval = 0; | |
| 1900 for (y = 180*stride; y > 0; y--) | |
| 1901 { | |
| 1902 uint32_t v = *p++; | |
| 1903 if (v > maxval) | |
| 1904 maxval = v, where = y; | |
| 1905 } | |
| 1906 if (where == 0) | |
| 1907 break; | |
| 1908 where = 180*stride - where; | |
| 1909 ang = edge[x].ang = where/stride; | |
| 1910 dis = edge[x].dis = where - edge[x].ang*stride; | |
| 1911 edge[x].strength = hough[where]; | |
| 1912 /* We don't want to find any other maxima that are too | |
| 1913 * close to this one, so we 'blot out' stuff around this | |
| 1914 * maxima. */ | |
| 1915 #ifdef WARP_DEBUG | |
| 1916 debug_printf(ctx, "Maxima %d: dist=%d ang=%d strength=%d\n", | |
| 1917 x, (dis<<reduce)-maxlen, ang-90, edge[x].strength); | |
| 1918 #endif | |
| 1919 minang = ang - BLOT_ANG; | |
| 1920 if (minang < 0) | |
| 1921 minang = 0; | |
| 1922 maxang = ang + BLOT_ANG; | |
| 1923 if (maxang > 180) | |
| 1924 maxang = 180; | |
| 1925 mindis = dis - BLOT_DIS; | |
| 1926 if (mindis < 0) | |
| 1927 mindis = 0; | |
| 1928 maxdis = dis + BLOT_DIS; | |
| 1929 if (maxdis > stride) | |
| 1930 maxdis = stride; | |
| 1931 p = hough + minang*stride + mindis; | |
| 1932 maxdis = (maxdis-mindis)*sizeof(uint32_t); | |
| 1933 for (y = maxang-minang; y > 0; y--) | |
| 1934 { | |
| 1935 memset(p, 0, maxdis); | |
| 1936 p += stride; | |
| 1937 } | |
| 1938 #ifdef WARP_DEBUG | |
| 1939 //save_hough_debug(ctx, hough, stride); | |
| 1940 #endif | |
| 1941 } | |
| 1942 num_edges = x; | |
| 1943 if (num_edges == 0) | |
| 1944 return 0; | |
| 1945 | |
| 1946 /* Find edges in terms of lines. */ | |
| 1947 for (x = 0; x < num_edges; x++) | |
| 1948 { | |
| 1949 int ang = edge[x].ang; | |
| 1950 int dis = edge[x].dis; | |
| 1951 int p = (dis<<reduce) - maxlen; | |
| 1952 if (ang < 45 || ang > 135) | |
| 1953 { | |
| 1954 /* Mostly horizontal line */ | |
| 1955 edge[x].x0 = 0; | |
| 1956 edge[x].x1 = w; | |
| 1957 edge[x].y0 = ((p<<SINTABLE_SHIFT) - edge[x].x0*sintable[ang])/costable[ang]; | |
| 1958 edge[x].y1 = ((p<<SINTABLE_SHIFT) - edge[x].x1*sintable[ang])/costable[ang]; | |
| 1959 } | |
| 1960 else | |
| 1961 { | |
| 1962 /* Mostly vertical line */ | |
| 1963 edge[x].y0 = 0; | |
| 1964 edge[x].y1 = h; | |
| 1965 edge[x].x0 = ((p<<SINTABLE_SHIFT) - edge[x].y0*costable[ang])/sintable[ang]; | |
| 1966 edge[x].x1 = ((p<<SINTABLE_SHIFT) - edge[x].y1*costable[ang])/sintable[ang]; | |
| 1967 } | |
| 1968 } | |
| 1969 | |
| 1970 /* Find the points of intersection */ | |
| 1971 num_points = 0; | |
| 1972 for (x = 0; x < num_edges-1; x++) | |
| 1973 for (y = x+1; y < num_edges; y++) | |
| 1974 num_points += intersect(&points[num_points], | |
| 1975 edge, x, y); | |
| 1976 | |
| 1977 #ifdef WARP_DEBUG | |
| 1978 { | |
| 1979 debug_printf(ctx, "%d edges, %d points\n", num_edges, num_points); | |
| 1980 for (x = 0; x < num_points; x++) | |
| 1981 { | |
| 1982 debug_printf(ctx, "p%d: %d %d (score %d, %d+%d)\n", x, | |
| 1983 (int)points[x].x, (int)points[x].y, points[x].score, | |
| 1984 points[x].e0, points[x].e1); | |
| 1985 } | |
| 1986 } | |
| 1987 #endif | |
| 1988 | |
| 1989 /* Now, go looking for 'routes' A->B->C->D->A */ | |
| 1990 { | |
| 1991 int i; | |
| 1992 route.w = src->w; | |
| 1993 route.h = src->h; | |
| 1994 route.best_score = -1; | |
| 1995 for (i = 0; i < num_points; i++) | |
| 1996 { | |
| 1997 route.edge[0] = points[i].e0; | |
| 1998 route.point[0] = i; | |
| 1999 route.edge[1] = points[i].e1; | |
| 2000 find_route(ctx, points, num_points, &route, 1); | |
| 2001 } | |
| 2002 | |
| 2003 #ifdef WARP_DEBUG | |
| 2004 if (route.best_score >= 0) | |
| 2005 { | |
| 2006 debug_printf(ctx, "Score: %d, Edges=%d->%d->%d->%d, Points=%d->%d->%d->%d\n", | |
| 2007 route.best_score, | |
| 2008 route.best_edge[0], | |
| 2009 route.best_edge[1], | |
| 2010 route.best_edge[2], | |
| 2011 route.best_edge[3], | |
| 2012 route.best_point[0], | |
| 2013 route.best_point[1], | |
| 2014 route.best_point[2], | |
| 2015 route.best_point[3]); | |
| 2016 debug_printf(ctx, "(%d,%d)->(%d,%d)->(%d,%d)->(%d,%d)\n", | |
| 2017 (int)points[route.best_point[0]].x, | |
| 2018 (int)points[route.best_point[0]].y, | |
| 2019 (int)points[route.best_point[1]].x, | |
| 2020 (int)points[route.best_point[1]].y, | |
| 2021 (int)points[route.best_point[2]].x, | |
| 2022 (int)points[route.best_point[2]].y, | |
| 2023 (int)points[route.best_point[3]].x, | |
| 2024 (int)points[route.best_point[3]].y); | |
| 2025 } | |
| 2026 #endif | |
| 2027 } | |
| 2028 | |
| 2029 #ifdef WARP_DEBUG | |
| 2030 /* Mark up the src (again, for debugging) */ | |
| 2031 { | |
| 2032 fz_device *dev = fz_new_draw_device(ctx, fz_identity, src); | |
| 2033 fz_stroke_state *stroke = fz_new_stroke_state(ctx); | |
| 2034 float col = 1; | |
| 2035 fz_color_params params = { FZ_RI_PERCEPTUAL }; | |
| 2036 #ifdef WARP_SPEW_DEBUG | |
| 2037 for (x = 0; x < num_edges; x++) | |
| 2038 { | |
| 2039 char text[64]; | |
| 2040 fz_path *path = fz_new_path(ctx); | |
| 2041 fz_moveto(ctx, path, edge[x].x0, edge[x].y0); | |
| 2042 fz_lineto(ctx, path, edge[x].x1, edge[x].y1); | |
| 2043 fz_stroke_path(ctx, dev, path, stroke, fz_identity, fz_device_gray(ctx), &col, 1, params); | |
| 2044 fz_drop_path(ctx, path); | |
| 2045 debug_printf(ctx, "%d %d -> %d %d\n", edge[x].x0, edge[x].y0, edge[x].x1, edge[x].y1); | |
| 2046 sprintf(text, "line%d.png", x); | |
| 2047 fz_save_pixmap_as_png(ctx, src, text); | |
| 2048 } | |
| 2049 #endif | |
| 2050 | |
| 2051 stroke->linewidth *= 4; | |
| 2052 if (route.best_score >= 0) | |
| 2053 { | |
| 2054 fz_path *path = fz_new_path(ctx); | |
| 2055 fz_moveto(ctx, path, points[route.best_point[0]].x, points[route.best_point[0]].y); | |
| 2056 fz_lineto(ctx, path, points[route.best_point[1]].x, points[route.best_point[1]].y); | |
| 2057 fz_lineto(ctx, path, points[route.best_point[2]].x, points[route.best_point[2]].y); | |
| 2058 fz_lineto(ctx, path, points[route.best_point[3]].x, points[route.best_point[3]].y); | |
| 2059 fz_closepath(ctx, path); | |
| 2060 fz_stroke_path(ctx, dev, path, stroke, fz_identity, fz_device_gray(ctx), &col, 1, params); | |
| 2061 fz_drop_path(ctx, path); | |
| 2062 } | |
| 2063 | |
| 2064 fz_drop_stroke_state(ctx, stroke); | |
| 2065 fz_close_device(ctx, dev); | |
| 2066 fz_drop_device(ctx, dev); | |
| 2067 } | |
| 2068 #endif | |
| 2069 | |
| 2070 fz_free(ctx, hough); | |
| 2071 | |
| 2072 if (route.best_score == -1) | |
| 2073 return 0; | |
| 2074 | |
| 2075 if (corners) | |
| 2076 { | |
| 2077 corners->ul.x = points[route.best_point[0]].x; | |
| 2078 corners->ul.y = points[route.best_point[0]].y; | |
| 2079 corners->ur.x = points[route.best_point[1]].x; | |
| 2080 corners->ur.y = points[route.best_point[1]].y; | |
| 2081 corners->ll.x = points[route.best_point[2]].x; | |
| 2082 corners->ll.y = points[route.best_point[2]].y; | |
| 2083 corners->lr.x = points[route.best_point[3]].x; | |
| 2084 corners->lr.y = points[route.best_point[3]].y; | |
| 2085 } | |
| 2086 | |
| 2087 /* Discard any possible matches that aren't at least 1/8 of the pixmap. */ | |
| 2088 { | |
| 2089 fz_rect r = fz_empty_rect; | |
| 2090 if (corners) | |
| 2091 { | |
| 2092 r = fz_include_point_in_rect(r, corners->ul); | |
| 2093 r = fz_include_point_in_rect(r, corners->ur); | |
| 2094 r = fz_include_point_in_rect(r, corners->ll); | |
| 2095 r = fz_include_point_in_rect(r, corners->lr); | |
| 2096 } | |
| 2097 if ((r.x1 - r.x0) * (r.y1 - r.y0) * 8 < (src->w * src->h)) | |
| 2098 return 0; | |
| 2099 } | |
| 2100 | |
| 2101 return 1; | |
| 2102 } | |
| 2103 | |
| 2104 #define DOC_DETECT_MAXDIM 500 | |
| 2105 int | |
| 2106 fz_detect_document(fz_context *ctx, fz_quad *points, fz_pixmap *orig_src) | |
| 2107 { | |
| 2108 fz_color_params p = {FZ_RI_PERCEPTUAL }; | |
| 2109 fz_pixmap *grey = NULL; | |
| 2110 fz_pixmap *src = NULL; | |
| 2111 #ifdef DETECT_DOCUMENT_RGB | |
| 2112 fz_pixmap *r = NULL; | |
| 2113 fz_pixmap *g = NULL; | |
| 2114 fz_pixmap *b = NULL; | |
| 2115 #endif | |
| 2116 fz_pixmap *processed = NULL; | |
| 2117 int i; | |
| 2118 int found = 0; | |
| 2119 | |
| 2120 /* Gauss function has std deviation of ~sqr(2), so a variance of | |
| 2121 * 2. Apply it twice and we get twice that. So: | |
| 2122 * n stddev variance | |
| 2123 * 1 1.4 2 | |
| 2124 * 2 2 4 | |
| 2125 * 3 2.8 8 | |
| 2126 * 4 4 16 | |
| 2127 * 5 5.6 32 | |
| 2128 * 6 8 64 | |
| 2129 * 7 11.3 128 | |
| 2130 * 8 16 256 | |
| 2131 * 9 22.6 512 | |
| 2132 * 10 32 1024 | |
| 2133 * 11 45 2048 | |
| 2134 * | |
| 2135 * The stddev is also known as the "width" by some sources. | |
| 2136 * | |
| 2137 * Our testing indicates that a width of 30ish works well for a | |
| 2138 * image with it's major axis being ~3k pixels. So figure on a | |
| 2139 * width of about 1% of the major axis dimension. | |
| 2140 * | |
| 2141 * By subsampling the incoming image, we can reduce the work we | |
| 2142 * do in the gauss phase, as we get to use a smaller width. | |
| 2143 */ | |
| 2144 int n = 10; /* Based on DOC_DETECT_MAXDIM */ | |
| 2145 int l2factor = 0; | |
| 2146 | |
| 2147 fz_var(src); | |
| 2148 fz_var(grey); | |
| 2149 #ifdef DETECT_DOCUMENT_RGB | |
| 2150 fz_var(r); | |
| 2151 fz_var(g); | |
| 2152 fz_var(b); | |
| 2153 #endif | |
| 2154 fz_var(processed); | |
| 2155 | |
| 2156 fz_try(ctx) | |
| 2157 { | |
| 2158 START_TIME(); | |
| 2159 { | |
| 2160 int maxdim = orig_src->w > orig_src->h ? orig_src->w : orig_src->h; | |
| 2161 while (maxdim > DOC_DETECT_MAXDIM) | |
| 2162 maxdim >>= 1, l2factor++; | |
| 2163 START_TIME(); | |
| 2164 if (l2factor == 0) | |
| 2165 src = fz_keep_pixmap(ctx, orig_src); | |
| 2166 else | |
| 2167 { | |
| 2168 src = fz_clone_pixmap(ctx, orig_src); | |
| 2169 fz_subsample_pixmap(ctx, src, l2factor); | |
| 2170 } | |
| 2171 END_TIME("subsample"); | |
| 2172 } | |
| 2173 START_TIME(); | |
| 2174 if (src->n == 1 && src->alpha == 0) | |
| 2175 { | |
| 2176 if (src == orig_src) | |
| 2177 grey = fz_clone_pixmap(ctx, src); | |
| 2178 else | |
| 2179 grey = fz_keep_pixmap(ctx, src); | |
| 2180 } | |
| 2181 else | |
| 2182 { | |
| 2183 grey = fz_convert_pixmap(ctx, src, | |
| 2184 fz_default_gray(ctx, NULL), NULL, NULL, p, 0); | |
| 2185 } | |
| 2186 END_TIME("clone"); | |
| 2187 START_TIME(); | |
| 2188 for (i = 0; i < n; i++) | |
| 2189 gauss5x5(ctx, grey); | |
| 2190 END_TIME("gauss grey"); | |
| 2191 #ifdef WARP_DEBUG | |
| 2192 fz_save_pixmap_as_png(ctx, grey, "gauss.png"); | |
| 2193 #endif | |
| 2194 #ifdef DO_HISTEQ | |
| 2195 START_TIME(); | |
| 2196 histeq(grey); | |
| 2197 END_TIME("histeq grey"); | |
| 2198 #ifdef WARP_DEBUG | |
| 2199 fz_save_pixmap_as_png(ctx, grey, "hist.png"); | |
| 2200 #endif | |
| 2201 #endif | |
| 2202 START_TIME(); | |
| 2203 grad(ctx, grey, pregrad(ctx, grey)); | |
| 2204 END_TIME("grad grey"); | |
| 2205 #ifdef WARP_DEBUG | |
| 2206 fz_save_pixmap_as_png(ctx, grey, "grad.png"); | |
| 2207 #endif | |
| 2208 | |
| 2209 #ifdef DETECT_DOCUMENT_RGB | |
| 2210 if (src->n == 3 && src->alpha == 0) | |
| 2211 { | |
| 2212 START_TIME(); | |
| 2213 r = fz_new_pixmap(ctx, NULL, src->w, src->h, NULL, 0); | |
| 2214 g = fz_new_pixmap(ctx, NULL, src->w, src->h, NULL, 0); | |
| 2215 b = fz_new_pixmap(ctx, NULL, src->w, src->h, NULL, 0); | |
| 2216 gauss5x5_3(ctx, r, src, 0); | |
| 2217 for (i = 1; i < n; i++) | |
| 2218 gauss5x5(ctx, r); | |
| 2219 gauss5x5_3(ctx, g, src, 1); | |
| 2220 for (i = 1; i < n; i++) | |
| 2221 gauss5x5(ctx, g); | |
| 2222 gauss5x5_3(ctx, b, src, 2); | |
| 2223 for (i = 1; i < n; i++) | |
| 2224 gauss5x5(ctx, b); | |
| 2225 #ifdef DO_HISTEQ | |
| 2226 histeq(r); | |
| 2227 histeq(g); | |
| 2228 histeq(b); | |
| 2229 #endif | |
| 2230 grad(ctx, r); | |
| 2231 grad(ctx, g); | |
| 2232 grad(ctx, b); | |
| 2233 #ifdef WARP_DEBUG | |
| 2234 fz_save_pixmap_as_png(ctx, r, "r.png"); | |
| 2235 fz_save_pixmap_as_png(ctx, g, "g.png"); | |
| 2236 fz_save_pixmap_as_png(ctx, b, "b.png"); | |
| 2237 #endif | |
| 2238 combine_grad(grey, r, g, b); | |
| 2239 END_TIME("rgb"); | |
| 2240 fz_drop_pixmap(ctx, r); | |
| 2241 fz_drop_pixmap(ctx, g); | |
| 2242 fz_drop_pixmap(ctx, b); | |
| 2243 r = NULL; | |
| 2244 g = NULL; | |
| 2245 b = NULL; | |
| 2246 } | |
| 2247 #ifdef WARP_DEBUG | |
| 2248 fz_save_pixmap_as_png(ctx, grey, "combined.png"); | |
| 2249 #endif | |
| 2250 #endif | |
| 2251 processed = fz_new_pixmap(ctx, fz_device_gray(ctx), grey->w, grey->h, NULL, 0); | |
| 2252 | |
| 2253 /* Do multiple passes if required, dropping the thresholds for | |
| 2254 * strong/weak pixels each time until we find a suitable result. */ | |
| 2255 for (i = 0; i < 6; i++) | |
| 2256 { | |
| 2257 START_TIME(); | |
| 2258 nonmax(ctx, processed, grey, i); | |
| 2259 END_TIME("nonmax"); | |
| 2260 #ifdef WARP_DEBUG | |
| 2261 fz_save_pixmap_as_png(ctx, processed, "nonmax.png"); | |
| 2262 #endif | |
| 2263 START_TIME(); | |
| 2264 hysteresis(ctx, processed); | |
| 2265 END_TIME("hysteresis"); | |
| 2266 #ifdef WARP_DEBUG | |
| 2267 fz_save_pixmap_as_png(ctx, processed, "hysteresis.png"); | |
| 2268 #endif | |
| 2269 START_TIME(); | |
| 2270 found = make_hough(ctx, processed, points); | |
| 2271 END_TIME("total hough"); | |
| 2272 END_TIME("Total time"); | |
| 2273 DUMP_TIMES(); | |
| 2274 #ifdef WARP_DEBUG | |
| 2275 clean(ctx, processed); | |
| 2276 fz_save_pixmap_as_png(ctx, processed, "out.png"); | |
| 2277 #endif | |
| 2278 if (found) | |
| 2279 break; | |
| 2280 } | |
| 2281 } | |
| 2282 fz_always(ctx) | |
| 2283 { | |
| 2284 fz_drop_pixmap(ctx, src); | |
| 2285 #ifdef DETECT_DOCUMENT_RGB | |
| 2286 fz_drop_pixmap(ctx, r); | |
| 2287 fz_drop_pixmap(ctx, g); | |
| 2288 fz_drop_pixmap(ctx, b); | |
| 2289 #endif | |
| 2290 fz_drop_pixmap(ctx, grey); | |
| 2291 fz_drop_pixmap(ctx, processed); | |
| 2292 } | |
| 2293 fz_catch(ctx) | |
| 2294 { | |
| 2295 fz_rethrow(ctx); | |
| 2296 } | |
| 2297 | |
| 2298 if (found && points) | |
| 2299 { | |
| 2300 float f = (1<<l2factor); | |
| 2301 float r = f/2; | |
| 2302 points->ul.x = points->ul.x *f + r; | |
| 2303 points->ul.y = points->ul.y *f + r; | |
| 2304 points->ur.x = points->ur.x *f + r; | |
| 2305 points->ur.y = points->ur.y *f + r; | |
| 2306 points->ll.x = points->ll.x *f + r; | |
| 2307 points->ll.y = points->ll.y *f + r; | |
| 2308 points->lr.x = points->lr.x *f + r; | |
| 2309 points->lr.y = points->lr.y *f + r; | |
| 2310 } | |
| 2311 | |
| 2312 return found; | |
| 2313 } |
