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

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

什麼是手寫辨識 API?

透過手寫辨識 API,您可以將使用者手寫的文字 (墨水) 轉換為文字。 部分作業系統長期以來都包含這類 API,而有了這項新功能,您的網頁應用程式終於可以使用這項功能。轉換作業直接在使用者裝置上進行,即使處於離線模式也能運作,而且完全不需要新增任何第三方程式庫或服務。

這個 API 會實作所謂的「線上」或近乎即時的辨識功能。也就是說,系統會擷取並分析單一筆劃,在使用者繪製手寫輸入內容時辨識內容。相較於「離線」程序 (例如光學字元辨識 (OCR),只會辨識最終產品),線上演算法可提供更高準確度,因為這類演算法會使用額外信號,例如個別筆劃的時間序列和壓力。

手寫辨識 API 的建議用途

用法範例包括:

  • 使用者想擷取手寫筆記並翻譯成文字的記事應用程式。
  • 使用者因時間限制而只能使用觸控筆或手指輸入的表單應用程式。
  • 需要填入字母或數字的遊戲,例如填字遊戲、猜單字或數獨。

目前狀態

手寫辨識 API 自 (Chromium 99) 起開放使用。

如何使用手寫辨識 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'],
});

如果瀏覽器可以滿足您的要求,這個方法會傳回 Promise,並以 HandwritingRecognizer 的執行個體解析。否則,系統會拒絕承諾並傳回錯誤,且無法使用手寫辨識功能。因此,您可能需要先查詢辨識器是否支援特定辨識功能。

查詢辨識器支援

呼叫 navigator.queryHandwritingRecognizer() 即可檢查目標平台是否支援您打算使用的手寫辨識功能。這個方法會採用與 navigator.createHandwritingRecognizer() 方法相同的限制物件,其中包含要求的語言清單。如果找到相容的辨識器,這個方法會傳回 Promise,並以結果物件解析。否則,Promise 會解析為 null。 在下列範例中,開發人員:

  • 想偵測英文文字
  • 在有替代預測結果時,取得較不可能的預測結果
  • 存取區隔結果,也就是辨識出的字元,包括構成字元的點和筆劃
const result =
  await navigator.queryHandwritingRecognizerSupport({
    languages: ['en']
  });

console.log(result?.textAlternatives); // true if alternatives are supported
console.log(result?.textSegmentation); // true if segmentation is supported

如果瀏覽器支援開發人員所需的功能,結果物件中的值會設為 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', 'stylus'].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 事件。

您可以使用 GitHub 上的這個示範試用元件。 也請務必查看原始碼。如要在應用程式中使用控制項,請從 npm 取得

安全性和權限

Chromium 團隊設計及實作 Handwriting Recognition API 時,採用了「控管強大的網頁平台功能存取權」中定義的核心原則,包括使用者控制權、透明度和人體工學。

使用者控制項

使用者無法關閉手寫辨識 API。這項功能僅適用於透過 HTTPS 傳送的網站,且只能從頂層瀏覽內容呼叫。

透明度

系統不會顯示手寫辨識功能是否啟用。為防止數位指紋採集,瀏覽器會實作反制措施,例如偵測到可能濫用行為時,向使用者顯示權限提示。

權限保留

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

意見回饋

Chromium 團隊想瞭解您使用 Handwriting Recognition API 的體驗。

介紹 API 設計

API 是否有任何功能無法如預期運作?或者,是否有缺少的屬性或方法需要實作,才能實現您的想法?對安全模型有任何問題或意見嗎?在對應的 GitHub 存放區中提出規格問題,或在現有問題中新增想法。

回報導入問題

您是否發現 Chromium 實作有錯誤?還是實作方式與規格不同? 在 new.crbug.com 提出錯誤回報。請務必盡可能提供詳細資料、重現問題的簡單操作說明,並在「Components」(元件) 方塊中輸入 Blink>Handwriting

支援 API

您是否打算使用手寫辨識 API?您的公開支持有助於 Chromium 團隊優先處理功能,並向其他瀏覽器供應商展現支援這些功能的重要性。

WICG Discourse 討論串中分享您的使用規劃。使用 #HandwritingRecognition 主題標記傳送推文給 @ChromiumDev,告訴我們您在何處使用這項功能,以及使用方式。

特別銘謝

本文件由 Joe Medley、 Honglin Yu 和 Jiewei Qian 審查。