漫谈WinCE的手写识别技术(二)
//========================================================================
//TITLE:
// 漫谈WinCE的手写识别技术(二)
//AUTHOR:
// norains
//DATE:
// Thursday 25-January -2007
//Environment:
// EVC4.0 + Standard SDK
//========================================================================
在第一章的时候,已经介绍了识别的一般性过程,对于实际运用来说,是完全可行的;但从便利性角度出发,却不免显得烦琐:每次输入笔画都需留意点阵是否屏幕坐标系,每次读取返回的字符总要分配内存然后获取等等,诸如总总,代码写一次还好,如果多处运用多次编写多方维护,实在不是一件快乐的事情.
而我,最讨厌做复杂又要花费脑筋的东东;所以,为了让自己感觉得写代码是一件快乐的事情,自己又很高兴地将识别过程封装为一个类.至于是否达到简便的效果,不敢祈求大家苟同,只愿自己舒坦即可.
//////////////////////////////////////////////////////////////////////
// Recognizer.h: interface for the CRecognizer class. //
//////////////////////////////////////////////////////////////////////
#ifndef RECOGNIZER_H
#define RECOGNIZER_H
//===========================================================================
//Include file
#include "recog.h"
//=====================================================================================
//Choose the build type for the recognizing function
//--------------------------------------------------------------------------
#define RECOGNIZE_FUNCTION_FROM_DLL
//#define RECOGNIZE_FUNCTION_FROM_LIB
#ifndef RECOGNIZE_FUNCTION_FROM_LIB
#ifndef RECOGNIZE_FUNCTION_FROM_DLL
#define RECOGNIZE_FUNCTION_FROM_DLL
#endif
#endif
#ifdef RECOGNIZE_FUNCTION_FROM_DLL
#define RECOGNIZE_DLL_PATH TEXT("/WINDOWS/hwxcht.dll")
#endif
//=====================================================================================
//-----------------------------------------------------------------------------------
//The data type
//The scale type for the coordinate
enum ScaleType
{
SCALE_APPWND,
SCALE_SCREEN
};
//------------------------------------------------------------------------------
class CRecognizer
{
public:
BOOL InputStroke(POINT *lpPnt, int iCount, ScaleType scale);
CRecognizer();
virtual ~CRecognizer();
int GetCharacter(WCHAR *pWchar, int iCount);
BOOL EndRecognize();
BOOL BeginRecognize();
BOOL Initialize(HWND hWnd,const RECT *prcWnd,ScaleType scale);
protected:
HRC m_hrc;
HWXGUIDE m_hwxGuide;
HWND m_hWndRecog;
ALC m_alc;
#ifdef RECOGNIZE_FUNCTION_FROM_DLL
typedef BOOL (WINAPI *DLL_HWXCONFIG)(void);
typedef HRC (WINAPI *DLL_HWXCREATE)(HRC = NULL);
typedef BOOL (WINAPI *DLL_HWXSETGUIDE)(HRC ,HWXGUIDE*);
typedef BOOL (WINAPI *DLL_HWXALCVALID)(HRC,ALC);
typedef BOOL (WINAPI *DLL_HWXALCPRIORITY)(HRC,ALC);
typedef BOOL (WINAPI *DLL_HWXSETCONTEXT)(HRC,WCHAR);
typedef BOOL (WINAPI *DLL_HWXINPUT)(HRC,POINT*,UINT, DWORD);
typedef BOOL (WINAPI *DLL_HWXENDINPUT)(HRC);
typedef BOOL (WINAPI *DLL_HWXPROCESS)(HRC);
typedef INT (WINAPI *DLL_HWXRESULTSAVAILABLE)(HRC);
typedef INT32 (WINAPI *DLL_HWXGETRESULTS)(HRC, UINT, UINT, UINT, HWXRESULTS*);
typedef BOOL (WINAPI *DLL_HWXDESTROY)(HRC);
DLL_HWXCONFIG HWXCONFIG;
DLL_HWXCREATE HWXCREATE;
DLL_HWXSETGUIDE HWXSETGUIDE;
DLL_HWXALCVALID HWXALCVALID;
DLL_HWXALCPRIORITY HWXALCPRIORITY;
DLL_HWXSETCONTEXT HWXSETCONTEXT;
DLL_HWXINPUT HWXINPUT;
DLL_HWXPROCESS HWXPROCESS;
DLL_HWXRESULTSAVAILABLE HWXRESULTSAVAILABLE;
DLL_HWXGETRESULTS HWXGETRESULTS;
DLL_HWXDESTROY HWXDESTROY;
DLL_HWXENDINPUT HWXENDINPUT;
#endif //RECOGNIZE_FUNCTION_FROM_DLL
#ifdef RECOGNIZE_FUNCTION_FROM_LIB
#define HWXCONFIG(void) HwxConfig(void)
#define HWXCREATE(hrc) HwxCreate(hrc)
#define HWXSETGUIDE(hrc,lpGuide) HwxSetGuide(hrc,lpGuide)
#define HWXALCVALID(hrc,alc) HwxALCValid(hrc,alc)
#define HWXALCPRIORITY(hrc,alc) HwxALCPriority(hrc,alc)
#define HWXSETCONTEXT(hrc,wContext) HwxSetContext(hrc,wContext)
#define HWXINPUT(hrc,lppnt,upoints,timestamp) HwxInput(hrc,lppnt,upoints,timestamp)
#define HWXPROCESS(hrc) HwxProcess(hrc)
#define HWXRESULTSAVAILABLE(hrc) HwxResultsAvailable(hrc)
#define HWXGETRESULTS(hrc,cAlt,iFirst,cBoxRes,rgBoxResults) HwxGetResults(hrc,cAlt,iFirst,cBoxRes,rgBoxResults)
#define HWXDESTROY(hrc) HwxDestroy(hrc)
#define HWXENDINPUT(hrc) HwxEndInput(hrc)
#endif //RECOGNIZE_FUNCTION_FROM_LIB
};
//============================================================================================
#endif // !defined RECOGNIZER_H
//////////////////////////////////////////////////////////////////////
// Recognizer.cpp: implementation of the CRecognizer class. //
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Recognizer.h"
//-------------------------------------------------------------------
//Macro define
//The default value of hwxGuide
#define DEFAULT_HWXGUIDE_CHORZBOX 1
#define DEFAULT_HWXGUIDE_CVERTBOX 1
#define DEFAULT_HWXGUIDE_CXOFFSET 1
#define DEFAULT_HWXGUIDE_CYOFFSET 1
//The default value of ALC
#define DEFAULT_ALC ALC_KANJI_ALL
//--------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CRecognizer::CRecognizer()
{
m_alc = NULL;
m_hrc = NULL;
m_hWndRecog = NULL;
memset(&m_hwxGuide,0,sizeof(m_hwxGuide));
}
CRecognizer::~CRecognizer()
{
}
//-----------------------------------------------------------------------
//Descriptiong:
// Initialize the recognizer
//
//Parameter:
// hWnd: [in] The handle of window to be recognized
// rcWnd: [in] The window area to be recognized
// scale: [in] The scale base of prcWnd point
//-----------------------------------------------------------------------
BOOL CRecognizer::Initialize(HWND hWnd,const RECT *prcWnd,ScaleType scale)
{
m_hWndRecog = hWnd;
m_alc = DEFAULT_ALC;
RECT rcWnd = {0};
switch(scale)
{
case SCALE_APPWND:
{
rcWnd = *prcWnd;
rcWnd.left *= 4;
rcWnd.right *= 4;
rcWnd.top *= 4;
rcWnd.bottom *= 4;
MapWindowPoints(hWnd,HWND_DESKTOP,(LPPOINT)(&rcWnd),(sizeof(RECT)/sizeof(POINT)));
break;
}
case SCALE_SCREEN:
{
rcWnd = *prcWnd;
break;
}
}
m_hwxGuide.cHorzBox = DEFAULT_HWXGUIDE_CHORZBOX;
m_hwxGuide.cVertBox = DEFAULT_HWXGUIDE_CVERTBOX;
m_hwxGuide.xOrigin = rcWnd.left;
m_hwxGuide.yOrigin = rcWnd.top;
m_hwxGuide.cxBox = rcWnd.right - rcWnd.left;
m_hwxGuide.cyBox = rcWnd.bottom - rcWnd.top;
m_hwxGuide.cxOffset = DEFAULT_HWXGUIDE_CXOFFSET;
m_hwxGuide.cyOffset = DEFAULT_HWXGUIDE_CYOFFSET;
m_hwxGuide.cxWriting = (rcWnd.right - rcWnd.left) - m_hwxGuide.cxOffset * 2;
m_hwxGuide.cyWriting = (rcWnd.bottom - rcWnd.top) - m_hwxGuide.cyOffset * 2;
m_hwxGuide.nDir = HWX_HORIZONTAL;
#ifdef RECOGNIZE_FUNCTION_FROM_DLL
HINSTANCE hInstDll;
hInstDll = LoadLibrary(RECOGNIZE_DLL_PATH);
if(hInstDll != NULL)
{
HWXCONFIG = (DLL_HWXCONFIG) GetProcAddress(hInstDll,TEXT("HwxConfig"));
HWXCREATE = (DLL_HWXCREATE) GetProcAddress(hInstDll,TEXT("HwxCreate"));
HWXSETGUIDE = (DLL_HWXSETGUIDE) GetProcAddress(hInstDll,TEXT("HwxSetGuide"));
HWXALCVALID = (DLL_HWXALCVALID) GetProcAddress(hInstDll,TEXT("HwxALCValid"));
HWXALCPRIORITY = (DLL_HWXALCPRIORITY) GetProcAddress(hInstDll,TEXT("HwxALCPriority"));
HWXSETCONTEXT = (DLL_HWXSETCONTEXT) GetProcAddress(hInstDll,TEXT("HwxSetContext"));
HWXINPUT = (DLL_HWXINPUT) GetProcAddress(hInstDll,TEXT("HwxInput"));
HWXPROCESS = (DLL_HWXPROCESS) GetProcAddress(hInstDll,TEXT("HwxProcess"));
HWXRESULTSAVAILABLE = (DLL_HWXRESULTSAVAILABLE) GetProcAddress(hInstDll,TEXT("HwxResultsAvailable"));
HWXGETRESULTS = (DLL_HWXGETRESULTS) GetProcAddress(hInstDll,TEXT("HwxGetResults"));
HWXDESTROY = (DLL_HWXDESTROY) GetProcAddress(hInstDll,TEXT("HwxDestroy"));
HWXENDINPUT = (DLL_HWXENDINPUT) GetProcAddress(hInstDll,TEXT("HwxEndInput"));
}
else
{
return FALSE;
}
#endif //RECOGNIZE_FUNCTION_FROM_DLL
if(HWXCONFIG() == FALSE)
{
return FALSE;
}
return TRUE;
}
//-----------------------------------------------------------------------
//Descriptiong:
// Begin recognizing
//-----------------------------------------------------------------------
BOOL CRecognizer::BeginRecognize()
{
BOOL bRes = FALSE;
m_hrc = HWXCREATE();
if(m_hrc == NULL)
{
goto END;
}
bRes = HWXSETGUIDE(m_hrc,&m_hwxGuide);
if(bRes == FALSE)
{
goto END;
}
bRes = HWXALCVALID(m_hrc,m_alc);
if(bRes == FALSE)
{
goto END;
}
bRes = TRUE;
END:
return bRes;
}
//-----------------------------------------------------------------------
//Descriptiong:
// End recognizing
//-----------------------------------------------------------------------
BOOL CRecognizer::EndRecognize()
{
BOOL bRes = FALSE;
//Destroy the recognizer
if(HWXDESTROY(m_hrc) == FALSE)
{
goto END;
}
bRes = TRUE;
END:
return bRes;
}
//-----------------------------------------------------------------------
//Descriptiong:
// Get the character
//
//Parameters:
// pWchar: [out] The character get to be stored
// iCount: [in] The number of pWchar
//
//Return Values:
// 0: Failed
// >0: The number of the characters to return
//-----------------------------------------------------------------------
int CRecognizer::GetCharacter(WCHAR *pWchar, int iCount)
{
int iGetNum = 0;
int i = 0;
HWXRESULTS *phwxResults;
//Because each HWXRESULTS after the first one could store two characters,
//so only allocate (iCount / 2 + 1)
int iNum = iCount / 2 + 1;
phwxResults = new HWXRESULTS[iNum];
memset(phwxResults,0,iNum * sizeof(HWXRESULTS));
//End the input
if(HWXENDINPUT(m_hrc) == FALSE)
{
goto END;
}
//Analyze the information
if(HWXPROCESS(m_hrc) == FALSE)
{
goto END;
}
//Get the character from recognizer
if(HWXGETRESULTS(m_hrc,iCount,0,1,phwxResults) == FALSE)
{
goto END;
}
//Set the character to the stored buffer
for(i = 0; i < iNum; i++)
{
if(i == 0)
{
if(phwxResults[i].rgChar[0] != 0)
{
pWchar[iGetNum ++] = phwxResults[i].rgChar[0];
}
else
{
break;
}
}
else
{
//The indxBox member also store the character
if(phwxResults[i].indxBox != 0)
{
pWchar[iGetNum ++] = phwxResults[i].indxBox ;
}
else
{
break;
}
if(phwxResults[i].rgChar[0] != 0)
{
pWchar[iGetNum ++] = phwxResults[i].rgChar[0];
}
else
{
break;
}
}
}
END:
if(phwxResults != NULL)
{
delete [] phwxResults;
}
return iGetNum;
}
//-----------------------------------------------------------------------
//Descriptiong:
// Input the stroke
//
//Parameter:
// lpPnt: [in] Pointer to the stroke POINT
// iCount: [in] The count of the lpPnt
// scale: [in] The scale base of lpPnt
//-----------------------------------------------------------------------
BOOL CRecognizer::InputStroke(POINT *lpPnt, int iCount, ScaleType scale)
{
BOOL bRes = FALSE;
int i = 0;
POINT *pt;
pt = new POINT[iCount];
if(pt == NULL)
{
goto END;
}
for(i = 0; i < iCount; i++)
{
pt[i] = lpPnt[i];
if(scale == SCALE_APPWND)
{
//Convert to the screen scale
pt[i].x *= 4;
pt[i].y *= 4;
MapWindowPoints(m_hWndRecog, HWND_DESKTOP, &pt[i], 1);
}
}
//Input stroke
bRes = HWXINPUT(m_hrc,pt,iCount,0);
if(bRes == FALSE)
{
goto END;
}
bRes = TRUE;
END:
if(pt != NULL)
{
delete [] pt;
}
return bRes;
}
不知道大家看到这段代码有什么感觉,反正我是挺高兴的,因为让我从繁琐的识别过程中脱离出来.
关于代码,也许最让人疑惑的可能是这两个宏:RECOGNIZE_FUNCTION_FROM_DLL,RECOGNIZE_FUNCTION_FROM_LIB.
顾名思义,RECOGNIZE_FUNCTION_FROM_DLL表明识别函数调用是来源于动态链接库(DLL),同理,RECOGNIZE_FUNCTION_FROM_LIB则是编译的时候链接到lib库.为什么需要定义这两个宏呢?因为在标准的SDK下,如果直接包含"recog.h"后调用相关识别函数,是会报link错误.因为标准的SDK是不包含任何手写识别组件的.从调试的便利性来说,这时候如果只拷贝识别库到模拟器就可以顺利测试程序,绝对比重新定制一个包含手写识别引擎的系统要来得方便.
在示例代码中,因为是识别繁体中文,所以包含的动态链接库为:hwxcht.dll.如果需要识别其它文字,则只要更改该动态链接库名称即可.当然,还要更改DEFAULT_ALC宏,这个宏定义了识别的范围.
因为示例代码中的识别函数全部是宏定义,具体意义根据函数的来源而不同,所以RECOGNIZE_FUNCTION_FROM_DLL和RECOGNIZE_FUNCTION_FROM_LIB同一时间只能定义一个.如果两个都定义,毫无疑问,出错!^_^
最后,用伪代码做范例说明如何使用该封装类,以此做本章结尾:
Rect rcWnd;
/*rcWnd 获取应用窗口hWnd的大小*/
//初始化
//直接赋值窗口坐标,函数体内部会根据标志直接转换为屏幕坐标
recog.Initialize(hWnd,&rcWnd,SCALE_APPWND);
//开始识别
recog.BeginRecognize();
POINT pt[200];
int iCount = 0;
/*获取笔画坐标给pt,坐标的数量储存在iCount中*/
//将笔画点阵传送给识别引擎
//如果有多个笔画,则每个笔画都需要调用该函数进行传入
recog.InputStroke(pt,iCount,SCALE_APPWND);
//获取十个最接近的字符,iReturn是实际返回的字符数
WCHAR wChar[10];
int iReturn = recog.GetCharacter(wChar,10);
//结束识别
recog.EndRecognize();
/*如果