195 lines
4.6 KiB
C
195 lines
4.6 KiB
C
|
#include "common.h"
|
||
|
#include "glyph.h"
|
||
|
#include "score.h"
|
||
|
#include "fugu.h"
|
||
|
#include "volcanop.h"
|
||
|
#include "nnet.h"
|
||
|
|
||
|
#define FUGU_SEG_MAX_SPACES 32
|
||
|
|
||
|
static int g_cSpaces;
|
||
|
static FUGU_INTEGER_WEIGHTS g_aCharDetNet[FUGU_SEG_MAX_SPACES];
|
||
|
|
||
|
POINT *DupPoints(POINT *pOldPoints, int nPoints);
|
||
|
GLYPH *GlyphFromStrokes(UINT cStrokes, STROKE *pStrokes);
|
||
|
|
||
|
// prepare net from pointer
|
||
|
BOOL PrepareCharDetNet (BYTE *pb, FUGU_INTEGER_WEIGHTS *pCharDetNet)
|
||
|
{
|
||
|
pCharDetNet->pHeader = (FUGU_INTEGER_WEIGHTS_HEADER *) pb;
|
||
|
pCharDetNet->pfdchMapping = (WCHAR *) (pb + sizeof(FUGU_INTEGER_WEIGHTS_HEADER));
|
||
|
pCharDetNet->pbWeights = pb + sizeof(FUGU_INTEGER_WEIGHTS_HEADER) +
|
||
|
sizeof(WCHAR) * pCharDetNet->pHeader->arch.nOutputs;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// validates the header of the brknet
|
||
|
int CheckCharDetHeader (void *pData)
|
||
|
{
|
||
|
NNET_HEADER *pHeader = (NNET_HEADER *)pData;
|
||
|
|
||
|
// wrong magic number
|
||
|
ASSERT (pHeader->dwFileType == CHARDET_FILE_TYPE);
|
||
|
|
||
|
if (pHeader->dwFileType != CHARDET_FILE_TYPE)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// check version
|
||
|
ASSERT(pHeader->iFileVer >= CHARDET_OLD_FILE_VERSION);
|
||
|
ASSERT(pHeader->iMinCodeVer <= CHARDET_CUR_FILE_VERSION);
|
||
|
|
||
|
ASSERT ( !memcmp ( pHeader->adwSignature,
|
||
|
g_locRunInfo.adwSignature,
|
||
|
sizeof (pHeader->adwSignature)
|
||
|
)
|
||
|
);
|
||
|
|
||
|
ASSERT (pHeader->cSpace <= FUGU_SEG_MAX_SPACES);
|
||
|
|
||
|
if ( pHeader->iFileVer >= CHARDET_OLD_FILE_VERSION &&
|
||
|
pHeader->iMinCodeVer <= CHARDET_CUR_FILE_VERSION &&
|
||
|
!memcmp ( pHeader->adwSignature,
|
||
|
g_locRunInfo.adwSignature,
|
||
|
sizeof (pHeader->adwSignature)
|
||
|
) &&
|
||
|
pHeader->cSpace <= FUGU_SEG_MAX_SPACES
|
||
|
)
|
||
|
{
|
||
|
return pHeader->cSpace;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// does the necessary preparations for a net to be used later
|
||
|
static BOOL CharDetLoadFromPointer (BYTE *pData)
|
||
|
{
|
||
|
int iSpc, cSpc, iSpaceID;
|
||
|
NNET_SPACE_HEADER *pSpaceHeader;
|
||
|
BYTE *pPtr;
|
||
|
|
||
|
if (!pData)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// check the header info
|
||
|
cSpc = CheckCharDetHeader (pData);
|
||
|
g_cSpaces = cSpc;
|
||
|
if (cSpc <= 0)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
pPtr = pData + sizeof (NNET_HEADER);
|
||
|
|
||
|
for (iSpc = 0; iSpc < cSpc; iSpc++)
|
||
|
{
|
||
|
// point to the one and only space that we have
|
||
|
pSpaceHeader = (NNET_SPACE_HEADER *)pPtr;
|
||
|
pPtr += sizeof (NNET_SPACE_HEADER);
|
||
|
|
||
|
// point to the actual data
|
||
|
iSpaceID = pSpaceHeader->iSpace;
|
||
|
|
||
|
ASSERT (iSpaceID >= 1 && iSpaceID <= FUGU_SEG_MAX_SPACES);
|
||
|
|
||
|
if (!PrepareCharDetNet ( pData + pSpaceHeader->iDataOffset,
|
||
|
g_aCharDetNet + iSpaceID - 1
|
||
|
)
|
||
|
)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL LoadCharDetFromFile(wchar_t *wszPath, LOAD_INFO *pLoadInfo)
|
||
|
{
|
||
|
wchar_t awszFileName[MAX_PATH];
|
||
|
|
||
|
// memory map the file
|
||
|
wsprintf (awszFileName, L"%s\\chardet.bin", wszPath);
|
||
|
|
||
|
if (!DoOpenFile(pLoadInfo, awszFileName))
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return CharDetLoadFromPointer (pLoadInfo->pbMapping);
|
||
|
}
|
||
|
|
||
|
BOOL LoadCharDetFromResource (HINSTANCE hInst, int nResID, int nType)
|
||
|
{
|
||
|
LOAD_INFO LoadInfo;
|
||
|
|
||
|
if (!DoLoadResource (&LoadInfo, hInst, nResID, nType))
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return CharDetLoadFromPointer (LoadInfo.pbMapping);
|
||
|
}
|
||
|
|
||
|
BOOL CharDetUnloadFile(LOAD_INFO *pInfo)
|
||
|
{
|
||
|
return DoCloseFile(pInfo);
|
||
|
}
|
||
|
|
||
|
int *ApplyFuguInteger(FUGU_INTEGER_WEIGHTS *pFugu, int aiInput[FUGU_INPUT_WIDTH][FUGU_INPUT_HEIGHT]);
|
||
|
|
||
|
float FuguSegScore(int cStrokes, STROKE *pStrokes, LOCRUN_INFO *pLocRunInfo)
|
||
|
{
|
||
|
GLYPH *pGlyph = GlyphFromStrokes(cStrokes, pStrokes);
|
||
|
int i;
|
||
|
int aiInput[FUGU_INPUT_WIDTH][FUGU_INPUT_HEIGHT];
|
||
|
int *piOutput = NULL;
|
||
|
float flOutput = 0;
|
||
|
|
||
|
if (cStrokes >= 1 && cStrokes <= g_cSpaces)
|
||
|
{
|
||
|
// First convert the ink to 29x29 input region
|
||
|
if (!FuguRender(pGlyph, NULL, aiInput))
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
// Apply the recognizer
|
||
|
piOutput = ApplyFuguInteger(g_aCharDetNet + cStrokes - 1, aiInput);
|
||
|
if (piOutput == NULL)
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < g_aCharDetNet[cStrokes - 1].pHeader->arch.nOutputs; i++)
|
||
|
{
|
||
|
wchar_t wch = g_aCharDetNet[cStrokes - 1].pfdchMapping[i];
|
||
|
// I'm assuming every space will have "not a char" samples. Note that
|
||
|
// there are two possible mappings for "not a char", the old one L'0'
|
||
|
// and the new one which is simply 0.
|
||
|
if (wch == 0 || wch == L'0')
|
||
|
{
|
||
|
int cRight = FUGU_ACTIVATION_SCALE - piOutput[i];
|
||
|
if (cRight == 0)
|
||
|
{
|
||
|
cRight = 1;
|
||
|
}
|
||
|
|
||
|
flOutput = (float) cRight / (float) FUGU_ACTIVATION_SCALE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DestroyFramesGLYPH(pGlyph);
|
||
|
DestroyGLYPH(pGlyph);
|
||
|
ExternFree(piOutput);
|
||
|
return flOutput;
|
||
|
}
|