//========================================================================
//TITLE:
// 漫谈WinCE的手写识别技术(二)
//AUTHOR:
// norains
//DATE:
// Thursday 25-January -2007
//Environment:
// EVC4.0 + Standard SDK
//========================================================================
在第一章的时候,已经介绍了识别的一般性过程,对于实际运用来说,是完全可行的;但从便利性角度出发,却不免显得烦琐:每次输入笔画都需留意点阵是否屏幕坐标系,每次读取返回的字符总要分配内存然后获取等等,诸如总总,代码写一次还好,如果多处运用多次编写多方维护,实在不是一件快乐的事情.
而我,最讨厌做复杂又要花费脑筋的东东;所以,为了让自己感觉得写代码是一件快乐的事情,自己又很高兴地将识别过程封装为一个类.至于是否达到简便的效果,不敢祈求大家苟同,只愿自己舒坦即可.


/**///////////////////////////////////////////////////////////////////////
//Recognizer.h:interfacefortheCRecognizerclass.//

/**///////////////////////////////////////////////////////////////////////
#ifndefRECOGNIZER_H
#defineRECOGNIZER_H

//===========================================================================
//Includefile
#include"recog.h"

//=====================================================================================
//Choosethebuildtypefortherecognizingfunction
//--------------------------------------------------------------------------
#defineRECOGNIZE_FUNCTION_FROM_DLL
//#defineRECOGNIZE_FUNCTION_FROM_LIB

#ifndefRECOGNIZE_FUNCTION_FROM_LIB
#ifndefRECOGNIZE_FUNCTION_FROM_DLL
#defineRECOGNIZE_FUNCTION_FROM_DLL
#endif
#endif

#ifdefRECOGNIZE_FUNCTION_FROM_DLL
#defineRECOGNIZE_DLL_PATHTEXT("/WINDOWS/hwxcht.dll")
#endif
//=====================================================================================



//-----------------------------------------------------------------------------------
//Thedatatype

//Thescaletypeforthecoordinate
enumScaleType

...{
SCALE_APPWND,
SCALE_SCREEN
};
//------------------------------------------------------------------------------
classCRecognizer

...{
public:
BOOLInputStroke(POINT*lpPnt,intiCount,ScaleTypescale);
CRecognizer();
virtual~CRecognizer();
intGetCharacter(WCHAR*pWchar,intiCount);
BOOLEndRecognize();
BOOLBeginRecognize();
BOOLInitialize(HWNDhWnd,constRECT*prcWnd,ScaleTypescale);
protected:
HRCm_hrc;
HWXGUIDEm_hwxGuide;
HWNDm_hWndRecog;
ALCm_alc;

#ifdefRECOGNIZE_FUNCTION_FROM_DLL
typedefBOOL(WINAPI*DLL_HWXCONFIG)(void);
typedefHRC(WINAPI*DLL_HWXCREATE)(HRC=NULL);
typedefBOOL(WINAPI*DLL_HWXSETGUIDE)(HRC,HWXGUIDE*);
typedefBOOL(WINAPI*DLL_HWXALCVALID)(HRC,ALC);
typedefBOOL(WINAPI*DLL_HWXALCPRIORITY)(HRC,ALC);
typedefBOOL(WINAPI*DLL_HWXSETCONTEXT)(HRC,WCHAR);
typedefBOOL(WINAPI*DLL_HWXINPUT)(HRC,POINT*,UINT,DWORD);
typedefBOOL(WINAPI*DLL_HWXENDINPUT)(HRC);
typedefBOOL(WINAPI*DLL_HWXPROCESS)(HRC);
typedefINT(WINAPI*DLL_HWXRESULTSAVAILABLE)(HRC);
typedefINT32(WINAPI*DLL_HWXGETRESULTS)(HRC,UINT,UINT,UINT,HWXRESULTS*);
typedefBOOL(WINAPI*DLL_HWXDESTROY)(HRC);
DLL_HWXCONFIGHWXCONFIG;
DLL_HWXCREATEHWXCREATE;
DLL_HWXSETGUIDEHWXSETGUIDE;
DLL_HWXALCVALIDHWXALCVALID;
DLL_HWXALCPRIORITYHWXALCPRIORITY;
DLL_HWXSETCONTEXTHWXSETCONTEXT;
DLL_HWXINPUTHWXINPUT;
DLL_HWXPROCESSHWXPROCESS;
DLL_HWXRESULTSAVAILABLEHWXRESULTSAVAILABLE;
DLL_HWXGETRESULTSHWXGETRESULTS;
DLL_HWXDESTROYHWXDESTROY;
DLL_HWXENDINPUTHWXENDINPUT;
#endif//RECOGNIZE_FUNCTION_FROM_DLL


#ifdefRECOGNIZE_FUNCTION_FROM_LIB
#defineHWXCONFIG(void)HwxConfig(void)
#defineHWXCREATE(hrc)HwxCreate(hrc)
#defineHWXSETGUIDE(hrc,lpGuide)HwxSetGuide(hrc,lpGuide)
#defineHWXALCVALID(hrc,alc)HwxALCValid(hrc,alc)
#defineHWXALCPRIORITY(hrc,alc)HwxALCPriority(hrc,alc)
#defineHWXSETCONTEXT(hrc,wContext)HwxSetContext(hrc,wContext)
#defineHWXINPUT(hrc,lppnt,upoints,timestamp)HwxInput(hrc,lppnt,upoints,timestamp)
#defineHWXPROCESS(hrc)HwxProcess(hrc)
#defineHWXRESULTSAVAILABLE(hrc)HwxResultsAvailable(hrc)
#defineHWXGETRESULTS(hrc,cAlt,iFirst,cBoxRes,rgBoxResults)HwxGetResults(hrc,cAlt,iFirst,cBoxRes,rgBoxResults)
#defineHWXDESTROY(hrc)HwxDestroy(hrc)
#defineHWXENDINPUT(hrc)HwxEndInput(hrc)
#endif//RECOGNIZE_FUNCTION_FROM_LIB
};



