/*====================================================================* - Copyright (C) 2001 Leptonica. All rights reserved. - This software is distributed in the hope that it will be - useful, but with NO WARRANTY OF ANY KIND. - No author or distributor accepts responsibility to anyone for the - consequences of using this software, or for whether it serves any - particular purpose or works at all, unless he or she says so in - writing. Everyone is granted permission to copy, modify and - redistribute this source code, for commercial or non-commercial - purposes, with the following restrictions: (1) the origin of this - source code must not be misrepresented; (2) modified versions must - be plainly marked as such; and (3) this notice may not be removed - or altered from any source or modified source distribution. *====================================================================*/ /* * warper.c * * High-level captcha interface * PIX *pixSimpleCaptcha() * * Random sinusoidal warping * PIX *pixRandomHarmonicWarp() * * Helper functions * static l_float64 *generateRandomNumberArray() * static l_int32 applyWarpTransform() * * Version using a LUT for sin * PIX *pixRandomHarmonicWarpLUT() * static l_int32 applyWarpTransformLUT() * static l_int32 makeSinLUT() * static l_float32 getSinFromLUT() */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include "allheaders.h" static l_float64 *generateRandomNumberArray(l_int32 size); static l_int32 applyWarpTransform(l_float32 xmag, l_float32 ymag, l_float32 xfreq, l_float32 yfreq, l_float64 *randa, l_int32 nx, l_int32 ny, l_int32 xp, l_int32 yp, l_float32 *px, l_float32 *py); #define USE_SIN_TABLE 0 /*----------------------------------------------------------------------* * High-level example captcha interface * *----------------------------------------------------------------------*/ /*! * pixSimpleCaptcha() * * Input: pixs (8 bpp; no colormap) * border (added white pixels on each side) * nterms (number of x and y harmonic terms) * seed (of random number generator) * color (for colorizing; in 0xrrggbb00 format; use 0 for black) * cmapflag (1 for colormap output; 0 for rgb) * Return: pixd (8 bpp cmap or 32 bpp rgb), or null on error * * Notes: * (1) This uses typical default values for generating captchas. * The magnitudes of the harmonic warp are typically to be * smaller when more terms are used, even though the phases * are random. See, for example, prog/warptest.c. */ PIX * pixSimpleCaptcha(PIX *pixs, l_int32 border, l_int32 nterms, l_uint32 seed, l_uint32 color, l_int32 cmapflag) { l_int32 k; l_float32 xmag[] = {7.0, 5.0, 4.0, 3.0}; l_float32 ymag[] = {10.0, 8.0, 6.0, 5.0}; l_float32 xfreq[] = {0.12, 0.10, 0.10, 0.11}; l_float32 yfreq[] = {0.15, 0.13, 0.13, 0.11}; PIX *pixg, *pixgb, *pixw, *pixd; PROCNAME("pixSimpleCaptcha"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (nterms < 1 || nterms > 4) return (PIX *)ERROR_PTR("nterms must be in {1,2,3,4}", procName, NULL); k = nterms - 1; pixg = pixConvertTo8(pixs, 0); pixgb = pixAddBorder(pixg, border, 255); pixw = pixRandomHarmonicWarp(pixgb, xmag[k], ymag[k], xfreq[k], yfreq[k], nterms, nterms, seed, 255); pixd = pixColorizeGray(pixw, color, cmapflag); pixDestroy(&pixg); pixDestroy(&pixgb); pixDestroy(&pixw); return pixd; } /*----------------------------------------------------------------------* * Random sinusoidal warping * *----------------------------------------------------------------------*/ /*! * pixRandomHarmonicWarp() * * Input: pixs (8 bpp; no colormap) * xmag, ymag (maximum magnitude of x and y distortion) * xfreq, yfreq (maximum magnitude of x and y frequency) * nx, ny (number of x and y harmonic terms) * seed (of random number generator) * grayval (color brought in from the outside; * 0 for black, 255 for white) * Return: pixd (8 bpp; no colormap), or null on error * * Notes: * (1) To generate the warped image p(x',y'), set up the transforms * that are in getWarpTransform(). For each (x',y') in the * dest, the warp function computes the originating location * (x, y) in the src. The differences (x - x') and (y - y') * are given as a sum of products of sinusoidal terms. Each * term is multiplied by a maximum amplitude (in pixels), and the * angle is determined by a frequency and phase, and depends * on the (x', y') value of the dest. Random numbers with * a variable input seed are used to allow the warping to be * unpredictable. A linear interpolation is used to find * the value for the source at (x, y); this value is written * into the dest. * (2) This can be used to generate 'captcha's, which are somewhat * randomly distorted images of text. A typical set of parameters * for a captcha are: * xmag = 4.0 ymag = 6.0 * xfreq = 0.10 yfreq = 0.13 * nx = 3 ny = 3 * Other examples can be found in prog/warptest.c. */ PIX * pixRandomHarmonicWarp(PIX *pixs, l_float32 xmag, l_float32 ymag, l_float32 xfreq, l_float32 yfreq, l_int32 nx, l_int32 ny, l_uint32 seed, l_int32 grayval) { l_int32 w, h, d, i, j, wpls, wpld, val; l_uint32 *datas, *datad, *lined; l_float32 x, y; l_float64 *randa; PIX *pixd; PROCNAME("pixRandomHarmonicWarp"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 8) return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); /* Compute filter output at each location. We iterate over * the destination pixels. For each dest pixel, use the * warp function to compute the four source pixels that * contribute, at the location (x, y). Each source pixel * is divided into 16 x 16 subpixels to get an approximate value. */ srand(seed); randa = generateRandomNumberArray(5 * (nx + ny)); pixd = pixCreateTemplate(pixs); datas = pixGetData(pixs); wpls = pixGetWpl(pixs); datad = pixGetData(pixd); wpld = pixGetWpl(pixd); for (i = 0; i < h; i++) { lined = datad + i * wpld; for (j = 0; j < w; j++) { applyWarpTransform(xmag, ymag, xfreq, yfreq, randa, nx, ny, j, i, &x, &y); linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); SET_DATA_BYTE(lined, j, val); } } FREE(randa); return pixd; } /*----------------------------------------------------------------------* * Static helper functions * *----------------------------------------------------------------------*/ static l_float64 * generateRandomNumberArray(l_int32 size) { l_int32 i; l_float64 *randa; PROCNAME("generateRandomNumberArray"); if ((randa = (l_float64 *)CALLOC(size, sizeof(l_float64))) == NULL) return (l_float64 *)ERROR_PTR("calloc fail for randa", procName, NULL); /* Return random values between 0.5 and 1.0 */ for (i = 0; i < size; i++) randa[i] = 0.5 * (1.0 + (l_float64)rand() / (l_float64)RAND_MAX); return randa; } /*! * applyWarpTransform() * * Notes: * (1) Uses the internal sin function. */ static l_int32 applyWarpTransform(l_float32 xmag, l_float32 ymag, l_float32 xfreq, l_float32 yfreq, l_float64 *randa, l_int32 nx, l_int32 ny, l_int32 xp, l_int32 yp, l_float32 *px, l_float32 *py) { l_int32 i; l_float64 twopi, x, y, anglex, angley; twopi = 6.283185; for (i = 0, x = xp; i < nx; i++) { anglex = xfreq * randa[3 * i + 1] * xp + twopi * randa[3 * i + 2]; angley = yfreq * randa[3 * i + 3] * yp + twopi * randa[3 * i + 4]; x += xmag * randa[3 * i] * sin(anglex) * sin(angley); } for (i = nx, y = yp; i < nx + ny; i++) { angley = yfreq * randa[3 * i + 1] * yp + twopi * randa[3 * i + 2]; anglex = xfreq * randa[3 * i + 3] * xp + twopi * randa[3 * i + 4]; y += ymag * randa[3 * i] * sin(angley) * sin(anglex); } *px = (l_float32)x; *py = (l_float32)y; return 0; } #if USE_SIN_TABLE /*----------------------------------------------------------------------* * Version using a LUT for sin * *----------------------------------------------------------------------*/ static l_int32 applyWarpTransformLUT(l_float32 xmag, l_float32 ymag, l_float32 xfreq, l_float32 yfreq, l_float64 *randa, l_int32 nx, l_int32 ny, l_int32 xp, l_int32 yp, l_float32 *lut, l_int32 npts, l_float32 *px, l_float32 *py); static l_int32 makeSinLUT(l_int32 npts, NUMA **pna); static l_float32 getSinFromLUT(l_float32 *tab, l_int32 npts, l_float32 radang); /*! * pixRandomHarmonicWarpLUT() * * Input: pixs (8 bpp; no colormap) * xmag, ymag (maximum magnitude of x and y distortion) * xfreq, yfreq (maximum magnitude of x and y frequency) * nx, ny (number of x and y harmonic terms) * seed (of random number generator) * grayval (color brought in from the outside; * 0 for black, 255 for white) * Return: pixd (8 bpp; no colormap), or null on error * * Notes: * (1) See notes and inline comments in pixRandomHarmonicWarp(). * This version uses a LUT for the sin function. It is not * appreciably faster than using the built-in sin function, * and is here for comparison only. */ PIX * pixRandomHarmonicWarpLUT(PIX *pixs, l_float32 xmag, l_float32 ymag, l_float32 xfreq, l_float32 yfreq, l_int32 nx, l_int32 ny, l_uint32 seed, l_int32 grayval) { l_int32 w, h, d, i, j, wpls, wpld, val, npts; l_uint32 *datas, *datad, *lined; l_float32 x, y; l_float32 *lut; l_float64 *randa; NUMA *na; PIX *pixd; PROCNAME("pixRandomHarmonicWarp"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 8) return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL); /* Compute filter output at each location. We iterate over * the destination pixels. For each dest pixel, use the * warp function to compute the four source pixels that * contribute, at the location (x, y). Each source pixel * is divided into 16 x 16 subpixels to get an approximate value. */ srand(seed); randa = generateRandomNumberArray(5 * (nx + ny)); pixd = pixCreateTemplate(pixs); datas = pixGetData(pixs); wpls = pixGetWpl(pixs); datad = pixGetData(pixd); wpld = pixGetWpl(pixd); npts = 100; makeSinLUT(npts, &na); lut = numaGetFArray(na, L_NOCOPY); for (i = 0; i < h; i++) { lined = datad + i * wpld; for (j = 0; j < w; j++) { applyWarpTransformLUT(xmag, ymag, xfreq, yfreq, randa, nx, ny, j, i, lut, npts, &x, &y); linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); SET_DATA_BYTE(lined, j, val); } } numaDestroy(&na); FREE(randa); return pixd; } /*! * applyWarpTransformLUT() * * Notes: * (1) Uses an LUT for computing sin(theta). There is little speed * advantage to using the LUT. */ static l_int32 applyWarpTransformLUT(l_float32 xmag, l_float32 ymag, l_float32 xfreq, l_float32 yfreq, l_float64 *randa, l_int32 nx, l_int32 ny, l_int32 xp, l_int32 yp, l_float32 *lut, l_int32 npts, l_float32 *px, l_float32 *py) { l_int32 i; l_float64 twopi, x, y, anglex, angley, sanglex, sangley; twopi = 6.283185; for (i = 0, x = xp; i < nx; i++) { anglex = xfreq * randa[3 * i + 1] * xp + twopi * randa[3 * i + 2]; angley = yfreq * randa[3 * i + 3] * yp + twopi * randa[3 * i + 4]; sanglex = getSinFromLUT(lut, npts, anglex); sangley = getSinFromLUT(lut, npts, angley); x += xmag * randa[3 * i] * sanglex * sangley; } for (i = nx, y = yp; i < nx + ny; i++) { angley = yfreq * randa[3 * i + 1] * yp + twopi * randa[3 * i + 2]; anglex = xfreq * randa[3 * i + 3] * xp + twopi * randa[3 * i + 4]; sanglex = getSinFromLUT(lut, npts, anglex); sangley = getSinFromLUT(lut, npts, angley); y += ymag * randa[3 * i] * sangley * sanglex; } *px = (l_float32)x; *py = (l_float32)y; return 0; } static l_int32 makeSinLUT(l_int32 npts, NUMA **pna) { l_int32 i, n; l_float32 delx, fval; NUMA *na; PROCNAME("makeSinLUT"); if (!pna) return ERROR_INT("&na not defined", procName, 1); *pna = NULL; if (npts < 2) return ERROR_INT("npts < 2", procName, 1); n = 2 * npts + 1; na = numaCreate(n); *pna = na; delx = 3.14159265 / (l_float32)npts; numaSetXParameters(na, 0.0, delx); for (i = 0; i < n / 2; i++) numaAddNumber(na, (l_float32)sin((l_float64)i * delx)); for (i = 0; i < n / 2; i++) { numaGetFValue(na, i, &fval); numaAddNumber(na, -fval); } numaAddNumber(na, 0); return 0; } static l_float32 getSinFromLUT(l_float32 *tab, l_int32 npts, l_float32 radang) { l_int32 index; l_float32 twopi, invtwopi, findex, diff; /* Restrict radang to [0, 2pi] */ twopi = 6.283185; invtwopi = 0.1591549; if (radang < 0.0) radang += twopi * (1.0 - (l_int32)(-radang * invtwopi)); else if (radang > 0.0) radang -= twopi * (l_int32)(radang * invtwopi); /* Interpolate */ findex = (2.0 * (l_float32)npts) * (radang * invtwopi); index = (l_int32)findex; if (index == 2 * npts) return tab[index]; diff = findex - index; return (1.0 - diff) * tab[index] + diff * tab[index + 1]; } #endif /* USE_SIN_TABLE */