1. 概述
HWR (HandWritten Recognition) 联机手写识别技术,可以将在手写设备上书写时产生的有序轨迹信息化转化为汉字内码。
手写识别从识别过程来说分成脱机识别(off-line)和联机识别(on-line)两大类。通俗来讲,联机手写输出的是预先设置好的标准字体,手写的时候即使是用连笔字、草书,输出之后也会变成标准的宋体、楷体或者其他字体,而脱机手写则是输入的是什么样子,就会以什么样子显示出来。
本文档旨在讲解如何快速地集成灵云HWR功能到开发者应用中。关于各服务接口更详细的说明,请参考灵云SDK开发手册。在集成过程中如有疑问,可登录灵云开发者论坛,查找答案或与其他开发者交流。
1.1 概念解释
名称 | 说明 |
---|---|
Session | Session用来标记一个能力运行过程的上下文。一个应用最多可同时创建的Session数受到授权的限制。 |
单字识别 | 只能将一段输入的笔迹识别为一个单字。这种模式目前可支持87种语言的识别 (包括英文、阿拉伯数字、符号)。单字识别支持本地识别和云端识别。 |
多字识别 | 支持叠写和行写模式,即会自动将输入的笔迹进行切割,按照多字进行识别。 目前这种模式支持云端和本地,支持中文简繁体、英文、韩语、日语的识别(都包括英文、阿拉伯数字、符号等)。多字识别可以满足单字识别的场景,一般都应该选用这种模式。 |
获取联想词 | 即输入一个字符,输入法自动匹配候选词汇。 |
笔势识别 | 输入的笔迹被识别为列表里50种笔势(笔势列表)中的一种,输出结果为对应的索引值,存于结果条目HWR_RECOG_RESULT_ITEM 中字段pszResult。详见灵云SDK开发手册 |
模拟笔形 | 输入点迹数据,通过这些点迹数据回显出笔形,以图片的形式表现出来。 |
拼音识别 | 你输入一个汉字,自动获取该汉字的拼音,比如你输入"重",得到拼音“chóng”,“zhòng”。目前仅在windows平台支持。 |
英文连写识别 | 即你输入英文连写单词,输入法自动获取候选词汇。 |
1.2 能力定义(capkey)
HWR支持多种能力,通过 capkey 区分不同能力的调用和配置。其中常用 capkey 如下所示:
- 本地能力
capkey | 说明 | 所需资源文件 |
---|---|---|
hwr.local.letter | 本地单字识别 | letter.dic letter.conf tibetanmap.txt(只藏文需要) |
hwr.local.freestylus | 本地多字识别 | fwlib.dic letter.dic letter.conf wwlib.dic |
hwr.local.associateword | 获取联想词 | wa.user.dct(用户词典) wa.system.dct(系统词典) |
hwr.local.gesture | 本地笔势识别 | gesture.dic |
hwr.local.pinyin | 本地拼音识别 | gp.system.dct |
hwr.local.freestylus.v7 | 本地英文连写识别 | wfse_cmn.bin.en wfse_mdl.bin.en.big wfse_mdl.bin.en.sml |
- 云端能力
capkey | 说明 | 所需资源文件 |
---|---|---|
hwr.cloud.letter | 云端单字识别 | 无 |
hwr.cloud.freewrite | 云端多字识别 | 无 |
hwr.cloud.gesture | 云端笔势识别 | 无 |
1.3 识别流程
备注:获取联想词和拼音、模拟笔形等,目前只有本地端能力。
2. HWR能力使用说明
2.1 准备工作
下载手写识别SDK并解压缩。
如果需要使用本地能力,请下载相应资源包并解压缩。
2.2 使用iOS版SDK
2.2.1 手写识别库目录介绍
必选模块
- libcurl_device_simulator_iOS7.1.a
- libhci_sys_device_simulator_iOS7.1.a
- libhci_hwr_device_simulator_iOS7.1.a
云端识别
- libhci_hwr_cloud_recog_device_simulator_iOS7.1.a
本地识别
- libhci_hwr_local_recog_device_simulator_iOS7.1.a
本地英文联写
- libhci_hwr_local_recog_v7_device_simulator_iOS7.1.a*
联想和笔形
- libhci_hwr_funs_device_simulator_iOS7.1.a
适配器选择(只选其一)
- libhci_hwr_only_cloud_adapter_device_simulator_iOS7.1.a(只使用云端能力)
- libhci_hwr_only_local_adapter_device_simulator_iOS7.1.a(只使用本地能力)
- libhci_hwr_only_funcs_adapter_device_simulator_iOS7.1.a(只使用联想和笔形)
- libhci_hwr_cloud_local_adapter_device_simulator_iOS7.1.a(既使用云端能力,又使用本地能力)
- libhci_hwr_local_funcs_adapter_device_simulator_iOS7.1.a(既使用本地能力,又使用联想和笔形)
- libhci_hwr_cloud_funcs_adapter_device_simulator_iOS7.1.a(既使用云端能力,又使用联想和笔形)
- libhci_hwr_cloud_local_funcs_adapter_device_simulator_iOS7.1.a(使用云端识别,本地识别,同时又使用联想和笔形)
PS:
在集成灵云的iOS版本SDK时,静态库目录中会提供一种名称中包含adapter的静态库文件。值得说明一点的是,包含adapter名称的静态库文件是作为适配器使用的,所谓适配器是指开发者使用的灵云能力,即capkey是云端能力、本地能力,亦或者是云+端能力,所以适配器文件也还会对应以上的能力范围。当开发者集成时,适配器静态库在集成到工程中,有且只能有一个适配器静态库被添加到Link目录中,如开发者使用ASR的本地自由说能力,则适配器只需要选择libhci_asr_only_local_adapter_device_simulator_iOS7.1.1.a即可。
2.2.2 导入静态库
在SDK解压目录下的libs文件夹中包含HWR能力所需要的静态库,开发者可以根据需要进行选择。
请在工程目录结构中,添加手写识别(HWR)SDK,在选项TARGETS--> Build Phases-->Link Binary With Libraries-->Add Other,选择相应静态库文件并确认。
- 添加系统依赖框架(framework)TARGETS-->Build Phases-->Link Binary With Libraries--> + -->UIKit.fframework,Foundation.framework,SystemConfiguration.framework,Accelerate.framework,AssetsLibrary.framework,CoreMedia.framework,AVFoundation.framework,CoreLocation.framework
- 使用笔势识别(hwr.local.penscript)能力需要添加CoreImage.framework、CoreFoundation.framework,具体请参考HWR_Pen_Example示例工程。
- 添加系统依赖框架(framework)TARGETS-->Build Phases-->Link Binary With Libraries--> + -->UIKit.fframework,Foundation.framework,SystemConfiguration.framework,Accelerate.framework,AssetsLibrary.framework,CoreMedia.framework,AVFoundation.framework,CoreLocation.framework
将手写识别(HWR)SDK的头文件导入您的工程中
- hci_sys.h
- hci_hwr.h
最后手写识别(HWR)SDK的编译环境是 libc++,在选项TARGETS--> Build Settings-->C++ Standard Library,选择libc++。
3. HWR能力集成说明
本章节主要讲述HWR能力的集成过程,开发者可根据本章内容完成HWR能力的简单调用。本章节以本地识别为例,讲述HWR能力的集成调用流程,调用顺序参考识别流程。
3.1 通用模块初始化
在调用HWR能力之前,需要初始化灵云SDK的通用模块。详见灵云SDK开发手册
iOS示例代码
HCI_ERR_CODE errCode = HCI_ERR_NONE;
// appkey, developerKey, cloudUrl, authPath为必填项。
// 其他可配置信息请查询灵云SDK开发手册hci_init函数说明。
NSString *appkey = @""; //开发者社区申请所得
NSString *developerKey = @""; //开发者社区申请所得
NSString *cloudUrl = @""; //开发者社区申请所得
NSString *authPath = @""; //授权文件所在路径,一般设置为沙盒Document路径
NSString *config = [NSString stringWithFormat:@"appkey=%@,
developerKey=%@,
cloudUrl=%@,
authPath=%@",
appkey,
developerKey,
cloudUrl,
authPath];
errCode = hci_init(config.UTF8String);
if (errCode != HCI_ERR_NONE && errCode != HCI_ERR_SYS_ALREADY_INIT) {
NSLog(@"hci_init failed, error: %d", errCode);
} else {
NSLog(@"hci_init success");
}
3.2 授权检测
在初始化灵云SDK的通用模块后,还需要调用授权检测函数获取云端授权。
- iOS代码
//获取过期时间 int64 expireTime; int64 currentTime = (int64)time(NULL); HCI_ERR_CODE errCode = hci_get_auth_expire_time(&expireTime); if (errCode == HCI_ERR_NONE) { if (expireTime < currentTime) { errCode = hci_check_auth(); if(errCode == HCI_ERR_NONE){ NSLog(@"check auth success"); return YES; }else{ NSLog(@"check auth failed, error: %@", errCode); return NO; } }else{ //没有过期 NSLog(@"auth can use continue"); return YES; } } //读取授权文件出现错误 else if(errCode == HCI_ERR_SYS_AUTHFILE_INVALID){ errCode = hci_check_auth(); if(errCode == HCI_ERR_NONE){ NSLog(@"check auth success"); return YES; }else{ NSLog(@"check auth failed, error: %@", errCode); return NO; } }else{ NSLog(@"check auth failed, error: %@", errCode); return NO; }
3.3 HWR初始化
在初始化灵通用模块和获取授权成功后,我们还需要调用初始化函数初始化HWR能力。
- iOS代码
//本地能力所需本地资源所在路径
NSString *dataPath = [[NSBundle mainBundle] pathForResource:@"data" ofType:Nil];
//initCapKeys为必填项。其他可配置信息请查询开发手册hci_afr_init函数说明。
//人脸识别(AFR)所需使用的能力列表,可以包含多种能力,用';'分号隔开
NSString *initCapKeys = @"hwr.local.freestylus";
NSString *config = [NSString stringWithFormat:@"initCapKeys = %@,dataPath = %@",
initCapKeys,dataPath];
HCI_ERR_CODE errCode = HCI_ERR_NONE;
errCode = hci_hwr_init(config.UTF8String);
if (errCode != HCI_ERR_NONE){
NSLog(@"hci_hwr_init failed, error:%@", errCode);
eturn NO;
}
NSLog(@"hci_hwr_init success");
return YES;
常见errcode,详见灵云SDK开发手册
对于使用本地多能力时,可以使用initCapKeys传入需要使用的所有能力,以';'隔开。 initCapKeys的作用是告知SDK,需要预先准备哪些能力并提前加载对应的库资源,提高执行效率。
- iOS代码
//本地能力所需本地资源所在路径 NSString *dataPath = [[NSBundle mainBundle] pathForResource:@"data" ofType:Nil]; //initCapKeys为必填项。其他可配置信息请查询开发手册hci_hwr_init函数说明。 NSString *initCapKeys = @"hwr.local.letter;hwr.local.freestylus;hwr.local.associateword"; NSString *config = [NSString stringWithFormat:@"initCapKeys = %@,dataPath = %@", initCapKeys,dataPath];
3.4 开启会话(session)
在HWR能力初始化之后,需要通过开启识别会话来启用一种具体的能力。
- iOS代码
// 开启session
HCI_ERR_CODE errCode = HCI_ERR_NONE;
NSString *config = @"capKey=hwr.local.freestylus";
int sessionID; // 开启会话成功后,会返回的会话ID
errCode = hci_hwr_session_start(config.UTF8String, &sessionID);
if (errCode != HCI_ERR_NONE){
NSLog(@"hci_hwr_session_start failed, error:%@", errCode);
return NO;
}
NSLog(@"hci_hwr_session_start success");
return YES;
3.5 HWR识别
在启动会话(session)成功后,即可进行 HWR 识别了。HWR识别具体可分为单字、多字识别,获取联想词,笔势识别以及拼音识别等,各识别功能以本地能力为例,详见如下各小章节。
配置参数详见灵云SDK开发手册。
3.5.1 单、多字识别
- iOS代码
HCI_ERR_CODE errCode = HCI_ERR_NONE;
HWR_RECOG_RESULT hwrResult;
int nLength = (int)points.count * 2;
short *psStrokingData = new short[nLength];
//points为笔迹的点迹集合,每一笔以(-1,0)结尾,整个字以(-1,-1)结尾
[points enumerateObjectsUsingBlock:^(NSValue *obj, NSUInteger idx, BOOL * _Nonnull stop) {
CGPoint point = CGPointFromString([NSString stringWithFormat:@"%@",obj]);
psStrokingData[idx * 2] = floor(point.x);
psStrokingData[idx * 2 + 1] = floor(point.y);
}];
errCode = hci_hwr_recog(nSessionId, psStrokingData, nLength * 2, config.UTF8String,
&hwrResult);
3.5.2 获取联想词
- iOS代码
ASSOCIATE_WORDS_RESULT words;
NSString *keyword = @"中华人民";
errCode = hci_hwr_associate_words(nSessionId, "", keyword.UTF8String, &words);
if (errCode != HCI_ERR_NONE) {
for (int i = 0; i < (int)sWords.uiItemCount; ++i){
//输出是UTF8的,在某些平台下输出需要转码
NSLog(@"Associate result[%d]: %s",i,sWords.pItemList[i].pszWord);}
//释放联想词结果
errCode = hci_hwr_free_associate_words_result(&sWords);
}
}
3.5.3 笔势识别
- iOS代码
int nSessionId = -1;
NSString session_config = @"capkey=hwr.local.gesture";
err_code = hci_hwr_session_start( session_config.UTF8String, &nSessionId );
HWR_RECOG_RESULT hwrRecogResult;
NSString *recog_config = @"";
err_code = hci_hwr_recog(nSessionId,
g_sGestureStrokeData,
sizeof(g_sGestureStrokeData),
recog_config.UTF8String,
&hwrRecogResult);
3.5.4 模拟笔形
- iOS代码
HCI_ERR_CODE errCode = HCI_ERR_NONE;
PEN_SCRIPT_RESULT hwrPenscriptResult;
//points为笔迹的点迹集合,每一笔以(-1,0)结尾,整个字以(-1,-1)结尾
for (NSValue *obj in points) {
CGPoint point = CGPointFromString([NSString stringWithFormat:@"%@",obj]);
int nX = floor(point.x);
int nY = floor(point.y);
errCode = hci_hwr_pen_script(_sessionID, config.UTF8String, nX, nY, &hwrPenscriptResult);
if (errCode == HCI_ERR_NONE){
for (unsigned int nIndex = 0; nIndex < hwrPenscriptResult.uiItemCount; nIndex++){
PEN_SCRIPT_RESULT_ITEM item = hwrPenscriptResult.pItemList[nIndex];
for(int i = 0; i < item.nHeight; i++ ){
for ( int j = 0; j < item.nWidth; j++ ){
if ( !item.psPageImg[i * item.nWidth + j] ){
//调用drawrect方法进行绘图,详见demo演示
[_writeView createBitmap:item];
}
}
}
}
errCode = hci_hwr_free_pen_script_result(&hwrPenscriptResult);
}
else{
break;
}
}
3.5.5 获取拼音
- iOS代码
暂不支持
3.5.6 实时识别
启用实时识别时,对于每次连续的识别内容,可以多次调用hci_hwr_recog() 。每次调用追加输入新的数据, 每次输入的数据以(-1,0,-1,-1)结束,也即每次输入的笔画是完整的,可以一次输入多个笔画。
每次实时识别时,不论是单个笔画或是一次输入多个笔画,笔迹最后需以(-1,0)(-1,-1)结束。
启动实时识别时,需要在创建识别会话时,加入realtime=yes选项。实时识别时,每次调用本函数都会返回从头开始的完整结果,新输入的数据会导致切分发生变化, 因此后一次结果不一定是前次结果再追加字符,可能会更改掉部分前次结果。实时识别中每次返回的识别结果都需要释放。
iOS代码
NSString *pszSessionConfig = @"capKey=hwr.local.freestylus,realtime=yes"; int nSessionId;errCode = hci_hwr_session_start(pszSessionConfig.UTF8String, &nSessionId); while (YES) { NSString *strRecogConfig = @""; HWR_RECOG_RESULT hwrRecogResult; //g_StrokeData:每次传入的笔迹,以(-1,0)(-1,-1)结尾。 //例如‘人’字是两划,那么左边一撇和右边一撇单独传入识别,以-1,0)(-1,-1)结尾。 errCode = hci_hwr_recog( nSessionId, g_StrokeData, g_StrokeLen, pszRecogConfig.UTF8String, &hwrRecogResult ); // 可以打印当前的识别结果,每次返回都是从头开始的完整结果 ... // 每次的返回结果都需要释放 errCode = hci_hwr_free_recog_result(&hwrRecogResult); // 如果是最后一笔,跳出循环 if (is_last_stroke()) break; }
3.5.7 英文连写识别
- iOS代码
// 开启session
HCI_ERR_CODE errCode = HCI_ERR_NONE;
NSString *config = @"capKey=hwr.local.freestylus.v7";
int sessionID; // 开启会话成功后,会返回的会话ID
errCode = hci_hwr_session_start(config.UTF8String, &sessionID);
if (errCode != HCI_ERR_NONE){
NSLog(@"hci_hwr_session_start failed, error:%@", errCode);
return NO;
}
HCI_ERR_CODE errCode = HCI_ERR_NONE;
HWR_RECOG_RESULT hwrResult;
int nLength = (int)points.count * 2;
short *psStrokingData = new short[nLength];
//points为笔迹的点迹集合,每一笔以(-1,0)结尾,整个字以(-1,-1)结尾
[points enumerateObjectsUsingBlock:^(NSValue *obj, NSUInteger idx, BOOL * _Nonnull stop) {
CGPoint point = CGPointFromString([NSString stringWithFormat:@"%@",obj]);
psStrokingData[idx * 2] = floor(point.x);
psStrokingData[idx * 2 + 1] = floor(point.y);
}];
errCode = hci_hwr_recog(nSessionId, psStrokingData, nLength * 2, config.UTF8String,
&hwrResult);
3.6 结束识别
最后我们需要反初始化,依次关闭会话,终止HWR能力,关闭灵云系统。
- iOS代码
//关闭HWR识别会话 errCode = hci_hwr_session_stop(nSessionId); //终止HWR能力 errCode = hci_hwr_release(); //终止灵云系统 errCode = hci_release();
5. FAQ
Q: 我运行你们的 SDK 报 19 号错误:START LOG FAILED, 应该如何解决?
A: 出现此问题,一般由两种原因造成。
- 工程文件里引入了中文路径,导致SDK启动时,读写对应的日志路径报错。
- 日志文件定义的路径由于权限问题不可读写,比如在 Android 平台上运行时,没有 WRITE_EXTERNAL_STORAGE的权限。
Q: 我运行你们的 SDK 报 23 号错误:LOAD_FUNCTION_FROM_DLL FAILED,应该如何解决?
A: 这种情况,通常是由于缺库。最常见的场景是使用本地能力的 capkey,如 hwr.loacl.letter,没有将库文件对应组入(我们 SDK 示例程序,与 example 同层有全套库文件,示例程序内默认只组入了云端库),将相关库补齐即可使用。此外,由于 AFR能力引用了第三方的 MKL 库,因此如果第三方库没有拷入,也会导致此问题。
Q: 我运行你们的 SDK 报 14 号错误:LOCAL_LIB_MISSING,应该如何解决?
A: 本地能力常会遇到此问题,此问题是由于没有组入对应的本地资源文件。请在我们开发者社区下载需求的本地资源(VPR本地模型)等,并将资源文件组入代码 datapath 指定路径,然后再次尝试运行程序。
Q: 我运行你们的 SDK 报 11 号错误:SERVICE_RESPONSE_FAILED ,应该如何解决?
A: 此问题原因较为复杂,可能是传入的参数问题与服务器不匹配,如调用私有云时,没有传入 property 等配置项。也可能是服务端内部的返回原因,请联系技术支持排查。
Q: 我运行你们的SDK报 7 号错误:CONFIG_UNSUPPORT ,应该如何解决?
A: 与 11号错误类似,还是配置串问题,7 号错误原因为传入了不支持的配置串。请仔细检查各接口传入的所有配置串,并联系技术支持排查。
Q: 我运行你们的SDK报 9 号错误: SERVICE_TIMEOUT ,应该如何解决?
A: 这个是因为您请求我们服务器到返回结果的时间,超过了上限。请检查网络环境相关的原因,并协助提供日志。
Q: 我运行你们的 SDK 报 16 号错误: SESSION_INVALID ,应该如何解决?
A: 这个需要检查详细的代码调用过程,调用每个能力核心接口时,都是需要传入 sessionid 的,如果在 sessionstart 的时候,已经发生了错误,或者使用的id,并不是 sessionstart 时候创建的,就会返回此错误,请联系技术支持排查。
Q: 我运行你们的 SDK 报 0 号错误: ERROR NONE,应该如何解决?
A: SDK 报 0 号错误, ERROR NONE,代表执行成功,请您继续后续的集成。