识别用户的手写内容

借助手写识别 API,您可以在手写输入中实时识别文字。

什么是手写识别 API?

借助手写识别 API,您可以将用户的手写内容(手写)转换为文本。 某些操作系统早就包含了此类 API,而借助这项新功能,您的 Web 应用 最后才使用这项功能转化直接在用户的设备上进行,甚至 离线模式下,所有这些都无需添加任何第三方库或服务。

此 API 实现所谓的“在线”或近乎实时的识别。这意味着 通过捕获和分析单个 划船。与“离线”相对程序,例如光学字符识别 (OCR),其中 由于只有最终结果是已知的,在线算法可以提供更高的准确度,这是由于 例如单个墨水笔画的时间顺序和压力。

手写识别 API 的推荐用例

应用示例包括:

  • 记事应用,用户希望捕捉手写便条并进行翻译 转换为文本。
  • 由于时间限制,用户可以使用触控笔或手指输入的表单应用。
  • 要求填入字母或数字的游戏,例如填字游戏、字谜或数独游戏。

当前状态

手写识别 API 从 (Chromium 99) 开始。

如何使用手写识别 API

功能检测

通过检查是否存在 createHandwritingRecognizer() 方法来检测浏览器支持情况 针对 navigator 对象:

if ('createHandwritingRecognizer' in navigator) {
  // 🎉 The Handwriting Recognition API is supported!
}

核心概念

无论使用哪种输入法,手写识别 API 都会将手写输入转换为文本 (鼠标、触摸、触控笔)。该 API 有四个主要实体:

  1. 表示指针在特定时间的位置。
  2. 一个描边,包含一个或多个点。当用户将手指放在某个位置时, (即点击鼠标主按钮,或用触控笔或触摸屏幕) (手指),当它们再次抬起指针时即结束。
  3. 一个绘图由一个或多个笔画组成。实际识别发生在此级别。
  4. 识别器配置了预期的输入语言。它用于创建实例 应用了识别器配置的绘图。

这些概念以特定的接口和字典的形式实现,我稍后将介绍这些内容。

手写识别 API 的核心实体:一个或多个点组成笔画,一个或多个笔画组成绘图,由识别器创建。实际的识别发生在绘制级别。

创建识别器

要识别手写输入中的文本,您需要获取一个 HandwritingRecognizer:通过调用 navigator.createHandwritingRecognizer() 并传递约束条件 。限制条件决定了应使用的手写识别模型。目前,您 可以按照优先顺序指定语言列表:

const recognizer = await navigator.createHandwritingRecognizer({
  languages: ['en'],
});

此方法返回一个使用 HandwritingRecognizer 实例解析的 promise, 能满足您的请求否则,它将拒绝 promise 并报错,并且 手写识别。因此,您可能需要查询 识别器对特定识别功能的支持。

查询识别器支持

通过调用 navigator.queryHandwritingRecognizerSupport(),您可以检查目标平台 支持您打算使用的手写识别功能。在以下示例中, 开发者:

  • 想要检测英语文本
  • 获得其他预测(如果有)的可能性较低
  • 获得对分割结果的访问权限,即识别出的字符,包括点和 构成它们的笔触
const { languages, alternatives, segmentationResults } =
  await navigator.queryHandwritingRecognizerSupport({
    languages: ['en'],
    alternatives: true,
    segmentationResult: true,
  });

console.log(languages); // true or false
console.log(alternatives); // true or false
console.log(segmentationResult); // true or false

该方法会返回使用结果对象解析的 promise。如果浏览器支持该功能 其值将设为 true。否则,将设置为 false。 您可以使用此信息来启用或停用应用中的某些功能,或 请调整您的查询,然后发送新的查询。

开始绘图

您应在应用中提供一个输入区域,供用户手写输入 条目。出于性能方面的考虑,建议借助 canvas 对象。确切的 本部分的实现方法不在本文讨论范围之内,但您可以参阅演示 看看实现方式。