//============================================================================================
#endif//!definedRECOGNIZER_H





/**///////////////////////////////////////////////////////////////////////
//Recognizer.cpp:implementationoftheCRecognizerclass.//

/**///////////////////////////////////////////////////////////////////////
#include"stdafx.h"
#include"Recognizer.h"

//-------------------------------------------------------------------
//Macrodefine

//ThedefaultvalueofhwxGuide
#defineDEFAULT_HWXGUIDE_CHORZBOX1
#defineDEFAULT_HWXGUIDE_CVERTBOX1
#defineDEFAULT_HWXGUIDE_CXOFFSET1
#defineDEFAULT_HWXGUIDE_CYOFFSET1

//ThedefaultvalueofALC
#defineDEFAULT_ALCALC_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:
//Initializetherecognizer
//
//Parameter:
//hWnd:[in]Thehandleofwindowtoberecognized
//rcWnd:[in]Thewindowareatoberecognized
//scale:[in]ThescalebaseofprcWndpoint
//-----------------------------------------------------------------------
BOOLCRecognizer::Initialize(HWNDhWnd,constRECT*prcWnd,ScaleTypescale)

...{
m_hWndRecog=hWnd;

m_alc=DEFAULT_ALC;


RECTrcWnd=...{0};
switch(scale)

...{
caseSCALE_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;
}
caseSCALE_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;

#ifdefRECOGNIZE_FUNCTION_FROM_DLL
HINSTANCEhInstDll;
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

...{
returnFALSE;
}
#endif//RECOGNIZE_FUNCTION_FROM_DLL

if(HWXCONFIG()==FALSE)

...{
returnFALSE;
}

returnTRUE;
}


//-----------------------------------------------------------------------
//Descriptiong:
//Beginrecognizing
//-----------------------------------------------------------------------
BOOLCRecognizer::BeginRecognize()

...{
BOOLbRes=FALSE;

m_hrc=HWXCREATE();
if(m_hrc==NULL)

...{
gotoEND;
}

bRes=HWXSETGUIDE(m_hrc,&m_hwxGuide);
if(bRes==FALSE)

...{
gotoEND;
}

bRes=HWXALCVALID(m_hrc,m_alc);
if(bRes==FALSE)

...{
gotoEND;
}

bRes=TRUE;

END:
returnbRes;
}


//-----------------------------------------------------------------------
//Descriptiong:
//Endrecognizing
//-----------------------------------------------------------------------
BOOLCRecognizer::EndRecognize()

