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

2685 lines
60 KiB

  1. // Code for Sole shape match function
  2. #include <limits.h>
  3. #include <stdio.h>
  4. #include "common.h"
  5. #include "cheby.h"
  6. #include "cowmath.h"
  7. #include "math16.h"
  8. #include "volcanop.h"
  9. #include "runNet.h"
  10. #include "nnet.h"
  11. GLYPH * GlyphFromStrokes(UINT cStrokes, STROKE *pStrokes);
  12. RECT GetGuideDrawnBox(HWXGUIDE *guide, int iBox);
  13. #define ABS(x) (((x) > 0) ? (x) : -(x))
  14. #define MAX(x, y) (((x) > (y)) ? (x) : (y))
  15. #define MIN(x, y) (((x) < (y)) ? (x) : (y))
  16. #define SIGN(x) ((x) > 0 ? 1 : ((x) < 0 ? -1 : 0))
  17. #define PEN_UP_VALUE LSHFT(-1)
  18. #define PEN_DOWN_VALUE LSHFT(1)
  19. #define XCHB 10
  20. #define YCHB 10
  21. #define ZCHB 10
  22. #define MAXTMP 3
  23. // assumption: GRIDSIZE <= 256 //This is beacuse the pointers to the mapped values are defined as Byte pointers
  24. #define GRIDSIZE 32
  25. typedef struct
  26. {
  27. int *xy; //Stores the sampled XY points
  28. int *z; //Stores the z points--values are either PEN_UP or PEN_DOWN
  29. int cPoint;//Stores the number of points that are there
  30. int cPointMax; //Stores the max number of points that can be allocated
  31. int iStepSize; //Stores the length of the step size--at present this is taken to be 1.5 % of the guide size
  32. int iStepSizeSqr; //Stroes the square of the step size
  33. } POINTINFO;
  34. //This macro is used for seeing if two points are neighbours to one another--ie the distance between them <=1
  35. #define Neighbor(a, b) ((a)-(b) < 2 && (b)-(a) < 2)
  36. static int
  37. solve2(int m[][(IMAXCHB+1)/2], int c[], int n)
  38. {
  39. int i, j, k, t, tmp;
  40. for (i=0; i<n; ++i)
  41. {
  42. t = m[i][i];
  43. // punt:
  44. if (t == 0)
  45. {
  46. memset(c, 0, n*sizeof(*c));
  47. return 0;
  48. }
  49. for (j=0; j<n; ++j)
  50. m[i][j] = Div16(m[i][j], t);
  51. c[i] = Div16(c[i], t);
  52. for (k=i+1; k<n; ++k)
  53. {
  54. t = m[k][i];
  55. for (j=0; j<n; ++j)
  56. {
  57. Mul16(t, m[i][j], tmp)
  58. m[k][j] -= tmp;
  59. }
  60. Mul16(t, c[i], tmp)
  61. c[k] -= tmp;
  62. }
  63. }
  64. for (i=(n-1); i>=0; --i)
  65. {
  66. for (k=i-1; k>=0; --k)
  67. {
  68. t = m[k][i];
  69. for (j=0; j<n; ++j)
  70. {
  71. Mul16(t, m[i][j], tmp)
  72. m[k][j] -= tmp;
  73. }
  74. Mul16(t, c[i], tmp)
  75. c[k] -= tmp;
  76. }
  77. }
  78. return 1;
  79. }
  80. static int
  81. solve(int m[IMAXCHB][IMAXCHB], int *c, int n)
  82. {
  83. int i, j, i2, j2;
  84. int mEven[((IMAXCHB+1))/2][((IMAXCHB+1)/2)];
  85. int mOdd[(IMAXCHB/2)][((IMAXCHB+1)/2)]; // # of cols is bigger than needed so that solve2() works
  86. int cEven[((IMAXCHB+1)/2)];
  87. int cOdd[(IMAXCHB/2)];
  88. for (i = 0, i2 = 0; i2 < n; ++i, i2 += 2)
  89. {
  90. for (j = 0, j2 = 0; j2 < n; ++j, j2 += 2)
  91. {
  92. mEven[i][j] = m[i2][j2];
  93. }
  94. cEven[i] = c[i2];
  95. }
  96. for (i = 0, i2 = 1; i2 < n; ++i, i2 += 2)
  97. {
  98. for (j = 0, j2 = 1; j2 < n; ++j, j2 += 2)
  99. {
  100. mOdd[i][j] = m[i2][j2];
  101. }
  102. cOdd[i] = c[i2];
  103. }
  104. if (!solve2(mEven, cEven, (n+1)/2)) return 0;
  105. if (!solve2(mOdd, cOdd, n/2)) return 0;
  106. for (i = 0, i2 = 0; i2 < n; ++i, i2 += 2)
  107. {
  108. c[i2] = cEven[i];
  109. }
  110. for (i = 0, i2 = 1; i2 < n; ++i, i2 += 2)
  111. {
  112. c[i2] = cOdd[i];
  113. }
  114. return 1;
  115. }
  116. // Assumptions:
  117. // c has size atleast cfeats
  118. // cfeats is at most IMAXCHB
  119. // c is uninitialized
  120. int LSCheby(int* y, int n, int *c, int cfeats)
  121. {
  122. int i, j, t, x, dx, n2, nMin;
  123. int meanGuess, tmp;
  124. int T[IMAXCHB], m[IMAXCHB][IMAXCHB];
  125. if (cfeats > IMAXCHB || cfeats <= 0)
  126. return 0;
  127. memset(c, 0, cfeats*sizeof(*c));
  128. n2 = n+n;
  129. nMin = cfeats + 2;
  130. if (n < nMin && n > 4)
  131. {
  132. cfeats = n - 2;
  133. nMin = cfeats + 2;
  134. }
  135. if (n < nMin) // approximate the stroke by a straight line
  136. {
  137. *c++ = (y[0] + y[n2-2]) >> 1;
  138. *c = (y[n2-2] - y[0]) >> 1;
  139. return 2;
  140. }
  141. memset(T, 0, sizeof(T));
  142. memset(m, 0, sizeof(m));
  143. meanGuess = y[0];
  144. x = LSHFT(-1);
  145. dx = LSHFT(2)/(n-1);
  146. for (t = 0; t < n2; t += 2)
  147. {
  148. T[0] = LSHFT(1);
  149. T[1] = x;
  150. for (i = 2; i < cfeats; ++i)
  151. {
  152. Mul16(x, T[i-1], tmp)
  153. T[i] = (tmp<<1) - T[i-2];
  154. }
  155. for (i = 0; i < cfeats; ++i)
  156. {
  157. for (j = 0; j < cfeats; ++j)
  158. {
  159. Mul16(T[i], T[j], tmp)
  160. m[i][j] += tmp;
  161. }
  162. Mul16(T[i], y[t] - meanGuess, tmp)
  163. c[i] += tmp;
  164. //c[i] += T[i]*(y[t] - meanGuess);
  165. }
  166. x += dx;
  167. }
  168. if (!solve(m, c, cfeats))
  169. return 0;
  170. c[0] += meanGuess;
  171. return cfeats;
  172. }
  173. int ISqrt(int x)
  174. {
  175. int n, lastN;
  176. if (x <= 0)
  177. return 0;
  178. if (x==1)
  179. return 1;
  180. n = x >> 1;
  181. do
  182. {
  183. lastN = n;
  184. n = (n + x/n) >> 1;
  185. }
  186. while (n < lastN);
  187. return n;
  188. }
  189. /******************************Public*Routine******************************\
  190. * YDeviation
  191. *
  192. * Function to compute average deviation of the y values in a sequence of
  193. * strokes (frames).
  194. * This is not exactly standard deviation. But it is a lot cheaper and
  195. * close enough. (See analysis and comments in Numerical Recipes in C).
  196. *
  197. * History:
  198. * 02-Sep-1999 -by- Angshuman Guha aguha
  199. * Wrote it.
  200. \**************************************************************************/
  201. int YDeviation(GLYPH *pGlyph)
  202. {
  203. int count, ymin, sum, ymean;
  204. GLYPH *glyph;
  205. FRAME *frame;
  206. if (pGlyph && pGlyph->frame && RgrawxyFRAME(pGlyph->frame))
  207. {
  208. ymin = RgrawxyFRAME(pGlyph->frame)->y;
  209. count = 0;
  210. sum = 0;
  211. }
  212. else
  213. return 1;
  214. // find min and mean in one scan
  215. for (glyph=pGlyph; glyph; glyph=glyph->next)
  216. {
  217. XY *xy;
  218. int cxy;
  219. frame = glyph->frame;
  220. xy = RgrawxyFRAME(frame);
  221. cxy = CrawxyFRAME(frame);
  222. for (; cxy; xy++, cxy--)
  223. {
  224. int y;
  225. y = xy->y;
  226. if (y < ymin)
  227. {
  228. sum += count*(ymin - y);
  229. ymin = y;
  230. }
  231. sum += y - ymin;
  232. count++;
  233. }
  234. }
  235. ASSERT(count > 0);
  236. ymean = sum/count + ymin;
  237. // find average deviation
  238. sum = 0;
  239. for (glyph=pGlyph; glyph; glyph=glyph->next)
  240. {
  241. XY *xy;
  242. int cxy;
  243. frame = glyph->frame;
  244. xy = RgrawxyFRAME(frame);
  245. cxy = CrawxyFRAME(frame);
  246. for (; cxy; xy++, cxy--)
  247. {
  248. int diff;
  249. diff = xy->y - ymean;
  250. if (diff < 0)
  251. sum -= diff;
  252. else
  253. sum += diff;
  254. }
  255. }
  256. sum = sum/count;
  257. if (sum < 1)
  258. sum = 1;
  259. return sum;
  260. }
  261. /******************************Private*Routine******************************\
  262. * AddPoint
  263. *
  264. * Given a new point and a sequence of points so far, zero or more points
  265. * are added at the end of the sequence. The points are effectively resampled
  266. * at a pre-computed interval (a distance of pPointinfo->iStepSize between
  267. * successive points). This function also effectively does a linear interpolation
  268. * of a pen-upstroke between the last point of a pen-down stroke and the first
  269. * point of the next pen-down stroke.
  270. *
  271. * History:
  272. * 26-Sep-1997 -by- Angshuman Guha aguha
  273. Explanation of parameters
  274. pPointInfo--The pointer to the Point Info structure--The points are added to the xy array of this structure
  275. x--The x coordinate of the point to be added
  276. y--The y coordinate of the point to be added
  277. bFirstPointOfStroke--If true indicates that this is the first point of the stroke
  278. Else it it not the first point
  279. * Wrote this comment.Addtional comments added by Mango
  280. \**************************************************************************/
  281. BOOL AddPoint(POINTINFO *pPointinfo, int x, int y, int bFirstPointOfStroke)
  282. {
  283. int dx, dy, dist2, dist;
  284. int bChangeLastPoint, x0, y0, zval;
  285. int *pTemp;
  286. if (!pPointinfo->cPoint)
  287. {
  288. pPointinfo->xy[0] = x;
  289. pPointinfo->xy[1] = y;
  290. pPointinfo->z[0] = PEN_DOWN_VALUE;
  291. pPointinfo->cPoint = 1;
  292. return TRUE;
  293. }
  294. bChangeLastPoint = 0;
  295. x0 = pPointinfo->xy[2*pPointinfo->cPoint-2];
  296. y0 = pPointinfo->xy[2*pPointinfo->cPoint-1];
  297. zval = bFirstPointOfStroke ? PEN_UP_VALUE : PEN_DOWN_VALUE;
  298. for (;;)
  299. {
  300. dx = x - x0;
  301. dy = y - y0;
  302. dist2 = dx*dx + dy*dy;
  303. if (dist2 < pPointinfo->iStepSizeSqr)
  304. break;
  305. // add a point at given step size
  306. dist = ISqrt(dist2);
  307. x0 += pPointinfo->iStepSize*dx/dist;
  308. y0 += pPointinfo->iStepSize*dy/dist;
  309. // a minimum iStepSize of 2 and the fact that ((float)dx/dist)^2 + ((float)dy/dist)^2 = 1 guarantees that
  310. // the previous two assignments change atleast one of x0 and y0 i.e. its not an infinite loop
  311. if (pPointinfo->cPoint == pPointinfo->cPointMax)
  312. {
  313. // need more space
  314. // hopefully we don't come here too often
  315. pPointinfo->cPointMax *= 2;
  316. pTemp = (int *) ExternRealloc(pPointinfo->xy, 2*pPointinfo->cPointMax*sizeof(int));
  317. if (!pTemp)
  318. {
  319. return FALSE;
  320. }
  321. pPointinfo->xy = pTemp;
  322. pTemp = (int *) ExternRealloc(pPointinfo->z, 2*pPointinfo->cPointMax*sizeof(int));
  323. if (!pTemp)
  324. {
  325. return FALSE;
  326. }
  327. pPointinfo->z = pTemp;
  328. }
  329. pPointinfo->xy[2*pPointinfo->cPoint] = x0;
  330. pPointinfo->xy[2*pPointinfo->cPoint+1] = y0;
  331. pPointinfo->z[2*pPointinfo->cPoint] = zval;
  332. pPointinfo->cPoint++;
  333. bChangeLastPoint = bFirstPointOfStroke;
  334. }
  335. // if we have interpolated from the last point of a stroke to the first point of another
  336. if (bChangeLastPoint)
  337. {
  338. ASSERT(pPointinfo->z[2*pPointinfo->cPoint - 2] == PEN_UP_VALUE);
  339. pPointinfo->z[2*pPointinfo->cPoint - 2] = PEN_DOWN_VALUE;
  340. }
  341. // this last "if" could be changed to a test on bFirstPointOfStroke and the assert can be removed
  342. return TRUE;
  343. }
  344. /******************************Private*Routine******************************\
  345. * AddGuideFeatures
  346. *
  347. * Given a piece of ink in a box, compute five features related to the size
  348. * and position of ink in the box.
  349. * The five features are:-
  350. * //First feature--Top of the ink relative to the guide box height
  351. * //Second feature--Width of the ink relative to the guide box width
  352. * //Third feature--Bottom of the ink relative to the guide box height
  353. * //Fourth feature--The width of the ink relative to the sum of its width and height
  354. * //Fifth feature--the iYMean value relative to the guide box height
  355. *
  356. * History:
  357. * 26-Sep-1997 -by- Angshuman Guha aguha
  358. * Wrote this comment.
  359. \**************************************************************************/
  360. int AddGuideFeatures(RECT *pGuide, RECT *pRect, int iYMean, unsigned short *rgFeat)
  361. {
  362. // get normalized ink size/position (box is 1000x1000 with top-left at 0,0)
  363. DRECTS drcs;
  364. RECT inkRect = *pRect;
  365. int x;
  366. //The x coordinate of the top left corner of the current box(note--you are adding the cxBase Value)
  367. drcs.x = pGuide->left;
  368. //The y coordinate of the top left corner of the current box
  369. drcs.y = pGuide->top;
  370. //This gives us the width of the current guide box
  371. drcs.w = pGuide->right - pGuide->left;
  372. //This gives us the height of the current guide box
  373. drcs.h = pGuide->bottom - pGuide->top;
  374. // Translate, convert to delta form
  375. //Stores the relative position w.rt. the top left of the guide box
  376. inkRect.left -= drcs.x;
  377. inkRect.top -= drcs.y;
  378. //Stores the width of the ink
  379. inkRect.right -= (drcs.x + inkRect.left);
  380. //Stores the height of the ink
  381. inkRect.bottom -= (drcs.y + inkRect.top);
  382. //Converts the yMean wrt a form relative to the top of the guide box
  383. iYMean -= drcs.y;
  384. // Scale. We do isotropic scaling and center the shorter dimension.
  385. //Y Mean as a fraction of the guide box size
  386. iYMean = ((1000 * iYMean) / drcs.h);
  387. //Sees where the top of the ink is relative to the guide box height
  388. drcs.y = ((1000 * inkRect.top) / drcs.h);
  389. //The width of the ink relative to the guide box width
  390. drcs.w = ((1000 * inkRect.right) / drcs.h);
  391. //The height of the ink relative to the guide box height
  392. drcs.h = ((1000 * inkRect.bottom) / drcs.h);
  393. //Why would any of these conditions happen
  394. if (drcs.y < 0)
  395. drcs.y = 0;
  396. else if (drcs.y > 1000)
  397. drcs.y = 1000;
  398. if (drcs.w < 0)
  399. drcs.w = 0;
  400. else if (drcs.w > 1000)
  401. drcs.w = 1000;
  402. if (drcs.h < 0)
  403. drcs.h = 0;
  404. else if (drcs.h > 1000)
  405. drcs.h = 1000;
  406. // 4 guide features
  407. //First feature--Top of the ink relative to the guide box height
  408. x = drcs.y;
  409. x = LSHFT(x)/1000;
  410. if (x >= 0x10000)
  411. x = 0xFFFF;
  412. *rgFeat++ = (unsigned short)x;
  413. //Second feature--Width of the ink relative to the guide box width
  414. x = drcs.w;
  415. x = LSHFT(x)/1000;
  416. if (x >= 0x10000)
  417. x = 0xFFFF;
  418. *rgFeat++ = (unsigned short)x;
  419. //Third feature--Bottom of the ink relative to the guide box height
  420. x = drcs.h;
  421. x = LSHFT(x)/1000;
  422. if (x >= 0x10000)
  423. x = 0xFFFF;
  424. *rgFeat++ = (unsigned short)x;
  425. //Fourth feature--The width of the ink relative to the sum of its width and height
  426. if (drcs.w <= 0)
  427. x = 0;
  428. else
  429. {
  430. x = drcs.w;
  431. x = LSHFT(x)/(drcs.w+drcs.h);
  432. if (x >= 0x10000)
  433. x = 0xFFFF;
  434. }
  435. *rgFeat++ = (unsigned short)x;
  436. //Fifth feature--the iYMean value relative to the guide box height
  437. // one more guide feature: y-CG
  438. if (iYMean < 0)
  439. iYMean = 0;
  440. else if (iYMean > 1000)
  441. iYMean = 1000;
  442. x = iYMean;
  443. x = LSHFT(x)/1000;
  444. if (x >= 0x10000)
  445. x = 0xFFFF;
  446. *rgFeat = (unsigned short)x;
  447. return 5;
  448. }
  449. /******************************Private*Routine******************************\
  450. * SmoothPoints
  451. *
  452. * Given an array of points and a destination array, this function fills the
  453. * destination array a smoothed version of the raw points. Smoothing is
  454. * done by local averaging ona window of 5 points with weights 1/8 1/4 1/4 1/4 1/8.
  455. *
  456. * History:
  457. * 26-Sep-1997 -by- Angshuman Guha aguha
  458. * Wrote this comment.
  459. \**************************************************************************/
  460. void SmoothPoints(XY *rgSrc, XY *rgDst, int cXY)
  461. {
  462. int i,j;
  463. for (i=0; i<cXY; i++)
  464. {
  465. j = cXY - i - 1;
  466. if (i < j)
  467. j = i;
  468. switch (j)
  469. {
  470. case 0:
  471. case 1:
  472. *rgDst = *rgSrc;
  473. break;
  474. //+4 is added here so that rounding off takes place rather than truncation
  475. default:
  476. rgDst->x = (int)((
  477. (rgSrc-2)->x +
  478. ((rgSrc-1)->x <<1) +
  479. (rgSrc->x <<1) +
  480. ((rgSrc+1)->x <<1) +
  481. (rgSrc+2)->x +
  482. 4
  483. ) >> 3);
  484. rgDst->y = (int)((
  485. (rgSrc-2)->y +
  486. ((rgSrc-1)->y <<1) +
  487. (rgSrc->y <<1) +
  488. ((rgSrc+1)->y <<1) +
  489. (rgSrc+2)->y +
  490. 4
  491. ) >> 3);
  492. break;
  493. }
  494. rgSrc++;
  495. rgDst++;
  496. }
  497. }
  498. /******************************Private*Routine******************************\
  499. * ComputeCurvedness
  500. *
  501. * Given a stroke, computes three curvature features--namely
  502. *--The sum of the modular change in angle with respect to + and - for the angles
  503. *--The total curviness of the stoke--just directly measure the change in angles.
  504. *--The maximum change in angle that occurs in that stroke in one sampling distance
  505. *
  506. * History:
  507. * 26-Sep-1997 -by- Angshuman Guha aguha
  508. * Wrote this comment.
  509. \**************************************************************************/
  510. void ComputeCurvedness(XY *rgXY, int cXY, int iStepSizeSqr, int *pSum1, int *pSum2, int *pMaxAngle)
  511. {
  512. int sum1, sum2;
  513. int x, y;
  514. XY *rgxy, *rgxySave;
  515. int ang, lastAng, diff, dx, dy;
  516. if (cXY <= 2)
  517. {
  518. *pSum1 = *pSum2 = 0;
  519. return;
  520. }
  521. // smooth points
  522. rgxySave = rgxy = (XY *) ExternAlloc(cXY*sizeof(XY));
  523. if (!rgxy)
  524. {
  525. *pSum1 = *pSum2 = 0;
  526. return;
  527. }
  528. SmoothPoints(rgXY, rgxy, cXY);
  529. sum1 = sum2 = 0;
  530. x = rgxy->x;
  531. y = rgxy->y;
  532. rgxy++;
  533. cXY--;
  534. // find first angle
  535. while (cXY)
  536. {
  537. dy = rgxy->y - y;
  538. dx = rgxy->x - x;
  539. if (dx*dx+dy*dy >= iStepSizeSqr)
  540. {
  541. //Function from common/mathx--returns the integer approx in degrees
  542. lastAng = Arctan2(dy, dx);
  543. cXY--;
  544. x = rgxy->x;
  545. y = rgxy->y;
  546. rgxy++;
  547. break;
  548. }
  549. cXY--;
  550. rgxy++;
  551. }
  552. // now find difference of every subsequent angle with its previous angle
  553. while (cXY)
  554. {
  555. dy = rgxy->y - y;
  556. dx = rgxy->x - x;
  557. if (dx*dx+dy*dy >= iStepSizeSqr)
  558. {
  559. ang = Arctan2(dy, dx);
  560. ANGLEDIFF(lastAng, ang, diff)
  561. sum1 += diff;
  562. if (diff < 0)
  563. diff = -diff;
  564. sum2 += diff;
  565. lastAng = ang;
  566. x = rgxy->x;
  567. y = rgxy->y;
  568. if (diff > *pMaxAngle)
  569. *pMaxAngle = diff;
  570. }
  571. cXY--;
  572. rgxy++;
  573. }
  574. // clean up
  575. ExternFree(rgxySave);
  576. *pSum1 = sum1;
  577. *pSum2 = sum2;
  578. }
  579. /******************************Private*Routine******************************\
  580. * AddCurveFeatures
  581. *
  582. * Given an ink (one or more strokes), computes three curvature features.
  583. *
  584. * History:
  585. * 26-Sep-1997 -by- Angshuman Guha aguha
  586. * Wrote this comment.
  587. \**************************************************************************/
  588. int AddCurveFeatures(GLYPH *pGlyph, int iStepSizeSqr, unsigned short *rgFeat)
  589. {
  590. GLYPH *glyph;
  591. FRAME *frame;
  592. int cXY;
  593. XY *rgXY;
  594. int sum1=0, sum2=0, ang1, ang2, maxAngle=0;
  595. for (glyph=pGlyph; glyph; glyph=glyph->next)
  596. {
  597. frame = glyph->frame;
  598. if (!IsVisibleFRAME(frame))
  599. continue;
  600. rgXY = RgrawxyFRAME(frame);
  601. cXY = CrawxyFRAME(frame);
  602. ASSERT(cXY > 0);
  603. //For each frame compute the curvedness
  604. ComputeCurvedness(rgXY, cXY, iStepSizeSqr, &ang1, &ang2, &maxAngle);
  605. //sum1 represents the sum of the modular change in angle(with respect to + and - for the angles
  606. sum1 += ang1;
  607. //sum2 represent the total curviness of the stoke--just directly measures the change in angles.
  608. sum2 += ang2;
  609. }
  610. // based on emperical obsevations, truncate sum1 between -1000 to 1000
  611. // and sum2 between 0 and 1200
  612. // (this results in no truncation in more than 99% cases)
  613. if (sum1 < -1000)
  614. sum1 = -1000;
  615. else if (sum1 > 1000)
  616. sum1 = 1000;
  617. if (sum2 < 0)
  618. sum2 = 0;
  619. else if (sum2 > 1200)
  620. sum2 = 1200;
  621. sum1 += 1000; // now between 0 and 2000
  622. sum1 = LSHFT(sum1)/2000;
  623. if (sum1 > 0xFFFF)
  624. sum1 = 0xFFFF;
  625. sum2 = LSHFT(sum2)/1200;
  626. if (sum2 > 0xFFFF)
  627. sum2 = 0xFFFF;
  628. // maxAngle should be between 0 and 180
  629. if (maxAngle < 0)
  630. maxAngle = 0;
  631. else if (maxAngle > 180)
  632. maxAngle = 180;
  633. maxAngle = LSHFT(maxAngle)/180;
  634. if (maxAngle > 0xFFFF)
  635. maxAngle = 0xFFFF;
  636. *rgFeat++ = (unsigned short) sum1;
  637. *rgFeat++ = (unsigned short) sum2;
  638. *rgFeat = (unsigned short) maxAngle;
  639. return 3;
  640. }
  641. /******************************Private*Routine******************************\
  642. * AddStrokeCountFeature
  643. *
  644. * Defines a single feature derived from stroke count of a char.
  645. *
  646. * History:
  647. * 26-Sep-1997 -by- Angshuman Guha aguha
  648. * Wrote this comment.
  649. \**************************************************************************/
  650. int AddStrokeCountFeature(int cStroke, unsigned short *rgFeat)
  651. {
  652. int tmp = LSHFT(cStroke-1)/cStroke;
  653. *rgFeat++ = (unsigned short)tmp;
  654. return 1;
  655. }
  656. /******************************Private*Routine******************************\
  657. * DoOneContour
  658. *
  659. * Once a contour has been found (defined by a sequence of values, X-values
  660. * for left- or right-contour, Y-values for top- or bottom-contour), this function
  661. * is called to fit a Chebychev polynomial to the contour generating 9 new
  662. * features.
  663. *
  664. * The arg "contour" is of length GRIDSIZE. The output features are filled in
  665. * the arg rgFeat and the count of features generated is returned.
  666. *
  667. * History:
  668. * 26-Sep-1997 -by- Angshuman Guha aguha
  669. * Wrote it.
  670. \**************************************************************************/
  671. int DoOneContour(int *contour, unsigned short *rgFeat)
  672. {
  673. int rgX[2*GRIDSIZE], *pX; //The rgX array is of 2*GRIDSIZE because the Chebyshev function takes alternate array values
  674. int chby[10];
  675. int norm = 0;
  676. int dT, i;
  677. // copy points into required format
  678. pX = rgX;
  679. for (i=0; i<GRIDSIZE; i++)
  680. {
  681. int x;
  682. x = 2 * (*contour++);
  683. //We are now LSHFTing--this is the first place where the 16.16 format surfaces
  684. //Why do we have to make the value between -1 and +1 ?
  685. *pX++ = LSHFT(x-GRIDSIZE)/(GRIDSIZE); // values are in the range -1 to +1
  686. pX++;
  687. }
  688. // fit a chebychev polynomial
  689. if (!LSCheby(rgX, GRIDSIZE, chby, 10))
  690. {
  691. ASSERT(0);
  692. return 0;
  693. }
  694. // find rms of coefficients
  695. //dT and norm are both in 16.16 notation
  696. for (i = 0; i < 10; ++i)
  697. {
  698. Mul16(chby[i], chby[i], dT)
  699. norm += dT;
  700. }
  701. norm = ISqrt(norm) << 8;
  702. if (norm < LSHFT(1))
  703. norm = LSHFT(1);//The normalization value should at least be 1
  704. // normalize coeffcients
  705. for (i = 0; i < 10; i++)
  706. {
  707. //Why this LSHFT(1) ??
  708. dT = Div16(chby[i], norm) + LSHFT(1);
  709. //Why this ??
  710. dT >>= 1;
  711. if (dT >= 0x10000)
  712. dT = 0xFFFF; //Fill it with 1's for the lower 16 bits
  713. else if (dT < 0)
  714. dT = 0;
  715. //Does converting to an unsigned short always take the lowest 16 bits ??
  716. *rgFeat++ = (unsigned short)dT;
  717. }
  718. return 10;
  719. }
  720. /******************************Private*Routine******************************\
  721. * MakeLine
  722. *
  723. * Given a point in the GRIDSIZE x GRIDSIZE grid (bitmap) and given the
  724. * sequence of points so far, this function adds one or more points in a
  725. * straight line joining the last point and the given point.
  726. *
  727. * Returns the number of points added.
  728. *
  729. * History:
  730. * 26-Sep-1997 -by- Angshuman Guha aguha
  731. * Wrote it.
  732. * 06-Oct-1997 -by- Angshuman Guha aguha
  733. * Fixed a bug.
  734. \**************************************************************************/
  735. int MakeLine(BYTE x, BYTE y, BYTE *pX, BYTE *pY, int space)
  736. {
  737. BYTE midx, midy;
  738. int ts1,ts2;
  739. int c, c2;
  740. if (space <= 0)
  741. {
  742. return 0;
  743. }
  744. ASSERT(x != *pX || y != *pY);
  745. if (Neighbor(*pX, x) && Neighbor(*pY, y))
  746. {
  747. ASSERT(x >= 0);
  748. ASSERT(x < GRIDSIZE);
  749. ASSERT(y >= 0);
  750. ASSERT(y < GRIDSIZE);
  751. *++pX = x;
  752. *++pY = y;
  753. return 1;
  754. }
  755. ts1=(*pX+x)/2;
  756. ts2=(*pY+y)/2;
  757. midx = (BYTE) ts1;
  758. midy = (BYTE) ts2;
  759. c = MakeLine(midx, midy, pX, pY, space);
  760. if (!c)
  761. return 0;
  762. c2 = MakeLine(x, y, pX+c, pY+c, space-c);
  763. if (!c2)
  764. return 0;
  765. return c + c2;
  766. }
  767. /******************************Private*Routine******************************\
  768. * AddContourFeatures
  769. *
  770. * Given a sequence of already-resampled points (pen-down strokes joined by
  771. * intervening pen-up strokes) and the bounding rect for the ink, this function
  772. * computes some contour features, fills up rgFeat with the features and
  773. * returns the count of features computed.
  774. *
  775. * History:
  776. * 26-Sep-1997 -by- Angshuman Guha aguha
  777. * Wrote it.
  778. * 06-Oct-1997 -by- Angshuman Guha aguha
  779. * Fixed a bug. Added Realloc for rgMappedX and rgMappedY.
  780. Comments added by Mango
  781. The idea of this function is to first map the points that we have to 2 GRIDSIZE*GRIDSIZE bitmap.
  782. Once the bitmap is made we use the contour features.
  783. Explanation of the arguments to the function
  784. pointinfo--Contains the strructure from which the points will be taken
  785. pRect--pointer to the bounding rectangle of the original ink
  786. rgFeat--This contains the finalk contour features
  787. \**************************************************************************/
  788. int AddContourFeatures(POINTINFO *pointinfo, RECT *pRect, unsigned short *rgFeat)
  789. {
  790. //BYTE rgGrid[GRIDSIZE][GRIDSIZE];
  791. int xMin = pRect->left; //Stores the minimum value of x
  792. int yMin = pRect->top; //Stores the minimum value of y
  793. int xRange = pRect->right - xMin; //Stores the width of the bounding rectangle
  794. int yRange = pRect->bottom - yMin; //Stores the height of the bounding rectangle
  795. int range, xOrigin, yOrigin;//Range stores the greated of the xRange and yRange
  796. 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
  797. BYTE *pMappedX, *pMappedY, *rgMappedX, *rgMappedY;//Since we are using BYTE * here,we are assuming that GRIDSIZE <256--a byte size
  798. int rgMaxX[GRIDSIZE], rgMinY[GRIDSIZE], rgMaxY[GRIDSIZE], lastz; //GRIDSIZE=32
  799. int lastx, lasty;
  800. int iRetValue;
  801. BYTE *pbTmpX = NULL, *pbTmpY = NULL;
  802. ASSERT(xRange > 0);
  803. ASSERT(yRange > 0);
  804. //We want to map the points to a GRIDSIZE*GRIDSIZE bitmap--but at the same time we want to preserve the aspect ratio
  805. //It xRange >yRange,then the xValues will span the whole of the bit map
  806. //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
  807. if (xRange > yRange)
  808. {
  809. range = xRange;
  810. xOrigin = 0;
  811. yOrigin = (GRIDSIZE - GRIDSIZE*(yRange-1)/range) / 2;
  812. }
  813. else if (yRange > 1)
  814. { //In this case the y will span the entire grid map.xOrigin is scaled according to the earlier comment
  815. range = yRange;
  816. xOrigin = (GRIDSIZE - GRIDSIZE*(xRange-1)/range) / 2;
  817. yOrigin = 0;
  818. }
  819. else // xRange == yRange == 1
  820. { //In this case the Ink will be centered
  821. range = 1;
  822. xOrigin = GRIDSIZE/2;
  823. yOrigin = GRIDSIZE/2;
  824. }
  825. // make list of grid points which will make up the binary pixel map--we will map only the pen down points
  826. //Initialize the pointers to the raw points
  827. xy = pointinfo->xy;
  828. z = pointinfo->z;
  829. //In case there is a continuation of a line,then the bit map should not have any vacant spaces in the grid
  830. //connecting the points of the line.Hence we add extra space by 2*GRIDSIZE in case we need to connect the points
  831. //cMappedMax represents the maximum number of points that will be mapped
  832. cMappedMax = 2*(pointinfo->cPoint+GRIDSIZE);
  833. rgMappedX = (BYTE *) ExternAlloc(cMappedMax*sizeof(BYTE));
  834. rgMappedY = (BYTE *) ExternAlloc(cMappedMax*sizeof(BYTE));
  835. if (!rgMappedX || !rgMappedY)
  836. {
  837. //Allocatation failed.FLEE !!
  838. ASSERT(0);
  839. iRetValue = 0;
  840. goto cleanup;
  841. }
  842. cMapped = 0; //This contains the total number of points that have been mapped
  843. //The pointer to which it has been initialized is one less than the actual start.
  844. //Hence need to use an increment operator prior to using the first time.
  845. //After that the pointer always points to the last value that was allocated
  846. pMappedX = rgMappedX - 1;
  847. pMappedY = rgMappedY - 1;
  848. lastz = PEN_UP_VALUE;
  849. for (iPoint=0; iPoint<pointinfo->cPoint; iPoint++)
  850. {
  851. int xRaw, yRaw, x, y, zRaw;
  852. // get point
  853. xRaw = *xy++;
  854. yRaw = *xy++;
  855. zRaw = *z++;
  856. z++;
  857. if (zRaw > 0) // pen-down point
  858. {
  859. // map x to grid
  860. x = xOrigin + GRIDSIZE*(xRaw - xMin)/range;
  861. ASSERT(x >= 0);
  862. ASSERT(x < GRIDSIZE);
  863. // map y to grid
  864. y = yOrigin + GRIDSIZE*(yRaw - yMin)/range;
  865. ASSERT(y >= 0);
  866. ASSERT(y < GRIDSIZE);
  867. // save this point in the list
  868. if (lastz < 0)
  869. {
  870. if (cMapped==cMappedMax)
  871. { //If the max space has already been used up then we ned to realloc.
  872. cMappedMax *= 2;
  873. pbTmpX = ExternRealloc(rgMappedX, cMappedMax*sizeof(BYTE));
  874. if (!pbTmpX)
  875. {
  876. ASSERT(0);
  877. iRetValue = 0;
  878. goto cleanup;
  879. }
  880. rgMappedX = pbTmpX;
  881. pMappedX = rgMappedX - 1 + cMapped;
  882. pbTmpY = ExternRealloc(rgMappedY, cMappedMax*sizeof(BYTE));
  883. if (!pbTmpY)
  884. {
  885. ASSERT(0);
  886. iRetValue = 0;
  887. goto cleanup;
  888. }
  889. rgMappedY = pbTmpY;
  890. pMappedY = rgMappedY - 1 + cMapped;
  891. }
  892. // first point of a stroke
  893. *++pMappedX = (BYTE)x;
  894. *++pMappedY = (BYTE)y;
  895. cMapped++;
  896. }
  897. else if (x != *pMappedX || y != *pMappedY)
  898. {
  899. // next unique mapped point
  900. //i contains the count of the number of points that have been added thru MakeLine
  901. ASSERT(*pMappedX < GRIDSIZE);
  902. ASSERT(*pMappedY <GRIDSIZE);
  903. ASSERT(*pMappedX >=0);
  904. ASSERT(*pMappedY >=0);
  905. while (!(i = MakeLine((BYTE)x, (BYTE)y, pMappedX, pMappedY, cMappedMax - cMapped)))
  906. {
  907. // these reallocs happen on 0.4% of the samples of gtrain02.ste (1860 of 443292 samples)
  908. cMappedMax *= 2;
  909. pbTmpX = ExternRealloc(rgMappedX, cMappedMax*sizeof(BYTE));
  910. if (!pbTmpX)
  911. {
  912. ASSERT(0);
  913. iRetValue = 0;
  914. goto cleanup;
  915. }
  916. rgMappedX = pbTmpX;
  917. pMappedX = rgMappedX - 1 + cMapped;
  918. pbTmpY = ExternRealloc(rgMappedY, cMappedMax*sizeof(BYTE));
  919. if (!pbTmpY)
  920. {
  921. ASSERT(0);
  922. iRetValue = 0;
  923. goto cleanup;
  924. }
  925. rgMappedY = pbTmpY;
  926. pMappedY = rgMappedY - 1 + cMapped;
  927. }
  928. //Increment the count of mapped points by the number of points that have been added.
  929. cMapped += i;
  930. ASSERT(cMapped <=cMappedMax);
  931. //Increment the pointers accordingly
  932. pMappedX += i;
  933. pMappedY += i;
  934. }
  935. } // if zRaw > 0
  936. lastz = zRaw;
  937. } // for iPoint=0
  938. // now dump the mapped point list into the grid, deleting redundant points along the way
  939. //memset(rgGrid, 0, sizeof(rgGrid));
  940. for (i=0; i<GRIDSIZE; i++)
  941. {
  942. rgMaxX[i] = -1;
  943. rgMinY[i] = GRIDSIZE;
  944. rgMaxY[i] = -1;
  945. }
  946. pMappedX = rgMappedX;
  947. pMappedY = rgMappedY;
  948. for (iPoint=0; iPoint<cMapped; iPoint++)
  949. {
  950. int x, y;
  951. x = *pMappedX++;
  952. y = *pMappedY++;
  953. if (iPoint > 0 && iPoint < cMapped-1)
  954. {
  955. int nextx = *pMappedX;
  956. int nexty = *pMappedY;
  957. if (Neighbor(lastx, nextx) &&
  958. Neighbor(lasty, nexty) &&
  959. lastx != nextx &&
  960. lasty != nexty)
  961. continue;
  962. }
  963. lastx = x;
  964. lasty = y;
  965. //rgGrid[x][y]=1;
  966. ASSERT(x >= 0);
  967. ASSERT(x < GRIDSIZE);
  968. ASSERT(y >= 0);
  969. ASSERT(y < GRIDSIZE);
  970. if (x > rgMaxX[y])
  971. rgMaxX[y] = x;
  972. if (y < rgMinY[x])
  973. rgMinY[x] = y;
  974. if (y > rgMaxY[x])
  975. rgMaxY[x] = y;
  976. }
  977. // now Chebychev'ize the three contours
  978. // right contour
  979. rgFeat += DoOneContour(rgMaxX, rgFeat);
  980. // top contour
  981. rgFeat += DoOneContour(rgMinY, rgFeat);
  982. // bottom contour
  983. rgFeat += DoOneContour(rgMaxY, rgFeat);
  984. iRetValue = 30;
  985. cleanup: // Clean up temp space
  986. if (rgMappedX)
  987. ExternFree(rgMappedX);
  988. if (rgMappedY)
  989. ExternFree(rgMappedY);
  990. return iRetValue;
  991. }
  992. /******************************Private*Routine******************************\
  993. * NormalizeCheby
  994. *
  995. * Routine to normalize the three (x, y and z) Chebychev polynomials.
  996. *
  997. * History:
  998. * 26-Sep-1997 -by- Angshuman Guha aguha
  999. * Wrote this comment.
  1000. \**************************************************************************/
  1001. void NormalizeCheby(int *chbyX, int *chbyY, int *chbyZ, unsigned short *rgFeat)
  1002. {
  1003. int norm = 0;
  1004. int dT;
  1005. int cFeat = 0, i;
  1006. //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
  1007. //
  1008. for (i = 1; i < XCHB; ++i) // 1st X coeff skipped
  1009. {
  1010. Mul16(chbyX[i], chbyX[i], dT)
  1011. norm += dT;
  1012. }
  1013. for (i = 1; i < YCHB; ++i) // 1st Y coeff skipped
  1014. {
  1015. Mul16(chbyY[i], chbyY[i], dT)
  1016. norm += dT;
  1017. }
  1018. norm = ISqrt(norm) << 8;
  1019. if (norm < LSHFT(1))
  1020. norm = LSHFT(1);
  1021. for (i=1; i<XCHB; i++)
  1022. {
  1023. dT = Div16(chbyX[i], norm) + LSHFT(1); // now between 0 and 2
  1024. dT >>= 1; // now between 0 and 1
  1025. if (dT >= 0x10000)
  1026. dT = 0xFFFF;
  1027. else if (dT < 0)
  1028. dT = 0;
  1029. rgFeat[cFeat++] = (unsigned short)dT;
  1030. }
  1031. for (i=1; i<YCHB; i++)
  1032. {
  1033. dT = Div16(chbyY[i], norm) + LSHFT(1);
  1034. dT >>= 1;
  1035. if (dT >= 0x10000)
  1036. dT = 0xFFFF;
  1037. else if (dT < 0)
  1038. dT = 0;
  1039. rgFeat[cFeat++] = (unsigned short)dT;
  1040. }
  1041. // Z
  1042. norm = 0;
  1043. for (i = 0; i < 8; ++i)
  1044. {
  1045. Mul16(chbyZ[i], chbyZ[i], dT)
  1046. norm += dT;
  1047. }
  1048. norm = ISqrt(norm) << 8;
  1049. if (norm < LSHFT(1))
  1050. norm = LSHFT(1);
  1051. for (i = 0; i < 8; i++)
  1052. {
  1053. dT = Div16(chbyZ[i], norm) + LSHFT(1);
  1054. dT >>= 1;
  1055. if (dT >= 0x10000)
  1056. dT = 0xFFFF;
  1057. else if (dT < 0)
  1058. dT = 0;
  1059. rgFeat[cFeat++] = (unsigned short)dT;
  1060. }
  1061. ASSERT(cFeat == 26);
  1062. }
  1063. /******************************Public*Routine******************************\
  1064. * SoleFeaturize
  1065. *
  1066. * The top-level routine for featurizing ink for a char.
  1067. *
  1068. * History:
  1069. * 26-Sep-1997 -by- Angshuman Guha aguha
  1070. * Modified it.
  1071. \**************************************************************************/
  1072. BOOL SoleFeaturize(GLYPH *pGlyph, RECT *pGuide, unsigned short *rgFeat)
  1073. {
  1074. int cStroke, iStroke;
  1075. int iPoint;
  1076. int sumX, sumY, sum;
  1077. double totvar;
  1078. int var;
  1079. int isumX, isumY;//These store the mean values of x and y
  1080. int chbyX[IMAXCHB], chbyY[IMAXCHB], chbyZ[IMAXCHB];
  1081. int retVal = 1;
  1082. XY *rgXY, lastXY;
  1083. int cXY, iXY, dx, dy, t;
  1084. GLYPH *glyph;
  1085. FRAME *frame;
  1086. int ydev; //Stores the ydev value used for storing the step size
  1087. POINTINFO pointinfo;
  1088. RECT rect;
  1089. // compute cStroke
  1090. for (cStroke=0, glyph=pGlyph; glyph; glyph=glyph->next)
  1091. {
  1092. frame = glyph->frame;
  1093. if (!IsVisibleFRAME(frame))
  1094. continue;
  1095. cXY = CrawxyFRAME(frame);
  1096. ASSERT(cXY > 0);
  1097. cStroke++;
  1098. }
  1099. if (cStroke < 1)
  1100. return 0;
  1101. // compute step size--originally this was done using the box height
  1102. // pointinfo.iStepSize = pGuide->cyBox*3/200; // 1.5% of box height
  1103. ydev= YDeviation(pGlyph);
  1104. if (ydev < 1)
  1105. ydev = 1; // a "-" or a "."
  1106. //The step size is computed from the ydev value
  1107. pointinfo.iStepSize = ydev/5;
  1108. if (pointinfo.iStepSize < 2)
  1109. pointinfo.iStepSize = 2;
  1110. pointinfo.iStepSizeSqr = pointinfo.iStepSize * pointinfo.iStepSize;
  1111. // estimate total count of points
  1112. pointinfo.cPointMax = 1; // make sure it does not end up being zero
  1113. for (iStroke=0, glyph=pGlyph; glyph; glyph=glyph->next)
  1114. {
  1115. frame = glyph->frame;
  1116. if (!IsVisibleFRAME(frame))
  1117. continue;
  1118. rgXY = RgrawxyFRAME(frame);
  1119. cXY = CrawxyFRAME(frame);
  1120. ASSERT(cXY > 0);
  1121. sum = 0;
  1122. for (iXY=1; iXY<cXY; iXY++)
  1123. {
  1124. dx = rgXY[iXY].x - rgXY[iXY-1].x;
  1125. if (dx < 0)
  1126. dx = -dx;
  1127. dy = rgXY[iXY].y - rgXY[iXY-1].y;
  1128. if (dy < 0)
  1129. dy = -dy;
  1130. if (dx > dy)
  1131. sum += dx;
  1132. else
  1133. sum += dy;
  1134. }
  1135. //The sum that we are computing here is an underestimate--we are only taking the max on |x| or |y|.The distance will
  1136. // be more
  1137. pointinfo.cPointMax += sum/pointinfo.iStepSize;
  1138. // if not first stroke simulate pen-up stroke
  1139. if (iStroke)
  1140. {
  1141. dx = lastXY.x - rgXY->x;
  1142. dy = lastXY.y - rgXY->y;
  1143. t = ISqrt(dx*dx + dy*dy)/pointinfo.iStepSize;
  1144. if (t >= 2)
  1145. pointinfo.cPointMax += t-1;
  1146. }
  1147. lastXY = rgXY[cXY-1];
  1148. iStroke++;
  1149. }
  1150. //Since we have computed an underestimate multiply by two
  1151. pointinfo.cPointMax *= 2;
  1152. // allocate space
  1153. pointinfo.xy = (int *) ExternAlloc(2*pointinfo.cPointMax*sizeof(int));
  1154. if (!pointinfo.xy)
  1155. return 0;
  1156. //The array size for z is double of what actually needs to be allocated because-
  1157. //--the chebyshev function expects the array to be spaced in this manner
  1158. //--indexing is easier in the functions--can exactly mirror what you do for the x and y
  1159. pointinfo.z = (int *) ExternAlloc(2*pointinfo.cPointMax*sizeof(int));
  1160. if (!pointinfo.z)
  1161. {
  1162. ExternFree(pointinfo.xy);
  1163. return 0;
  1164. }
  1165. // join all strokes into one stream
  1166. pointinfo.cPoint = 0;
  1167. for (glyph=pGlyph; glyph; glyph=glyph->next)
  1168. {
  1169. frame = glyph->frame;
  1170. if (!IsVisibleFRAME(frame))
  1171. continue;
  1172. rgXY = RgrawxyFRAME(frame);
  1173. cXY = CrawxyFRAME(frame);
  1174. for (iXY = 0; iXY < cXY; iXY++)
  1175. if (!AddPoint(&pointinfo, rgXY[iXY].x, rgXY[iXY].y, !iXY))
  1176. {
  1177. retVal = 0;
  1178. goto freeReturn;
  1179. }
  1180. }
  1181. // contour features (computed from resampled raw points)
  1182. GetRectGLYPH(pGlyph, &rect);
  1183. //PLEASE NOTE--rgFeat is unsigned short.Its value comes from the lower 16 bits of the 16.16 format that had been defined earlier.
  1184. rgFeat += AddContourFeatures(&pointinfo, &rect, rgFeat);
  1185. //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 ??
  1186. // compute X-mean and Y-mean
  1187. sumX = sumY = 0;
  1188. for (iPoint=0; iPoint<2*pointinfo.cPoint; iPoint+=2)
  1189. {
  1190. sumX += pointinfo.xy[iPoint] - rect.left;
  1191. sumY += pointinfo.xy[iPoint+1] - rect.top;
  1192. }
  1193. //isumX and isumY represent the mean values of the x and y coordinates.
  1194. isumX = (sumX / pointinfo.cPoint) + rect.left;
  1195. isumY = (sumY / pointinfo.cPoint) + rect.top;
  1196. // shift points by means
  1197. for (iPoint=0; iPoint<2*pointinfo.cPoint; iPoint+=2)
  1198. {
  1199. pointinfo.xy[iPoint] -= isumX;
  1200. pointinfo.xy[iPoint+1] -= isumY;
  1201. }
  1202. // compute variance
  1203. totvar = 0;
  1204. for (iPoint=0; iPoint<2*pointinfo.cPoint; iPoint++)
  1205. {
  1206. totvar += (double) pointinfo.xy[iPoint] * (double) pointinfo.xy[iPoint];
  1207. }
  1208. var = (int) sqrt(totvar/pointinfo.cPoint);
  1209. if (var < 1)
  1210. var = 1;
  1211. // scale points by standard deviation
  1212. // From this point on,the pointinfo values are in 16.16
  1213. //IMPORTTANT NOTE---THE pointinfo array is not directly used after this point
  1214. //If it is,you will have to use 16.16 arithmetic
  1215. for (iPoint=0; iPoint<2*pointinfo.cPoint; iPoint++)
  1216. {
  1217. pointinfo.xy[iPoint] = LSHFT(pointinfo.xy[iPoint])/var;
  1218. }
  1219. //Basically,since we effectively have a normal distribution(hopefully)most of the values will be between +-3.
  1220. // chebychev'ize!
  1221. if (!LSCheby(pointinfo.xy, pointinfo.cPoint, chbyX, XCHB))
  1222. {
  1223. retVal = 0;
  1224. goto freeReturn;
  1225. }
  1226. if (!LSCheby(pointinfo.xy+1, pointinfo.cPoint, chbyY, YCHB))
  1227. {
  1228. retVal = 0;
  1229. goto freeReturn;
  1230. }
  1231. if (!LSCheby(pointinfo.z, pointinfo.cPoint, chbyZ, ZCHB))
  1232. {
  1233. retVal = 0;
  1234. goto freeReturn;
  1235. }
  1236. NormalizeCheby(chbyX, chbyY, chbyZ, rgFeat);
  1237. rgFeat += 26;
  1238. // stroke count feature--1 feature is added
  1239. rgFeat += AddStrokeCountFeature(cStroke, rgFeat);
  1240. // guide features
  1241. //The rect had been comptured prior to scaling the points --by mean and standard deviation
  1242. //isumY represents the mean Y value
  1243. //We add 5 guide features
  1244. rgFeat += AddGuideFeatures(pGuide, &rect, isumY, rgFeat);
  1245. // curved-ness features
  1246. //Note the original glyph is being passed here
  1247. //Why not simply use the sampled points ??
  1248. rgFeat += AddCurveFeatures(pGlyph, pointinfo.iStepSizeSqr, rgFeat);
  1249. freeReturn:
  1250. ExternFree(pointinfo.xy);
  1251. ExternFree(pointinfo.z);
  1252. return retVal;
  1253. }
  1254. /*
  1255. void DumpFeatures (unsigned short *pFeat)
  1256. {
  1257. static int c = 0;
  1258. static FILE *fp = NULL;
  1259. int i, cMap;
  1260. if (!fp)
  1261. {
  1262. fp = fopen ("feat.dmp", "wt");
  1263. if (!fp)
  1264. return;
  1265. }
  1266. cMap = sizeof (s_aaMap) / sizeof (s_aaMap[0]);
  1267. for (i = 0; i < cMap; i++)
  1268. {
  1269. if (s_aaMap[i] == s_wch)
  1270. break;
  1271. }
  1272. if (i == cMap)
  1273. return;
  1274. fprintf (fp, "%d\t", i + 1);
  1275. for (i = 0; i < 65; i++)
  1276. {
  1277. fprintf (fp, "%d%c",
  1278. pFeat[i],
  1279. i == 64 ? '\n' : ' ');
  1280. }
  1281. fflush (fp);
  1282. c++;
  1283. }
  1284. */
  1285. #pragma optimize("",off)
  1286. int SoleNN(SOLE_LOAD_INFO *pSole, int cStrk, unsigned short *pFeat, ALT_LIST *pAlt)
  1287. {
  1288. RREAL *pNetMem, *pNetOut;
  1289. int iWinner, cOut;
  1290. int i, j, k;
  1291. int *pIndex;
  1292. wchar_t *pwchMap;
  1293. pAlt->cAlt = -1;
  1294. if (cStrk == 1)
  1295. {
  1296. pNetMem = ExternAlloc(pSole->iNet1Size * sizeof (*pNetMem));
  1297. if (!pNetMem)
  1298. return -1;
  1299. for (i = 0; i < SOLE_NUM_FEATURES; i++)
  1300. {
  1301. pNetMem[i] = pFeat[i];
  1302. }
  1303. pNetOut = runLocalConnectNet(&pSole->net1, pNetMem, &iWinner, &cOut);
  1304. pwchMap = pSole->pMap1;
  1305. }
  1306. else
  1307. {
  1308. pNetMem = ExternAlloc(pSole->iNet2Size * sizeof (*pNetMem));
  1309. if (!pNetMem)
  1310. return -1;
  1311. for (i = 0; i < SOLE_NUM_FEATURES; i++)
  1312. {
  1313. pNetMem[i] = pFeat[i];
  1314. }
  1315. pNetOut = runLocalConnectNet(&pSole->net2, pNetMem, &iWinner, &cOut);
  1316. pwchMap = pSole->pMap2;
  1317. }
  1318. pIndex = ExternAlloc(sizeof(int) * cOut);
  1319. if (pIndex == NULL)
  1320. {
  1321. ExternFree(pNetMem);
  1322. return -1;
  1323. }
  1324. for (i = 0; i < cOut; i++)
  1325. {
  1326. pIndex[i] = i;
  1327. }
  1328. for (i = 0; i < MAX_ALT_LIST; i++)
  1329. {
  1330. for (j = i + 1; j < cOut; j++)
  1331. {
  1332. if (pNetOut[pIndex[i]] < pNetOut[pIndex[j]])
  1333. {
  1334. k = pIndex[i];
  1335. pIndex[i] = pIndex[j];
  1336. pIndex[j] = k;
  1337. }
  1338. }
  1339. if (pNetOut[pIndex[i]] == 0)
  1340. {
  1341. break;
  1342. }
  1343. pAlt->awchList[i] = pwchMap[pIndex[i]];
  1344. pAlt->aeScore[i] = (float) pNetOut[pIndex[i]] / (float) SOFT_MAX_UNITY;
  1345. }
  1346. //DumpFeatures (pFeat);
  1347. // If the following line is compiled with optimization turned on,
  1348. // the VS6 compiler goes into an infinite loop.
  1349. pAlt->cAlt = i;
  1350. ExternFree(pIndex);
  1351. ExternFree(pNetMem);
  1352. return pAlt->cAlt;
  1353. }
  1354. #pragma optimize("",on)
  1355. int SoleMatch(SOLE_LOAD_INFO *pSole,
  1356. ALT_LIST *pAlt, int cAlt, GLYPH *pGlyph, RECT *pGuide, CHARSET *pCS, LOCRUN_INFO * pLocRunInfo)
  1357. {
  1358. int cStrk;
  1359. unsigned short aiSoleFeat[SOLE_NUM_FEATURES];
  1360. cStrk = CframeGLYPH (pGlyph);
  1361. if (SoleFeaturize(pGlyph, pGuide, aiSoleFeat) != 1)
  1362. {
  1363. return -1;
  1364. }
  1365. return SoleNN (pSole, cStrk, aiSoleFeat, pAlt);
  1366. }
  1367. static void AddChar(wchar_t *pwchTop1, float *pflTop1, ALT_LIST *pAltList, wchar_t dch, float flProb,
  1368. LOCRUN_INFO *pLocRunInfo, CHARSET *pCS)
  1369. {
  1370. int j;
  1371. for (j = 0; j < (int) pAltList->cAlt; j++)
  1372. {
  1373. if (pAltList->awchList[j] == dch)
  1374. {
  1375. pAltList->aeScore[j] = flProb;
  1376. }
  1377. }
  1378. if (flProb > 0)
  1379. {
  1380. // Check whether the character (or folding set) passes the filter,
  1381. // if so see if it is the new top 1.
  1382. if (IsAllowedChar(pLocRunInfo, pCS, dch))
  1383. {
  1384. if (*pwchTop1 == 0xFFFE || flProb > *pflTop1)
  1385. {
  1386. *pflTop1 = flProb;
  1387. *pwchTop1 = dch;
  1388. }
  1389. }
  1390. }
  1391. }
  1392. int SoleMatchRescore(SOLE_LOAD_INFO *pSole,
  1393. wchar_t *pwchTop1, float *pflTop1,
  1394. ALT_LIST *pAltList, int cAlt, GLYPH *pGlyph, RECT *pGuide,
  1395. CHARSET *pCharSet, LOCRUN_INFO *pLocRunInfo)
  1396. {
  1397. int i;
  1398. int cStrk;
  1399. RREAL *pNetMem, *pNetOut;
  1400. int iWinner, cOut;
  1401. int j;
  1402. wchar_t *pwchMap;
  1403. unsigned short aiSoleFeat[SOLE_NUM_FEATURES];
  1404. // First set all the scores to zero. This is because some code points Fugu
  1405. // supports may not be supported by Sole. This implicitly says Sole gives
  1406. // them a score of zero.
  1407. for (j = 0; j < (int) pAltList->cAlt; j++)
  1408. {
  1409. pAltList->aeScore[j] = 0;
  1410. }
  1411. *pflTop1 = 0;
  1412. *pwchTop1 = 0xFFFE;
  1413. cStrk = CframeGLYPH (pGlyph);
  1414. if (SoleFeaturize (pGlyph, pGuide, aiSoleFeat) != 1)
  1415. {
  1416. return pAltList->cAlt;
  1417. }
  1418. if (cStrk == 1)
  1419. {
  1420. pNetMem = ExternAlloc(pSole->iNet1Size * sizeof (*pNetMem));
  1421. if (!pNetMem)
  1422. return -1;
  1423. for (i = 0; i < SOLE_NUM_FEATURES; i++)
  1424. {
  1425. pNetMem[i] = aiSoleFeat[i];
  1426. }
  1427. pNetOut = runLocalConnectNet(&pSole->net1, pNetMem, &iWinner, &cOut);
  1428. pwchMap = pSole->pMap1;
  1429. }
  1430. else
  1431. {
  1432. pNetMem = ExternAlloc(pSole->iNet2Size * sizeof (*pNetMem));
  1433. if (!pNetMem)
  1434. return -1;
  1435. for (i = 0; i < SOLE_NUM_FEATURES; i++)
  1436. {
  1437. pNetMem[i] = aiSoleFeat[i];
  1438. }
  1439. pNetOut = runLocalConnectNet(&pSole->net2, pNetMem, &iWinner, &cOut);
  1440. pwchMap = pSole->pMap2;
  1441. }
  1442. // This is the version for Fugu trained on dense codes, which will usually be
  1443. // what we use. Loops over the outputs
  1444. for (i = 0; i < cOut; i++)
  1445. {
  1446. wchar_t fdch = pwchMap[i];
  1447. float flProb = (float) pNetOut[i] / (float) SOFT_MAX_UNITY;
  1448. #if 1
  1449. AddChar(pwchTop1, pflTop1, pAltList, fdch, flProb, pLocRunInfo, pCharSet);
  1450. #else
  1451. if (LocRunIsFoldedCode(pLocRunInfo, fdch))
  1452. {
  1453. // If it is a folded code, look up the folding set
  1454. wchar_t *pFoldingSet = LocRunFolded2FoldingSet(pLocRunInfo, fdch);
  1455. // Run through the folding set, adding non-NUL items to the output list
  1456. // (until the output list is full)
  1457. for (j = 0; j < LOCRUN_FOLD_MAX_ALTERNATES && pFoldingSet[j] != 0; j++)
  1458. {
  1459. AddChar(pwchTop1, pflTop1, pAltList, pFoldingSet[j], flProb, pLocRunInfo, pCharSet);
  1460. }
  1461. }
  1462. else
  1463. {
  1464. AddChar(pwchTop1, pflTop1, pAltList, fdch, flProb, pLocRunInfo, pCharSet);
  1465. }
  1466. #endif
  1467. }
  1468. ExternFree(pNetMem);
  1469. return pAltList->cAlt;
  1470. }
  1471. BOOL SoleLoadPointer(SOLE_LOAD_INFO *pSole, LOCRUN_INFO *pLocRunInfo)
  1472. {
  1473. NNET_HEADER *pHeader;
  1474. NNET_SPACE_HEADER *apSpcHeader[2];
  1475. pHeader = (NNET_HEADER *) pSole->info.pbMapping;
  1476. // check version and signature
  1477. ASSERT (pHeader->dwFileType == SOLE_FILE_TYPE);
  1478. ASSERT (pHeader->iFileVer >= SOLE_OLD_FILE_VERSION);
  1479. ASSERT (pHeader->iMinCodeVer <= SOLE_CUR_FILE_VERSION);
  1480. ASSERT ( !memcmp ( pHeader->adwSignature,
  1481. g_locRunInfo.adwSignature,
  1482. sizeof (pHeader->adwSignature)
  1483. )
  1484. );
  1485. if (pHeader->dwFileType != SOLE_FILE_TYPE ||
  1486. pHeader->adwSignature[0] != pLocRunInfo->adwSignature[0] ||
  1487. pHeader->adwSignature[1] != pLocRunInfo->adwSignature[1] ||
  1488. pHeader->adwSignature[2] != pLocRunInfo->adwSignature[2] ||
  1489. pHeader->iFileVer < SOLE_OLD_FILE_VERSION ||
  1490. pHeader->iMinCodeVer > SOLE_CUR_FILE_VERSION)
  1491. {
  1492. return FALSE;
  1493. }
  1494. // # of spaces has to be two
  1495. ASSERT (pHeader->cSpace == 2);
  1496. apSpcHeader[0] =
  1497. (NNET_SPACE_HEADER *) (pSole->info.pbMapping + sizeof (NNET_HEADER));
  1498. apSpcHeader[1] =
  1499. (NNET_SPACE_HEADER *) ( pSole->info.pbMapping + sizeof (NNET_HEADER) +
  1500. sizeof (NNET_SPACE_HEADER));
  1501. if (restoreLocalConnectNet ( pSole->info.pbMapping + apSpcHeader[0]->iDataOffset,
  1502. 0,
  1503. &pSole->net1
  1504. ) == NULL
  1505. )
  1506. {
  1507. return FALSE;
  1508. }
  1509. if (restoreLocalConnectNet ( pSole->info.pbMapping + apSpcHeader[1]->iDataOffset,
  1510. 0,
  1511. &pSole->net2
  1512. ) == NULL
  1513. )
  1514. {
  1515. return FALSE;
  1516. }
  1517. pSole->iNet1Size =
  1518. getRunTimeNetMemoryRequirements(pSole->info.pbMapping + apSpcHeader[0]->iDataOffset);
  1519. pSole->iNet2Size =
  1520. getRunTimeNetMemoryRequirements(pSole->info.pbMapping + apSpcHeader[1]->iDataOffset);
  1521. pSole->pMap1 = (wchar_t *) (pSole->info.pbMapping + apSpcHeader[0]->iMapOffset);
  1522. pSole->pMap2 = (wchar_t *) (pSole->info.pbMapping + apSpcHeader[1]->iMapOffset);
  1523. return TRUE;
  1524. }
  1525. BOOL SoleLoadRes(SOLE_LOAD_INFO *pSole, HINSTANCE hInst, int nResID, int nType, LOCRUN_INFO *pLocRunInfo)
  1526. {
  1527. if (DoLoadResource(&pSole->info, hInst, nResID, nType) == NULL)
  1528. {
  1529. return FALSE;
  1530. }
  1531. return SoleLoadPointer(pSole, pLocRunInfo);
  1532. }
  1533. BOOL SoleLoadFile(SOLE_LOAD_INFO *pSole, wchar_t *wszRecogDir, LOCRUN_INFO *pLocRunInfo)
  1534. {
  1535. wchar_t wszName[MAX_PATH];
  1536. FormatPath(wszName, wszRecogDir, NULL, NULL, NULL, L"sole.bin");
  1537. if (DoOpenFile(&pSole->info, wszName) == NULL)
  1538. {
  1539. return FALSE;
  1540. }
  1541. return SoleLoadPointer(pSole, pLocRunInfo);
  1542. }
  1543. BOOL SoleUnloadFile(SOLE_LOAD_INFO *pSole)
  1544. {
  1545. return DoCloseFile(&pSole->info);
  1546. }
  1547. #if 0
  1548. BOOL SoleReject (int cStrk, wchar_t wchDense)
  1549. {
  1550. RREAL *pNetMem, *pNetOut;
  1551. int iWinner, cOut;
  1552. int i;
  1553. if (cStrk == 1)
  1554. {
  1555. pNetMem = _alloca(s_iSoleRejNetSize1 * sizeof (*pNetMem));
  1556. if (!pNetMem)
  1557. return FALSE;
  1558. for (i = 0; i < SOLE_NUM_FEATURES; i++)
  1559. {
  1560. pNetMem[i] = s_aSoleFeat[i];
  1561. }
  1562. pNetOut = runLocalConnectNet(&s_SoleRejNet1, pNetMem, &iWinner, &cOut);
  1563. ASSERT (cOut == SOLE_OUT_1);
  1564. return s_aaMap[0][iWinner] != LocRunDense2Unicode (&g_locRunInfo, wchDense);
  1565. }
  1566. else
  1567. {
  1568. pNetMem = _alloca(s_iSoleRejNetSize2 * sizeof (*pNetMem));
  1569. if (!pNetMem)
  1570. return FALSE;
  1571. for (i = 0; i < SOLE_NUM_FEATURES; i++)
  1572. {
  1573. pNetMem[i] = s_aSoleFeat[i];
  1574. }
  1575. pNetOut = runLocalConnectNet(&s_SoleRejNet2, pNetMem, &iWinner, &cOut);
  1576. ASSERT (cOut == SOLE_OUT_2);
  1577. return s_aaMap[1][iWinner] != LocRunDense2Unicode (&g_locRunInfo, wchDense);
  1578. }
  1579. }
  1580. extern wchar_t s_wch;
  1581. void SaveRecoInfo (wchar_t wch)
  1582. {
  1583. static FILE *fp = NULL;
  1584. if (!fp)
  1585. {
  1586. fp = fopen ("recores.txt", "wt");
  1587. if (!fp)
  1588. return;
  1589. }
  1590. fprintf (fp, "%d\n", wch == s_wch);
  1591. fflush (fp);
  1592. }
  1593. #define JAWS_ALT 10
  1594. int GetCharID (int cStrk, wchar_t wch)
  1595. {
  1596. int i, cClass;
  1597. cClass = (cStrk == 1 ? SOLE_OUT_1 : SOLE_OUT_2);
  1598. for (i = 0; i < cClass; i++)
  1599. {
  1600. if (s_aaMap[cStrk - 1][i] == wch)
  1601. return i;
  1602. }
  1603. return -1;
  1604. }
  1605. void SaveJAWSInfo (LATTICE *pLat)
  1606. {
  1607. static FILE *fpDisagree1 = NULL, *fpDisagree2 = NULL,
  1608. *fpSole1 = NULL, *fpSole2 = NULL;
  1609. FILE *fp, *fpSole;
  1610. int i, iWinningCand, iSoleBest, cAlt, j, cProbAlt, cStrk, cSoleOut, iClass;
  1611. LATTICE_ALT_LIST *pAlt;
  1612. int aSoleCost[JAWS_ALT];
  1613. wchar_t wch;
  1614. RECOG_ALT aProbAlt[JAWS_ALT];
  1615. BOXINFO box;
  1616. RECT bbox;
  1617. RECT rGuide;
  1618. GLYPH *pGlyph;
  1619. if (!pLat->fUseGuide)
  1620. return;
  1621. if (!fpDisagree1)
  1622. {
  1623. fpDisagree1 = fopen ("jawsdis1.txt", "wt");
  1624. if (!fpDisagree1)
  1625. return;
  1626. }
  1627. if (!fpDisagree2)
  1628. {
  1629. fpDisagree2 = fopen ("jawsdis2.txt", "wt");
  1630. if (!fpDisagree2)
  1631. return;
  1632. }
  1633. if (!fpSole1)
  1634. {
  1635. fpSole1 = fopen ("sole1.txt", "wt");
  1636. if (!fpSole1)
  1637. return;
  1638. }
  1639. if (!fpSole2)
  1640. {
  1641. fpSole2 = fopen ("sole2.txt", "wt");
  1642. if (!fpSole2)
  1643. return;
  1644. }
  1645. // find out if the right answer is in the list
  1646. iWinningCand =
  1647. iSoleBest = -1;
  1648. pAlt = pLat->pAltList + pLat->nStrokes - 1;
  1649. cAlt = pAlt->nUsed;
  1650. // This is a temporary call to get probs directly, until we have Hawk.
  1651. cProbAlt = GetProbsTsunami(pLat->nStrokes, pAlt, JAWS_ALT, aProbAlt);
  1652. // Convert strokes to GLYPHs and FRAMEs so that we can call the
  1653. // old code.
  1654. pGlyph = GlyphFromStrokes(pLat->nStrokes, pLat->pStroke);
  1655. if (!pGlyph)
  1656. {
  1657. return;
  1658. }
  1659. cStrk = pLat->nStrokes;
  1660. // Get the bounding box for the character
  1661. GetRectGLYPH(pGlyph, &bbox);
  1662. // Free the glyph structure.
  1663. DestroyFramesGLYPH(pGlyph);
  1664. DestroyGLYPH(pGlyph);
  1665. rGuide = GetGuideDrawnBox(&pLat->guide, pLat->pStroke[pLat->nStrokes - 1].iBox);
  1666. // Build up a BOXINFO structure from the guide, for use in the baseline/height scoring
  1667. box.size = rGuide.bottom - rGuide.top;
  1668. box.baseline = rGuide.bottom;
  1669. box.xheight = box.size / 2;
  1670. box.midline = box.baseline - box.xheight;
  1671. cSoleOut = (cStrk == 1 ? SOLE_OUT_1 : SOLE_OUT_2);
  1672. for (i = 0; i < cAlt && i < JAWS_ALT; i++)
  1673. {
  1674. wch = LocRunDense2Unicode(&g_locRunInfo, pAlt->alts[i].wChar);
  1675. if (wch == s_wch)
  1676. {
  1677. iWinningCand = i;
  1678. }
  1679. // sole cost
  1680. for (j = 0; j < cSoleOut; j++)
  1681. {
  1682. if (s_aaMap[cStrk -1][j] == wch)
  1683. {
  1684. break;
  1685. }
  1686. }
  1687. if (j == cSoleOut)
  1688. {
  1689. aSoleCost[i] = 0xFFFF;
  1690. }
  1691. else
  1692. {
  1693. aSoleCost[i] = (0xFFFF * s_aSoleOut[j]) / SOFT_MAX_UNITY;
  1694. aSoleCost[i] = 0xFFFF - aSoleCost[i];
  1695. }
  1696. if (iSoleBest == -1 || aSoleCost[iSoleBest] > aSoleCost[i])
  1697. {
  1698. iSoleBest = i;
  1699. }
  1700. }
  1701. for (i = cAlt; i < JAWS_ALT; i++)
  1702. {
  1703. aSoleCost[i] = 0xFFFF;
  1704. }
  1705. if (iWinningCand == -1)
  1706. return;
  1707. if (cStrk == 1)
  1708. {
  1709. fpSole = fpSole1;
  1710. }
  1711. else
  1712. {
  1713. fpSole = fpSole2;
  1714. }
  1715. // do not run if sole and otter agree
  1716. if (!SoleReject (cStrk, pAlt->alts[0].wChar))
  1717. {
  1718. return;
  1719. }
  1720. else
  1721. {
  1722. if (cStrk == 1)
  1723. {
  1724. fp = fpDisagree1;
  1725. }
  1726. else
  1727. {
  1728. fp = fpDisagree2;
  1729. }
  1730. }
  1731. fprintf (fp, "{ ");
  1732. // write the alternate features
  1733. for (i = 0; i < cAlt && i < JAWS_ALT; i++)
  1734. {
  1735. int iOttCost, iUni;
  1736. float fCost;
  1737. wch = LocRunDense2Unicode(&g_locRunInfo, pAlt->alts[i].wChar);
  1738. // otter cost
  1739. iOttCost = min (0xFFFF, (int)(-pAlt->alts[i].logProbPath * 1000));
  1740. fprintf (fp, "%d ", iOttCost);
  1741. // sole cost
  1742. fprintf (fp, "%d ", aSoleCost[i]);
  1743. // unigram cost
  1744. iUni = (int)(-255 * 100 * UnigramCost(&g_unigramInfo, pAlt->alts[i].wChar));
  1745. iUni = min (0xFFFF, iUni);
  1746. fprintf (fp, "%d ", iUni);
  1747. // baseline trans cost
  1748. fCost = BaselineTransitionCost(0, bbox, &box, pAlt->alts[i].wChar, bbox, &box);
  1749. fprintf (fp, "%d ", min (0xFFFF, (int) (-100000.0 * fCost)));
  1750. // base line cost
  1751. fCost = BaselineBoxCost(pAlt->alts[i].wChar, bbox, &box);
  1752. fprintf (fp, "%d ", min (0xFFFF, (int) (-100000.0 * fCost)));
  1753. // height trans cost
  1754. fCost = HeightTransitionCost(0, bbox, &box, pAlt->alts[i].wChar, bbox, &box);
  1755. fprintf (fp, "%d ", min (0xFFFF, (int) (-100000.0 * fCost)));
  1756. // height cost
  1757. fCost = HeightBoxCost(pAlt->alts[i].wChar, bbox, &box);
  1758. fprintf (fp, "%d ", min (0xFFFF, (int) (-100000.0 * fCost)));
  1759. // describe the codepoint
  1760. // is it a digit
  1761. if (wch >= L'0' && wch <= '9')
  1762. {
  1763. fprintf (fp, "65535 ");
  1764. }
  1765. else
  1766. {
  1767. fprintf (fp, "0 ");
  1768. }
  1769. // is alpha
  1770. if ((wch >= L'a' && wch <= 'z') || (wch >= L'A' && wch <= 'Z'))
  1771. {
  1772. fprintf (fp, "65535 ");
  1773. }
  1774. else
  1775. {
  1776. fprintf (fp, "0 ");
  1777. }
  1778. // is punct
  1779. if (iswpunct (wch))
  1780. {
  1781. fprintf (fp, "65535 ");
  1782. }
  1783. else
  1784. {
  1785. fprintf (fp, "0 ");
  1786. }
  1787. // is hiragana
  1788. if (wch >= 0x3040 && wch <= 0x309f)
  1789. {
  1790. fprintf (fp, "65535 ");
  1791. }
  1792. else
  1793. {
  1794. fprintf (fp, "0 ");
  1795. }
  1796. // is katakana
  1797. if (wch >= 0x30a0 && wch <= 0x30ff)
  1798. {
  1799. fprintf (fp, "65535 ");
  1800. }
  1801. else
  1802. {
  1803. fprintf (fp, "0 ");
  1804. }
  1805. // is kanji
  1806. if (wch >= 0x3190 && wch <= 0xabff)
  1807. {
  1808. fprintf (fp, "65535 ");
  1809. }
  1810. else
  1811. {
  1812. fprintf (fp, "0 ");
  1813. }
  1814. }
  1815. // the rest of the candidates
  1816. for (i = cAlt; i < JAWS_ALT; i++)
  1817. {
  1818. // otter cost
  1819. fprintf (fp, "%d ", 0xFFFF);
  1820. // sole cost
  1821. fprintf (fp, "%d ", 0xFFFF);
  1822. // unigram cost
  1823. fprintf (fp, "%d ", 0xFFFF);
  1824. // baseline trans cost
  1825. fprintf (fp, "%d ", 0xFFFF);
  1826. // baseline cost
  1827. fprintf (fp, "%d ", 0xFFFF);
  1828. // height trans cost
  1829. fprintf (fp, "%d ", 0xFFFF);
  1830. // height cost
  1831. fprintf (fp, "%d ", 0xFFFF);
  1832. // describe the codepoint
  1833. // digit
  1834. fprintf (fp, "0 ");
  1835. // is alpha
  1836. fprintf (fp, "0 ");
  1837. // is punct
  1838. fprintf (fp, "0 ");
  1839. // is hiragana
  1840. fprintf (fp, "0 ");
  1841. // is katakana
  1842. fprintf (fp, "0 ");
  1843. // is kanji
  1844. fprintf (fp, "0 ");
  1845. }
  1846. // write sole features for fpSole
  1847. if (iWinningCand != 0)
  1848. {
  1849. iClass = GetCharID (cStrk, s_wch);
  1850. if (iClass >= 0)
  1851. {
  1852. fprintf (fpSole, "{ ");
  1853. for (i = 0; i < SOLE_NUM_FEATURES; i++)
  1854. {
  1855. fprintf (fpSole, "%d ", s_aSoleFeat[i]);
  1856. }
  1857. fprintf (fpSole, "} { %d }\n", iClass);
  1858. fflush (fpSole);
  1859. }
  1860. }
  1861. fprintf (fp, "} { %d }\n", iWinningCand);
  1862. fflush (fp);
  1863. fflush (fpSole);
  1864. }
  1865. void RunJaws (LATTICE *pLat, HWXRESULTS *rgRes)
  1866. {
  1867. int i, iSoleBest, cAlt, j, cFeat, k, iWinningCand, iOttBest;
  1868. LATTICE_ALT_LIST *pAlt;
  1869. int aSoleCost[JAWS_ALT], aIdx[JAWS_ALT], iWinner, cOut, cStrk, cSoleOut;
  1870. wchar_t wch, awch[JAWS_ALT];
  1871. RREAL *pJawsNetMem, *pJawsNetOut;
  1872. float fCost;
  1873. BOXINFO box;
  1874. RECT bbox;
  1875. RECT rGuide;
  1876. GLYPH *pGlyph;
  1877. pJawsNetMem = _alloca(s_iJawsNetSize * sizeof (*pJawsNetMem));
  1878. if (!pJawsNetMem)
  1879. return;
  1880. // featurize for JAWS
  1881. iOttBest =
  1882. iWinningCand =
  1883. iSoleBest = -1;
  1884. pAlt = pLat->pAltList + pLat->nStrokes - 1;
  1885. cAlt = pAlt->nUsed;
  1886. // Convert strokes to GLYPHs and FRAMEs so that we can call the
  1887. // old code.
  1888. pGlyph = GlyphFromStrokes(pLat->nStrokes, pLat->pStroke);
  1889. if (!pGlyph)
  1890. {
  1891. return;
  1892. }
  1893. cStrk = CframeGLYPH (pGlyph);
  1894. // do not run if sole/reject and otter agree
  1895. if (!SoleReject (cStrk, pAlt->alts[0].wChar))
  1896. return;
  1897. // Get the bounding box for the character
  1898. GetRectGLYPH(pGlyph, &bbox);
  1899. // Free the glyph structure.
  1900. DestroyFramesGLYPH(pGlyph);
  1901. DestroyGLYPH(pGlyph);
  1902. rGuide = GetGuideDrawnBox(&pLat->guide, pLat->pStroke[pLat->nStrokes - 1].iBox);
  1903. // Build up a BOXINFO structure from the guide, for use in the baseline/height scoring
  1904. box.size = rGuide.bottom - rGuide.top;
  1905. box.baseline = rGuide.bottom;
  1906. box.xheight = box.size / 2;
  1907. box.midline = box.baseline - box.xheight;
  1908. cSoleOut = (cStrk == 1 ? SOLE_OUT_1 : SOLE_OUT_2);
  1909. for (i = 0; i < cAlt && i < JAWS_ALT; i++)
  1910. {
  1911. wch = LocRunDense2Unicode(&g_locRunInfo, pAlt->alts[i].wChar);
  1912. if (wch == s_wch)
  1913. {
  1914. iWinningCand = i;
  1915. }
  1916. // sole cost
  1917. for (j = 0; j < cSoleOut; j++)
  1918. {
  1919. if (s_aaMap[cStrk - 1][j] == wch)
  1920. {
  1921. break;
  1922. }
  1923. }
  1924. if (j == cSoleOut)
  1925. {
  1926. aSoleCost[i] = 0xFFFF;
  1927. }
  1928. else
  1929. {
  1930. aSoleCost[i] = (0xFFFF * s_aSoleOut[j]) / SOFT_MAX_UNITY;
  1931. aSoleCost[i] = 0xFFFF - aSoleCost[i];
  1932. }
  1933. if (iSoleBest == -1 || aSoleCost[iSoleBest] > aSoleCost[i])
  1934. {
  1935. iSoleBest = i;
  1936. }
  1937. if (iOttBest == -1 || pAlt->alts[iOttBest].logProbPath < pAlt->alts[i].logProbPath)
  1938. {
  1939. iOttBest = i;
  1940. }
  1941. }
  1942. for (i = cAlt; i < JAWS_ALT; i++)
  1943. {
  1944. aSoleCost[i] = 0xFFFF;
  1945. }
  1946. // for all candidates
  1947. cFeat = 0;
  1948. for (i = 0; i < cAlt && i < JAWS_ALT; i++)
  1949. {
  1950. int iOttCost, iUni;
  1951. wch = LocRunDense2Unicode(&g_locRunInfo, pAlt->alts[i].wChar);
  1952. // otter cost
  1953. iOttCost = min (0xFFFF, (int)(-pAlt->alts[i].logProb * 1000));
  1954. pJawsNetMem[cFeat++] = iOttCost;
  1955. // sole cost
  1956. pJawsNetMem[cFeat++] = aSoleCost[i];
  1957. // unigram cost
  1958. iUni = (int)(-255 * 100 * UnigramCost(&g_unigramInfo, pAlt->alts[i].wChar));
  1959. pJawsNetMem[cFeat++] = iUni;
  1960. // baseline trans cost
  1961. fCost = BaselineTransitionCost(0, bbox, &box, pAlt->alts[i].wChar, bbox, &box);
  1962. pJawsNetMem[cFeat++] = min (0xFFFF, (int) (-100000.0 * fCost));
  1963. // base line cost
  1964. fCost = BaselineBoxCost(pAlt->alts[i].wChar, bbox, &box);
  1965. pJawsNetMem[cFeat++] = min (0xFFFF, (int) (-100000.0 * fCost));
  1966. // height trans cost
  1967. fCost = HeightTransitionCost(0, bbox, &box, pAlt->alts[i].wChar, bbox, &box);
  1968. pJawsNetMem[cFeat++] = min (0xFFFF, (int) (-100000.0 * fCost));
  1969. // height cost
  1970. fCost = HeightBoxCost(pAlt->alts[i].wChar, bbox, &box);
  1971. pJawsNetMem[cFeat++] = min (0xFFFF, (int) (-100000.0 * fCost));
  1972. // describe the codepoint
  1973. // digit
  1974. if (wch >= L'0' && wch <= '9')
  1975. {
  1976. pJawsNetMem[cFeat++] = 65535;
  1977. }
  1978. else
  1979. {
  1980. pJawsNetMem[cFeat++] = 0;
  1981. }
  1982. // is alpha
  1983. if ((wch >= L'a' && wch <= 'z') || (wch >= L'A' && wch <= 'Z'))
  1984. {
  1985. pJawsNetMem[cFeat++] = 65535;
  1986. }
  1987. else
  1988. {
  1989. pJawsNetMem[cFeat++] = 0;
  1990. }
  1991. // is punct
  1992. if (iswpunct (wch))
  1993. {
  1994. pJawsNetMem[cFeat++] = 65535;
  1995. }
  1996. else
  1997. {
  1998. pJawsNetMem[cFeat++] = 0;
  1999. }
  2000. // is hiragana
  2001. if (wch >= 0x3040 && wch <= 0x309f)
  2002. {
  2003. pJawsNetMem[cFeat++] = 65535;
  2004. }
  2005. else
  2006. {
  2007. pJawsNetMem[cFeat++] = 0;
  2008. }
  2009. // is katakana
  2010. if (wch >= 0x30a0 && wch <= 0x30ff)
  2011. {
  2012. pJawsNetMem[cFeat++] = 65535;
  2013. }
  2014. else
  2015. {
  2016. pJawsNetMem[cFeat++] = 0;
  2017. }
  2018. // is kanji
  2019. if (wch >= 0x3190 && wch <= 0xabff)
  2020. {
  2021. pJawsNetMem[cFeat++] = 65535;
  2022. }
  2023. else
  2024. {
  2025. pJawsNetMem[cFeat++] = 0;
  2026. }
  2027. }
  2028. // the rest of the candidates
  2029. for (i = cAlt; i < JAWS_ALT; i++)
  2030. {
  2031. // otter cost
  2032. pJawsNetMem[cFeat++] = 65535;
  2033. // sole cost
  2034. pJawsNetMem[cFeat++] = 65535;
  2035. // unigram cost
  2036. pJawsNetMem[cFeat++] = 65535;
  2037. // baseline trans cost
  2038. pJawsNetMem[cFeat++] = 65535;
  2039. // baseline cost
  2040. pJawsNetMem[cFeat++] = 65535;
  2041. // height trans cost
  2042. pJawsNetMem[cFeat++] = 65535;
  2043. // height cost
  2044. pJawsNetMem[cFeat++] = 65535;
  2045. // describe the codepoint
  2046. // digit
  2047. pJawsNetMem[cFeat++] = 0;
  2048. // is alpha
  2049. pJawsNetMem[cFeat++] = 0;
  2050. // is punct
  2051. pJawsNetMem[cFeat++] = 0;
  2052. // is hiragana
  2053. pJawsNetMem[cFeat++] = 0;
  2054. // is katakana
  2055. pJawsNetMem[cFeat++] = 0;
  2056. // is kanji
  2057. pJawsNetMem[cFeat++] = 0;
  2058. }
  2059. // call the JAWS net
  2060. pJawsNetOut = runLocalConnectNet(&s_JawsNet, pJawsNetMem, &iWinner, &cOut);
  2061. ASSERT (cOut == JAWS_ALT);
  2062. // index the result
  2063. for (i = 0; i < JAWS_ALT; i++)
  2064. {
  2065. aIdx[i] = i;
  2066. }
  2067. for (i = 0; i < JAWS_ALT; i++)
  2068. {
  2069. for (j = i + 1; j < JAWS_ALT; j++)
  2070. {
  2071. if (pJawsNetOut[aIdx[i]] < pJawsNetOut[aIdx[j]])
  2072. {
  2073. k = aIdx[i];
  2074. aIdx[i] = aIdx[j];
  2075. aIdx[j] = k;
  2076. }
  2077. }
  2078. awch[i] = pAlt->alts[aIdx[i]].wChar;
  2079. }
  2080. for (i = 0; i < cAlt && i < JAWS_ALT; i++)
  2081. {
  2082. rgRes->rgChar[i] = LocRunDense2Unicode(&g_locRunInfo, awch[i]);
  2083. }
  2084. }
  2085. #endif
  2086. #if 0
  2087. LOCAL_NET *LoadNet(void *pData, int *piNetSize, LOCAL_NET *pNet)
  2088. {
  2089. if ( !pData || !(pNet = restoreLocalConnectNet(pData, 0, pNet)) )
  2090. {
  2091. return NULL;
  2092. }
  2093. (*piNetSize) = getRunTimeNetMemoryRequirements(pData);
  2094. if ((*piNetSize) <= 0)
  2095. {
  2096. return NULL;
  2097. }
  2098. return pNet;
  2099. }
  2100. BOOL LoadSoleFromFile(wchar_t *pwszPath)
  2101. {
  2102. BYTE *pData;
  2103. wchar_t aPath[128];
  2104. HANDLE hFile, hMap;
  2105. // Generate path to file. By passing in name as "locale" we can get FormatPath
  2106. // to do what we want.
  2107. wsprintf (aPath, L"%s\\sole1.bin", pwszPath);
  2108. // Try to open the file.
  2109. hFile = CreateMappingCall (aPath,
  2110. GENERIC_READ,
  2111. FILE_SHARE_READ,
  2112. NULL,
  2113. OPEN_EXISTING,
  2114. FILE_ATTRIBUTE_NORMAL,
  2115. NULL);
  2116. if (hFile == INVALID_HANDLE_VALUE)
  2117. {
  2118. return FALSE;
  2119. }
  2120. // Create a mapping handle
  2121. hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  2122. if (hMap == NULL)
  2123. {
  2124. return FALSE;
  2125. }
  2126. // Map the entire file starting at the first byte
  2127. pData = (void *) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
  2128. if (pData == NULL)
  2129. {
  2130. return FALSE;
  2131. }
  2132. // Sole net
  2133. if (!LoadNet(pData, &s_iSoleNetSize1, &s_SoleNet1))
  2134. {
  2135. return FALSE;
  2136. }
  2137. // Generate path to file. By passing in name as "locale" we can get FormatPath
  2138. // to do what we want.
  2139. wsprintf (aPath, L"%s\\sole2.bin", pwszPath);
  2140. // Try to open the file.
  2141. hFile = CreateMappingCall (aPath,
  2142. GENERIC_READ,
  2143. FILE_SHARE_READ,
  2144. NULL,
  2145. OPEN_EXISTING,
  2146. FILE_ATTRIBUTE_NORMAL,
  2147. NULL);
  2148. if (hFile == INVALID_HANDLE_VALUE)
  2149. {
  2150. return FALSE;
  2151. }
  2152. // Create a mapping handle
  2153. hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  2154. if (hMap == NULL)
  2155. {
  2156. return FALSE;
  2157. }
  2158. // Map the entire file starting at the first byte
  2159. pData = (void *) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
  2160. if (pData == NULL)
  2161. {
  2162. return FALSE;
  2163. }
  2164. // Sole net
  2165. if (!LoadNet(pData, &s_iSoleNetSize2, &s_SoleNet2))
  2166. {
  2167. return FALSE;
  2168. }
  2169. // Generate path to file. By passing in name as "locale" we can get FormatPath
  2170. // to do what we want.
  2171. wsprintf (aPath, L"%s\\solerej1.bin", pwszPath);
  2172. // Try to open the file.
  2173. hFile = CreateMappingCall (aPath,
  2174. GENERIC_READ,
  2175. FILE_SHARE_READ,
  2176. NULL,
  2177. OPEN_EXISTING,
  2178. FILE_ATTRIBUTE_NORMAL,
  2179. NULL);
  2180. if (hFile == INVALID_HANDLE_VALUE)
  2181. {
  2182. return FALSE;
  2183. }
  2184. // Create a mapping handle
  2185. hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  2186. if (hMap == NULL)
  2187. {
  2188. return FALSE;
  2189. }
  2190. // Map the entire file starting at the first byte
  2191. pData = (void *) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
  2192. if (pData == NULL)
  2193. {
  2194. return FALSE;
  2195. }
  2196. // Sole net
  2197. if (!LoadNet(pData, &s_iSoleRejNetSize1, &s_SoleRejNet1))
  2198. {
  2199. return FALSE;
  2200. }
  2201. // Generate path to file. By passing in name as "locale" we can get FormatPath
  2202. // to do what we want.
  2203. wsprintf (aPath, L"%s\\solerej2.bin", pwszPath);
  2204. // Try to open the file.
  2205. hFile = CreateMappingCall (aPath,
  2206. GENERIC_READ,
  2207. FILE_SHARE_READ,
  2208. NULL,
  2209. OPEN_EXISTING,
  2210. FILE_ATTRIBUTE_NORMAL,
  2211. NULL);
  2212. if (hFile == INVALID_HANDLE_VALUE)
  2213. {
  2214. return FALSE;
  2215. }
  2216. // Create a mapping handle
  2217. hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  2218. if (hMap == NULL)
  2219. {
  2220. return FALSE;
  2221. }
  2222. // Map the entire file starting at the first byte
  2223. pData = (void *) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
  2224. if (pData == NULL)
  2225. {
  2226. return FALSE;
  2227. }
  2228. // Sole net
  2229. if (!LoadNet(pData, &s_iSoleRejNetSize2, &s_SoleRejNet2))
  2230. {
  2231. return FALSE;
  2232. }
  2233. return TRUE;
  2234. }
  2235. #endif
  2236. #if 0
  2237. BOOL LoadJawsFromFile(wchar_t *pwszPath)
  2238. {
  2239. BYTE *pData;
  2240. wchar_t aPath[128];
  2241. HANDLE hFile, hMap;
  2242. // Generate path to file. By passing in name as "locale" we can get FormatPath
  2243. // to do what we want.
  2244. wsprintf (aPath, L"%s\\jaws.bin", pwszPath);
  2245. // Try to open the file.
  2246. hFile = CreateMappingCall (aPath,
  2247. GENERIC_READ,
  2248. FILE_SHARE_READ,
  2249. NULL,
  2250. OPEN_EXISTING,
  2251. FILE_ATTRIBUTE_NORMAL,
  2252. NULL);
  2253. if (hFile == INVALID_HANDLE_VALUE)
  2254. {
  2255. return FALSE;
  2256. }
  2257. // Create a mapping handle
  2258. hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  2259. if (hMap == NULL)
  2260. {
  2261. return FALSE;
  2262. }
  2263. // Map the entire file starting at the first byte
  2264. pData = (void *) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
  2265. if (pData == NULL)
  2266. {
  2267. return FALSE;
  2268. }
  2269. // Jaws net
  2270. if (!LoadNet(pData, &s_iJawsNetSize, &s_JawsNet))
  2271. {
  2272. return FALSE;
  2273. }
  2274. return TRUE;
  2275. }
  2276. #endif