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示例工程。
  • 将手写识别(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,代表执行成功,请您继续后续的集成。

results matching ""

    No results matching ""