辨識使用者(');手寫功能

手寫辨識 API 可讓您即時辨識手寫輸入的文字。

克里斯蒂安.利貝爾
Christian Liebel

什麼是手寫辨識 API?

手寫辨識 API 可讓您將使用者的手寫內容 (墨水) 轉換為文字。有些作業系統已長時間包含這類 API,且透過這項新功能,您的網頁應用程式可以最終使用此功能。轉換作業會直接在使用者的裝置上進行,即使在離線模式下也能運作,不必新增任何第三方程式庫或服務。

這個 API 實作所謂的「即時」或近乎即時的辨識技術。也就是說,當使用者繪圖時,系統會擷取及分析單次筆觸,在繪圖時辨識手寫輸入內容。相較於光學字元辨識 (OCR) 這類「非線」程序,只有已知最終產品,線上演算法可能會因時間序列和個別墨筆筆劃等額外信號而提供更準確的準確率。

手寫辨識 API 的建議用途

用途範例包括:

  • 筆記應用程式可讓使用者擷取手寫筆記並翻譯成文字。
  • 表單應用程式,可讓使用者在時間限制下使用畫筆或手指輸入。
  • 需要填入字母或數字的遊戲,例如填字字詞、漢字或數庫。

目前狀態

適用於 (Chromium 99) 的手寫辨識 API。

如何使用手寫辨識 API

特徵偵測

檢查導覽器物件上是否存在 createHandwritingRecognizer() 方法,藉此偵測瀏覽器支援:

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

核心概念

手寫辨識 API 可將各種輸入法 (滑鼠、觸控、筆) 轉換為文字,這個 API 有四個主要實體:

  1. 代表指標在特定時間的位置。
  2. 筆劃包含一或多個點。當使用者將指標向下移動 (例如按一下滑鼠主要按鈕,或用筆或手指輕觸螢幕) 時,就會開始錄製筆劃,並在將指標向上舉起時結束。
  3. 「繪製」包含一或多個筆劃。實際辨識結果是在這個層級進行。
  4. 辨識工具已設定所需的輸入語言。用於建立套用辨識工具設定的繪圖執行個體。

這些概念是以特定介面和字典的形式實作,我稍後會加以說明。

手寫辨識 API 的核心實體:一或多個筆劃會撰寫筆劃、一或多筆筆劃寫經、辨識器所建立的。實際辨識作業是在繪圖層級進行。

建立辨識工具

如要辨識手寫輸入的文字,您必須呼叫 navigator.createHandwritingRecognizer() 並為其傳送限制,藉此取得 HandwritingRecognizer 的例項。限制會決定要使用的手寫辨識模型。目前,您可以依照偏好順序指定語言清單:

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

瀏覽器可以執行您的要求時,這個方法會傳回承諾解析,並顯示 HandwritingRecognizer 的例項。否則,系統會拒絕發生錯誤的承諾,且您將無法使用手寫辨識功能。因此,您可能需要先查詢辨識工具對特定辨識功能的支援功能。

查詢辨識工具支援

呼叫 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

這個方法會傳回利用結果物件的承諾解析。如果瀏覽器支援開發人員指定的功能,這個值會設為 true。否則,系統會將該欄位設為 false。您可以使用這項資訊在應用程式中啟用或停用特定功能,或是調整查詢並傳送新的查詢。

開始繪圖

您應該在應用程式中提供輸入區域,供使用者進行手寫項目。基於效能考量,建議您使用畫布物件來實作此功能。本文內容並未涵蓋此部分的確切實作方式,但您也可以參考這個示範來瞭解如何完成這項作業。

如要建立新的繪圖,請對辨識器呼叫 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 事件處理常式。這些點也必須加入筆劃。如果指標未處於「向下」狀態 (例如在畫面上移動遊標而未按下滑鼠按鈕),也可能會引發事件。以下範例的事件處理常式會檢查是否存在進行中的筆劃,並在其中加入新的點。

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,並使用預測結果陣列 (依可能性排序) 來解析。元素數量取決於您傳遞至 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);
      });
    },
  );
}

您可以參考這項資訊,再次找出畫布上已知的石塊。

每個辨識的石塊周圍都繪有箱子

完整辨識

辨識完成後,您可以透過對 HandwritingDrawing 呼叫 clear() 方法和 HandwritingRecognizer 上的 finish() 方法,釋出資源:

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

示範

網頁元件 <handwriting-textarea> 會實作逐步強化,編輯控制項能夠辨識手寫功能。按一下編輯控制項右下角的按鈕,即可啟用繪圖模式。當您完成繪圖時,網頁元件會自動啟動辨識,並將辨識的文字加回編輯控制項。如果完全不支援手寫辨識 API,或是平台不支援要求的功能,系統會隱藏編輯按鈕。不過,基本編輯控制項仍可做為 <textarea> 使用。

網頁元件提供屬性和屬性,來定義外部的辨識行為,包括 languagesrecognitiontype。您可以透過 value 屬性設定控制項的內容:

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

如要掌握該值的任何變更,您可以監聽 input 事件。

您可以使用 Glitch 上的示範影片來試用元件。此外,請務必查看原始碼。如要在應用程式中使用控制項,請從 npm 取得控制項

安全性和權限

Chromium 團隊採用「控管強大網路平台功能存取權」一節中定義的核心原則,設計及實作手寫辨識 API,包括使用者控制、資訊公開和人體工學。

使用者控制項

使用者無法關閉手寫辨識 API。這個做法僅適用於透過 HTTPS 放送的網站,而且只能從頂層瀏覽環境呼叫。

資訊公開

但不會顯示手寫辨識功能。為了防止數位指紋採集,瀏覽器會採取因應措施,例如在偵測到可能濫用行為時向使用者顯示權限提示。

權限持續性

目前手寫辨識 API 不會顯示任何權限提示。因此,權限不需要以任何方式保留。

意見回饋

Chromium 團隊希望瞭解你的手寫辨識 API 使用體驗。

告訴我們 API 設計

API 是否有任何事情不如預期?或者,是否缺少實作構想所需的方法或屬性?對安全模式有任何疑問或意見嗎?在對應的 GitHub 存放區中提交規格問題,或是在現有問題中新增想法。

回報導入作業的問題

您在使用 Chromium 時發現錯誤嗎?還是實作方式與規格不同?請前往 new.crbug.com 回報錯誤。請務必盡可能提供更多詳細資料和重現的簡易操作說明,並在「元件」方塊中輸入 Blink>HandwritingGlitch 適合用於分享快速簡便的重新提交內容。

顯示 API 支援

您打算使用手寫辨識 API 嗎?您的公開支援可協助 Chromium 團隊決定功能的優先順序,並讓其他瀏覽器供應商瞭解支援這些功能的重要性。

請在 WICG 討論會話串上分享您打算如何使用這項工具。請使用主題標記 #HandwritingRecognition 傳送 Twitter 推文至 @ChromiumDev,讓我們說明您使用了這項功能的位置和方式。

特別銘謝

本文是由 Joe Medley、Honglin Yu 和 Jiewei Qian 審查。主頁橫幅由 Samir Bouaked 印在 Unsplash 上。