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.

1110 lines
22 KiB

  1. // Panel.c
  2. // James A. Pittman
  3. // July 23, 1998
  4. // Recognizes an entire panel at once by looping over lines, then looping over
  5. // ink blobs that have large gaps between them, and finally looping over the strokes
  6. // within a blob, using recognition scores to help decide what is a word and what is not.
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <limits.h>
  10. #include "common.h"
  11. #include "inferno.h"
  12. #include "nfeature.h"
  13. #include "engine.h"
  14. #include "Panel.h"
  15. #include "PorkyPost.h"
  16. #include "combiner.h"
  17. #include "Normal.h"
  18. #include "linebrk.h"
  19. #include "privdefs.h"
  20. #include "recoutil.h"
  21. #define STREQ(s,t) !strcmp(s,t)
  22. #define STRDIFF(s,t) strcmp(s,t)
  23. #define PHRASE_GROW_SIZE 4
  24. #define NOT_RECOGNIZED "\a"
  25. #define BASELIMIT 700 // scale ink to this guide hgt if we have a guide
  26. #define MAX_SCALE 10 // We will not scale more than this.
  27. #define MAXPHRASES 50
  28. static int _cdecl ResultCmp(const XRCRESULT *a, const XRCRESULT *b)
  29. {
  30. if (a->cost > b->cost)
  31. return(1);
  32. else if (a->cost < b->cost)
  33. return(-1);
  34. return(0);
  35. }
  36. // Add a map structere to the XRCRESULT containing the stroke
  37. // iDs in the glyph
  38. static int AddStrokeIdFromGlyph(GLYPH *pGlyph, XRCRESULT *pAns)
  39. {
  40. int ret = HRCR_ERROR;
  41. ASSERT(pGlyph);
  42. ASSERT(pAns);
  43. ASSERT(pAns->pMap);
  44. if (pGlyph && pAns && pAns->pMap)
  45. {
  46. int iLastIdx = -1, *piIdx;
  47. pAns->pMap->cStrokes = CframeGLYPH(pGlyph);
  48. piIdx = pAns->pMap->piStrokeIndex = (int *)ExternAlloc(sizeof(*pAns->pMap->piStrokeIndex) * pAns->pMap->cStrokes);
  49. ASSERT(pAns->pMap->piStrokeIndex);
  50. if (!pAns->pMap->piStrokeIndex)
  51. {
  52. return HRCR_MEMERR;
  53. }
  54. // Add the strokes in ascending order
  55. for ( ; pGlyph ; pGlyph = pGlyph->next)
  56. {
  57. if (pGlyph->frame)
  58. {
  59. int iFrame = pGlyph->frame->iframe;
  60. ASSERT(iFrame != iLastIdx);
  61. if (iFrame < iLastIdx)
  62. {
  63. int *piInsert = pAns->pMap->piStrokeIndex;
  64. // Search to find insertion point
  65. for ( ; piInsert < piIdx ; ++piInsert)
  66. {
  67. if (iFrame < *piInsert)
  68. {
  69. int iSwap = *piInsert;
  70. *piInsert = iFrame;
  71. iFrame = iSwap;
  72. }
  73. }
  74. }
  75. iLastIdx = *piIdx = iFrame;
  76. ++piIdx;
  77. ASSERT (piIdx - pAns->pMap->piStrokeIndex <= pAns->pMap->cStrokes);
  78. }
  79. }
  80. ret = HRCR_OK;
  81. }
  82. return ret;
  83. }
  84. // This function was pulled out of MadHwxProcess(), and then modified. I took out the test that
  85. // allows us to skip the post processor when its answer appears certain, so that the numerical
  86. // score on the top 1 word is on the same scale for every word.
  87. static int RecognizeWord(XRC *pxrc, int yDev)
  88. {
  89. XRCRESULT *pAns;
  90. int cAns, ret;
  91. ret = InfProcessHRC((HRC)pxrc, yDev);
  92. if (HRCR_OK != ret)
  93. return ret;
  94. pAns = pxrc->answer.aAlt;
  95. cAns = pxrc->answer.cAlt;
  96. // Inferno, if it cannot featurize the ink,
  97. // Fill in 0 words recognized
  98. if (!(pxrc->nfeatureset) || 0 == cAns)
  99. {
  100. GLYPH *pGlyph = pxrc->pGlyph;
  101. if (pAns[0].szWord)
  102. {
  103. ExternFree(pAns[0].szWord);
  104. }
  105. pAns[0].cost = INT_MAX;
  106. pAns->szWord = (char *)ExternAlloc(sizeof(*pAns->szWord)*(strlen(NOT_RECOGNIZED) + 1));
  107. ASSERT(pAns->szWord);
  108. if (!pAns->szWord)
  109. {
  110. return HRCR_MEMERR;
  111. }
  112. strcpy(pAns->szWord , NOT_RECOGNIZED);
  113. pxrc->answer.cAlt = 1;
  114. pAns->cWords = 1;
  115. pAns->pXRC = pxrc;
  116. pAns->pMap = ExternAlloc(sizeof(*pAns->pMap));
  117. ASSERT(pAns->pMap);
  118. if (!pAns->pMap)
  119. {
  120. ret = HRCR_MEMERR;
  121. goto fail;
  122. }
  123. pAns->pMap->start = 0;
  124. pAns->pMap->len = strlen(pAns->szWord);
  125. memset(&pAns->pMap->alt, 0, sizeof(pAns->pMap->alt));
  126. ret = AddStrokeIdFromGlyph(pGlyph, pAns);
  127. }
  128. return ret;
  129. fail:
  130. if (pAns->szWord)
  131. {
  132. ExternFree(pAns->szWord);
  133. }
  134. return ret;
  135. }
  136. //!!! BUG - This should be setting up the HRC from the values set in the xrc
  137. //!!! for madcow. We need that xrc passed down to here and set the exact
  138. //!!! same alc, dict mode, lang mode etc in from it.
  139. // set up a the HRC for word recognition (Do not allow white space)
  140. static int initWordHRC(XRC *pMainXrc, GLYPH *pGlyph, HRC *phrc)
  141. {
  142. int cFrame;
  143. HRC hrc = CreateCompatibleHRC((HRC)pMainXrc, NULL);
  144. int ret = HRCR_OK;
  145. XRC *pxrcNew;
  146. *phrc = (HRC)0;
  147. if (!hrc) // don't go to failure as we do not need to destroy an HRC
  148. return HRCR_MEMERR;
  149. // disable ALC_WHITE
  150. //ret = SetAlphabetHRC(hrc, pMainXrc->alc & ~ALC_WHITE, NULL);
  151. ret = SetHwxFlags(hrc, pMainXrc->flags | RECOFLAG_WORDMODE);
  152. if (ret != HRCR_OK)
  153. goto failure;
  154. pxrcNew = (XRC *) hrc;
  155. pxrcNew->answer.cAltMax = MAX_ALT_WORD;
  156. // build glyph of specific frames inside hrc
  157. // we later may be able to alter the API to allow additional frames to be
  158. // added after recognition has already been run
  159. for ( cFrame = 0 ; pGlyph; pGlyph = pGlyph->next, ++cFrame)
  160. {
  161. FRAME *pFrame = pGlyph->frame, *pAddedFrame;
  162. ASSERT(pFrame);
  163. if (!pFrame)
  164. {
  165. ret = HRCR_ERROR;
  166. goto failure;
  167. }
  168. ret = AddPenInputHRC(hrc, RgrawxyFRAME(pFrame), NULL, 0, &(pFrame->info));
  169. if (ret != HRCR_OK)
  170. goto failure;
  171. // Keep globally allocated frame numbers
  172. if ( (pAddedFrame = FrameAtGLYPH(((XRC *)hrc)->pGlyph, cFrame)))
  173. {
  174. pAddedFrame->iframe = pFrame->iframe;
  175. pAddedFrame->rect = pFrame->rect;
  176. }
  177. }
  178. *phrc = hrc;
  179. return HRCR_OK;
  180. failure:
  181. DestroyHRC(hrc);
  182. *phrc = (HRC)0;
  183. return ret;
  184. }
  185. // set up a the HRC for phrase recognition (Allow white space)
  186. static int initPhraseHRC(XRC *pMainXrc, GLYPH *pGlyph, HRC *phrc)
  187. {
  188. int ret = HRCR_OK;
  189. if (HRCR_OK == initWordHRC(pMainXrc, pGlyph, phrc))
  190. {
  191. XRC *pxrcNew = (XRC *)(*phrc);
  192. ret = SetHwxFlags(*phrc, pMainXrc->flags & ~RECOFLAG_WORDMODE);
  193. pxrcNew->answer.cAltMax = MAX_ALT_PHRASE;
  194. if (ret != HRCR_OK)
  195. goto failure;
  196. }
  197. return HRCR_OK;
  198. failure:
  199. DestroyHRC(*phrc);
  200. *phrc = (HRC)0;
  201. return ret;
  202. }
  203. // Special efficient version of ClearRCRESALT() that knows
  204. // there are no mappings.
  205. static void clearAlt(ALTERNATES *p)
  206. {
  207. unsigned int i;
  208. for (i = 0; i < p->cAlt; i++)
  209. ExternFree(p->aAlt[i].szWord);
  210. p->cAlt = 0;
  211. }
  212. /******************************************************************
  213. *
  214. * isolate
  215. *
  216. * Store away a recognized isolated word together with all its alternates
  217. * in the answer set
  218. *
  219. **********************************************************************/
  220. static int isolate(XRC *pXrc, ANSWER_SET *pAnsSet)
  221. {
  222. ALTERNATES *pAlt;
  223. // Only add cases that were succesfully recognized
  224. if (pXrc->answer.cAlt > 0)
  225. {
  226. if (pAnsSet->cAnsSets >= pAnsSet->capSegments)
  227. {
  228. pAnsSet->pAlternates = ExternRealloc(pAnsSet->pAlternates, sizeof(*pAnsSet->pAlternates) * (pAnsSet->cAnsSets + PHRASE_GROW_SIZE) );
  229. }
  230. ASSERT(pAnsSet->pAlternates);
  231. if (! pAnsSet->pAlternates)
  232. {
  233. return HRCR_MEMERR;
  234. }
  235. pAlt = pAnsSet->pAlternates + pAnsSet->cAnsSets;
  236. // Copy over the answers
  237. memcpy(pAlt, &pXrc->answer, sizeof(*pAnsSet->pAlternates));
  238. // Because we copied over the pointers to allocated
  239. // memory make sure allocated buffers in the XRC are not freed
  240. memset(&(pXrc->answer), 0, sizeof(pXrc->answer));
  241. ++pAnsSet->cAnsSets;
  242. #ifndef NDEBUG
  243. ValidateALTERNATES(pAlt);
  244. #endif
  245. }
  246. DestroyHRC((HRC)pXrc);
  247. return HRCR_OK;
  248. }
  249. /*******************************************************************************
  250. *
  251. * ProcessPhraseAlts
  252. *
  253. * Process phrase alternates. First collect all alternates that have
  254. * a single word and post process them to get modified scores.
  255. * Combine the best multiple words together into a single phrase
  256. *
  257. *******************************************************************************/
  258. static int ProcessPhraseAlts(XRC *pXrc)
  259. {
  260. XRCRESULT *pRes;
  261. unsigned int iAlt, cAlt;
  262. int ret = HRCR_OK;
  263. int cMultWord = 0;
  264. ALTERNATES alt;
  265. memset(&alt, 0, sizeof(alt));
  266. alt.cAltMax = MAXMAXALT;
  267. ASSERT(pXrc);
  268. pRes = pXrc->answer.aAlt;
  269. //cAlt = pXrc->answer.cAlt;
  270. if (pXrc->iSpeed < 50)
  271. {
  272. ASSERT(pXrc->iSpeed >= 0);
  273. cAlt = 10 - (7 * pXrc->iSpeed) / 50;
  274. }
  275. else
  276. {
  277. ASSERT(pXrc->iSpeed <= 100);
  278. cAlt = 10 - (3 * pXrc->iSpeed) / 50 - 4;
  279. }
  280. cAlt = min(cAlt, pXrc->answer.cAlt);
  281. // Dont bother if nothing was recognized
  282. if ( cAlt <= 1)
  283. {
  284. return ret;
  285. }
  286. ASSERT(pRes);
  287. for (iAlt = 0 ; iAlt < cAlt ; ++iAlt, ++pRes)
  288. {
  289. if (1 == pRes->cWords)
  290. {
  291. XRCRESULT *pWordRes = alt.aAlt + alt.cAlt;
  292. memcpy(pWordRes, pRes, sizeof(*pRes));
  293. ++alt.cAlt;
  294. }
  295. else
  296. {
  297. // For now dont deal with multi-word phrases alternates
  298. // Free their memory
  299. FreeIdxWORDMAP(pRes);
  300. if (pRes->pMap)
  301. {
  302. ExternFree(pRes->pMap);
  303. }
  304. if (pRes->szWord)
  305. {
  306. ExternFree(pRes->szWord);
  307. }
  308. }
  309. }
  310. // Combiner recognizer scores
  311. // for cases with a single word
  312. ret = bNnonlyEnabledXRC(pXrc) ? HRCR_OK : CombineHMMScore(pXrc->pGlyph, &alt, pXrc->nfeatureset->iPrint);
  313. if (ret != HRCR_OK)
  314. {
  315. return ret;
  316. }
  317. // Run on multiple words and copy back results from single words
  318. // WARNING: only copy back the number you asked the Post processor to
  319. // process. So that the sosts for the unprocessed alternates will be on
  320. // a very different scale
  321. pRes = pXrc->answer.aAlt;
  322. for (iAlt = 0 ; HRCR_OK == ret && iAlt < alt.cAlt ; ++iAlt, ++pRes)
  323. {
  324. memcpy(pRes, alt.aAlt + iAlt, sizeof(*pRes));
  325. }
  326. return ret;
  327. }
  328. // Search for a frame by frame ID
  329. static FRAME *FindFrame(GLYPH *pGlyph, int iFrame)
  330. {
  331. for ( ; pGlyph ; pGlyph = pGlyph->next)
  332. {
  333. if (pGlyph->frame && pGlyph->frame->iframe == iFrame)
  334. {
  335. return pGlyph->frame;
  336. }
  337. }
  338. return NULL;
  339. }
  340. /***********************************************************************
  341. *
  342. * RecognizePhrase
  343. *
  344. * Run recognition on a "chunk" (or phrase) of ink. Calls the recognizer.
  345. * If search comes back with a white space break. Then recurse till get
  346. * single words
  347. *
  348. ***********************************************************************/
  349. static int RecognizePhrase(XRC *pMainXrc, GLYPH *pGlyph, int yDev, int iDepth, ANSWER_SET *pAnsSet)
  350. {
  351. int ret;
  352. HRC full;
  353. XRC *pXrc;
  354. int bRedo = 0;
  355. if (iDepth > 0)
  356. {
  357. // When called recursively use Word mode
  358. ret = initWordHRC(pMainXrc, pGlyph, &full);
  359. }
  360. else
  361. {
  362. // First call use panel mode
  363. ret = initPhraseHRC(pMainXrc, pGlyph, &full);
  364. }
  365. if (ret != HRCR_OK)
  366. return ret;
  367. pXrc = (XRC *)full;
  368. ret = RecognizeWord(pXrc, yDev);
  369. if (ret != HRCR_OK)
  370. {
  371. DestroyHRC(full);
  372. return ret;
  373. }
  374. ++iDepth;
  375. // Check if any alternates have a word break
  376. if (iDepth <= 1)
  377. {
  378. unsigned int i;
  379. XRCRESULT *pRes = pXrc->answer.aAlt;
  380. for (i = 0 ; i < pXrc->answer.cAlt && !bRedo ; ++i, ++pRes)
  381. {
  382. if (pRes->cWords > 1)
  383. {
  384. bRedo = 1;
  385. }
  386. }
  387. }
  388. // Did we get a multiword back?
  389. if (pXrc->answer.aAlt->cWords > 1 || bRedo)
  390. {
  391. // Keep recursing till get no word breaks
  392. unsigned int i;
  393. XRCRESULT *pRes = pXrc->answer.aAlt;
  394. for (i = 0 ; i < pRes->cWords && HRCR_OK == ret ; ++i)
  395. {
  396. int iStroke;
  397. GLYPH *pNewGlyph = NULL;
  398. WORDMAP *pMap = pRes->pMap + i;
  399. ASSERT(pMap);
  400. pNewGlyph = NewGLYPH();
  401. if (!pNewGlyph)
  402. {
  403. ret = HRCR_MEMERR;
  404. break;
  405. }
  406. for (iStroke = 0 ; pMap && iStroke < pMap->cStrokes ; ++iStroke)
  407. {
  408. FRAME *pFrame = FindFrame(pGlyph, pMap->piStrokeIndex[iStroke]);
  409. ASSERT(pFrame);
  410. if (!pFrame)
  411. {
  412. ret = HRCR_ERROR;
  413. break;
  414. }
  415. if (!AddFrameGLYPH(pNewGlyph, pFrame))
  416. {
  417. ret = HRCR_MEMERR;
  418. break;
  419. }
  420. }
  421. if (HRCR_OK == ret)
  422. {
  423. ret = RecognizePhrase(pMainXrc, pNewGlyph, yDev, iDepth, pAnsSet);
  424. }
  425. if (pNewGlyph)
  426. {
  427. DestroyGLYPH(pNewGlyph);
  428. }
  429. }
  430. // Throw away this contect as we have delt with it word by word
  431. DestroyHRC(full);
  432. return (ret);
  433. }
  434. ret = ProcessPhraseAlts(pXrc);
  435. if (ret != HRCR_OK)
  436. {
  437. DestroyHRC(full);
  438. return ret;
  439. }
  440. return isolate(pXrc, pAnsSet);
  441. }
  442. // Finds between-stroke gaps large enough to disallow grouping together within a single word,
  443. // and calls RecognizePhrase() for each group of strokes between such gaps.
  444. // Need to be able to go back and put a long-delayed stroke back in the right group.
  445. // Anything stradling 2 groups, or between 2 groups, should be its own group.
  446. // pGuide points to a 1-line guide that we already know our ink is within, so we don't need
  447. // to compute the line number.
  448. static int RecognizeLine ( XRC *pxrc,
  449. INKLINE *pLine,
  450. ANSWER_SET *pAnsSet
  451. )
  452. {
  453. GLYPH **aGlyphs;
  454. int cGlyphs = 0,
  455. i,
  456. last,
  457. right = INT_MIN;
  458. int maxgap;
  459. GLYPH *pScaledGlyph = NULL;
  460. int iRet = HRCR_ERROR;
  461. int cOldAnsSet;
  462. int yDev;
  463. GUIDE OrigGuide;
  464. if (!pxrc || !pLine || !pAnsSet || !pLine->pGlyph)
  465. {
  466. return HRCR_ERROR;
  467. }
  468. // init the output line segmentation
  469. pLine->pResults = NULL;
  470. // point to the guide
  471. if (pxrc->bGuide)
  472. {
  473. OrigGuide = pxrc->guide;
  474. // scale the ink & guide
  475. pScaledGlyph = TranslateAndScaleLine (pLine, &pxrc->guide);
  476. }
  477. else
  478. {
  479. // scale the ink
  480. pScaledGlyph = TranslateAndScaleLine (pLine, NULL);
  481. }
  482. // compute yDev of the scaled ink
  483. yDev = YDeviation (pScaledGlyph);
  484. // compute maxgap
  485. maxgap = 11 * yDev / 2;
  486. // Do the hard breaking of the line based on obvious gaps
  487. aGlyphs = (GLYPH **)_alloca(CframeGLYPH(pScaledGlyph) * sizeof(GLYPH *));
  488. for (; pScaledGlyph; pScaledGlyph = pScaledGlyph->next)
  489. {
  490. FRAME *pFrame = pScaledGlyph->frame;
  491. RECT *pRect = RectFRAME(pFrame);
  492. ASSERT((pRect->left - maxgap) >= INT_MIN);
  493. if (right < (pRect->left - maxgap))
  494. {
  495. if (!(cGlyphs < MAXPHRASES))
  496. goto exit;
  497. aGlyphs[cGlyphs] = NewGLYPH();
  498. if (!aGlyphs[cGlyphs])
  499. goto exit;
  500. aGlyphs[cGlyphs]->frame = pFrame;
  501. right = pRect->right;
  502. cGlyphs++;
  503. }
  504. else
  505. {
  506. if (!AddFrameGLYPH(aGlyphs[cGlyphs-1], pFrame))
  507. goto exit;
  508. if (right < pRect->right)
  509. right = pRect->right;
  510. }
  511. }
  512. cOldAnsSet = pAnsSet->cAnsSets;
  513. last = cGlyphs - 1;
  514. for (i = 0; i < cGlyphs; i++)
  515. {
  516. iRet = RecognizePhrase(pxrc, aGlyphs[i], yDev, 0, pAnsSet);
  517. if (iRet != HRCR_OK)
  518. goto exit;
  519. DestroyGLYPH(aGlyphs[i]);
  520. aGlyphs[i] = NULL;
  521. }
  522. // convert the last answerset alternates to linesegm
  523. if (pAnsSet->cAnsSets > cOldAnsSet)
  524. {
  525. pLine->pResults = GenLineSegm (pAnsSet->cAnsSets - cOldAnsSet, pAnsSet->pAlternates + cOldAnsSet);
  526. if (!pLine->pResults)
  527. goto exit;
  528. }
  529. iRet = HRCR_OK;
  530. exit:
  531. // restore the xrc's guide
  532. if (pxrc->bGuide)
  533. {
  534. pxrc->guide = OrigGuide;
  535. }
  536. for (i = 0; i < cGlyphs; i++)
  537. {
  538. if (aGlyphs[i])
  539. {
  540. DestroyGLYPH(aGlyphs[i]);
  541. }
  542. }
  543. return iRet;
  544. }
  545. /***********************************************************************
  546. *
  547. * BuildStringFromParts
  548. *
  549. * Merge the isolated words in a array of alternates into a single string
  550. * and keep the alternates for each word in the compound
  551. *************************************************************************/
  552. int BuildStringFromParts(XRC *pXrc, ALTERNATES *ppWords, unsigned int cWords)
  553. {
  554. XRCRESULT *pRes;
  555. WORDMAP *pMaps;
  556. unsigned int len, pos;
  557. char *sz;
  558. int cStroke = 0;
  559. int cTotStroke;
  560. int *piIndex;
  561. cTotStroke = CframeGLYPH(pXrc->pGlyph);
  562. ASSERT(pXrc);
  563. pRes = pXrc->answer.aAlt;
  564. ASSERT(pRes);
  565. pRes->cWords = cWords;
  566. if (cWords <=0)
  567. {
  568. pRes->pMap = NULL;
  569. pRes->szWord = NULL;
  570. pXrc->answer.cAlt = 0;
  571. return cWords;
  572. }
  573. pXrc->answer.cAlt = 1;
  574. ASSERT(cWords);
  575. pMaps = (WORDMAP *)ExternAlloc(sizeof(WORDMAP) * cWords);
  576. ASSERT(pMaps);
  577. if (!pMaps)
  578. goto failure;
  579. // Count total number of chars and number of strokes
  580. // across all alternates
  581. for (len = 0, pos = 0; pos < cWords; pos++)
  582. {
  583. if (ppWords[pos].cAlt)
  584. {
  585. len += strlen(ppWords[pos].aAlt[0].szWord) + 1;
  586. if (ppWords[pos].aAlt[0].pMap)
  587. {
  588. cStroke += ppWords[pos].aAlt[0].pMap->cStrokes;
  589. }
  590. }
  591. }
  592. ASSERT(len);
  593. // ??? Are all strokes accounted for
  594. ASSERT(cStroke == cTotStroke);
  595. piIndex = (int *)ExternAlloc(sizeof(*pMaps->piStrokeIndex) * cStroke);
  596. pRes->cWords = cWords;
  597. pRes->pMap = pMaps;
  598. pRes->szWord = (char *)ExternAlloc(len * sizeof(*pRes->szWord));
  599. ASSERT(pRes->szWord);
  600. if (!(pRes->szWord))
  601. {
  602. ExternFree(pMaps);
  603. goto failure;
  604. }
  605. pRes->cost = 0;
  606. pRes->pXRC = pXrc;
  607. pos = 0;
  608. sz = pRes->szWord;
  609. // Finally build the string and
  610. // set alternate lists for each word in the string
  611. for (; cWords; cWords--, ppWords++, pMaps++)
  612. {
  613. int cAlt = ppWords->cAlt;
  614. char *szWord;
  615. XRCRESULT *pAltRes;
  616. unsigned int iAlt;
  617. // Should always have something recognized
  618. ASSERT (cAlt > 0);
  619. if (cAlt <= 0)
  620. {
  621. continue;
  622. }
  623. szWord = ppWords->aAlt[0].szWord;
  624. if (pos)
  625. {
  626. pos++;
  627. *sz++ = ' ';
  628. }
  629. pMaps->start = (unsigned short int)pos;
  630. strcpy(sz, szWord);
  631. pMaps->len = (unsigned short int)strlen(szWord);
  632. pos += pMaps->len;
  633. sz += pMaps->len;
  634. if (cAlt > 0)
  635. {
  636. pMaps->cStrokes = ppWords->aAlt->pMap->cStrokes;
  637. cStroke -= pMaps->cStrokes;
  638. ASSERT(cStroke >= 0);
  639. pMaps->piStrokeIndex = piIndex + cStroke;
  640. memcpy(pMaps->piStrokeIndex, ppWords->aAlt->pMap->piStrokeIndex, sizeof(*pMaps->piStrokeIndex) * pMaps->cStrokes);
  641. }
  642. else
  643. {
  644. pMaps->cStrokes = 0;
  645. ASSERT(cStroke >= 0);
  646. pMaps->piStrokeIndex = piIndex + cStroke;
  647. }
  648. // Special Case Check for recognition failure
  649. if (1 == cAlt && strcmp(szWord, NOT_RECOGNIZED) == 0)
  650. {
  651. // Free up the memory associated with the alternates
  652. // because we now say there are 0 alternates
  653. ExternFree(ppWords->aAlt[0].szWord);
  654. ExternFree(ppWords->aAlt->pMap->piStrokeIndex);
  655. ExternFree(ppWords->aAlt->pMap);
  656. memset(&pMaps->alt, 0, sizeof(pMaps->alt));
  657. }
  658. else
  659. {
  660. memcpy(&(pMaps->alt), ppWords, sizeof(ALTERNATES));
  661. }
  662. // Set correct backPointers for each alternate
  663. pAltRes = pMaps->alt.aAlt;
  664. for (iAlt = 0 ; iAlt < pMaps->alt.cAlt ; ++iAlt, ++pAltRes)
  665. {
  666. pAltRes->pXRC = pXrc;
  667. }
  668. pRes->cost += ppWords->aAlt->cost;
  669. if (pRes->cost < 0)
  670. pRes->cost = INT_MAX;
  671. ppWords->cAlt = 0;
  672. }
  673. // Check that we have not forgot a terminating null
  674. ASSERT(strlen(pRes->szWord) < 400);
  675. ASSERT(strlen(pRes->szWord) < len);
  676. ASSERT(cStroke == 0);
  677. return 1;
  678. failure:
  679. pRes->cWords = 0;
  680. pRes->pMap = NULL;
  681. pRes->szWord = NULL;
  682. pRes->cost = 0;
  683. pRes->pXRC = NULL;
  684. return 0;
  685. }
  686. // Update the line information in an xrc presumeably after new ink had been added
  687. BOOL UpdateLineInfo (XRC *pxrc)
  688. {
  689. BOOL bRet = FALSE;
  690. GUIDE *pGuide = &(pxrc->guide);
  691. LINEBRK LineBrk;
  692. // do we have a guide?
  693. if (pxrc->bGuide)
  694. {
  695. if (GuideLineSep (pxrc->pGlyph, pGuide, &LineBrk) < 1)
  696. goto exit;
  697. }
  698. // We do not have a guide
  699. else
  700. {
  701. // Run the nn line sep
  702. if (NNLineSep (pxrc->pGlyph, &LineBrk) < 1)
  703. goto exit;
  704. }
  705. // Allocate a line breaking structure in the XRC if needed
  706. if (!pxrc->pLineBrk)
  707. {
  708. // alloc a line brk structure if needed
  709. pxrc->pLineBrk = (LINEBRK *) ExternAlloc (sizeof (*pxrc->pLineBrk));
  710. if (!pxrc->pLineBrk)
  711. {
  712. goto exit;
  713. }
  714. memset (pxrc->pLineBrk, 0, sizeof (*pxrc->pLineBrk));
  715. }
  716. // compare the lines with the old configuration
  717. CompareLineBrk (&LineBrk, pxrc->pLineBrk);
  718. // free the contents of the old structure
  719. FreeLines (pxrc->pLineBrk);
  720. // copy the new one
  721. memcpy (pxrc->pLineBrk, &LineBrk, sizeof (*pxrc->pLineBrk));
  722. bRet = TRUE;
  723. exit:
  724. return bRet;
  725. }
  726. // Performs recognition of a piece of ink in word mode
  727. int WordModeRecognize (XRC *pxrc)
  728. {
  729. GLYPH *pglAll;
  730. INKLINE *pLine;
  731. GUIDE LocalGuide, *pLocalGuide;
  732. int iRet;
  733. XRCRESULT *pRes;
  734. int cRes;
  735. // check the validity of the xrc
  736. if (!pxrc)
  737. {
  738. return HRCR_ERROR;
  739. }
  740. // Preset in case we abort
  741. iRet = HRCR_ERROR;
  742. pxrc->answer.cAlt = 0;
  743. // save the original glyph
  744. pglAll = pxrc->pGlyph;
  745. // refresh the the line information
  746. if (!CreateSingleLine (pxrc) || !pxrc->pLineBrk)
  747. {
  748. goto exit;
  749. }
  750. // point to the guide if any
  751. if (pxrc->bGuide)
  752. {
  753. LocalGuide = pxrc->guide;
  754. pLocalGuide = &LocalGuide;
  755. }
  756. else
  757. {
  758. pLocalGuide = NULL;
  759. }
  760. pLine = pxrc->pLineBrk->pLine;
  761. // if this line is not dirty, or the line is empty then exit
  762. if (!pLine->pGlyph || pLine->cStroke <= 0)
  763. {
  764. goto exit;
  765. }
  766. // Ink preprocessing: scale and translate the line ink if necessary
  767. pxrc->pGlyph = TranslateAndScaleLine (pLine, pLocalGuide);
  768. if (!pxrc->pGlyph)
  769. {
  770. goto exit;
  771. }
  772. // make sure that the line segmentation info is freed
  773. if (pLine->pResults)
  774. {
  775. FreeLineSegm (pLine->pResults);
  776. ExternFree (pLine->pResults);
  777. pLine->pResults = NULL;
  778. }
  779. if (InfProcessHRC ((HRC)pxrc, -1) != HRCR_OK || pxrc->answer.cAlt <= 0)
  780. {
  781. goto exit;
  782. }
  783. pRes = pxrc->answer.aAlt;
  784. cRes = pxrc->answer.cAlt;
  785. if (!bNnonlyEnabledXRC(pxrc))
  786. {
  787. if (CombineHMMScore(pxrc->pGlyph, &(pxrc->answer), pxrc->nfeatureset->iPrint) != HRCR_OK)
  788. {
  789. goto exit;
  790. }
  791. }
  792. // we no longer need this glyph
  793. DestroyFramesGLYPH (pxrc->pGlyph);
  794. // generate the line segmentation
  795. // if this function fail, we'll fail as well
  796. if (!WordModeGenLineSegm (pxrc))
  797. {
  798. goto exit;
  799. }
  800. iRet = HRCR_OK;
  801. exit:
  802. // restore back the original ink
  803. pxrc->pGlyph = pglAll;
  804. return iRet;
  805. }
  806. // recognizes a whole panel of ink
  807. // first breaks the lines and then each line is recognized separately
  808. int PanelModeRecognize (XRC *pxrc, DWORD dwRecoMode)
  809. {
  810. ANSWER_SET AnsSet;
  811. INKLINE *pLine;
  812. int iRet, iLine;
  813. // check the validity of the xrc
  814. if (!pxrc)
  815. {
  816. return HRCR_ERROR;
  817. }
  818. // Preset in case we abort
  819. iRet = HRCR_ERROR;
  820. pxrc->answer.cAlt = 0;
  821. // init the AnsSet
  822. memset(&AnsSet, 0, sizeof(AnsSet));
  823. // Prepare the AnsSet
  824. AnsSet.capSegments = 0;
  825. AnsSet.cAnsSets = 0;
  826. AnsSet.pAlternates = NULL;
  827. // refresh the the line information
  828. if (!UpdateLineInfo (pxrc) || !pxrc->pLineBrk)
  829. {
  830. goto exit;
  831. }
  832. // go thru all the lines
  833. for (iLine = 0; iLine < pxrc->pLineBrk->cLine; iLine++)
  834. {
  835. pLine = pxrc->pLineBrk->pLine + iLine;
  836. // if this line empty then skip it
  837. if (!pLine->pGlyph || pLine->cStroke <= 0)
  838. {
  839. continue;
  840. }
  841. // make sure that the line segmentation info is freed
  842. if (pLine->pResults)
  843. {
  844. FreeLineSegm (pLine->pResults);
  845. ExternFree (pLine->pResults);
  846. pLine->pResults = NULL;
  847. }
  848. // Recognize this line
  849. iRet = RecognizeLine (pxrc, pLine, &AnsSet);
  850. if (iRet != HRCR_OK)
  851. {
  852. goto exit;
  853. }
  854. // if we are in partial incremental mode, then we stop processing here
  855. if (dwRecoMode == RECO_MODE_INCREMENTAL)
  856. {
  857. break;
  858. }
  859. } // iLine Loop
  860. exit:
  861. // if we succeeded, build the answer
  862. if (iRet == HRCR_OK)
  863. {
  864. BuildStringFromParts(pxrc, AnsSet.pAlternates, AnsSet.cAnsSets);
  865. }
  866. // free the answer set
  867. ExternFree(AnsSet.pAlternates);
  868. return iRet;
  869. }