如需开始新绘图,请对识别器调用 startDrawing() 方法。此方法采用 对象,其中包含用于微调识别算法的不同提示。所有提示都是可选的:

  • 输入的文字类型:文字、电子邮件地址、数字或单个字符 (recognitionType)
  • 输入设备的类型:鼠标、触控或触控笔输入 (inputType)
  • 前面的文本 (textContext)
  • 应返回的概率更低的替代预测数量 (alternatives)
  • 用户最有可能输入的可识别字符(“字形”)列表 (graphemeSet)

手写识别 API 可与 指针事件 - 提供 抽象接口,以使用来自任何指控设备的输入。指针事件参数包含 所使用的指针类型。这意味着您可以使用指针事件来确定输入类型 。在以下示例中,系统会自动使用手写识别绘图功能 在手写区域首次发生 pointerdown 事件时创建。作为 pointerType 可能为空或设置为专有值,但我引入了一致性检查,以确保 确保仅为绘图的输入类型设置受支持的值。

let drawing;
let activeStroke;

canvas.addEventListener('pointerdown', (event) => {
  if (!drawing) {
    drawing = recognizer.startDrawing({
      recognitionType: 'text', // email, number, per-character
      inputType: ['mouse', 'touch', 'pen'].find((type) => type === event.pointerType),
      textContext: 'Hello, ',
      alternatives: 2,
      graphemeSet: ['f', 'i', 'z', 'b', 'u'], // for a fizz buzz entry form
    });
  }
  startStroke(event);
});

添加描边

pointerdown 事件也是开始新描边的正确位置。为此,请创建一个新的 HandwritingStroke 的实例。此外,您还应将当前时间存储为 之后添加到其中的点:

function startStroke(event) {
  activeStroke = {
    stroke: new HandwritingStroke(),
    startTime: Date.now(),
  };
  addPoint(event);
}

添加一个点

创建描边后,您应直接向其添加第一个点。随着你不断地添加更多 点,那么最好在单独的方法中实现点创建逻辑。在 在以下示例中,addPoint() 方法会根据参考时间戳计算所用时间。 时间信息是可选的,但可以提高识别质量。然后读取 X 和 获取指针事件中的 Y 坐标,并将相应点添加到当前笔触。

function addPoint(event) {
  const timeElapsed = Date.now() - activeStroke.startTime;
  activeStroke.stroke.addPoint({
    x: event.offsetX,
    y: event.offsetY,
    t: timeElapsed,
  });
}

当指针在屏幕上移动时,系统会调用 pointermove 事件处理脚本。这些要点 同样需要向描边添加文字如果指针不在 “down”状态,例如,在没有按鼠标的情况下在屏幕上移动光标时 按钮。以下示例中的事件处理程序检查是否存在有效笔触,并将 指向它的新点

canvas.addEventListener('pointermove', (event) => {
  if (activeStroke) {
    addPoint(event);
  }
});

识别文字

当用户再次抬起指针时,您可以通过调用其 addStroke() 方法结合使用。以下示例还会重置 activeStroke,因此 pointermove 处理程序不会向已完成的笔触添加点。

接下来,是时候通过对getPrediction() 绘图。识别过程通常需要不到几百毫秒,因此您可以反复运行 预测。以下示例会在每个已完成的笔画后运行新的预测。

canvas.addEventListener('pointerup', async (event) => {
  drawing.addStroke(activeStroke.stroke);
  activeStroke = null;

  const [mostLikelyPrediction, ...lessLikelyAlternatives] = await drawing.getPrediction();
  if (mostLikelyPrediction) {
    console.log(mostLikelyPrediction.text);
  }
  lessLikelyAlternatives?.forEach((alternative) => console.log(alternative.text));
});

此方法返回一个 promise,该 promise 可通过一组预测(按其排序)进行解析 概率。元素数量取决于您传递给 alternatives 提示的值。您 可以使用此数组向用户显示可能的匹配选项, 选项。或者,您也可以直接使用最有可能的预测, 示例。