...{
BOOLbRes=FALSE;

//Destroytherecognizer
if(HWXDESTROY(m_hrc)==FALSE)

...{
gotoEND;
}

bRes=TRUE;

END:
returnbRes;
}


//-----------------------------------------------------------------------
//Descriptiong:
//Getthecharacter
//
//Parameters:
//pWchar:[out]Thecharactergettobestored
//iCount:[in]ThenumberofpWchar
//
//ReturnValues:
//0:Failed
//>0:Thenumberofthecharacterstoreturn
//-----------------------------------------------------------------------
intCRecognizer::GetCharacter(WCHAR*pWchar,intiCount)

...{
intiGetNum=0;
inti=0;
HWXRESULTS*phwxResults;
//BecauseeachHWXRESULTSafterthefirstonecouldstoretwocharacters,
//soonlyallocate(iCount/2+1)
intiNum=iCount/2+1;
phwxResults=newHWXRESULTS[iNum];
memset(phwxResults,0,iNum*sizeof(HWXRESULTS));

//Endtheinput
if(HWXENDINPUT(m_hrc)==FALSE)

...{
gotoEND;
}

//Analyzetheinformation
if(HWXPROCESS(m_hrc)==FALSE)

...{
gotoEND;
}



//Getthecharacterfromrecognizer
if(HWXGETRESULTS(m_hrc,iCount,0,1,phwxResults)==FALSE)

...{
gotoEND;
}

//Setthecharactertothestoredbuffer
for(i=0;i<iNum;i++)

...{
if(i==0)

...{
if(phwxResults[i].rgChar[0]!=0)

...{
pWchar[iGetNum++]=phwxResults[i].rgChar[0];
}
else

...{
break;
}
}
else

...{
//TheindxBoxmemberalsostorethecharacter
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;
}
returniGetNum;
}


//-----------------------------------------------------------------------
//Descriptiong:
//Inputthestroke
//
//Parameter:
//lpPnt:[in]PointertothestrokePOINT
//iCount:[in]ThecountofthelpPnt
//scale:[in]ThescalebaseoflpPnt
//-----------------------------------------------------------------------
BOOLCRecognizer::InputStroke(POINT*lpPnt,intiCount,ScaleTypescale)

...{
BOOLbRes=FALSE;
inti=0;

POINT*pt;
pt=newPOINT[iCount];
if(pt==NULL)

...{
gotoEND;
}

for(i=0;i<iCount;i++)

...{
pt[i]=lpPnt[i];

if(scale==SCALE_APPWND)

...{
//Converttothescreenscale
pt[i].x*=4;
pt[i].y*=4;
MapWindowPoints(m_hWndRecog,HWND_DESKTOP,&pt[i],1);
}
}

//Inputstroke
bRes=HWXINPUT(m_hrc,pt,iCount,0);

if(bRes==FALSE)

...{
gotoEND;
}

bRes=TRUE;

END:
if(pt!=NULL)

...{
delete[]pt;
}
returnbRes;
}
不知道大家看到这段代码有什么感觉,反正我是挺高兴的,因为让我从繁琐的识别过程中脱离出来.
关于代码,也许最让人疑惑的可能是这两个宏: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同一时间只能定义一个.如果两个都定义,毫无疑问,出错!^_^
最后,用伪代码做范例说明如何使用该封装类,以此做本章结尾:
CRecognizerrecog;

RectrcWnd;

/**//*rcWnd获取应用窗口hWnd的大小*/

//初始化
//直接赋值窗口坐标,函数体内部会根据标志直接转换为屏幕坐标
recog.Initialize(hWnd,&rcWnd,SCALE_APPWND);

//开始识别
recog.BeginRecognize();

POINTpt[200];
intiCount=0;

/**//*获取笔画坐标给pt,坐标的数量储存在iCount中*/

//将笔画点阵传送给识别引擎
//如果有多个笔画,则每个笔画都需要调用该函数进行传入
recog.InputStroke(pt,iCount,SCALE_APPWND);

//获取十个最接近的字符,iReturn是实际返回的字符数
WCHARwChar[10];
intiReturn=recog.GetCharacter(wChar,10);

//结束识别
recog.EndRecognize();


/**//*如果