漫談WinCE的手寫(xiě)識(shí)別技術(shù)(二)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
//========================================================================
//TITLE:
// 漫談WinCE的手寫(xiě)識(shí)別技術(shù)(二)
//AUTHOR:
// norains
//DATE:
// Thursday 25-January -2007
//Environment:
// EVC4.0 + Standard SDK
//========================================================================
在第一章的時(shí)候,已經(jīng)介紹了識(shí)別的一般性過(guò)程,對(duì)于實(shí)際運(yùn)用來(lái)說(shuō),是完全可行的;但從便利性角度出發(fā),卻不免顯得煩瑣:每次輸入筆畫(huà)都需留意點(diǎn)陣是否屏幕坐標(biāo)系,每次讀取返回的字符總要分配內(nèi)存然后獲取等等,諸如總總,代碼寫(xiě)一次還好,如果多處運(yùn)用多次編寫(xiě)多方維護(hù),實(shí)在不是一件快樂(lè)的事情.
而我,最討厭做復(fù)雜又要花費(fèi)腦筋的東東;所以,為了讓自己感覺(jué)得寫(xiě)代碼是一件快樂(lè)的事情,自己又很高興地將識(shí)別過(guò)程封裝為一個(gè)類(lèi).至于是否達(dá)到簡(jiǎn)便的效果,不敢祈求大家茍同,只愿自己舒坦即可.
//////////////////////////////////////////////////////////////////////
// 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
//=====================================================================================
//-----------------------------------------------------------------------------------[!--empirenews.page--]
//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[!--empirenews.page--]
#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"));[!--empirenews.page--]
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)[!--empirenews.page--]
{
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;
}
不知道大家看到這段代碼有什么感覺(jué),反正我是挺高興的,因?yàn)樽屛覐姆爆嵉淖R(shí)別過(guò)程中脫離出來(lái).
關(guān)于代碼,也許最讓人疑惑的可能是這兩個(gè)宏:RECOGNIZE_FUNCTION_FROM_DLL,RECOGNIZE_FUNCTION_FROM_LIB.
顧名思義,RECOGNIZE_FUNCTION_FROM_DLL表明識(shí)別函數(shù)調(diào)用是來(lái)源于動(dòng)態(tài)鏈接庫(kù)(DLL),同理,RECOGNIZE_FUNCTION_FROM_LIB則是編譯的時(shí)候鏈接到lib庫(kù).為什么需要定義這兩個(gè)宏呢?因?yàn)樵跇?biāo)準(zhǔn)的SDK下,如果直接包含"recog.h"后調(diào)用相關(guān)識(shí)別函數(shù),是會(huì)報(bào)link錯(cuò)誤.因?yàn)闃?biāo)準(zhǔn)的SDK是不包含任何手寫(xiě)識(shí)別組件的.從調(diào)試的便利性來(lái)說(shuō),這時(shí)候如果只拷貝識(shí)別庫(kù)到模擬器就可以順利測(cè)試程序,絕對(duì)比重新定制一個(gè)包含手寫(xiě)識(shí)別引擎的系統(tǒng)要來(lái)得方便.
在示例代碼中,因?yàn)槭亲R(shí)別繁體中文,所以包含的動(dòng)態(tài)鏈接庫(kù)為:hwxcht.dll.如果需要識(shí)別其它文字,則只要更改該動(dòng)態(tài)鏈接庫(kù)名稱(chēng)即可.當(dāng)然,還要更改DEFAULT_ALC宏,這個(gè)宏定義了識(shí)別的范圍.
因?yàn)槭纠a中的識(shí)別函數(shù)全部是宏定義,具體意義根據(jù)函數(shù)的來(lái)源而不同,所以RECOGNIZE_FUNCTION_FROM_DLL和RECOGNIZE_FUNCTION_FROM_LIB同一時(shí)間只能定義一個(gè).如果兩個(gè)都定義,毫無(wú)疑問(wèn),出錯(cuò)!^_^
最后,用偽代碼做范例說(shuō)明如何使用該封裝類(lèi),以此做本章結(jié)尾:
CRecognizer recog;
Rect rcWnd;
/*rcWnd 獲取應(yīng)用窗口hWnd的大小*/
//初始化
//直接賦值窗口坐標(biāo),函數(shù)體內(nèi)部會(huì)根據(jù)標(biāo)志直接轉(zhuǎn)換為屏幕坐標(biāo)
recog.Initialize(hWnd,&rcWnd,SCALE_APPWND);[!--empirenews.page--]
//開(kāi)始識(shí)別
recog.BeginRecognize();
POINT pt[200];
int iCount = 0;
/*獲取筆畫(huà)坐標(biāo)給pt,坐標(biāo)的數(shù)量?jī)?chǔ)存在iCount中*/
//將筆畫(huà)點(diǎn)陣傳送給識(shí)別引擎
//如果有多個(gè)筆畫(huà),則每個(gè)筆畫(huà)都需要調(diào)用該函數(shù)進(jìn)行傳入
recog.InputStroke(pt,iCount,SCALE_APPWND);;
//獲取十個(gè)最接近的字符,iReturn是實(shí)際返回的字符數(shù)
WCHAR wChar[10];
int iReturn = recog.GetCharacter(wChar,10);
//結(jié)束識(shí)別
recog.EndRecognize();