预测对象包含识别出的文本和一个可选的分割结果, 我们将进一步讨论这些内容

包含细分结果的详细数据分析

如果目标平台支持,则预测对象还可以包含细分结果。 这是一个包含所有已识别的手写区段的数组,由已识别的 用户可识别字符 (grapheme) 及其在识别出的文本中的位置 (beginIndexendIndex)以及创建该点的笔画和点。

if (mostLikelyPrediction.segmentationResult) {
  mostLikelyPrediction.segmentationResult.forEach(
    ({ grapheme, beginIndex, endIndex, drawingSegments }) => {
      console.log(grapheme, beginIndex, endIndex);
      drawingSegments.forEach(({ strokeIndex, beginPointIndex, endPointIndex }) => {
        console.log(strokeIndex, beginPointIndex, endPointIndex);
      });
    },
  );
}

您可以使用这些信息再次在画布上追踪识别出的字形。

系统会在每个识别出的字形周围绘制方框

全面识别

完成识别后,您可以通过调用 clear() 方法上的 HandwritingDrawing 以及 HandwritingRecognizer 中的 finish() 方法:

drawing.clear();
recognizer.finish();

演示

网页组件 <handwriting-textarea> 会实现 渐进式增强,可手写的编辑控件 识别。点击编辑控件右下角的按钮,激活 绘图模式完成绘制后,网络组件会自动启动 并将识别出的文本添加回编辑控件。如果手写识别 API 完全不受支持,或者平台不支持所请求的功能,请点击“修改”按钮 将隐藏。不过,基本编辑控件仍可作为 <textarea> 使用。

Web 组件提供了属性,用于定义从 外部,包括languagesrecognitiontype。您可以通过 value 属性:

<handwriting-textarea languages="en" recognitiontype="text" value="Hello"></handwriting-textarea>

如需了解该值的任何更改,您可以监听 input 事件。

您可以使用 Glitch 上的演示来试用该组件。 此外,请务必查看 源代码。要使用 应用,请从 npm 获取

安全与权限

Chromium 团队根据核心原则设计和实现了手写识别 API (如控制对强大的 Web 平台功能的访问权限中所述),包括 透明度和人体工学设计。

用户控制

用户无法关闭手写识别 API。它仅适用于网站 且只能从顶级浏览上下文中调用。

透明度

系统不会显示手写识别功能是否已启用的指示。为防止数字“指纹”收集,浏览器 实施对抗措施,例如在检测到以下情况时向用户显示权限提示: 滥用行为。

权限保留

手写识别 API 目前不显示任何权限提示。因此,权限 不需要以任何方式保留

反馈

Chromium 团队希望了解您使用手写识别 API 的体验。

向我们介绍 API 设计

API 是否有什么无法按预期运行?或者是否缺少方法 需要哪些资源或属性来实现您的想法?对安全性有疑问或意见 模型?在相应的 GitHub 代码库中提交规范问题,或将您的想法添加到 现有问题。

报告实现存在的问题

您是否发现了 Chromium 实现方面的错误?或者,实现是否与规范不同? 在 new.crbug.com 上提交 bug。请务必提供尽可能多的细节信息 简单的重现说明,并在组件框中输入 Blink>HandwritingGlitch 非常适用于分享轻松快速的重现问题。

表示对 API 的支持

您打算使用手写识别 API 吗?您的公开支持对 Chromium 团队有所帮助 确定功能的优先级,并向其他浏览器供应商展示支持这些功能的重要性。

请在 WICG Discourse 会话中说明您打算如何使用它。发推文给 @ChromiumDev(使用 # 标签) #HandwritingRecognition 并告诉我们您使用它的地点和方式。

致谢

本文由 Joe Medley、Honglin Yu 和 Jiewei Qian 审核。主打图片提供方 Samir Bouaked 不启动