사용자 필기 인식

필기 인식 API를 사용하면 필기 입력에서 텍스트를 인식할 수 있습니다.

필기 인식 API란 무엇인가요?

필기 인식 API를 사용하면 사용자의 필기 입력 (잉크)을 텍스트로 변환할 수 있습니다. 일부 운영 체제에는 오랫동안 이러한 API가 포함되어 있었으며, 이 새로운 기능을 통해 웹 앱은 마지막으로 이 기능을 사용하세요. 전환은 사용자 기기에서 직접 발생하며 서드 파티 라이브러리나 서비스를 추가하지 않고도 오프라인 모드에서 작동합니다

이 API는 소위 말하는 '온라인' 실시간에 가까운 인식을 제공합니다 즉, 필기 입력은 사용자가 그리는 동안 단일 텍스트를 캡처하고 분석하여 합니다. '오프라인'과 대조 OCR (광학 문자 인식)과 같은 최종 제품만 알려진 경우에는 온라인 알고리즘이 개별 잉크 획의 시간적 순서 및 압력과 같은 추가 신호를 제공합니다.

필기 인식 API의 권장 사용 사례

사용 예는 다음과 같습니다.

  • 사용자가 필기 메모를 캡처하고 번역하려는 메모 작성 애플리케이션 텍스트로 변환합니다
  • 시간 제약으로 인해 사용자가 펜이나 손가락 입력을 사용할 수 있는 Forms 애플리케이션
  • 십자말풀이, 행맨, 스도쿠 등 문자나 숫자를 채워야 하는 게임입니다.

현재 상태

필기 인식 API는 Chromium 99부터 사용할 수 있습니다.

필기 인식 API 사용 방법

특성 감지

createHandwritingRecognizer() 메서드가 있는지 확인하여 브라우저 지원 감지 navgator 객체에서

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

핵심 개념

필기 인식 API는 입력 방법에 관계없이 필기 입력을 텍스트로 변환합니다. (마우스, 터치, 펜) API에는 다음과 같은 네 가지 주요 항목이 있습니다.

  1. 은 특정 시간에 포인터가 있었던 위치를 나타냅니다.
  2. 은 하나 이상의 점으로 구성됩니다. 사용자가 다음을 내려놓으면 획 기록이 시작됩니다. 포인터를 아래로 (예: 기본 마우스 버튼을 클릭하거나, 펜으로 화면을 터치하거나, 손가락)으로 이동하고 포인터를 다시 위로 올리면 종료됩니다.
  3. 그림은 하나 이상의 획으로 구성됩니다. 실제 인식은 이 수준에서 이루어집니다.
  4. recognizer가 예상 입력 언어로 구성됩니다. 인스턴스를 만드는 데 사용됩니다. 인식기 구성이 적용된 그림

이러한 개념은 구체적인 인터페이스와 사전으로 구현되며, 이에 대해서는 곧 다루겠습니다.

필기 인식 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));
});

이 메서드는 가능성이 있습니다. 요소 수는 alternatives 힌트에 전달한 값에 따라 다릅니다. 나 이 배열을 사용하여 사용자에게 가능한 일치 항목을 선택하고 옵션을 선택합니다. 대신에 가장 가능성이 높은 예측으로 가서 예로 들 수 있습니다

예상 검색어 객체에는 인식된 텍스트와 선택적 세분화 결과가 포함되어 있습니다. 여기에 대해서는 다음 섹션에서 설명합니다.

세분화 결과가 포함된 자세한 통계

타겟 플랫폼에서 지원하는 경우 예상 검색어 객체에 세분화 결과도 포함될 수 있습니다. 이는 인식된 모든 필기 입력 세그먼트를 포함하는 배열로, 인식된 인식된 텍스트에서의 위치 및 사용자 식별 가능 문자 (grapheme) (beginIndex, endIndex) 및 속성을 생성한 획과 점

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, HandwritingRecognizerfinish() 메서드:

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>Handwriting를 입력합니다. Glitch는 쉽고 빠른 재현을 공유하는 데 효과적입니다.

API 지원 표시

필기 인식 API를 사용할 계획이신가요? 공개 지원은 Chromium팀에 도움이 됩니다 기능의 우선순위를 지정하고 해당 기능을 지원하는 것이 얼마나 중요한지 다른 브라우저 공급업체에 보여줍니다.

WICG 담화 대화목록에서 사용 계획을 공유합니다. 트윗을 보낼 대상 @ChromiumDev와 해시태그 사용 #HandwritingRecognition 어디서 어떻게 사용하는지 Google에 알려주세요.

감사의 말씀

이 도움말은 Joe Medley, Honglin Yu, Jiewei Qian이 검토했습니다. 히어로 이미지 제공자 사미르 부크드 - 스플래시 해제.