Распознавайте своих пользователей' почерк

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 распознавания рукописного текста: одна или несколько точек составляют штрих, один или несколько штрихов составляют рисунок, создаваемый распознавателем. Фактическое распознавание происходит на уровне чертежа.

Создание распознавательного устройства

Чтобы распознавать текст из рукописного ввода, вам необходимо получить экземпляр HandwritingRecognizer , вызвав navigator.createHandwritingRecognizer() и передав ему ограничения. Ограничения определяют модель распознавания рукописного текста, которую следует использовать. В настоящее время вы можете указать список языков в порядке предпочтения:

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 и метод finish() в HandwritingRecognizer :

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

Демо

Веб-компонент <handwriting-textarea> реализует прогрессивно улучшенный элемент управления редактированием, способный распознавать рукописный ввод. Нажав кнопку в правом нижнем углу элемента управления редактированием, вы активируете режим рисования. Когда вы завершите рисование, веб-компонент автоматически начнет распознавание и добавит распознанный текст обратно в элемент управления редактированием. Если API распознавания рукописного ввода вообще не поддерживается или платформа не поддерживает запрошенные функции, кнопка редактирования будет скрыта. Но базовый элемент управления редактированием остается доступным в виде <textarea> .

Веб-компонент предлагает свойства и атрибуты для определения поведения распознавания извне, включая languages и recognitiontype . Вы можете установить содержимое элемента управления через атрибут 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 , и сообщите нам, где и как вы его используете.

Благодарности

Этот документ был рецензирован Джо Медли , Хунлин Ю и Дживэй Цянь.