mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 14:16:10 -06:00
453 lines
16 KiB
C
453 lines
16 KiB
C
/*====================================================================*
|
|
- 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 */
|
|
|
|
|