|
|
// Panel.c
// James A. Pittman
// July 23, 1998
// Recognizes an entire panel at once by looping over lines, then looping over
// ink blobs that have large gaps between them, and finally looping over the strokes
// within a blob, using recognition scores to help decide what is a word and what is not.
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "common.h"
#include "inferno.h"
#include "nfeature.h"
#include "engine.h"
#include "Panel.h"
#include "PorkyPost.h"
#include "combiner.h"
#include "Normal.h"
#include "linebrk.h"
#include "privdefs.h"
#include "recoutil.h"
#define STREQ(s,t) !strcmp(s,t)
#define STRDIFF(s,t) strcmp(s,t)
#define PHRASE_GROW_SIZE 4
#define NOT_RECOGNIZED "\a"
#define BASELIMIT 700 // scale ink to this guide hgt if we have a guide
#define MAX_SCALE 10 // We will not scale more than this.
#define MAXPHRASES 50
static int _cdecl ResultCmp(const XRCRESULT *a, const XRCRESULT *b) { if (a->cost > b->cost) return(1); else if (a->cost < b->cost) return(-1);
return(0); }
// Add a map structere to the XRCRESULT containing the stroke
// iDs in the glyph
static int AddStrokeIdFromGlyph(GLYPH *pGlyph, XRCRESULT *pAns) { int ret = HRCR_ERROR;
ASSERT(pGlyph); ASSERT(pAns); ASSERT(pAns->pMap);
if (pGlyph && pAns && pAns->pMap) { int iLastIdx = -1, *piIdx; pAns->pMap->cStrokes = CframeGLYPH(pGlyph); piIdx = pAns->pMap->piStrokeIndex = (int *)ExternAlloc(sizeof(*pAns->pMap->piStrokeIndex) * pAns->pMap->cStrokes); ASSERT(pAns->pMap->piStrokeIndex); if (!pAns->pMap->piStrokeIndex) { return HRCR_MEMERR; } // Add the strokes in ascending order
for ( ; pGlyph ; pGlyph = pGlyph->next) { if (pGlyph->frame) { int iFrame = pGlyph->frame->iframe; ASSERT(iFrame != iLastIdx); if (iFrame < iLastIdx) { int *piInsert = pAns->pMap->piStrokeIndex; // Search to find insertion point
for ( ; piInsert < piIdx ; ++piInsert) { if (iFrame < *piInsert) { int iSwap = *piInsert; *piInsert = iFrame; iFrame = iSwap; } } } iLastIdx = *piIdx = iFrame; ++piIdx; ASSERT (piIdx - pAns->pMap->piStrokeIndex <= pAns->pMap->cStrokes); } }
ret = HRCR_OK; }
return ret; } // This function was pulled out of MadHwxProcess(), and then modified. I took out the test that
// allows us to skip the post processor when its answer appears certain, so that the numerical
// score on the top 1 word is on the same scale for every word.
static int RecognizeWord(XRC *pxrc, int yDev) { XRCRESULT *pAns; int cAns, ret;
ret = InfProcessHRC((HRC)pxrc, yDev);
if (HRCR_OK != ret) return ret;
pAns = pxrc->answer.aAlt; cAns = pxrc->answer.cAlt;
// Inferno, if it cannot featurize the ink,
// Fill in 0 words recognized
if (!(pxrc->nfeatureset) || 0 == cAns) { GLYPH *pGlyph = pxrc->pGlyph;
if (pAns[0].szWord) { ExternFree(pAns[0].szWord); }
pAns[0].cost = INT_MAX;
pAns->szWord = (char *)ExternAlloc(sizeof(*pAns->szWord)*(strlen(NOT_RECOGNIZED) + 1)); ASSERT(pAns->szWord); if (!pAns->szWord) { return HRCR_MEMERR; } strcpy(pAns->szWord , NOT_RECOGNIZED);
pxrc->answer.cAlt = 1; pAns->cWords = 1; pAns->pXRC = pxrc;
pAns->pMap = ExternAlloc(sizeof(*pAns->pMap)); ASSERT(pAns->pMap); if (!pAns->pMap) { ret = HRCR_MEMERR; goto fail; } pAns->pMap->start = 0; pAns->pMap->len = strlen(pAns->szWord);
memset(&pAns->pMap->alt, 0, sizeof(pAns->pMap->alt)); ret = AddStrokeIdFromGlyph(pGlyph, pAns); }
return ret;
fail: if (pAns->szWord) { ExternFree(pAns->szWord); }
return ret; }
//!!! BUG - This should be setting up the HRC from the values set in the xrc
//!!! for madcow. We need that xrc passed down to here and set the exact
//!!! same alc, dict mode, lang mode etc in from it.
// set up a the HRC for word recognition (Do not allow white space)
static int initWordHRC(XRC *pMainXrc, GLYPH *pGlyph, HRC *phrc) { int cFrame; HRC hrc = CreateCompatibleHRC((HRC)pMainXrc, NULL); int ret = HRCR_OK; XRC *pxrcNew;
*phrc = (HRC)0;
if (!hrc) // don't go to failure as we do not need to destroy an HRC
return HRCR_MEMERR;
// disable ALC_WHITE
//ret = SetAlphabetHRC(hrc, pMainXrc->alc & ~ALC_WHITE, NULL);
ret = SetHwxFlags(hrc, pMainXrc->flags | RECOFLAG_WORDMODE); if (ret != HRCR_OK) goto failure;
pxrcNew = (XRC *) hrc; pxrcNew->answer.cAltMax = MAX_ALT_WORD;
// build glyph of specific frames inside hrc
// we later may be able to alter the API to allow additional frames to be
// added after recognition has already been run
for ( cFrame = 0 ; pGlyph; pGlyph = pGlyph->next, ++cFrame) { FRAME *pFrame = pGlyph->frame, *pAddedFrame; ASSERT(pFrame);
if (!pFrame) { ret = HRCR_ERROR; goto failure; }
ret = AddPenInputHRC(hrc, RgrawxyFRAME(pFrame), NULL, 0, &(pFrame->info)); if (ret != HRCR_OK) goto failure;
// Keep globally allocated frame numbers
if ( (pAddedFrame = FrameAtGLYPH(((XRC *)hrc)->pGlyph, cFrame))) { pAddedFrame->iframe = pFrame->iframe; pAddedFrame->rect = pFrame->rect; } }
*phrc = hrc; return HRCR_OK;
failure: DestroyHRC(hrc); *phrc = (HRC)0; return ret; }
// set up a the HRC for phrase recognition (Allow white space)
static int initPhraseHRC(XRC *pMainXrc, GLYPH *pGlyph, HRC *phrc) { int ret = HRCR_OK;
if (HRCR_OK == initWordHRC(pMainXrc, pGlyph, phrc)) { XRC *pxrcNew = (XRC *)(*phrc);
ret = SetHwxFlags(*phrc, pMainXrc->flags & ~RECOFLAG_WORDMODE); pxrcNew->answer.cAltMax = MAX_ALT_PHRASE; if (ret != HRCR_OK) goto failure; }
return HRCR_OK;
failure: DestroyHRC(*phrc); *phrc = (HRC)0; return ret; }
// Special efficient version of ClearRCRESALT() that knows
// there are no mappings.
static void clearAlt(ALTERNATES *p) { unsigned int i;
for (i = 0; i < p->cAlt; i++) ExternFree(p->aAlt[i].szWord); p->cAlt = 0; }
/******************************************************************
* * isolate * * Store away a recognized isolated word together with all its alternates * in the answer set * **********************************************************************/ static int isolate(XRC *pXrc, ANSWER_SET *pAnsSet) { ALTERNATES *pAlt;
// Only add cases that were succesfully recognized
if (pXrc->answer.cAlt > 0) { if (pAnsSet->cAnsSets >= pAnsSet->capSegments) { pAnsSet->pAlternates = ExternRealloc(pAnsSet->pAlternates, sizeof(*pAnsSet->pAlternates) * (pAnsSet->cAnsSets + PHRASE_GROW_SIZE) ); } ASSERT(pAnsSet->pAlternates); if (! pAnsSet->pAlternates) { return HRCR_MEMERR; } pAlt = pAnsSet->pAlternates + pAnsSet->cAnsSets; // Copy over the answers
memcpy(pAlt, &pXrc->answer, sizeof(*pAnsSet->pAlternates)); // Because we copied over the pointers to allocated
// memory make sure allocated buffers in the XRC are not freed
memset(&(pXrc->answer), 0, sizeof(pXrc->answer)); ++pAnsSet->cAnsSets;
#ifndef NDEBUG
ValidateALTERNATES(pAlt); #endif
}
DestroyHRC((HRC)pXrc);
return HRCR_OK; }
/*******************************************************************************
* * ProcessPhraseAlts * * Process phrase alternates. First collect all alternates that have * a single word and post process them to get modified scores. * Combine the best multiple words together into a single phrase * *******************************************************************************/ static int ProcessPhraseAlts(XRC *pXrc) { XRCRESULT *pRes; unsigned int iAlt, cAlt; int ret = HRCR_OK; int cMultWord = 0; ALTERNATES alt;
memset(&alt, 0, sizeof(alt));
alt.cAltMax = MAXMAXALT;
ASSERT(pXrc); pRes = pXrc->answer.aAlt; //cAlt = pXrc->answer.cAlt;
if (pXrc->iSpeed < 50) { ASSERT(pXrc->iSpeed >= 0); cAlt = 10 - (7 * pXrc->iSpeed) / 50; } else { ASSERT(pXrc->iSpeed <= 100); cAlt = 10 - (3 * pXrc->iSpeed) / 50 - 4; }
cAlt = min(cAlt, pXrc->answer.cAlt);
// Dont bother if nothing was recognized
if ( cAlt <= 1) { return ret; }
ASSERT(pRes);
for (iAlt = 0 ; iAlt < cAlt ; ++iAlt, ++pRes) { if (1 == pRes->cWords) { XRCRESULT *pWordRes = alt.aAlt + alt.cAlt;
memcpy(pWordRes, pRes, sizeof(*pRes));
++alt.cAlt; } else { // For now dont deal with multi-word phrases alternates
// Free their memory
FreeIdxWORDMAP(pRes); if (pRes->pMap) { ExternFree(pRes->pMap); } if (pRes->szWord) { ExternFree(pRes->szWord); } } }
// Combiner recognizer scores
// for cases with a single word
ret = bNnonlyEnabledXRC(pXrc) ? HRCR_OK : CombineHMMScore(pXrc->pGlyph, &alt, pXrc->nfeatureset->iPrint);
if (ret != HRCR_OK) { return ret; }
// Run on multiple words and copy back results from single words
// WARNING: only copy back the number you asked the Post processor to
// process. So that the sosts for the unprocessed alternates will be on
// a very different scale
pRes = pXrc->answer.aAlt;
for (iAlt = 0 ; HRCR_OK == ret && iAlt < alt.cAlt ; ++iAlt, ++pRes) { memcpy(pRes, alt.aAlt + iAlt, sizeof(*pRes)); } return ret; }
// Search for a frame by frame ID
static FRAME *FindFrame(GLYPH *pGlyph, int iFrame) { for ( ; pGlyph ; pGlyph = pGlyph->next) { if (pGlyph->frame && pGlyph->frame->iframe == iFrame) { return pGlyph->frame; } }
return NULL; }
/***********************************************************************
* * RecognizePhrase * * Run recognition on a "chunk" (or phrase) of ink. Calls the recognizer. * If search comes back with a white space break. Then recurse till get * single words * ***********************************************************************/ static int RecognizePhrase(XRC *pMainXrc, GLYPH *pGlyph, int yDev, int iDepth, ANSWER_SET *pAnsSet) { int ret; HRC full; XRC *pXrc; int bRedo = 0;
if (iDepth > 0) { // When called recursively use Word mode
ret = initWordHRC(pMainXrc, pGlyph, &full); } else { // First call use panel mode
ret = initPhraseHRC(pMainXrc, pGlyph, &full); }
if (ret != HRCR_OK) return ret;
pXrc = (XRC *)full;
ret = RecognizeWord(pXrc, yDev); if (ret != HRCR_OK) { DestroyHRC(full); return ret; }
++iDepth;
// Check if any alternates have a word break
if (iDepth <= 1) { unsigned int i; XRCRESULT *pRes = pXrc->answer.aAlt;
for (i = 0 ; i < pXrc->answer.cAlt && !bRedo ; ++i, ++pRes) { if (pRes->cWords > 1) { bRedo = 1; } }
}
// Did we get a multiword back?
if (pXrc->answer.aAlt->cWords > 1 || bRedo) { // Keep recursing till get no word breaks
unsigned int i; XRCRESULT *pRes = pXrc->answer.aAlt;
for (i = 0 ; i < pRes->cWords && HRCR_OK == ret ; ++i) { int iStroke; GLYPH *pNewGlyph = NULL; WORDMAP *pMap = pRes->pMap + i;
ASSERT(pMap); pNewGlyph = NewGLYPH();
if (!pNewGlyph) { ret = HRCR_MEMERR; break; }
for (iStroke = 0 ; pMap && iStroke < pMap->cStrokes ; ++iStroke) { FRAME *pFrame = FindFrame(pGlyph, pMap->piStrokeIndex[iStroke]);
ASSERT(pFrame); if (!pFrame) { ret = HRCR_ERROR; break; }
if (!AddFrameGLYPH(pNewGlyph, pFrame)) { ret = HRCR_MEMERR; break; } }
if (HRCR_OK == ret) { ret = RecognizePhrase(pMainXrc, pNewGlyph, yDev, iDepth, pAnsSet); }
if (pNewGlyph) { DestroyGLYPH(pNewGlyph); } }
// Throw away this contect as we have delt with it word by word
DestroyHRC(full); return (ret); }
ret = ProcessPhraseAlts(pXrc);
if (ret != HRCR_OK) { DestroyHRC(full); return ret; }
return isolate(pXrc, pAnsSet); }
// Finds between-stroke gaps large enough to disallow grouping together within a single word,
// and calls RecognizePhrase() for each group of strokes between such gaps.
// Need to be able to go back and put a long-delayed stroke back in the right group.
// Anything stradling 2 groups, or between 2 groups, should be its own group.
// pGuide points to a 1-line guide that we already know our ink is within, so we don't need
// to compute the line number.
static int RecognizeLine ( XRC *pxrc, INKLINE *pLine, ANSWER_SET *pAnsSet )
{ GLYPH **aGlyphs; int cGlyphs = 0, i, last, right = INT_MIN; int maxgap;
GLYPH *pScaledGlyph = NULL; int iRet = HRCR_ERROR; int cOldAnsSet; int yDev; GUIDE OrigGuide;
if (!pxrc || !pLine || !pAnsSet || !pLine->pGlyph) { return HRCR_ERROR; }
// init the output line segmentation
pLine->pResults = NULL;
// point to the guide
if (pxrc->bGuide) { OrigGuide = pxrc->guide;
// scale the ink & guide
pScaledGlyph = TranslateAndScaleLine (pLine, &pxrc->guide); } else { // scale the ink
pScaledGlyph = TranslateAndScaleLine (pLine, NULL); } // compute yDev of the scaled ink
yDev = YDeviation (pScaledGlyph);
// compute maxgap
maxgap = 11 * yDev / 2;
// Do the hard breaking of the line based on obvious gaps
aGlyphs = (GLYPH **)_alloca(CframeGLYPH(pScaledGlyph) * sizeof(GLYPH *));
for (; pScaledGlyph; pScaledGlyph = pScaledGlyph->next) { FRAME *pFrame = pScaledGlyph->frame; RECT *pRect = RectFRAME(pFrame);
ASSERT((pRect->left - maxgap) >= INT_MIN); if (right < (pRect->left - maxgap)) { if (!(cGlyphs < MAXPHRASES)) goto exit;
aGlyphs[cGlyphs] = NewGLYPH();
if (!aGlyphs[cGlyphs]) goto exit;
aGlyphs[cGlyphs]->frame = pFrame; right = pRect->right; cGlyphs++; } else { if (!AddFrameGLYPH(aGlyphs[cGlyphs-1], pFrame)) goto exit;
if (right < pRect->right) right = pRect->right; } }
cOldAnsSet = pAnsSet->cAnsSets;
last = cGlyphs - 1;
for (i = 0; i < cGlyphs; i++) { iRet = RecognizePhrase(pxrc, aGlyphs[i], yDev, 0, pAnsSet); if (iRet != HRCR_OK) goto exit;
DestroyGLYPH(aGlyphs[i]); aGlyphs[i] = NULL; }
// convert the last answerset alternates to linesegm
if (pAnsSet->cAnsSets > cOldAnsSet) { pLine->pResults = GenLineSegm (pAnsSet->cAnsSets - cOldAnsSet, pAnsSet->pAlternates + cOldAnsSet); if (!pLine->pResults) goto exit; }
iRet = HRCR_OK;
exit:
// restore the xrc's guide
if (pxrc->bGuide) { pxrc->guide = OrigGuide; }
for (i = 0; i < cGlyphs; i++) { if (aGlyphs[i]) { DestroyGLYPH(aGlyphs[i]); } }
return iRet; }
/***********************************************************************
* * BuildStringFromParts * * Merge the isolated words in a array of alternates into a single string * and keep the alternates for each word in the compound *************************************************************************/ int BuildStringFromParts(XRC *pXrc, ALTERNATES *ppWords, unsigned int cWords) { XRCRESULT *pRes; WORDMAP *pMaps; unsigned int len, pos; char *sz; int cStroke = 0; int cTotStroke; int *piIndex;
cTotStroke = CframeGLYPH(pXrc->pGlyph); ASSERT(pXrc);
pRes = pXrc->answer.aAlt; ASSERT(pRes);
pRes->cWords = cWords;
if (cWords <=0) { pRes->pMap = NULL; pRes->szWord = NULL; pXrc->answer.cAlt = 0; return cWords; }
pXrc->answer.cAlt = 1;
ASSERT(cWords);
pMaps = (WORDMAP *)ExternAlloc(sizeof(WORDMAP) * cWords); ASSERT(pMaps); if (!pMaps) goto failure;
// Count total number of chars and number of strokes
// across all alternates
for (len = 0, pos = 0; pos < cWords; pos++) { if (ppWords[pos].cAlt) { len += strlen(ppWords[pos].aAlt[0].szWord) + 1;
if (ppWords[pos].aAlt[0].pMap) { cStroke += ppWords[pos].aAlt[0].pMap->cStrokes; } } } ASSERT(len);
// ??? Are all strokes accounted for
ASSERT(cStroke == cTotStroke);
piIndex = (int *)ExternAlloc(sizeof(*pMaps->piStrokeIndex) * cStroke);
pRes->cWords = cWords; pRes->pMap = pMaps; pRes->szWord = (char *)ExternAlloc(len * sizeof(*pRes->szWord)); ASSERT(pRes->szWord);
if (!(pRes->szWord)) { ExternFree(pMaps); goto failure; } pRes->cost = 0; pRes->pXRC = pXrc;
pos = 0; sz = pRes->szWord;
// Finally build the string and
// set alternate lists for each word in the string
for (; cWords; cWords--, ppWords++, pMaps++) { int cAlt = ppWords->cAlt; char *szWord; XRCRESULT *pAltRes; unsigned int iAlt;
// Should always have something recognized
ASSERT (cAlt > 0); if (cAlt <= 0) { continue; }
szWord = ppWords->aAlt[0].szWord;
if (pos) { pos++; *sz++ = ' '; }
pMaps->start = (unsigned short int)pos; strcpy(sz, szWord);
pMaps->len = (unsigned short int)strlen(szWord); pos += pMaps->len; sz += pMaps->len;
if (cAlt > 0) { pMaps->cStrokes = ppWords->aAlt->pMap->cStrokes; cStroke -= pMaps->cStrokes; ASSERT(cStroke >= 0);
pMaps->piStrokeIndex = piIndex + cStroke;
memcpy(pMaps->piStrokeIndex, ppWords->aAlt->pMap->piStrokeIndex, sizeof(*pMaps->piStrokeIndex) * pMaps->cStrokes); } else { pMaps->cStrokes = 0; ASSERT(cStroke >= 0); pMaps->piStrokeIndex = piIndex + cStroke; }
// Special Case Check for recognition failure
if (1 == cAlt && strcmp(szWord, NOT_RECOGNIZED) == 0) { // Free up the memory associated with the alternates
// because we now say there are 0 alternates
ExternFree(ppWords->aAlt[0].szWord); ExternFree(ppWords->aAlt->pMap->piStrokeIndex); ExternFree(ppWords->aAlt->pMap); memset(&pMaps->alt, 0, sizeof(pMaps->alt)); } else { memcpy(&(pMaps->alt), ppWords, sizeof(ALTERNATES)); }
// Set correct backPointers for each alternate
pAltRes = pMaps->alt.aAlt; for (iAlt = 0 ; iAlt < pMaps->alt.cAlt ; ++iAlt, ++pAltRes) { pAltRes->pXRC = pXrc; } pRes->cost += ppWords->aAlt->cost;
if (pRes->cost < 0) pRes->cost = INT_MAX; ppWords->cAlt = 0; }
// Check that we have not forgot a terminating null
ASSERT(strlen(pRes->szWord) < 400); ASSERT(strlen(pRes->szWord) < len);
ASSERT(cStroke == 0); return 1;
failure: pRes->cWords = 0; pRes->pMap = NULL; pRes->szWord = NULL; pRes->cost = 0; pRes->pXRC = NULL; return 0; }
// Update the line information in an xrc presumeably after new ink had been added
BOOL UpdateLineInfo (XRC *pxrc) { BOOL bRet = FALSE; GUIDE *pGuide = &(pxrc->guide); LINEBRK LineBrk;
// do we have a guide?
if (pxrc->bGuide) { if (GuideLineSep (pxrc->pGlyph, pGuide, &LineBrk) < 1) goto exit; } // We do not have a guide
else { // Run the nn line sep
if (NNLineSep (pxrc->pGlyph, &LineBrk) < 1) goto exit; }
// Allocate a line breaking structure in the XRC if needed
if (!pxrc->pLineBrk) { // alloc a line brk structure if needed
pxrc->pLineBrk = (LINEBRK *) ExternAlloc (sizeof (*pxrc->pLineBrk)); if (!pxrc->pLineBrk) { goto exit; }
memset (pxrc->pLineBrk, 0, sizeof (*pxrc->pLineBrk)); }
// compare the lines with the old configuration
CompareLineBrk (&LineBrk, pxrc->pLineBrk);
// free the contents of the old structure
FreeLines (pxrc->pLineBrk);
// copy the new one
memcpy (pxrc->pLineBrk, &LineBrk, sizeof (*pxrc->pLineBrk)); bRet = TRUE;
exit: return bRet; }
// Performs recognition of a piece of ink in word mode
int WordModeRecognize (XRC *pxrc) { GLYPH *pglAll; INKLINE *pLine; GUIDE LocalGuide, *pLocalGuide; int iRet; XRCRESULT *pRes; int cRes;
// check the validity of the xrc
if (!pxrc) { return HRCR_ERROR; }
// Preset in case we abort
iRet = HRCR_ERROR; pxrc->answer.cAlt = 0;
// save the original glyph
pglAll = pxrc->pGlyph;
// refresh the the line information
if (!CreateSingleLine (pxrc) || !pxrc->pLineBrk) { goto exit; }
// point to the guide if any
if (pxrc->bGuide) { LocalGuide = pxrc->guide; pLocalGuide = &LocalGuide; } else { pLocalGuide = NULL; }
pLine = pxrc->pLineBrk->pLine;
// if this line is not dirty, or the line is empty then exit
if (!pLine->pGlyph || pLine->cStroke <= 0) { goto exit; }
// Ink preprocessing: scale and translate the line ink if necessary
pxrc->pGlyph = TranslateAndScaleLine (pLine, pLocalGuide); if (!pxrc->pGlyph) { goto exit; }
// make sure that the line segmentation info is freed
if (pLine->pResults) { FreeLineSegm (pLine->pResults); ExternFree (pLine->pResults);
pLine->pResults = NULL; }
if (InfProcessHRC ((HRC)pxrc, -1) != HRCR_OK || pxrc->answer.cAlt <= 0) { goto exit; }
pRes = pxrc->answer.aAlt; cRes = pxrc->answer.cAlt;
if (!bNnonlyEnabledXRC(pxrc)) { if (CombineHMMScore(pxrc->pGlyph, &(pxrc->answer), pxrc->nfeatureset->iPrint) != HRCR_OK) { goto exit; } }
// we no longer need this glyph
DestroyFramesGLYPH (pxrc->pGlyph);
// generate the line segmentation
// if this function fail, we'll fail as well
if (!WordModeGenLineSegm (pxrc)) { goto exit; }
iRet = HRCR_OK;
exit: // restore back the original ink
pxrc->pGlyph = pglAll;
return iRet; }
// recognizes a whole panel of ink
// first breaks the lines and then each line is recognized separately
int PanelModeRecognize (XRC *pxrc, DWORD dwRecoMode) { ANSWER_SET AnsSet; INKLINE *pLine; int iRet, iLine; // check the validity of the xrc
if (!pxrc) { return HRCR_ERROR; }
// Preset in case we abort
iRet = HRCR_ERROR; pxrc->answer.cAlt = 0;
// init the AnsSet
memset(&AnsSet, 0, sizeof(AnsSet)); // Prepare the AnsSet
AnsSet.capSegments = 0; AnsSet.cAnsSets = 0; AnsSet.pAlternates = NULL;
// refresh the the line information
if (!UpdateLineInfo (pxrc) || !pxrc->pLineBrk) { goto exit; }
// go thru all the lines
for (iLine = 0; iLine < pxrc->pLineBrk->cLine; iLine++) { pLine = pxrc->pLineBrk->pLine + iLine;
// if this line empty then skip it
if (!pLine->pGlyph || pLine->cStroke <= 0) { continue; }
// make sure that the line segmentation info is freed
if (pLine->pResults) { FreeLineSegm (pLine->pResults); ExternFree (pLine->pResults);
pLine->pResults = NULL; }
// Recognize this line
iRet = RecognizeLine (pxrc, pLine, &AnsSet); if (iRet != HRCR_OK) { goto exit; }
// if we are in partial incremental mode, then we stop processing here
if (dwRecoMode == RECO_MODE_INCREMENTAL) { break; } } // iLine Loop
exit: // if we succeeded, build the answer
if (iRet == HRCR_OK) { BuildStringFromParts(pxrc, AnsSet.pAlternates, AnsSet.cAnsSets); }
// free the answer set
ExternFree(AnsSet.pAlternates); return iRet; }
|