2686 lines
60 KiB
C
2686 lines
60 KiB
C
// Code for Sole shape match function
|
|
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include "common.h"
|
|
#include "cheby.h"
|
|
#include "cowmath.h"
|
|
#include "math16.h"
|
|
#include "volcanop.h"
|
|
#include "runNet.h"
|
|
#include "nnet.h"
|
|
|
|
GLYPH * GlyphFromStrokes(UINT cStrokes, STROKE *pStrokes);
|
|
RECT GetGuideDrawnBox(HWXGUIDE *guide, int iBox);
|
|
|
|
#define ABS(x) (((x) > 0) ? (x) : -(x))
|
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
|
#define SIGN(x) ((x) > 0 ? 1 : ((x) < 0 ? -1 : 0))
|
|
#define PEN_UP_VALUE LSHFT(-1)
|
|
#define PEN_DOWN_VALUE LSHFT(1)
|
|
|
|
#define XCHB 10
|
|
#define YCHB 10
|
|
#define ZCHB 10
|
|
|
|
#define MAXTMP 3
|
|
|
|
// assumption: GRIDSIZE <= 256 //This is beacuse the pointers to the mapped values are defined as Byte pointers
|
|
#define GRIDSIZE 32
|
|
|
|
typedef struct
|
|
{
|
|
int *xy; //Stores the sampled XY points
|
|
int *z; //Stores the z points--values are either PEN_UP or PEN_DOWN
|
|
int cPoint;//Stores the number of points that are there
|
|
int cPointMax; //Stores the max number of points that can be allocated
|
|
int iStepSize; //Stores the length of the step size--at present this is taken to be 1.5 % of the guide size
|
|
int iStepSizeSqr; //Stroes the square of the step size
|
|
} POINTINFO;
|
|
//This macro is used for seeing if two points are neighbours to one another--ie the distance between them <=1
|
|
#define Neighbor(a, b) ((a)-(b) < 2 && (b)-(a) < 2)
|
|
|
|
static int
|
|
solve2(int m[][(IMAXCHB+1)/2], int c[], int n)
|
|
{
|
|
int i, j, k, t, tmp;
|
|
|
|
for (i=0; i<n; ++i)
|
|
{
|
|
t = m[i][i];
|
|
|
|
// punt:
|
|
if (t == 0)
|
|
{
|
|
memset(c, 0, n*sizeof(*c));
|
|
return 0;
|
|
}
|
|
|
|
for (j=0; j<n; ++j)
|
|
m[i][j] = Div16(m[i][j], t);
|
|
c[i] = Div16(c[i], t);
|
|
|
|
for (k=i+1; k<n; ++k)
|
|
{
|
|
t = m[k][i];
|
|
|
|
for (j=0; j<n; ++j)
|
|
{
|
|
Mul16(t, m[i][j], tmp)
|
|
m[k][j] -= tmp;
|
|
}
|
|
Mul16(t, c[i], tmp)
|
|
c[k] -= tmp;
|
|
}
|
|
}
|
|
|
|
for (i=(n-1); i>=0; --i)
|
|
{
|
|
for (k=i-1; k>=0; --k)
|
|
{
|
|
t = m[k][i];
|
|
|
|
for (j=0; j<n; ++j)
|
|
{
|
|
Mul16(t, m[i][j], tmp)
|
|
m[k][j] -= tmp;
|
|
}
|
|
Mul16(t, c[i], tmp)
|
|
c[k] -= tmp;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
solve(int m[IMAXCHB][IMAXCHB], int *c, int n)
|
|
{
|
|
int i, j, i2, j2;
|
|
int mEven[((IMAXCHB+1))/2][((IMAXCHB+1)/2)];
|
|
int mOdd[(IMAXCHB/2)][((IMAXCHB+1)/2)]; // # of cols is bigger than needed so that solve2() works
|
|
int cEven[((IMAXCHB+1)/2)];
|
|
int cOdd[(IMAXCHB/2)];
|
|
|
|
for (i = 0, i2 = 0; i2 < n; ++i, i2 += 2)
|
|
{
|
|
for (j = 0, j2 = 0; j2 < n; ++j, j2 += 2)
|
|
{
|
|
mEven[i][j] = m[i2][j2];
|
|
}
|
|
cEven[i] = c[i2];
|
|
}
|
|
for (i = 0, i2 = 1; i2 < n; ++i, i2 += 2)
|
|
{
|
|
for (j = 0, j2 = 1; j2 < n; ++j, j2 += 2)
|
|
{
|
|
mOdd[i][j] = m[i2][j2];
|
|
}
|
|
cOdd[i] = c[i2];
|
|
}
|
|
if (!solve2(mEven, cEven, (n+1)/2)) return 0;
|
|
if (!solve2(mOdd, cOdd, n/2)) return 0;
|
|
|
|
for (i = 0, i2 = 0; i2 < n; ++i, i2 += 2)
|
|
{
|
|
c[i2] = cEven[i];
|
|
}
|
|
for (i = 0, i2 = 1; i2 < n; ++i, i2 += 2)
|
|
{
|
|
c[i2] = cOdd[i];
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Assumptions:
|
|
// c has size atleast cfeats
|
|
// cfeats is at most IMAXCHB
|
|
// c is uninitialized
|
|
int LSCheby(int* y, int n, int *c, int cfeats)
|
|
{
|
|
int i, j, t, x, dx, n2, nMin;
|
|
int meanGuess, tmp;
|
|
int T[IMAXCHB], m[IMAXCHB][IMAXCHB];
|
|
|
|
if (cfeats > IMAXCHB || cfeats <= 0)
|
|
return 0;
|
|
|
|
memset(c, 0, cfeats*sizeof(*c));
|
|
|
|
n2 = n+n;
|
|
nMin = cfeats + 2;
|
|
|
|
|
|
if (n < nMin && n > 4)
|
|
{
|
|
cfeats = n - 2;
|
|
nMin = cfeats + 2;
|
|
}
|
|
|
|
if (n < nMin) // approximate the stroke by a straight line
|
|
{
|
|
*c++ = (y[0] + y[n2-2]) >> 1;
|
|
*c = (y[n2-2] - y[0]) >> 1;
|
|
return 2;
|
|
}
|
|
|
|
memset(T, 0, sizeof(T));
|
|
memset(m, 0, sizeof(m));
|
|
|
|
meanGuess = y[0];
|
|
|
|
x = LSHFT(-1);
|
|
dx = LSHFT(2)/(n-1);
|
|
|
|
for (t = 0; t < n2; t += 2)
|
|
{
|
|
T[0] = LSHFT(1);
|
|
T[1] = x;
|
|
for (i = 2; i < cfeats; ++i)
|
|
{
|
|
Mul16(x, T[i-1], tmp)
|
|
T[i] = (tmp<<1) - T[i-2];
|
|
}
|
|
|
|
for (i = 0; i < cfeats; ++i)
|
|
{
|
|
for (j = 0; j < cfeats; ++j)
|
|
{
|
|
Mul16(T[i], T[j], tmp)
|
|
m[i][j] += tmp;
|
|
}
|
|
|
|
Mul16(T[i], y[t] - meanGuess, tmp)
|
|
c[i] += tmp;
|
|
//c[i] += T[i]*(y[t] - meanGuess);
|
|
}
|
|
|
|
x += dx;
|
|
}
|
|
|
|
if (!solve(m, c, cfeats))
|
|
return 0;
|
|
|
|
c[0] += meanGuess;
|
|
|
|
return cfeats;
|
|
}
|
|
|
|
|
|
|
|
int ISqrt(int x)
|
|
{
|
|
int n, lastN;
|
|
|
|
if (x <= 0)
|
|
return 0;
|
|
if (x==1)
|
|
return 1;
|
|
|
|
n = x >> 1;
|
|
do
|
|
{
|
|
lastN = n;
|
|
n = (n + x/n) >> 1;
|
|
}
|
|
while (n < lastN);
|
|
|
|
return n;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* YDeviation
|
|
*
|
|
* Function to compute average deviation of the y values in a sequence of
|
|
* strokes (frames).
|
|
* This is not exactly standard deviation. But it is a lot cheaper and
|
|
* close enough. (See analysis and comments in Numerical Recipes in C).
|
|
*
|
|
* History:
|
|
* 02-Sep-1999 -by- Angshuman Guha aguha
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
int YDeviation(GLYPH *pGlyph)
|
|
{
|
|
int count, ymin, sum, ymean;
|
|
GLYPH *glyph;
|
|
FRAME *frame;
|
|
|
|
if (pGlyph && pGlyph->frame && RgrawxyFRAME(pGlyph->frame))
|
|
{
|
|
ymin = RgrawxyFRAME(pGlyph->frame)->y;
|
|
count = 0;
|
|
sum = 0;
|
|
}
|
|
else
|
|
return 1;
|
|
|
|
// find min and mean in one scan
|
|
for (glyph=pGlyph; glyph; glyph=glyph->next)
|
|
{
|
|
XY *xy;
|
|
int cxy;
|
|
|
|
frame = glyph->frame;
|
|
xy = RgrawxyFRAME(frame);
|
|
cxy = CrawxyFRAME(frame);
|
|
|
|
for (; cxy; xy++, cxy--)
|
|
{
|
|
int y;
|
|
|
|
y = xy->y;
|
|
if (y < ymin)
|
|
{
|
|
sum += count*(ymin - y);
|
|
ymin = y;
|
|
}
|
|
sum += y - ymin;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
|
|
ASSERT(count > 0);
|
|
ymean = sum/count + ymin;
|
|
|
|
// find average deviation
|
|
sum = 0;
|
|
for (glyph=pGlyph; glyph; glyph=glyph->next)
|
|
{
|
|
XY *xy;
|
|
int cxy;
|
|
|
|
frame = glyph->frame;
|
|
xy = RgrawxyFRAME(frame);
|
|
cxy = CrawxyFRAME(frame);
|
|
|
|
for (; cxy; xy++, cxy--)
|
|
{
|
|
int diff;
|
|
|
|
diff = xy->y - ymean;
|
|
if (diff < 0)
|
|
sum -= diff;
|
|
else
|
|
sum += diff;
|
|
}
|
|
}
|
|
|
|
sum = sum/count;
|
|
if (sum < 1)
|
|
sum = 1;
|
|
return sum;
|
|
}
|
|
/******************************Private*Routine******************************\
|
|
* AddPoint
|
|
*
|
|
* Given a new point and a sequence of points so far, zero or more points
|
|
* are added at the end of the sequence. The points are effectively resampled
|
|
* at a pre-computed interval (a distance of pPointinfo->iStepSize between
|
|
* successive points). This function also effectively does a linear interpolation
|
|
* of a pen-upstroke between the last point of a pen-down stroke and the first
|
|
* point of the next pen-down stroke.
|
|
*
|
|
* History:
|
|
* 26-Sep-1997 -by- Angshuman Guha aguha
|
|
|
|
|
|
Explanation of parameters
|
|
pPointInfo--The pointer to the Point Info structure--The points are added to the xy array of this structure
|
|
x--The x coordinate of the point to be added
|
|
y--The y coordinate of the point to be added
|
|
bFirstPointOfStroke--If true indicates that this is the first point of the stroke
|
|
Else it it not the first point
|
|
* Wrote this comment.Addtional comments added by Mango
|
|
\**************************************************************************/
|
|
BOOL AddPoint(POINTINFO *pPointinfo, int x, int y, int bFirstPointOfStroke)
|
|
{
|
|
int dx, dy, dist2, dist;
|
|
int bChangeLastPoint, x0, y0, zval;
|
|
int *pTemp;
|
|
|
|
if (!pPointinfo->cPoint)
|
|
{
|
|
pPointinfo->xy[0] = x;
|
|
pPointinfo->xy[1] = y;
|
|
pPointinfo->z[0] = PEN_DOWN_VALUE;
|
|
pPointinfo->cPoint = 1;
|
|
return TRUE;
|
|
}
|
|
|
|
bChangeLastPoint = 0;
|
|
x0 = pPointinfo->xy[2*pPointinfo->cPoint-2];
|
|
y0 = pPointinfo->xy[2*pPointinfo->cPoint-1];
|
|
zval = bFirstPointOfStroke ? PEN_UP_VALUE : PEN_DOWN_VALUE;
|
|
for (;;)
|
|
{
|
|
dx = x - x0;
|
|
dy = y - y0;
|
|
dist2 = dx*dx + dy*dy;
|
|
if (dist2 < pPointinfo->iStepSizeSqr)
|
|
break;
|
|
|
|
// add a point at given step size
|
|
dist = ISqrt(dist2);
|
|
x0 += pPointinfo->iStepSize*dx/dist;
|
|
y0 += pPointinfo->iStepSize*dy/dist;
|
|
// a minimum iStepSize of 2 and the fact that ((float)dx/dist)^2 + ((float)dy/dist)^2 = 1 guarantees that
|
|
// the previous two assignments change atleast one of x0 and y0 i.e. its not an infinite loop
|
|
if (pPointinfo->cPoint == pPointinfo->cPointMax)
|
|
{
|
|
// need more space
|
|
// hopefully we don't come here too often
|
|
pPointinfo->cPointMax *= 2;
|
|
pTemp = (int *) ExternRealloc(pPointinfo->xy, 2*pPointinfo->cPointMax*sizeof(int));
|
|
if (!pTemp)
|
|
{
|
|
return FALSE;
|
|
}
|
|
pPointinfo->xy = pTemp;
|
|
pTemp = (int *) ExternRealloc(pPointinfo->z, 2*pPointinfo->cPointMax*sizeof(int));
|
|
if (!pTemp)
|
|
{
|
|
return FALSE;
|
|
}
|
|
pPointinfo->z = pTemp;
|
|
}
|
|
pPointinfo->xy[2*pPointinfo->cPoint] = x0;
|
|
pPointinfo->xy[2*pPointinfo->cPoint+1] = y0;
|
|
pPointinfo->z[2*pPointinfo->cPoint] = zval;
|
|
pPointinfo->cPoint++;
|
|
bChangeLastPoint = bFirstPointOfStroke;
|
|
}
|
|
|
|
// if we have interpolated from the last point of a stroke to the first point of another
|
|
if (bChangeLastPoint)
|
|
{
|
|
ASSERT(pPointinfo->z[2*pPointinfo->cPoint - 2] == PEN_UP_VALUE);
|
|
pPointinfo->z[2*pPointinfo->cPoint - 2] = PEN_DOWN_VALUE;
|
|
}
|
|
// this last "if" could be changed to a test on bFirstPointOfStroke and the assert can be removed
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************Private*Routine******************************\
|
|
* AddGuideFeatures
|
|
*
|
|
* Given a piece of ink in a box, compute five features related to the size
|
|
* and position of ink in the box.
|
|
* The five features are:-
|
|
* //First feature--Top of the ink relative to the guide box height
|
|
* //Second feature--Width of the ink relative to the guide box width
|
|
* //Third feature--Bottom of the ink relative to the guide box height
|
|
* //Fourth feature--The width of the ink relative to the sum of its width and height
|
|
* //Fifth feature--the iYMean value relative to the guide box height
|
|
*
|
|
* History:
|
|
* 26-Sep-1997 -by- Angshuman Guha aguha
|
|
* Wrote this comment.
|
|
\**************************************************************************/
|
|
int AddGuideFeatures(RECT *pGuide, RECT *pRect, int iYMean, unsigned short *rgFeat)
|
|
{
|
|
// get normalized ink size/position (box is 1000x1000 with top-left at 0,0)
|
|
DRECTS drcs;
|
|
RECT inkRect = *pRect;
|
|
int x;
|
|
|
|
//The x coordinate of the top left corner of the current box(note--you are adding the cxBase Value)
|
|
drcs.x = pGuide->left;
|
|
|
|
//The y coordinate of the top left corner of the current box
|
|
drcs.y = pGuide->top;
|
|
|
|
//This gives us the width of the current guide box
|
|
drcs.w = pGuide->right - pGuide->left;
|
|
|
|
//This gives us the height of the current guide box
|
|
drcs.h = pGuide->bottom - pGuide->top;
|
|
|
|
// Translate, convert to delta form
|
|
//Stores the relative position w.rt. the top left of the guide box
|
|
inkRect.left -= drcs.x;
|
|
inkRect.top -= drcs.y;
|
|
//Stores the width of the ink
|
|
inkRect.right -= (drcs.x + inkRect.left);
|
|
//Stores the height of the ink
|
|
inkRect.bottom -= (drcs.y + inkRect.top);
|
|
//Converts the yMean wrt a form relative to the top of the guide box
|
|
iYMean -= drcs.y;
|
|
|
|
// Scale. We do isotropic scaling and center the shorter dimension.
|
|
//Y Mean as a fraction of the guide box size
|
|
iYMean = ((1000 * iYMean) / drcs.h);
|
|
//Sees where the top of the ink is relative to the guide box height
|
|
drcs.y = ((1000 * inkRect.top) / drcs.h);
|
|
//The width of the ink relative to the guide box width
|
|
drcs.w = ((1000 * inkRect.right) / drcs.h);
|
|
//The height of the ink relative to the guide box height
|
|
drcs.h = ((1000 * inkRect.bottom) / drcs.h);
|
|
|
|
//Why would any of these conditions happen
|
|
if (drcs.y < 0)
|
|
drcs.y = 0;
|
|
else if (drcs.y > 1000)
|
|
drcs.y = 1000;
|
|
if (drcs.w < 0)
|
|
drcs.w = 0;
|
|
else if (drcs.w > 1000)
|
|
drcs.w = 1000;
|
|
if (drcs.h < 0)
|
|
drcs.h = 0;
|
|
else if (drcs.h > 1000)
|
|
drcs.h = 1000;
|
|
|
|
// 4 guide features
|
|
|
|
//First feature--Top of the ink relative to the guide box height
|
|
x = drcs.y;
|
|
x = LSHFT(x)/1000;
|
|
if (x >= 0x10000)
|
|
x = 0xFFFF;
|
|
*rgFeat++ = (unsigned short)x;
|
|
|
|
//Second feature--Width of the ink relative to the guide box width
|
|
x = drcs.w;
|
|
x = LSHFT(x)/1000;
|
|
if (x >= 0x10000)
|
|
x = 0xFFFF;
|
|
*rgFeat++ = (unsigned short)x;
|
|
|
|
//Third feature--Bottom of the ink relative to the guide box height
|
|
x = drcs.h;
|
|
x = LSHFT(x)/1000;
|
|
if (x >= 0x10000)
|
|
x = 0xFFFF;
|
|
*rgFeat++ = (unsigned short)x;
|
|
|
|
//Fourth feature--The width of the ink relative to the sum of its width and height
|
|
if (drcs.w <= 0)
|
|
x = 0;
|
|
else
|
|
{
|
|
x = drcs.w;
|
|
x = LSHFT(x)/(drcs.w+drcs.h);
|
|
if (x >= 0x10000)
|
|
x = 0xFFFF;
|
|
}
|
|
*rgFeat++ = (unsigned short)x;
|
|
|
|
//Fifth feature--the iYMean value relative to the guide box height
|
|
|
|
// one more guide feature: y-CG
|
|
if (iYMean < 0)
|
|
iYMean = 0;
|
|
else if (iYMean > 1000)
|
|
iYMean = 1000;
|
|
x = iYMean;
|
|
x = LSHFT(x)/1000;
|
|
if (x >= 0x10000)
|
|
x = 0xFFFF;
|
|
*rgFeat = (unsigned short)x;
|
|
|
|
return 5;
|
|
}
|
|
|
|
/******************************Private*Routine******************************\
|
|
* SmoothPoints
|
|
*
|
|
* Given an array of points and a destination array, this function fills the
|
|
* destination array a smoothed version of the raw points. Smoothing is
|
|
* done by local averaging ona window of 5 points with weights 1/8 1/4 1/4 1/4 1/8.
|
|
*
|
|
* History:
|
|
* 26-Sep-1997 -by- Angshuman Guha aguha
|
|
* Wrote this comment.
|
|
\**************************************************************************/
|
|
void SmoothPoints(XY *rgSrc, XY *rgDst, int cXY)
|
|
{
|
|
int i,j;
|
|
|
|
for (i=0; i<cXY; i++)
|
|
{
|
|
j = cXY - i - 1;
|
|
if (i < j)
|
|
j = i;
|
|
|
|
switch (j)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
*rgDst = *rgSrc;
|
|
break;
|
|
//+4 is added here so that rounding off takes place rather than truncation
|
|
default:
|
|
rgDst->x = (int)((
|
|
(rgSrc-2)->x +
|
|
((rgSrc-1)->x <<1) +
|
|
(rgSrc->x <<1) +
|
|
((rgSrc+1)->x <<1) +
|
|
(rgSrc+2)->x +
|
|
4
|
|
) >> 3);
|
|
rgDst->y = (int)((
|
|
(rgSrc-2)->y +
|
|
((rgSrc-1)->y <<1) +
|
|
(rgSrc->y <<1) +
|
|
((rgSrc+1)->y <<1) +
|
|
(rgSrc+2)->y +
|
|
4
|
|
) >> 3);
|
|
break;
|
|
}
|
|
rgSrc++;
|
|
rgDst++;
|
|
}
|
|
|
|
}
|
|
|
|
/******************************Private*Routine******************************\
|
|
* ComputeCurvedness
|
|
*
|
|
* Given a stroke, computes three curvature features--namely
|
|
*--The sum of the modular change in angle with respect to + and - for the angles
|
|
*--The total curviness of the stoke--just directly measure the change in angles.
|
|
*--The maximum change in angle that occurs in that stroke in one sampling distance
|
|
*
|
|
* History:
|
|
* 26-Sep-1997 -by- Angshuman Guha aguha
|
|
* Wrote this comment.
|
|
\**************************************************************************/
|
|
void ComputeCurvedness(XY *rgXY, int cXY, int iStepSizeSqr, int *pSum1, int *pSum2, int *pMaxAngle)
|
|
{
|
|
int sum1, sum2;
|
|
int x, y;
|
|
XY *rgxy, *rgxySave;
|
|
int ang, lastAng, diff, dx, dy;
|
|
|
|
if (cXY <= 2)
|
|
{
|
|
*pSum1 = *pSum2 = 0;
|
|
return;
|
|
}
|
|
|
|
// smooth points
|
|
rgxySave = rgxy = (XY *) ExternAlloc(cXY*sizeof(XY));
|
|
if (!rgxy)
|
|
{
|
|
*pSum1 = *pSum2 = 0;
|
|
return;
|
|
}
|
|
SmoothPoints(rgXY, rgxy, cXY);
|
|
|
|
sum1 = sum2 = 0;
|
|
x = rgxy->x;
|
|
y = rgxy->y;
|
|
rgxy++;
|
|
cXY--;
|
|
// find first angle
|
|
while (cXY)
|
|
{
|
|
dy = rgxy->y - y;
|
|
dx = rgxy->x - x;
|
|
if (dx*dx+dy*dy >= iStepSizeSqr)
|
|
{
|
|
//Function from common/mathx--returns the integer approx in degrees
|
|
lastAng = Arctan2(dy, dx);
|
|
cXY--;
|
|
x = rgxy->x;
|
|
y = rgxy->y;
|
|
rgxy++;
|
|
break;
|
|
}
|
|
cXY--;
|
|
rgxy++;
|
|
}
|
|
// now find difference of every subsequent angle with its previous angle
|
|
while (cXY)
|
|
{
|
|
dy = rgxy->y - y;
|
|
dx = rgxy->x - x;
|
|
if (dx*dx+dy*dy >= iStepSizeSqr)
|
|
{
|
|
ang = Arctan2(dy, dx);
|
|
ANGLEDIFF(lastAng, ang, diff)
|
|
sum1 += diff;
|
|
if (diff < 0)
|
|
diff = -diff;
|
|
sum2 += diff;
|
|
lastAng = ang;
|
|
x = rgxy->x;
|
|
y = rgxy->y;
|
|
if (diff > *pMaxAngle)
|
|
*pMaxAngle = diff;
|
|
}
|
|
cXY--;
|
|
rgxy++;
|
|
}
|
|
|
|
// clean up
|
|
ExternFree(rgxySave);
|
|
*pSum1 = sum1;
|
|
*pSum2 = sum2;
|
|
}
|
|
|
|
/******************************Private*Routine******************************\
|
|
* AddCurveFeatures
|
|
*
|
|
* Given an ink (one or more strokes), computes three curvature features.
|
|
*
|
|
* History:
|
|
* 26-Sep-1997 -by- Angshuman Guha aguha
|
|
* Wrote this comment.
|
|
\**************************************************************************/
|
|
int AddCurveFeatures(GLYPH *pGlyph, int iStepSizeSqr, unsigned short *rgFeat)
|
|
{
|
|
GLYPH *glyph;
|
|
FRAME *frame;
|
|
int cXY;
|
|
XY *rgXY;
|
|
int sum1=0, sum2=0, ang1, ang2, maxAngle=0;
|
|
|
|
for (glyph=pGlyph; glyph; glyph=glyph->next)
|
|
{
|
|
frame = glyph->frame;
|
|
if (!IsVisibleFRAME(frame))
|
|
continue;
|
|
rgXY = RgrawxyFRAME(frame);
|
|
cXY = CrawxyFRAME(frame);
|
|
ASSERT(cXY > 0);
|
|
//For each frame compute the curvedness
|
|
ComputeCurvedness(rgXY, cXY, iStepSizeSqr, &ang1, &ang2, &maxAngle);
|
|
//sum1 represents the sum of the modular change in angle(with respect to + and - for the angles
|
|
sum1 += ang1;
|
|
//sum2 represent the total curviness of the stoke--just directly measures the change in angles.
|
|
sum2 += ang2;
|
|
}
|
|
|
|
// based on emperical obsevations, truncate sum1 between -1000 to 1000
|
|
// and sum2 between 0 and 1200
|
|
// (this results in no truncation in more than 99% cases)
|
|
if (sum1 < -1000)
|
|
sum1 = -1000;
|
|
else if (sum1 > 1000)
|
|
sum1 = 1000;
|
|
if (sum2 < 0)
|
|
sum2 = 0;
|
|
else if (sum2 > 1200)
|
|
sum2 = 1200;
|
|
sum1 += 1000; // now between 0 and 2000
|
|
sum1 = LSHFT(sum1)/2000;
|
|
if (sum1 > 0xFFFF)
|
|
sum1 = 0xFFFF;
|
|
sum2 = LSHFT(sum2)/1200;
|
|
if (sum2 > 0xFFFF)
|
|
sum2 = 0xFFFF;
|
|
// maxAngle should be between 0 and 180
|
|
if (maxAngle < 0)
|
|
maxAngle = 0;
|
|
else if (maxAngle > 180)
|
|
maxAngle = 180;
|
|
maxAngle = LSHFT(maxAngle)/180;
|
|
if (maxAngle > 0xFFFF)
|
|
maxAngle = 0xFFFF;
|
|
|
|
*rgFeat++ = (unsigned short) sum1;
|
|
*rgFeat++ = (unsigned short) sum2;
|
|
*rgFeat = (unsigned short) maxAngle;
|
|
return 3;
|
|
}
|
|
|
|
/******************************Private*Routine******************************\
|
|
* AddStrokeCountFeature
|
|
*
|
|
* Defines a single feature derived from stroke count of a char.
|
|
*
|
|
* History:
|
|
* 26-Sep-1997 -by- Angshuman Guha aguha
|
|
* Wrote this comment.
|
|
\**************************************************************************/
|
|
int AddStrokeCountFeature(int cStroke, unsigned short *rgFeat)
|
|
{
|
|
int tmp = LSHFT(cStroke-1)/cStroke;
|
|
*rgFeat++ = (unsigned short)tmp;
|
|
return 1;
|
|
}
|
|
|
|
/******************************Private*Routine******************************\
|
|
* DoOneContour
|
|
*
|
|
* Once a contour has been found (defined by a sequence of values, X-values
|
|
* for left- or right-contour, Y-values for top- or bottom-contour), this function
|
|
* is called to fit a Chebychev polynomial to the contour generating 9 new
|
|
* features.
|
|
*
|
|
* The arg "contour" is of length GRIDSIZE. The output features are filled in
|
|
* the arg rgFeat and the count of features generated is returned.
|
|
*
|
|
* History:
|
|
* 26-Sep-1997 -by- Angshuman Guha aguha
|
|
* Wrote it.
|
|
\**************************************************************************/
|
|
int DoOneContour(int *contour, unsigned short *rgFeat)
|
|
{
|
|
int rgX[2*GRIDSIZE], *pX; //The rgX array is of 2*GRIDSIZE because the Chebyshev function takes alternate array values
|
|
int chby[10];
|
|
int norm = 0;
|
|
int dT, i;
|
|
|
|
// copy points into required format
|
|
pX = rgX;
|
|
for (i=0; i<GRIDSIZE; i++)
|
|
{
|
|
int x;
|
|
x = 2 * (*contour++);
|
|
//We are now LSHFTing--this is the first place where the 16.16 format surfaces
|
|
//Why do we have to make the value between -1 and +1 ?
|
|
*pX++ = LSHFT(x-GRIDSIZE)/(GRIDSIZE); // values are in the range -1 to +1
|
|
pX++;
|
|
}
|
|
// fit a chebychev polynomial
|
|
if (!LSCheby(rgX, GRIDSIZE, chby, 10))
|
|
{
|
|
ASSERT(0);
|
|
return 0;
|
|
}
|
|
|
|
// find rms of coefficients
|
|
//dT and norm are both in 16.16 notation
|
|
for (i = 0; i < 10; ++i)
|
|
{
|
|
Mul16(chby[i], chby[i], dT)
|
|
norm += dT;
|
|
}
|
|
norm = ISqrt(norm) << 8;
|
|
if (norm < LSHFT(1))
|
|
norm = LSHFT(1);//The normalization value should at least be 1
|
|
|
|
// normalize coeffcients
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
//Why this LSHFT(1) ??
|
|
dT = Div16(chby[i], norm) + LSHFT(1);
|
|
//Why this ??
|
|
dT >>= 1;
|
|
if (dT >= 0x10000)
|
|
dT = 0xFFFF; //Fill it with 1's for the lower 16 bits
|
|
else if (dT < 0)
|
|
dT = 0;
|
|
//Does converting to an unsigned short always take the lowest 16 bits ??
|
|
*rgFeat++ = (unsigned short)dT;
|
|
}
|
|
return 10;
|
|
}
|
|
|
|
/******************************Private*Routine******************************\
|
|
* MakeLine
|
|
*
|
|
* Given a point in the GRIDSIZE x GRIDSIZE grid (bitmap) and given the
|
|
* sequence of points so far, this function adds one or more points in a
|
|
* straight line joining the last point and the given point.
|
|
*
|
|
* Returns the number of points added.
|
|
*
|
|
* History:
|
|
* 26-Sep-1997 -by- Angshuman Guha aguha
|
|
* Wrote it.
|
|
* 06-Oct-1997 -by- Angshuman Guha aguha
|
|
* Fixed a bug.
|
|
\**************************************************************************/
|
|
int MakeLine(BYTE x, BYTE y, BYTE *pX, BYTE *pY, int space)
|
|
{
|
|
BYTE midx, midy;
|
|
int ts1,ts2;
|
|
int c, c2;
|
|
|
|
if (space <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
ASSERT(x != *pX || y != *pY);
|
|
if (Neighbor(*pX, x) && Neighbor(*pY, y))
|
|
{
|
|
ASSERT(x >= 0);
|
|
ASSERT(x < GRIDSIZE);
|
|
ASSERT(y >= 0);
|
|
ASSERT(y < GRIDSIZE);
|
|
*++pX = x;
|
|
*++pY = y;
|
|
return 1;
|
|
}
|
|
ts1=(*pX+x)/2;
|
|
ts2=(*pY+y)/2;
|
|
midx = (BYTE) ts1;
|
|
midy = (BYTE) ts2;
|
|
|
|
c = MakeLine(midx, midy, pX, pY, space);
|
|
if (!c)
|
|
return 0;
|
|
c2 = MakeLine(x, y, pX+c, pY+c, space-c);
|
|
if (!c2)
|
|
return 0;
|
|
return c + c2;
|
|
}
|
|
|
|
/******************************Private*Routine******************************\
|
|
* AddContourFeatures
|
|
*
|
|
* Given a sequence of already-resampled points (pen-down strokes joined by
|
|
* intervening pen-up strokes) and the bounding rect for the ink, this function
|
|
* computes some contour features, fills up rgFeat with the features and
|
|
* returns the count of features computed.
|
|
*
|
|
* History:
|
|
* 26-Sep-1997 -by- Angshuman Guha aguha
|
|
* Wrote it.
|
|
* 06-Oct-1997 -by- Angshuman Guha aguha
|
|
* Fixed a bug. Added Realloc for rgMappedX and rgMappedY.
|
|
|
|
Comments added by Mango
|
|
|
|
The idea of this function is to first map the points that we have to 2 GRIDSIZE*GRIDSIZE bitmap.
|
|
Once the bitmap is made we use the contour features.
|
|
Explanation of the arguments to the function
|
|
pointinfo--Contains the strructure from which the points will be taken
|
|
pRect--pointer to the bounding rectangle of the original ink
|
|
rgFeat--This contains the finalk contour features
|
|
\**************************************************************************/
|
|
int AddContourFeatures(POINTINFO *pointinfo, RECT *pRect, unsigned short *rgFeat)
|
|
{
|
|
//BYTE rgGrid[GRIDSIZE][GRIDSIZE];
|
|
int xMin = pRect->left; //Stores the minimum value of x
|
|
int yMin = pRect->top; //Stores the minimum value of y
|
|
int xRange = pRect->right - xMin; //Stores the width of the bounding rectangle
|
|
int yRange = pRect->bottom - yMin; //Stores the height of the bounding rectangle
|
|
int range, xOrigin, yOrigin;//Range stores the greated of the xRange and yRange
|
|
int iPoint, *xy, *z, i, cMapped, cMappedMax; //cMapped stores the number of points that have been mapped. cMappedMAx--the max number of points that could be mapped
|
|
BYTE *pMappedX, *pMappedY, *rgMappedX, *rgMappedY;//Since we are using BYTE * here,we are assuming that GRIDSIZE <256--a byte size
|
|
int rgMaxX[GRIDSIZE], rgMinY[GRIDSIZE], rgMaxY[GRIDSIZE], lastz; //GRIDSIZE=32
|
|
int lastx, lasty;
|
|
int iRetValue;
|
|
BYTE *pbTmpX = NULL, *pbTmpY = NULL;
|
|
|
|
ASSERT(xRange > 0);
|
|
ASSERT(yRange > 0);
|
|
//We want to map the points to a GRIDSIZE*GRIDSIZE bitmap--but at the same time we want to preserve the aspect ratio
|
|
//It xRange >yRange,then the xValues will span the whole of the bit map
|
|
//The y values will not span the entire bit map--but will simply be centered on the bit map.This helps to preserve the aspect ratio
|
|
|
|
if (xRange > yRange)
|
|
{
|
|
range = xRange;
|
|
xOrigin = 0;
|
|
yOrigin = (GRIDSIZE - GRIDSIZE*(yRange-1)/range) / 2;
|
|
}
|
|
else if (yRange > 1)
|
|
{ //In this case the y will span the entire grid map.xOrigin is scaled according to the earlier comment
|
|
range = yRange;
|
|
xOrigin = (GRIDSIZE - GRIDSIZE*(xRange-1)/range) / 2;
|
|
yOrigin = 0;
|
|
}
|
|
else // xRange == yRange == 1
|
|
{ //In this case the Ink will be centered
|
|
range = 1;
|
|
xOrigin = GRIDSIZE/2;
|
|
yOrigin = GRIDSIZE/2;
|
|
}
|
|
|
|
// make list of grid points which will make up the binary pixel map--we will map only the pen down points
|
|
|
|
//Initialize the pointers to the raw points
|
|
xy = pointinfo->xy;
|
|
z = pointinfo->z;
|
|
|
|
|
|
//In case there is a continuation of a line,then the bit map should not have any vacant spaces in the grid
|
|
//connecting the points of the line.Hence we add extra space by 2*GRIDSIZE in case we need to connect the points
|
|
//cMappedMax represents the maximum number of points that will be mapped
|
|
|
|
cMappedMax = 2*(pointinfo->cPoint+GRIDSIZE);
|
|
rgMappedX = (BYTE *) ExternAlloc(cMappedMax*sizeof(BYTE));
|
|
rgMappedY = (BYTE *) ExternAlloc(cMappedMax*sizeof(BYTE));
|
|
if (!rgMappedX || !rgMappedY)
|
|
{
|
|
//Allocatation failed.FLEE !!
|
|
ASSERT(0);
|
|
iRetValue = 0;
|
|
goto cleanup;
|
|
}
|
|
cMapped = 0; //This contains the total number of points that have been mapped
|
|
//The pointer to which it has been initialized is one less than the actual start.
|
|
//Hence need to use an increment operator prior to using the first time.
|
|
//After that the pointer always points to the last value that was allocated
|
|
|
|
pMappedX = rgMappedX - 1;
|
|
pMappedY = rgMappedY - 1;
|
|
lastz = PEN_UP_VALUE;
|
|
for (iPoint=0; iPoint<pointinfo->cPoint; iPoint++)
|
|
{
|
|
int xRaw, yRaw, x, y, zRaw;
|
|
// get point
|
|
xRaw = *xy++;
|
|
yRaw = *xy++;
|
|
zRaw = *z++;
|
|
z++;
|
|
if (zRaw > 0) // pen-down point
|
|
{
|
|
// map x to grid
|
|
x = xOrigin + GRIDSIZE*(xRaw - xMin)/range;
|
|
ASSERT(x >= 0);
|
|
ASSERT(x < GRIDSIZE);
|
|
// map y to grid
|
|
y = yOrigin + GRIDSIZE*(yRaw - yMin)/range;
|
|
ASSERT(y >= 0);
|
|
ASSERT(y < GRIDSIZE);
|
|
// save this point in the list
|
|
if (lastz < 0)
|
|
{
|
|
if (cMapped==cMappedMax)
|
|
{ //If the max space has already been used up then we ned to realloc.
|
|
cMappedMax *= 2;
|
|
pbTmpX = ExternRealloc(rgMappedX, cMappedMax*sizeof(BYTE));
|
|
if (!pbTmpX)
|
|
{
|
|
ASSERT(0);
|
|
iRetValue = 0;
|
|
goto cleanup;
|
|
}
|
|
rgMappedX = pbTmpX;
|
|
pMappedX = rgMappedX - 1 + cMapped;
|
|
|
|
pbTmpY = ExternRealloc(rgMappedY, cMappedMax*sizeof(BYTE));
|
|
if (!pbTmpY)
|
|
{
|
|
ASSERT(0);
|
|
iRetValue = 0;
|
|
goto cleanup;
|
|
}
|
|
rgMappedY = pbTmpY;
|
|
pMappedY = rgMappedY - 1 + cMapped;
|
|
}
|
|
|
|
// first point of a stroke
|
|
*++pMappedX = (BYTE)x;
|
|
*++pMappedY = (BYTE)y;
|
|
cMapped++;
|
|
}
|
|
else if (x != *pMappedX || y != *pMappedY)
|
|
{
|
|
// next unique mapped point
|
|
//i contains the count of the number of points that have been added thru MakeLine
|
|
ASSERT(*pMappedX < GRIDSIZE);
|
|
ASSERT(*pMappedY <GRIDSIZE);
|
|
ASSERT(*pMappedX >=0);
|
|
ASSERT(*pMappedY >=0);
|
|
|
|
|
|
while (!(i = MakeLine((BYTE)x, (BYTE)y, pMappedX, pMappedY, cMappedMax - cMapped)))
|
|
{
|
|
// these reallocs happen on 0.4% of the samples of gtrain02.ste (1860 of 443292 samples)
|
|
cMappedMax *= 2;
|
|
pbTmpX = ExternRealloc(rgMappedX, cMappedMax*sizeof(BYTE));
|
|
if (!pbTmpX)
|
|
{
|
|
ASSERT(0);
|
|
iRetValue = 0;
|
|
goto cleanup;
|
|
}
|
|
rgMappedX = pbTmpX;
|
|
pMappedX = rgMappedX - 1 + cMapped;
|
|
|
|
pbTmpY = ExternRealloc(rgMappedY, cMappedMax*sizeof(BYTE));
|
|
if (!pbTmpY)
|
|
{
|
|
ASSERT(0);
|
|
iRetValue = 0;
|
|
goto cleanup;
|
|
}
|
|
rgMappedY = pbTmpY;
|
|
pMappedY = rgMappedY - 1 + cMapped;
|
|
}
|
|
|
|
//Increment the count of mapped points by the number of points that have been added.
|
|
cMapped += i;
|
|
ASSERT(cMapped <=cMappedMax);
|
|
//Increment the pointers accordingly
|
|
pMappedX += i;
|
|
pMappedY += i;
|
|
}
|
|
} // if zRaw > 0
|
|
lastz = zRaw;
|
|
} // for iPoint=0
|
|
|
|
// now dump the mapped point list into the grid, deleting redundant points along the way
|
|
//memset(rgGrid, 0, sizeof(rgGrid));
|
|
for (i=0; i<GRIDSIZE; i++)
|
|
{
|
|
rgMaxX[i] = -1;
|
|
rgMinY[i] = GRIDSIZE;
|
|
rgMaxY[i] = -1;
|
|
}
|
|
pMappedX = rgMappedX;
|
|
pMappedY = rgMappedY;
|
|
for (iPoint=0; iPoint<cMapped; iPoint++)
|
|
{
|
|
int x, y;
|
|
|
|
x = *pMappedX++;
|
|
y = *pMappedY++;
|
|
|
|
|
|
if (iPoint > 0 && iPoint < cMapped-1)
|
|
{
|
|
int nextx = *pMappedX;
|
|
int nexty = *pMappedY;
|
|
if (Neighbor(lastx, nextx) &&
|
|
Neighbor(lasty, nexty) &&
|
|
lastx != nextx &&
|
|
lasty != nexty)
|
|
continue;
|
|
}
|
|
lastx = x;
|
|
lasty = y;
|
|
|
|
//rgGrid[x][y]=1;
|
|
ASSERT(x >= 0);
|
|
ASSERT(x < GRIDSIZE);
|
|
ASSERT(y >= 0);
|
|
ASSERT(y < GRIDSIZE);
|
|
if (x > rgMaxX[y])
|
|
rgMaxX[y] = x;
|
|
if (y < rgMinY[x])
|
|
rgMinY[x] = y;
|
|
if (y > rgMaxY[x])
|
|
rgMaxY[x] = y;
|
|
}
|
|
|
|
// now Chebychev'ize the three contours
|
|
|
|
// right contour
|
|
rgFeat += DoOneContour(rgMaxX, rgFeat);
|
|
// top contour
|
|
rgFeat += DoOneContour(rgMinY, rgFeat);
|
|
// bottom contour
|
|
rgFeat += DoOneContour(rgMaxY, rgFeat);
|
|
|
|
iRetValue = 30;
|
|
cleanup: // Clean up temp space
|
|
|
|
if (rgMappedX)
|
|
ExternFree(rgMappedX);
|
|
if (rgMappedY)
|
|
ExternFree(rgMappedY);
|
|
|
|
return iRetValue;
|
|
}
|
|
|
|
/******************************Private*Routine******************************\
|
|
* NormalizeCheby
|
|
*
|
|
* Routine to normalize the three (x, y and z) Chebychev polynomials.
|
|
*
|
|
* History:
|
|
* 26-Sep-1997 -by- Angshuman Guha aguha
|
|
* Wrote this comment.
|
|
\**************************************************************************/
|
|
void NormalizeCheby(int *chbyX, int *chbyY, int *chbyZ, unsigned short *rgFeat)
|
|
{
|
|
int norm = 0;
|
|
int dT;
|
|
int cFeat = 0, i;
|
|
//The norm is applied both to x and y prior to dividing,so that the relative sizes of x and y can be kept intact
|
|
//
|
|
for (i = 1; i < XCHB; ++i) // 1st X coeff skipped
|
|
{
|
|
Mul16(chbyX[i], chbyX[i], dT)
|
|
norm += dT;
|
|
}
|
|
for (i = 1; i < YCHB; ++i) // 1st Y coeff skipped
|
|
{
|
|
Mul16(chbyY[i], chbyY[i], dT)
|
|
norm += dT;
|
|
}
|
|
norm = ISqrt(norm) << 8;
|
|
if (norm < LSHFT(1))
|
|
norm = LSHFT(1);
|
|
|
|
for (i=1; i<XCHB; i++)
|
|
{
|
|
dT = Div16(chbyX[i], norm) + LSHFT(1); // now between 0 and 2
|
|
dT >>= 1; // now between 0 and 1
|
|
if (dT >= 0x10000)
|
|
dT = 0xFFFF;
|
|
else if (dT < 0)
|
|
dT = 0;
|
|
rgFeat[cFeat++] = (unsigned short)dT;
|
|
}
|
|
|
|
for (i=1; i<YCHB; i++)
|
|
{
|
|
dT = Div16(chbyY[i], norm) + LSHFT(1);
|
|
dT >>= 1;
|
|
if (dT >= 0x10000)
|
|
dT = 0xFFFF;
|
|
else if (dT < 0)
|
|
dT = 0;
|
|
rgFeat[cFeat++] = (unsigned short)dT;
|
|
}
|
|
|
|
// Z
|
|
norm = 0;
|
|
for (i = 0; i < 8; ++i)
|
|
{
|
|
Mul16(chbyZ[i], chbyZ[i], dT)
|
|
norm += dT;
|
|
}
|
|
norm = ISqrt(norm) << 8;
|
|
if (norm < LSHFT(1))
|
|
norm = LSHFT(1);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
dT = Div16(chbyZ[i], norm) + LSHFT(1);
|
|
dT >>= 1;
|
|
if (dT >= 0x10000)
|
|
dT = 0xFFFF;
|
|
else if (dT < 0)
|
|
dT = 0;
|
|
rgFeat[cFeat++] = (unsigned short)dT;
|
|
}
|
|
ASSERT(cFeat == 26);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* SoleFeaturize
|
|
*
|
|
* The top-level routine for featurizing ink for a char.
|
|
*
|
|
* History:
|
|
* 26-Sep-1997 -by- Angshuman Guha aguha
|
|
* Modified it.
|
|
\**************************************************************************/
|
|
BOOL SoleFeaturize(GLYPH *pGlyph, RECT *pGuide, unsigned short *rgFeat)
|
|
{
|
|
int cStroke, iStroke;
|
|
int iPoint;
|
|
int sumX, sumY, sum;
|
|
double totvar;
|
|
int var;
|
|
int isumX, isumY;//These store the mean values of x and y
|
|
int chbyX[IMAXCHB], chbyY[IMAXCHB], chbyZ[IMAXCHB];
|
|
int retVal = 1;
|
|
XY *rgXY, lastXY;
|
|
int cXY, iXY, dx, dy, t;
|
|
GLYPH *glyph;
|
|
FRAME *frame;
|
|
int ydev; //Stores the ydev value used for storing the step size
|
|
POINTINFO pointinfo;
|
|
RECT rect;
|
|
|
|
// compute cStroke
|
|
for (cStroke=0, glyph=pGlyph; glyph; glyph=glyph->next)
|
|
{
|
|
frame = glyph->frame;
|
|
if (!IsVisibleFRAME(frame))
|
|
continue;
|
|
cXY = CrawxyFRAME(frame);
|
|
ASSERT(cXY > 0);
|
|
cStroke++;
|
|
}
|
|
|
|
if (cStroke < 1)
|
|
return 0;
|
|
|
|
// compute step size--originally this was done using the box height
|
|
// pointinfo.iStepSize = pGuide->cyBox*3/200; // 1.5% of box height
|
|
|
|
|
|
|
|
ydev= YDeviation(pGlyph);
|
|
if (ydev < 1)
|
|
ydev = 1; // a "-" or a "."
|
|
//The step size is computed from the ydev value
|
|
pointinfo.iStepSize = ydev/5;
|
|
|
|
if (pointinfo.iStepSize < 2)
|
|
pointinfo.iStepSize = 2;
|
|
pointinfo.iStepSizeSqr = pointinfo.iStepSize * pointinfo.iStepSize;
|
|
|
|
// estimate total count of points
|
|
pointinfo.cPointMax = 1; // make sure it does not end up being zero
|
|
for (iStroke=0, glyph=pGlyph; glyph; glyph=glyph->next)
|
|
{
|
|
frame = glyph->frame;
|
|
if (!IsVisibleFRAME(frame))
|
|
continue;
|
|
rgXY = RgrawxyFRAME(frame);
|
|
cXY = CrawxyFRAME(frame);
|
|
ASSERT(cXY > 0);
|
|
|
|
sum = 0;
|
|
for (iXY=1; iXY<cXY; iXY++)
|
|
{
|
|
dx = rgXY[iXY].x - rgXY[iXY-1].x;
|
|
if (dx < 0)
|
|
dx = -dx;
|
|
dy = rgXY[iXY].y - rgXY[iXY-1].y;
|
|
if (dy < 0)
|
|
dy = -dy;
|
|
if (dx > dy)
|
|
sum += dx;
|
|
else
|
|
sum += dy;
|
|
}
|
|
|
|
//The sum that we are computing here is an underestimate--we are only taking the max on |x| or |y|.The distance will
|
|
// be more
|
|
pointinfo.cPointMax += sum/pointinfo.iStepSize;
|
|
|
|
// if not first stroke simulate pen-up stroke
|
|
if (iStroke)
|
|
{
|
|
dx = lastXY.x - rgXY->x;
|
|
dy = lastXY.y - rgXY->y;
|
|
t = ISqrt(dx*dx + dy*dy)/pointinfo.iStepSize;
|
|
if (t >= 2)
|
|
pointinfo.cPointMax += t-1;
|
|
}
|
|
lastXY = rgXY[cXY-1];
|
|
iStroke++;
|
|
}
|
|
|
|
//Since we have computed an underestimate multiply by two
|
|
pointinfo.cPointMax *= 2;
|
|
|
|
// allocate space
|
|
pointinfo.xy = (int *) ExternAlloc(2*pointinfo.cPointMax*sizeof(int));
|
|
if (!pointinfo.xy)
|
|
return 0;
|
|
|
|
//The array size for z is double of what actually needs to be allocated because-
|
|
//--the chebyshev function expects the array to be spaced in this manner
|
|
//--indexing is easier in the functions--can exactly mirror what you do for the x and y
|
|
pointinfo.z = (int *) ExternAlloc(2*pointinfo.cPointMax*sizeof(int));
|
|
if (!pointinfo.z)
|
|
{
|
|
ExternFree(pointinfo.xy);
|
|
return 0;
|
|
}
|
|
|
|
// join all strokes into one stream
|
|
pointinfo.cPoint = 0;
|
|
for (glyph=pGlyph; glyph; glyph=glyph->next)
|
|
{
|
|
frame = glyph->frame;
|
|
if (!IsVisibleFRAME(frame))
|
|
continue;
|
|
rgXY = RgrawxyFRAME(frame);
|
|
cXY = CrawxyFRAME(frame);
|
|
for (iXY = 0; iXY < cXY; iXY++)
|
|
if (!AddPoint(&pointinfo, rgXY[iXY].x, rgXY[iXY].y, !iXY))
|
|
{
|
|
retVal = 0;
|
|
goto freeReturn;
|
|
}
|
|
}
|
|
|
|
// contour features (computed from resampled raw points)
|
|
GetRectGLYPH(pGlyph, &rect);
|
|
|
|
//PLEASE NOTE--rgFeat is unsigned short.Its value comes from the lower 16 bits of the 16.16 format that had been defined earlier.
|
|
rgFeat += AddContourFeatures(&pointinfo, &rect, rgFeat);
|
|
|
|
//IS there any reason why the mean and the variance has been subtracted only AFTER the contour features ??Maybe because there we are dumping to a bit map ??
|
|
|
|
// compute X-mean and Y-mean
|
|
sumX = sumY = 0;
|
|
for (iPoint=0; iPoint<2*pointinfo.cPoint; iPoint+=2)
|
|
{
|
|
sumX += pointinfo.xy[iPoint] - rect.left;
|
|
sumY += pointinfo.xy[iPoint+1] - rect.top;
|
|
}
|
|
//isumX and isumY represent the mean values of the x and y coordinates.
|
|
isumX = (sumX / pointinfo.cPoint) + rect.left;
|
|
isumY = (sumY / pointinfo.cPoint) + rect.top;
|
|
|
|
// shift points by means
|
|
for (iPoint=0; iPoint<2*pointinfo.cPoint; iPoint+=2)
|
|
{
|
|
pointinfo.xy[iPoint] -= isumX;
|
|
pointinfo.xy[iPoint+1] -= isumY;
|
|
}
|
|
|
|
// compute variance
|
|
totvar = 0;
|
|
for (iPoint=0; iPoint<2*pointinfo.cPoint; iPoint++)
|
|
{
|
|
totvar += (double) pointinfo.xy[iPoint] * (double) pointinfo.xy[iPoint];
|
|
}
|
|
var = (int) sqrt(totvar/pointinfo.cPoint);
|
|
if (var < 1)
|
|
var = 1;
|
|
|
|
// scale points by standard deviation
|
|
|
|
|
|
// From this point on,the pointinfo values are in 16.16
|
|
//IMPORTTANT NOTE---THE pointinfo array is not directly used after this point
|
|
//If it is,you will have to use 16.16 arithmetic
|
|
|
|
for (iPoint=0; iPoint<2*pointinfo.cPoint; iPoint++)
|
|
{
|
|
pointinfo.xy[iPoint] = LSHFT(pointinfo.xy[iPoint])/var;
|
|
}
|
|
//Basically,since we effectively have a normal distribution(hopefully)most of the values will be between +-3.
|
|
// chebychev'ize!
|
|
if (!LSCheby(pointinfo.xy, pointinfo.cPoint, chbyX, XCHB))
|
|
{
|
|
retVal = 0;
|
|
goto freeReturn;
|
|
}
|
|
if (!LSCheby(pointinfo.xy+1, pointinfo.cPoint, chbyY, YCHB))
|
|
{
|
|
retVal = 0;
|
|
goto freeReturn;
|
|
}
|
|
if (!LSCheby(pointinfo.z, pointinfo.cPoint, chbyZ, ZCHB))
|
|
{
|
|
retVal = 0;
|
|
goto freeReturn;
|
|
}
|
|
|
|
NormalizeCheby(chbyX, chbyY, chbyZ, rgFeat);
|
|
rgFeat += 26;
|
|
|
|
// stroke count feature--1 feature is added
|
|
rgFeat += AddStrokeCountFeature(cStroke, rgFeat);
|
|
|
|
// guide features
|
|
//The rect had been comptured prior to scaling the points --by mean and standard deviation
|
|
//isumY represents the mean Y value
|
|
//We add 5 guide features
|
|
rgFeat += AddGuideFeatures(pGuide, &rect, isumY, rgFeat);
|
|
|
|
// curved-ness features
|
|
//Note the original glyph is being passed here
|
|
//Why not simply use the sampled points ??
|
|
rgFeat += AddCurveFeatures(pGlyph, pointinfo.iStepSizeSqr, rgFeat);
|
|
|
|
freeReturn:
|
|
ExternFree(pointinfo.xy);
|
|
ExternFree(pointinfo.z);
|
|
return retVal;
|
|
}
|
|
|
|
/*
|
|
void DumpFeatures (unsigned short *pFeat)
|
|
{
|
|
static int c = 0;
|
|
static FILE *fp = NULL;
|
|
int i, cMap;
|
|
|
|
if (!fp)
|
|
{
|
|
fp = fopen ("feat.dmp", "wt");
|
|
if (!fp)
|
|
return;
|
|
}
|
|
|
|
cMap = sizeof (s_aaMap) / sizeof (s_aaMap[0]);
|
|
for (i = 0; i < cMap; i++)
|
|
{
|
|
if (s_aaMap[i] == s_wch)
|
|
break;
|
|
}
|
|
|
|
if (i == cMap)
|
|
return;
|
|
|
|
fprintf (fp, "%d\t", i + 1);
|
|
|
|
for (i = 0; i < 65; i++)
|
|
{
|
|
fprintf (fp, "%d%c",
|
|
pFeat[i],
|
|
i == 64 ? '\n' : ' ');
|
|
}
|
|
|
|
fflush (fp);
|
|
|
|
c++;
|
|
}
|
|
*/
|
|
|
|
#pragma optimize("",off)
|
|
|
|
int SoleNN(SOLE_LOAD_INFO *pSole, int cStrk, unsigned short *pFeat, ALT_LIST *pAlt)
|
|
{
|
|
RREAL *pNetMem, *pNetOut;
|
|
int iWinner, cOut;
|
|
int i, j, k;
|
|
int *pIndex;
|
|
wchar_t *pwchMap;
|
|
|
|
pAlt->cAlt = -1;
|
|
|
|
if (cStrk == 1)
|
|
{
|
|
pNetMem = ExternAlloc(pSole->iNet1Size * sizeof (*pNetMem));
|
|
if (!pNetMem)
|
|
return -1;
|
|
|
|
for (i = 0; i < SOLE_NUM_FEATURES; i++)
|
|
{
|
|
pNetMem[i] = pFeat[i];
|
|
}
|
|
|
|
pNetOut = runLocalConnectNet(&pSole->net1, pNetMem, &iWinner, &cOut);
|
|
pwchMap = pSole->pMap1;
|
|
}
|
|
else
|
|
{
|
|
pNetMem = ExternAlloc(pSole->iNet2Size * sizeof (*pNetMem));
|
|
if (!pNetMem)
|
|
return -1;
|
|
|
|
for (i = 0; i < SOLE_NUM_FEATURES; i++)
|
|
{
|
|
pNetMem[i] = pFeat[i];
|
|
}
|
|
|
|
pNetOut = runLocalConnectNet(&pSole->net2, pNetMem, &iWinner, &cOut);
|
|
pwchMap = pSole->pMap2;
|
|
}
|
|
|
|
pIndex = ExternAlloc(sizeof(int) * cOut);
|
|
if (pIndex == NULL)
|
|
{
|
|
ExternFree(pNetMem);
|
|
return -1;
|
|
}
|
|
for (i = 0; i < cOut; i++)
|
|
{
|
|
pIndex[i] = i;
|
|
}
|
|
|
|
for (i = 0; i < MAX_ALT_LIST; i++)
|
|
{
|
|
for (j = i + 1; j < cOut; j++)
|
|
{
|
|
if (pNetOut[pIndex[i]] < pNetOut[pIndex[j]])
|
|
{
|
|
k = pIndex[i];
|
|
pIndex[i] = pIndex[j];
|
|
pIndex[j] = k;
|
|
}
|
|
}
|
|
|
|
if (pNetOut[pIndex[i]] == 0)
|
|
{
|
|
break;
|
|
}
|
|
pAlt->awchList[i] = pwchMap[pIndex[i]];
|
|
pAlt->aeScore[i] = (float) pNetOut[pIndex[i]] / (float) SOFT_MAX_UNITY;
|
|
}
|
|
|
|
//DumpFeatures (pFeat);
|
|
|
|
// If the following line is compiled with optimization turned on,
|
|
// the VS6 compiler goes into an infinite loop.
|
|
pAlt->cAlt = i;
|
|
|
|
ExternFree(pIndex);
|
|
ExternFree(pNetMem);
|
|
return pAlt->cAlt;
|
|
}
|
|
|
|
#pragma optimize("",on)
|
|
|
|
int SoleMatch(SOLE_LOAD_INFO *pSole,
|
|
ALT_LIST *pAlt, int cAlt, GLYPH *pGlyph, RECT *pGuide, CHARSET *pCS, LOCRUN_INFO * pLocRunInfo)
|
|
{
|
|
int cStrk;
|
|
unsigned short aiSoleFeat[SOLE_NUM_FEATURES];
|
|
|
|
cStrk = CframeGLYPH (pGlyph);
|
|
|
|
if (SoleFeaturize(pGlyph, pGuide, aiSoleFeat) != 1)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return SoleNN (pSole, cStrk, aiSoleFeat, pAlt);
|
|
}
|
|
|
|
static void AddChar(wchar_t *pwchTop1, float *pflTop1, ALT_LIST *pAltList, wchar_t dch, float flProb,
|
|
LOCRUN_INFO *pLocRunInfo, CHARSET *pCS)
|
|
{
|
|
int j;
|
|
for (j = 0; j < (int) pAltList->cAlt; j++)
|
|
{
|
|
if (pAltList->awchList[j] == dch)
|
|
{
|
|
pAltList->aeScore[j] = flProb;
|
|
}
|
|
}
|
|
if (flProb > 0)
|
|
{
|
|
// Check whether the character (or folding set) passes the filter,
|
|
// if so see if it is the new top 1.
|
|
if (IsAllowedChar(pLocRunInfo, pCS, dch))
|
|
{
|
|
if (*pwchTop1 == 0xFFFE || flProb > *pflTop1)
|
|
{
|
|
*pflTop1 = flProb;
|
|
*pwchTop1 = dch;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int SoleMatchRescore(SOLE_LOAD_INFO *pSole,
|
|
wchar_t *pwchTop1, float *pflTop1,
|
|
ALT_LIST *pAltList, int cAlt, GLYPH *pGlyph, RECT *pGuide,
|
|
CHARSET *pCharSet, LOCRUN_INFO *pLocRunInfo)
|
|
{
|
|
int i;
|
|
int cStrk;
|
|
|
|
RREAL *pNetMem, *pNetOut;
|
|
int iWinner, cOut;
|
|
int j;
|
|
wchar_t *pwchMap;
|
|
|
|
unsigned short aiSoleFeat[SOLE_NUM_FEATURES];
|
|
|
|
// First set all the scores to zero. This is because some code points Fugu
|
|
// supports may not be supported by Sole. This implicitly says Sole gives
|
|
// them a score of zero.
|
|
for (j = 0; j < (int) pAltList->cAlt; j++)
|
|
{
|
|
pAltList->aeScore[j] = 0;
|
|
}
|
|
|
|
*pflTop1 = 0;
|
|
*pwchTop1 = 0xFFFE;
|
|
|
|
cStrk = CframeGLYPH (pGlyph);
|
|
|
|
if (SoleFeaturize (pGlyph, pGuide, aiSoleFeat) != 1)
|
|
{
|
|
return pAltList->cAlt;
|
|
}
|
|
|
|
if (cStrk == 1)
|
|
{
|
|
pNetMem = ExternAlloc(pSole->iNet1Size * sizeof (*pNetMem));
|
|
if (!pNetMem)
|
|
return -1;
|
|
|
|
for (i = 0; i < SOLE_NUM_FEATURES; i++)
|
|
{
|
|
pNetMem[i] = aiSoleFeat[i];
|
|
}
|
|
|
|
pNetOut = runLocalConnectNet(&pSole->net1, pNetMem, &iWinner, &cOut);
|
|
pwchMap = pSole->pMap1;
|
|
}
|
|
else
|
|
{
|
|
pNetMem = ExternAlloc(pSole->iNet2Size * sizeof (*pNetMem));
|
|
if (!pNetMem)
|
|
return -1;
|
|
|
|
for (i = 0; i < SOLE_NUM_FEATURES; i++)
|
|
{
|
|
pNetMem[i] = aiSoleFeat[i];
|
|
}
|
|
|
|
pNetOut = runLocalConnectNet(&pSole->net2, pNetMem, &iWinner, &cOut);
|
|
pwchMap = pSole->pMap2;
|
|
}
|
|
|
|
// This is the version for Fugu trained on dense codes, which will usually be
|
|
// what we use. Loops over the outputs
|
|
for (i = 0; i < cOut; i++)
|
|
{
|
|
wchar_t fdch = pwchMap[i];
|
|
float flProb = (float) pNetOut[i] / (float) SOFT_MAX_UNITY;
|
|
#if 1
|
|
AddChar(pwchTop1, pflTop1, pAltList, fdch, flProb, pLocRunInfo, pCharSet);
|
|
#else
|
|
if (LocRunIsFoldedCode(pLocRunInfo, fdch))
|
|
{
|
|
// If it is a folded code, look up the folding set
|
|
wchar_t *pFoldingSet = LocRunFolded2FoldingSet(pLocRunInfo, fdch);
|
|
|
|
// Run through the folding set, adding non-NUL items to the output list
|
|
// (until the output list is full)
|
|
for (j = 0; j < LOCRUN_FOLD_MAX_ALTERNATES && pFoldingSet[j] != 0; j++)
|
|
{
|
|
AddChar(pwchTop1, pflTop1, pAltList, pFoldingSet[j], flProb, pLocRunInfo, pCharSet);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AddChar(pwchTop1, pflTop1, pAltList, fdch, flProb, pLocRunInfo, pCharSet);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ExternFree(pNetMem);
|
|
|
|
return pAltList->cAlt;
|
|
}
|
|
|
|
BOOL SoleLoadPointer(SOLE_LOAD_INFO *pSole, LOCRUN_INFO *pLocRunInfo)
|
|
{
|
|
NNET_HEADER *pHeader;
|
|
NNET_SPACE_HEADER *apSpcHeader[2];
|
|
|
|
pHeader = (NNET_HEADER *) pSole->info.pbMapping;
|
|
|
|
// check version and signature
|
|
ASSERT (pHeader->dwFileType == SOLE_FILE_TYPE);
|
|
|
|
ASSERT (pHeader->iFileVer >= SOLE_OLD_FILE_VERSION);
|
|
ASSERT (pHeader->iMinCodeVer <= SOLE_CUR_FILE_VERSION);
|
|
|
|
ASSERT ( !memcmp ( pHeader->adwSignature,
|
|
g_locRunInfo.adwSignature,
|
|
sizeof (pHeader->adwSignature)
|
|
)
|
|
);
|
|
|
|
if (pHeader->dwFileType != SOLE_FILE_TYPE ||
|
|
pHeader->adwSignature[0] != pLocRunInfo->adwSignature[0] ||
|
|
pHeader->adwSignature[1] != pLocRunInfo->adwSignature[1] ||
|
|
pHeader->adwSignature[2] != pLocRunInfo->adwSignature[2] ||
|
|
pHeader->iFileVer < SOLE_OLD_FILE_VERSION ||
|
|
pHeader->iMinCodeVer > SOLE_CUR_FILE_VERSION)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// # of spaces has to be two
|
|
ASSERT (pHeader->cSpace == 2);
|
|
|
|
apSpcHeader[0] =
|
|
(NNET_SPACE_HEADER *) (pSole->info.pbMapping + sizeof (NNET_HEADER));
|
|
|
|
apSpcHeader[1] =
|
|
(NNET_SPACE_HEADER *) ( pSole->info.pbMapping + sizeof (NNET_HEADER) +
|
|
sizeof (NNET_SPACE_HEADER));
|
|
|
|
if (restoreLocalConnectNet ( pSole->info.pbMapping + apSpcHeader[0]->iDataOffset,
|
|
0,
|
|
&pSole->net1
|
|
) == NULL
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (restoreLocalConnectNet ( pSole->info.pbMapping + apSpcHeader[1]->iDataOffset,
|
|
0,
|
|
&pSole->net2
|
|
) == NULL
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
pSole->iNet1Size =
|
|
getRunTimeNetMemoryRequirements(pSole->info.pbMapping + apSpcHeader[0]->iDataOffset);
|
|
|
|
pSole->iNet2Size =
|
|
getRunTimeNetMemoryRequirements(pSole->info.pbMapping + apSpcHeader[1]->iDataOffset);
|
|
|
|
pSole->pMap1 = (wchar_t *) (pSole->info.pbMapping + apSpcHeader[0]->iMapOffset);
|
|
pSole->pMap2 = (wchar_t *) (pSole->info.pbMapping + apSpcHeader[1]->iMapOffset);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SoleLoadRes(SOLE_LOAD_INFO *pSole, HINSTANCE hInst, int nResID, int nType, LOCRUN_INFO *pLocRunInfo)
|
|
{
|
|
if (DoLoadResource(&pSole->info, hInst, nResID, nType) == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
return SoleLoadPointer(pSole, pLocRunInfo);
|
|
}
|
|
|
|
BOOL SoleLoadFile(SOLE_LOAD_INFO *pSole, wchar_t *wszRecogDir, LOCRUN_INFO *pLocRunInfo)
|
|
{
|
|
wchar_t wszName[MAX_PATH];
|
|
FormatPath(wszName, wszRecogDir, NULL, NULL, NULL, L"sole.bin");
|
|
if (DoOpenFile(&pSole->info, wszName) == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
return SoleLoadPointer(pSole, pLocRunInfo);
|
|
}
|
|
|
|
BOOL SoleUnloadFile(SOLE_LOAD_INFO *pSole)
|
|
{
|
|
return DoCloseFile(&pSole->info);
|
|
}
|
|
|
|
#if 0
|
|
|
|
BOOL SoleReject (int cStrk, wchar_t wchDense)
|
|
{
|
|
RREAL *pNetMem, *pNetOut;
|
|
int iWinner, cOut;
|
|
int i;
|
|
|
|
if (cStrk == 1)
|
|
{
|
|
pNetMem = _alloca(s_iSoleRejNetSize1 * sizeof (*pNetMem));
|
|
if (!pNetMem)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < SOLE_NUM_FEATURES; i++)
|
|
{
|
|
pNetMem[i] = s_aSoleFeat[i];
|
|
}
|
|
|
|
pNetOut = runLocalConnectNet(&s_SoleRejNet1, pNetMem, &iWinner, &cOut);
|
|
|
|
ASSERT (cOut == SOLE_OUT_1);
|
|
|
|
return s_aaMap[0][iWinner] != LocRunDense2Unicode (&g_locRunInfo, wchDense);
|
|
}
|
|
else
|
|
{
|
|
pNetMem = _alloca(s_iSoleRejNetSize2 * sizeof (*pNetMem));
|
|
if (!pNetMem)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < SOLE_NUM_FEATURES; i++)
|
|
{
|
|
pNetMem[i] = s_aSoleFeat[i];
|
|
}
|
|
|
|
pNetOut = runLocalConnectNet(&s_SoleRejNet2, pNetMem, &iWinner, &cOut);
|
|
|
|
ASSERT (cOut == SOLE_OUT_2);
|
|
|
|
return s_aaMap[1][iWinner] != LocRunDense2Unicode (&g_locRunInfo, wchDense);
|
|
}
|
|
}
|
|
|
|
extern wchar_t s_wch;
|
|
|
|
void SaveRecoInfo (wchar_t wch)
|
|
{
|
|
static FILE *fp = NULL;
|
|
|
|
if (!fp)
|
|
{
|
|
fp = fopen ("recores.txt", "wt");
|
|
|
|
if (!fp)
|
|
return;
|
|
}
|
|
|
|
fprintf (fp, "%d\n", wch == s_wch);
|
|
fflush (fp);
|
|
}
|
|
|
|
#define JAWS_ALT 10
|
|
|
|
int GetCharID (int cStrk, wchar_t wch)
|
|
{
|
|
int i, cClass;
|
|
|
|
cClass = (cStrk == 1 ? SOLE_OUT_1 : SOLE_OUT_2);
|
|
|
|
for (i = 0; i < cClass; i++)
|
|
{
|
|
if (s_aaMap[cStrk - 1][i] == wch)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void SaveJAWSInfo (LATTICE *pLat)
|
|
{
|
|
static FILE *fpDisagree1 = NULL, *fpDisagree2 = NULL,
|
|
*fpSole1 = NULL, *fpSole2 = NULL;
|
|
|
|
FILE *fp, *fpSole;
|
|
int i, iWinningCand, iSoleBest, cAlt, j, cProbAlt, cStrk, cSoleOut, iClass;
|
|
LATTICE_ALT_LIST *pAlt;
|
|
int aSoleCost[JAWS_ALT];
|
|
wchar_t wch;
|
|
RECOG_ALT aProbAlt[JAWS_ALT];
|
|
BOXINFO box;
|
|
RECT bbox;
|
|
RECT rGuide;
|
|
GLYPH *pGlyph;
|
|
|
|
if (!pLat->fUseGuide)
|
|
return;
|
|
|
|
if (!fpDisagree1)
|
|
{
|
|
fpDisagree1 = fopen ("jawsdis1.txt", "wt");
|
|
|
|
if (!fpDisagree1)
|
|
return;
|
|
}
|
|
|
|
if (!fpDisagree2)
|
|
{
|
|
fpDisagree2 = fopen ("jawsdis2.txt", "wt");
|
|
|
|
if (!fpDisagree2)
|
|
return;
|
|
}
|
|
|
|
if (!fpSole1)
|
|
{
|
|
fpSole1 = fopen ("sole1.txt", "wt");
|
|
|
|
if (!fpSole1)
|
|
return;
|
|
}
|
|
|
|
if (!fpSole2)
|
|
{
|
|
fpSole2 = fopen ("sole2.txt", "wt");
|
|
|
|
if (!fpSole2)
|
|
return;
|
|
}
|
|
|
|
// find out if the right answer is in the list
|
|
iWinningCand =
|
|
iSoleBest = -1;
|
|
pAlt = pLat->pAltList + pLat->nStrokes - 1;
|
|
cAlt = pAlt->nUsed;
|
|
|
|
// This is a temporary call to get probs directly, until we have Hawk.
|
|
cProbAlt = GetProbsTsunami(pLat->nStrokes, pAlt, JAWS_ALT, aProbAlt);
|
|
|
|
// Convert strokes to GLYPHs and FRAMEs so that we can call the
|
|
// old code.
|
|
pGlyph = GlyphFromStrokes(pLat->nStrokes, pLat->pStroke);
|
|
if (!pGlyph)
|
|
{
|
|
return;
|
|
}
|
|
|
|
cStrk = pLat->nStrokes;
|
|
|
|
// Get the bounding box for the character
|
|
GetRectGLYPH(pGlyph, &bbox);
|
|
|
|
// Free the glyph structure.
|
|
DestroyFramesGLYPH(pGlyph);
|
|
DestroyGLYPH(pGlyph);
|
|
|
|
rGuide = GetGuideDrawnBox(&pLat->guide, pLat->pStroke[pLat->nStrokes - 1].iBox);
|
|
|
|
// Build up a BOXINFO structure from the guide, for use in the baseline/height scoring
|
|
box.size = rGuide.bottom - rGuide.top;
|
|
box.baseline = rGuide.bottom;
|
|
box.xheight = box.size / 2;
|
|
box.midline = box.baseline - box.xheight;
|
|
|
|
cSoleOut = (cStrk == 1 ? SOLE_OUT_1 : SOLE_OUT_2);
|
|
|
|
for (i = 0; i < cAlt && i < JAWS_ALT; i++)
|
|
{
|
|
wch = LocRunDense2Unicode(&g_locRunInfo, pAlt->alts[i].wChar);
|
|
|
|
if (wch == s_wch)
|
|
{
|
|
iWinningCand = i;
|
|
}
|
|
|
|
// sole cost
|
|
for (j = 0; j < cSoleOut; j++)
|
|
{
|
|
if (s_aaMap[cStrk -1][j] == wch)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j == cSoleOut)
|
|
{
|
|
aSoleCost[i] = 0xFFFF;
|
|
}
|
|
else
|
|
{
|
|
aSoleCost[i] = (0xFFFF * s_aSoleOut[j]) / SOFT_MAX_UNITY;
|
|
aSoleCost[i] = 0xFFFF - aSoleCost[i];
|
|
}
|
|
|
|
if (iSoleBest == -1 || aSoleCost[iSoleBest] > aSoleCost[i])
|
|
{
|
|
iSoleBest = i;
|
|
}
|
|
}
|
|
|
|
for (i = cAlt; i < JAWS_ALT; i++)
|
|
{
|
|
aSoleCost[i] = 0xFFFF;
|
|
}
|
|
|
|
if (iWinningCand == -1)
|
|
return;
|
|
|
|
if (cStrk == 1)
|
|
{
|
|
fpSole = fpSole1;
|
|
}
|
|
else
|
|
{
|
|
fpSole = fpSole2;
|
|
}
|
|
|
|
// do not run if sole and otter agree
|
|
if (!SoleReject (cStrk, pAlt->alts[0].wChar))
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (cStrk == 1)
|
|
{
|
|
fp = fpDisagree1;
|
|
}
|
|
else
|
|
{
|
|
fp = fpDisagree2;
|
|
}
|
|
}
|
|
|
|
fprintf (fp, "{ ");
|
|
|
|
// write the alternate features
|
|
for (i = 0; i < cAlt && i < JAWS_ALT; i++)
|
|
{
|
|
int iOttCost, iUni;
|
|
float fCost;
|
|
|
|
wch = LocRunDense2Unicode(&g_locRunInfo, pAlt->alts[i].wChar);
|
|
|
|
// otter cost
|
|
iOttCost = min (0xFFFF, (int)(-pAlt->alts[i].logProbPath * 1000));
|
|
fprintf (fp, "%d ", iOttCost);
|
|
|
|
// sole cost
|
|
fprintf (fp, "%d ", aSoleCost[i]);
|
|
|
|
// unigram cost
|
|
iUni = (int)(-255 * 100 * UnigramCost(&g_unigramInfo, pAlt->alts[i].wChar));
|
|
iUni = min (0xFFFF, iUni);
|
|
|
|
fprintf (fp, "%d ", iUni);
|
|
|
|
// baseline trans cost
|
|
fCost = BaselineTransitionCost(0, bbox, &box, pAlt->alts[i].wChar, bbox, &box);
|
|
fprintf (fp, "%d ", min (0xFFFF, (int) (-100000.0 * fCost)));
|
|
|
|
// base line cost
|
|
fCost = BaselineBoxCost(pAlt->alts[i].wChar, bbox, &box);
|
|
fprintf (fp, "%d ", min (0xFFFF, (int) (-100000.0 * fCost)));
|
|
|
|
// height trans cost
|
|
fCost = HeightTransitionCost(0, bbox, &box, pAlt->alts[i].wChar, bbox, &box);
|
|
fprintf (fp, "%d ", min (0xFFFF, (int) (-100000.0 * fCost)));
|
|
|
|
// height cost
|
|
fCost = HeightBoxCost(pAlt->alts[i].wChar, bbox, &box);
|
|
fprintf (fp, "%d ", min (0xFFFF, (int) (-100000.0 * fCost)));
|
|
|
|
// describe the codepoint
|
|
// is it a digit
|
|
if (wch >= L'0' && wch <= '9')
|
|
{
|
|
fprintf (fp, "65535 ");
|
|
}
|
|
else
|
|
{
|
|
fprintf (fp, "0 ");
|
|
}
|
|
|
|
// is alpha
|
|
if ((wch >= L'a' && wch <= 'z') || (wch >= L'A' && wch <= 'Z'))
|
|
{
|
|
fprintf (fp, "65535 ");
|
|
}
|
|
else
|
|
{
|
|
fprintf (fp, "0 ");
|
|
}
|
|
|
|
// is punct
|
|
if (iswpunct (wch))
|
|
{
|
|
fprintf (fp, "65535 ");
|
|
}
|
|
else
|
|
{
|
|
fprintf (fp, "0 ");
|
|
}
|
|
|
|
// is hiragana
|
|
if (wch >= 0x3040 && wch <= 0x309f)
|
|
{
|
|
fprintf (fp, "65535 ");
|
|
}
|
|
else
|
|
{
|
|
fprintf (fp, "0 ");
|
|
}
|
|
|
|
// is katakana
|
|
if (wch >= 0x30a0 && wch <= 0x30ff)
|
|
{
|
|
fprintf (fp, "65535 ");
|
|
}
|
|
else
|
|
{
|
|
fprintf (fp, "0 ");
|
|
}
|
|
|
|
// is kanji
|
|
if (wch >= 0x3190 && wch <= 0xabff)
|
|
{
|
|
fprintf (fp, "65535 ");
|
|
}
|
|
else
|
|
{
|
|
fprintf (fp, "0 ");
|
|
}
|
|
}
|
|
|
|
// the rest of the candidates
|
|
for (i = cAlt; i < JAWS_ALT; i++)
|
|
{
|
|
// otter cost
|
|
fprintf (fp, "%d ", 0xFFFF);
|
|
|
|
// sole cost
|
|
fprintf (fp, "%d ", 0xFFFF);
|
|
|
|
// unigram cost
|
|
fprintf (fp, "%d ", 0xFFFF);
|
|
|
|
// baseline trans cost
|
|
fprintf (fp, "%d ", 0xFFFF);
|
|
|
|
// baseline cost
|
|
fprintf (fp, "%d ", 0xFFFF);
|
|
|
|
// height trans cost
|
|
fprintf (fp, "%d ", 0xFFFF);
|
|
|
|
// height cost
|
|
fprintf (fp, "%d ", 0xFFFF);
|
|
|
|
// describe the codepoint
|
|
|
|
// digit
|
|
fprintf (fp, "0 ");
|
|
|
|
// is alpha
|
|
fprintf (fp, "0 ");
|
|
|
|
// is punct
|
|
fprintf (fp, "0 ");
|
|
|
|
// is hiragana
|
|
fprintf (fp, "0 ");
|
|
|
|
// is katakana
|
|
fprintf (fp, "0 ");
|
|
|
|
// is kanji
|
|
fprintf (fp, "0 ");
|
|
}
|
|
|
|
// write sole features for fpSole
|
|
if (iWinningCand != 0)
|
|
{
|
|
iClass = GetCharID (cStrk, s_wch);
|
|
|
|
if (iClass >= 0)
|
|
{
|
|
fprintf (fpSole, "{ ");
|
|
|
|
for (i = 0; i < SOLE_NUM_FEATURES; i++)
|
|
{
|
|
fprintf (fpSole, "%d ", s_aSoleFeat[i]);
|
|
}
|
|
|
|
fprintf (fpSole, "} { %d }\n", iClass);
|
|
|
|
fflush (fpSole);
|
|
}
|
|
}
|
|
|
|
fprintf (fp, "} { %d }\n", iWinningCand);
|
|
|
|
|
|
fflush (fp);
|
|
fflush (fpSole);
|
|
}
|
|
|
|
void RunJaws (LATTICE *pLat, HWXRESULTS *rgRes)
|
|
{
|
|
int i, iSoleBest, cAlt, j, cFeat, k, iWinningCand, iOttBest;
|
|
LATTICE_ALT_LIST *pAlt;
|
|
int aSoleCost[JAWS_ALT], aIdx[JAWS_ALT], iWinner, cOut, cStrk, cSoleOut;
|
|
wchar_t wch, awch[JAWS_ALT];
|
|
RREAL *pJawsNetMem, *pJawsNetOut;
|
|
float fCost;
|
|
BOXINFO box;
|
|
RECT bbox;
|
|
RECT rGuide;
|
|
GLYPH *pGlyph;
|
|
|
|
pJawsNetMem = _alloca(s_iJawsNetSize * sizeof (*pJawsNetMem));
|
|
if (!pJawsNetMem)
|
|
return;
|
|
|
|
// featurize for JAWS
|
|
iOttBest =
|
|
iWinningCand =
|
|
iSoleBest = -1;
|
|
pAlt = pLat->pAltList + pLat->nStrokes - 1;
|
|
cAlt = pAlt->nUsed;
|
|
|
|
// Convert strokes to GLYPHs and FRAMEs so that we can call the
|
|
// old code.
|
|
pGlyph = GlyphFromStrokes(pLat->nStrokes, pLat->pStroke);
|
|
if (!pGlyph)
|
|
{
|
|
return;
|
|
}
|
|
|
|
cStrk = CframeGLYPH (pGlyph);
|
|
|
|
// do not run if sole/reject and otter agree
|
|
if (!SoleReject (cStrk, pAlt->alts[0].wChar))
|
|
return;
|
|
|
|
|
|
// Get the bounding box for the character
|
|
GetRectGLYPH(pGlyph, &bbox);
|
|
|
|
// Free the glyph structure.
|
|
DestroyFramesGLYPH(pGlyph);
|
|
DestroyGLYPH(pGlyph);
|
|
|
|
rGuide = GetGuideDrawnBox(&pLat->guide, pLat->pStroke[pLat->nStrokes - 1].iBox);
|
|
|
|
// Build up a BOXINFO structure from the guide, for use in the baseline/height scoring
|
|
box.size = rGuide.bottom - rGuide.top;
|
|
box.baseline = rGuide.bottom;
|
|
box.xheight = box.size / 2;
|
|
box.midline = box.baseline - box.xheight;
|
|
|
|
cSoleOut = (cStrk == 1 ? SOLE_OUT_1 : SOLE_OUT_2);
|
|
|
|
for (i = 0; i < cAlt && i < JAWS_ALT; i++)
|
|
{
|
|
wch = LocRunDense2Unicode(&g_locRunInfo, pAlt->alts[i].wChar);
|
|
|
|
if (wch == s_wch)
|
|
{
|
|
iWinningCand = i;
|
|
}
|
|
|
|
// sole cost
|
|
for (j = 0; j < cSoleOut; j++)
|
|
{
|
|
if (s_aaMap[cStrk - 1][j] == wch)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j == cSoleOut)
|
|
{
|
|
aSoleCost[i] = 0xFFFF;
|
|
}
|
|
else
|
|
{
|
|
aSoleCost[i] = (0xFFFF * s_aSoleOut[j]) / SOFT_MAX_UNITY;
|
|
aSoleCost[i] = 0xFFFF - aSoleCost[i];
|
|
}
|
|
|
|
if (iSoleBest == -1 || aSoleCost[iSoleBest] > aSoleCost[i])
|
|
{
|
|
iSoleBest = i;
|
|
}
|
|
|
|
if (iOttBest == -1 || pAlt->alts[iOttBest].logProbPath < pAlt->alts[i].logProbPath)
|
|
{
|
|
iOttBest = i;
|
|
}
|
|
}
|
|
|
|
for (i = cAlt; i < JAWS_ALT; i++)
|
|
{
|
|
aSoleCost[i] = 0xFFFF;
|
|
}
|
|
|
|
// for all candidates
|
|
cFeat = 0;
|
|
for (i = 0; i < cAlt && i < JAWS_ALT; i++)
|
|
{
|
|
int iOttCost, iUni;
|
|
|
|
wch = LocRunDense2Unicode(&g_locRunInfo, pAlt->alts[i].wChar);
|
|
|
|
// otter cost
|
|
iOttCost = min (0xFFFF, (int)(-pAlt->alts[i].logProb * 1000));
|
|
pJawsNetMem[cFeat++] = iOttCost;
|
|
|
|
// sole cost
|
|
pJawsNetMem[cFeat++] = aSoleCost[i];
|
|
|
|
// unigram cost
|
|
iUni = (int)(-255 * 100 * UnigramCost(&g_unigramInfo, pAlt->alts[i].wChar));
|
|
pJawsNetMem[cFeat++] = iUni;
|
|
|
|
// baseline trans cost
|
|
fCost = BaselineTransitionCost(0, bbox, &box, pAlt->alts[i].wChar, bbox, &box);
|
|
pJawsNetMem[cFeat++] = min (0xFFFF, (int) (-100000.0 * fCost));
|
|
|
|
// base line cost
|
|
fCost = BaselineBoxCost(pAlt->alts[i].wChar, bbox, &box);
|
|
pJawsNetMem[cFeat++] = min (0xFFFF, (int) (-100000.0 * fCost));
|
|
|
|
// height trans cost
|
|
fCost = HeightTransitionCost(0, bbox, &box, pAlt->alts[i].wChar, bbox, &box);
|
|
pJawsNetMem[cFeat++] = min (0xFFFF, (int) (-100000.0 * fCost));
|
|
|
|
// height cost
|
|
fCost = HeightBoxCost(pAlt->alts[i].wChar, bbox, &box);
|
|
pJawsNetMem[cFeat++] = min (0xFFFF, (int) (-100000.0 * fCost));
|
|
|
|
// describe the codepoint
|
|
|
|
// digit
|
|
if (wch >= L'0' && wch <= '9')
|
|
{
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
}
|
|
else
|
|
{
|
|
pJawsNetMem[cFeat++] = 0;
|
|
}
|
|
|
|
// is alpha
|
|
if ((wch >= L'a' && wch <= 'z') || (wch >= L'A' && wch <= 'Z'))
|
|
{
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
}
|
|
else
|
|
{
|
|
pJawsNetMem[cFeat++] = 0;
|
|
}
|
|
|
|
// is punct
|
|
if (iswpunct (wch))
|
|
{
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
}
|
|
else
|
|
{
|
|
pJawsNetMem[cFeat++] = 0;
|
|
}
|
|
|
|
// is hiragana
|
|
if (wch >= 0x3040 && wch <= 0x309f)
|
|
{
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
}
|
|
else
|
|
{
|
|
pJawsNetMem[cFeat++] = 0;
|
|
}
|
|
|
|
// is katakana
|
|
if (wch >= 0x30a0 && wch <= 0x30ff)
|
|
{
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
}
|
|
else
|
|
{
|
|
pJawsNetMem[cFeat++] = 0;
|
|
}
|
|
|
|
// is kanji
|
|
if (wch >= 0x3190 && wch <= 0xabff)
|
|
{
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
}
|
|
else
|
|
{
|
|
pJawsNetMem[cFeat++] = 0;
|
|
}
|
|
}
|
|
|
|
// the rest of the candidates
|
|
for (i = cAlt; i < JAWS_ALT; i++)
|
|
{
|
|
// otter cost
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
|
|
// sole cost
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
|
|
// unigram cost
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
|
|
// baseline trans cost
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
|
|
// baseline cost
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
|
|
// height trans cost
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
|
|
// height cost
|
|
pJawsNetMem[cFeat++] = 65535;
|
|
|
|
// describe the codepoint
|
|
|
|
// digit
|
|
pJawsNetMem[cFeat++] = 0;
|
|
|
|
// is alpha
|
|
pJawsNetMem[cFeat++] = 0;
|
|
|
|
// is punct
|
|
pJawsNetMem[cFeat++] = 0;
|
|
|
|
// is hiragana
|
|
pJawsNetMem[cFeat++] = 0;
|
|
|
|
// is katakana
|
|
pJawsNetMem[cFeat++] = 0;
|
|
|
|
// is kanji
|
|
pJawsNetMem[cFeat++] = 0;
|
|
}
|
|
|
|
// call the JAWS net
|
|
pJawsNetOut = runLocalConnectNet(&s_JawsNet, pJawsNetMem, &iWinner, &cOut);
|
|
|
|
ASSERT (cOut == JAWS_ALT);
|
|
|
|
// index the result
|
|
for (i = 0; i < JAWS_ALT; i++)
|
|
{
|
|
aIdx[i] = i;
|
|
}
|
|
|
|
for (i = 0; i < JAWS_ALT; i++)
|
|
{
|
|
for (j = i + 1; j < JAWS_ALT; j++)
|
|
{
|
|
if (pJawsNetOut[aIdx[i]] < pJawsNetOut[aIdx[j]])
|
|
{
|
|
k = aIdx[i];
|
|
aIdx[i] = aIdx[j];
|
|
aIdx[j] = k;
|
|
}
|
|
}
|
|
|
|
awch[i] = pAlt->alts[aIdx[i]].wChar;
|
|
}
|
|
|
|
for (i = 0; i < cAlt && i < JAWS_ALT; i++)
|
|
{
|
|
rgRes->rgChar[i] = LocRunDense2Unicode(&g_locRunInfo, awch[i]);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if 0
|
|
LOCAL_NET *LoadNet(void *pData, int *piNetSize, LOCAL_NET *pNet)
|
|
{
|
|
if ( !pData || !(pNet = restoreLocalConnectNet(pData, 0, pNet)) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
(*piNetSize) = getRunTimeNetMemoryRequirements(pData);
|
|
|
|
if ((*piNetSize) <= 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return pNet;
|
|
}
|
|
|
|
BOOL LoadSoleFromFile(wchar_t *pwszPath)
|
|
{
|
|
BYTE *pData;
|
|
wchar_t aPath[128];
|
|
HANDLE hFile, hMap;
|
|
|
|
// Generate path to file. By passing in name as "locale" we can get FormatPath
|
|
// to do what we want.
|
|
wsprintf (aPath, L"%s\\sole1.bin", pwszPath);
|
|
|
|
// Try to open the file.
|
|
hFile = CreateMappingCall (aPath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Create a mapping handle
|
|
hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
if (hMap == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Map the entire file starting at the first byte
|
|
pData = (void *) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
|
if (pData == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Sole net
|
|
if (!LoadNet(pData, &s_iSoleNetSize1, &s_SoleNet1))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Generate path to file. By passing in name as "locale" we can get FormatPath
|
|
// to do what we want.
|
|
wsprintf (aPath, L"%s\\sole2.bin", pwszPath);
|
|
|
|
// Try to open the file.
|
|
hFile = CreateMappingCall (aPath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Create a mapping handle
|
|
hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
if (hMap == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Map the entire file starting at the first byte
|
|
pData = (void *) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
|
if (pData == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Sole net
|
|
if (!LoadNet(pData, &s_iSoleNetSize2, &s_SoleNet2))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Generate path to file. By passing in name as "locale" we can get FormatPath
|
|
// to do what we want.
|
|
wsprintf (aPath, L"%s\\solerej1.bin", pwszPath);
|
|
|
|
// Try to open the file.
|
|
hFile = CreateMappingCall (aPath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Create a mapping handle
|
|
hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
if (hMap == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Map the entire file starting at the first byte
|
|
pData = (void *) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
|
if (pData == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Sole net
|
|
if (!LoadNet(pData, &s_iSoleRejNetSize1, &s_SoleRejNet1))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Generate path to file. By passing in name as "locale" we can get FormatPath
|
|
// to do what we want.
|
|
wsprintf (aPath, L"%s\\solerej2.bin", pwszPath);
|
|
|
|
// Try to open the file.
|
|
hFile = CreateMappingCall (aPath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Create a mapping handle
|
|
hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
if (hMap == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Map the entire file starting at the first byte
|
|
pData = (void *) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
|
if (pData == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Sole net
|
|
if (!LoadNet(pData, &s_iSoleRejNetSize2, &s_SoleRejNet2))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
|
|
BOOL LoadJawsFromFile(wchar_t *pwszPath)
|
|
{
|
|
BYTE *pData;
|
|
wchar_t aPath[128];
|
|
HANDLE hFile, hMap;
|
|
|
|
// Generate path to file. By passing in name as "locale" we can get FormatPath
|
|
// to do what we want.
|
|
wsprintf (aPath, L"%s\\jaws.bin", pwszPath);
|
|
|
|
// Try to open the file.
|
|
hFile = CreateMappingCall (aPath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Create a mapping handle
|
|
hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
if (hMap == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Map the entire file starting at the first byte
|
|
pData = (void *) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
|
if (pData == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Jaws net
|
|
if (!LoadNet(pData, &s_iJawsNetSize, &s_JawsNet))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|