Herken uw gebruikers' handschrift

Met de Handschriftherkennings-API kunt u tekst uit handgeschreven invoer herkennen zodra deze zich voordoet.

Wat is de handschriftherkennings-API?

Met de Handschriftherkenning API kunt u handschrift (inkt) van uw gebruikers omzetten in tekst. Sommige besturingssystemen bevatten al lang dergelijke API's, en met deze nieuwe mogelijkheid kunnen uw webapps eindelijk deze functionaliteit gebruiken. De conversie vindt rechtstreeks op het apparaat van de gebruiker plaats en werkt zelfs in de offline modus, en dit alles zonder toevoeging van bibliotheken of services van derden.

Deze API implementeert zogenaamde "on-line" of bijna realtime herkenning. Dit betekent dat de handgeschreven invoer wordt herkend terwijl de gebruiker deze tekent door de afzonderlijke streken vast te leggen en te analyseren. In tegenstelling tot ‘off-line’ procedures zoals Optical Character Recognition (OCR), waarbij alleen het eindproduct bekend is, kunnen online-algoritmen een hoger nauwkeurigheidsniveau bieden dankzij aanvullende signalen zoals de temporele volgorde en druk van individuele inkt. beroertes.

Voorgestelde gebruiksscenario's voor de Handschriftherkenning-API

Voorbeelden van toepassingen zijn onder meer:

  • Toepassingen voor het maken van notities waarbij gebruikers handgeschreven notities willen vastleggen en deze in tekst willen laten vertalen.
  • Formuliertoepassingen waarbij gebruikers vanwege tijdgebrek pen- of vingerinvoer kunnen gebruiken.
  • Spellen waarbij letters of cijfers moeten worden ingevuld, zoals kruiswoordraadsels, beul of sudoku.

Huidige status

De Handschriftherkenning-API is beschikbaar vanaf (Chromium 99).

Hoe u de handschriftherkennings-API gebruikt

Functiedetectie

Detecteer browserondersteuning door te controleren op het bestaan ​​van de methode createHandwritingRecognizer() op het navigatorobject:

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

Hoofdconcepten

De Handschriftherkenning API zet handgeschreven invoer om in tekst, ongeacht de invoermethode (muis, aanraking, pen). De API heeft vier hoofdentiteiten:

  1. Een punt geeft aan waar de aanwijzer zich op een bepaald tijdstip bevond.
  2. Een slag bestaat uit één of meer punten. De opname van een streek begint wanneer de gebruiker de aanwijzer neerzet (dwz op de primaire muisknop klikt, of het scherm aanraakt met zijn pen of vinger) en eindigt wanneer hij de aanwijzer weer omhoog brengt.
  3. A drawing consists of one or more strokes. Op dit niveau vindt de daadwerkelijke herkenning plaats.
  4. De herkenner is geconfigureerd met de verwachte invoertaal. Het wordt gebruikt om een ​​exemplaar van een tekening te maken waarop de herkennerconfiguratie is toegepast.

Deze concepten worden geïmplementeerd als specifieke interfaces en woordenboeken, die ik binnenkort zal bespreken.

De kernentiteiten van de Handschriftherkenning API: Een of meer punten vormen een lijn, een of meer lijnen vormen een tekening die de herkenner maakt. De daadwerkelijke herkenning vindt plaats op tekenniveau.

Een herkenner maken

Om tekst uit handgeschreven invoer te herkennen, moet u een exemplaar van een HandwritingRecognizer verkrijgen door navigator.createHandwritingRecognizer() aan te roepen en er beperkingen aan door te geven. Beperkingen bepalen welk handschriftherkenningsmodel moet worden gebruikt. Momenteel kunt u een lijst met talen opgeven in volgorde van voorkeur:

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

De methode retourneert een belofte en lost op met een exemplaar van een HandwritingRecognizer wanneer de browser aan uw verzoek kan voldoen. Anders wordt de belofte met een fout afgewezen en is handschriftherkenning niet beschikbaar. Om deze reden kunt u het beste eerst de ondersteuning van de herkenner voor bepaalde herkenningsfuncties opvragen.

Herkennerondersteuning opvragen

Door navigator.queryHandwritingRecognizerSupport() aan te roepen, kunt u controleren of het doelplatform de handschriftherkenningsfuncties ondersteunt die u wilt gebruiken. In het volgende voorbeeld doet de ontwikkelaar:

  • wil teksten in het Engels detecteren
  • alternatieve, minder waarschijnlijke voorspellingen krijgen, indien beschikbaar
  • toegang krijgen tot het segmentatieresultaat, dat wil zeggen de herkende karakters, inclusief de punten en streken waaruit ze bestaan
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

De methode retourneert een belofte die wordt opgelost met een resultaatobject. Als de browser de door de ontwikkelaar gespecificeerde functie ondersteunt, wordt de waarde ervan ingesteld op true . Anders wordt deze ingesteld op false . U kunt deze informatie gebruiken om bepaalde functies binnen uw applicatie in of uit te schakelen, of om uw vraag aan te passen en een nieuwe te sturen.

Begin met een tekening

Binnen uw toepassing moet u een invoergebied aanbieden waar de gebruiker zijn handgeschreven gegevens kan invoeren. For performance reasons, it is recommended to implement this with the help of a canvas object . De exacte implementatie van dit onderdeel valt buiten het bestek van dit artikel, maar u kunt de demo raadplegen om te zien hoe dit kan worden gedaan.

Om een ​​nieuwe tekening te starten, roept u de methode startDrawing() op de herkenner aan. Deze methode gebruikt een object dat verschillende hints bevat om het herkenningsalgoritme te verfijnen. Alle hints zijn optioneel:

  • Het soort tekst dat wordt ingevoerd: tekst, e-mailadressen, cijfers of een afzonderlijk teken ( recognitionType )
  • Het type invoerapparaat: muis-, aanraak- of peninvoer ( inputType )
  • De voorgaande tekst ( textContext )
  • Het aantal minder waarschijnlijke alternatieve voorspellingen dat moet worden geretourneerd ( alternatives )
  • Een lijst met door de gebruiker identificeerbare tekens ("graphemes") die de gebruiker waarschijnlijk zal invoeren ( graphemeSet )

De Handschriftherkenning API werkt goed met Pointer Events , die een abstracte interface bieden om invoer van elk aanwijsapparaat te verwerken. De pointergebeurtenisargumenten bevatten het type pointer dat wordt gebruikt. Dit betekent dat u pointergebeurtenissen kunt gebruiken om het invoertype automatisch te bepalen. In het volgende voorbeeld wordt de tekening voor handschriftherkenning automatisch gemaakt bij de eerste keer dat een pointerdown gebeurtenis in het handschriftgebied voorkomt. Omdat het pointerType mogelijk leeg is of op een eigen waarde is ingesteld, heb ik een consistentiecontrole geïntroduceerd om er zeker van te zijn dat alleen ondersteunde waarden worden ingesteld voor het invoertype van de tekening.

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);
});

Voeg een streek toe

Het pointerdown evenement is ook de juiste plaats om een ​​nieuwe slag te beginnen. To do so, create a new instance of HandwritingStroke . U moet ook de huidige tijd opslaan als referentiepunt voor de volgende punten die eraan worden toegevoegd:

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

Voeg een punt toe

Nadat u de streek heeft gemaakt, moet u er direct het eerste punt aan toevoegen. Omdat u later meer punten gaat toevoegen, is het zinvol om de logica voor het maken van punten in een aparte methode te implementeren. In het volgende voorbeeld berekent de methode addPoint() de verstreken tijd op basis van de referentietijdstempel. De temporele informatie is optioneel, maar kan de herkenningskwaliteit verbeteren. Vervolgens leest het de X- en Y-coördinaten van de pointergebeurtenis en voegt het punt toe aan de huidige streek.

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

De pointermove gebeurtenishandler wordt aangeroepen wanneer de aanwijzer over het scherm wordt bewogen. Deze punten moeten ook aan de slag worden toegevoegd. De gebeurtenis kan ook worden geactiveerd als de aanwijzer niet in een "omlaag"-status staat, bijvoorbeeld wanneer u de cursor over het scherm beweegt zonder op de muisknop te drukken. De gebeurtenishandler uit het volgende voorbeeld controleert of er een actieve streek bestaat en voegt het nieuwe punt eraan toe.

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

Herken tekst

Wanneer de gebruiker de aanwijzer weer optilt, kunt u de lijn aan uw tekening toevoegen door de addStroke() -methode aan te roepen. The following example also resets the activeStroke , so the pointermove handler will not add points to the completed stroke.

Vervolgens is het tijd om de invoer van de gebruiker te herkennen door de methode getPrediction() op de tekening aan te roepen. Herkenning duurt meestal minder dan een paar honderd milliseconden, dus u kunt indien nodig herhaaldelijk voorspellingen uitvoeren. In het volgende voorbeeld wordt na elke voltooide slag een nieuwe voorspelling uitgevoerd.

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));
});

Deze methode retourneert een belofte die wordt opgelost met een reeks voorspellingen, gerangschikt op waarschijnlijkheid. Het aantal elementen hangt af van de waarde die u aan de alternatives hint hebt doorgegeven. U kunt deze array gebruiken om de gebruiker een keuze uit mogelijke overeenkomsten te bieden en hem een ​​optie te laten selecteren. Als alternatief kunt u gewoon uitgaan van de meest waarschijnlijke voorspelling, wat ik in het voorbeeld doe.

Het voorspellingsobject bevat de herkende tekst en een optioneel segmentatieresultaat, dat ik in de volgende sectie zal bespreken.

Gedetailleerde inzichten met segmentatieresultaten

Indien ondersteund door het doelplatform kan het voorspellingsobject ook een segmentatieresultaat bevatten. Dit is een array die alle herkende handschriftsegmenten bevat, een combinatie van het herkende, door de gebruiker identificeerbare teken ( grapheme ) samen met zijn positie in de herkende tekst ( beginIndex , endIndex ) en de streken en punten waaruit het is ontstaan.

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);
      });
    },
  );
}

Met deze informatie kun je de herkende grafemen weer op het canvas opsporen.

Rond elk herkend grafeem worden kaders getekend

Volledige herkenning

Nadat de herkenning is voltooid, kunt u bronnen vrijmaken door de methode clear() aan te roepen op HandwritingDrawing en de methode finish() op HandwritingRecognizer :

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

Demo

De webcomponent <handwriting-textarea> implementeert een geleidelijk verbeterde bewerkingsbesturing die handschriftherkenning mogelijk maakt. Door op de knop in de rechter benedenhoek van het bewerkingsbesturingselement te klikken, activeert u de tekenmodus. Wanneer u de tekening voltooit, start de webcomponent automatisch de herkenning en voegt de herkende tekst weer toe aan het bewerkingsbesturingselement. Als de Handschriftherkenning API helemaal niet wordt ondersteund, of als het platform de gevraagde functies niet ondersteunt, wordt de bewerkingsknop verborgen. Maar de basisbewerkingsbesturing blijft bruikbaar als <textarea> .

De webcomponent biedt eigenschappen en attributen om het herkenningsgedrag van buitenaf te definiëren, inclusief languages en recognitiontype . Via het value attribuut kunt u de inhoud van het besturingselement instellen:

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

Om geïnformeerd te worden over eventuele wijzigingen in de waarde, kunt u naar de input luisteren.

Je kunt de component uitproberen met behulp van deze demo op Glitch . Bekijk ook zeker de broncode . Als u het besturingselement in uw toepassing wilt gebruiken, haalt u het op via npm .

Beveiliging en machtigingen

Het Chromium-team heeft de Handschriftherkenning-API ontworpen en geïmplementeerd met behulp van de kernprincipes die zijn gedefinieerd in Toegangscontrole tot krachtige webplatformfuncties , waaronder gebruikerscontrole, transparantie en ergonomie.

Gebruikerscontrole

De Handschriftherkenning-API kan niet door de gebruiker worden uitgeschakeld. Het is alleen beschikbaar voor websites die worden geleverd via HTTPS en kan alleen worden aangeroepen vanuit de browsercontext op het hoogste niveau.

Transparantie

Er is geen indicatie of handschriftherkenning actief is. Om vingerafdrukken te voorkomen, implementeert de browser tegenmaatregelen, zoals het tonen van een toestemmingsprompt aan de gebruiker wanneer mogelijk misbruik wordt gedetecteerd.

Persistentie van toestemming

De Handschriftherkenning-API toont momenteel geen toestemmingsprompts. De toestemming hoeft dus op geen enkele manier te worden gehandhaafd.

Feedback

Het Chromium-team wil graag horen wat uw ervaringen zijn met de Handschriftherkenning-API.

Vertel ons over het API-ontwerp

Is er iets aan de API dat niet werkt zoals je had verwacht? Of ontbreken er methoden of eigenschappen die je nodig hebt om je idee te implementeren? Heeft u een vraag of opmerking over het beveiligingsmodel? Dien een spec issue in op de corresponderende GitHub repository , of voeg uw gedachten toe aan een bestaand issue.

Meld een probleem met de implementatie

Heb je een bug gevonden in de implementatie van Chromium? Of wijkt de uitvoering af van de specificaties? Dien een bug in op new.crbug.com . Zorg ervoor dat u zoveel mogelijk details en eenvoudige instructies voor het reproduceren opneemt, en typ Blink>Handwriting in het vak Componenten . Glitch werkt uitstekend voor het delen van snelle en gemakkelijke reproducties.

Toon ondersteuning voor de API

Bent u van plan de Handschriftherkenning API te gebruiken? Jouw publieke steun helpt het Chromium-team bij het prioriteren van functies en laat andere browserleveranciers zien hoe belangrijk het is om deze te ondersteunen.

Deel hoe u het wilt gebruiken in de WICG Discourse-thread . Stuur een tweet naar @ChromiumDev met de hashtag #HandwritingRecognition en laat ons weten waar en hoe u deze gebruikt.

Dankbetuigingen

Dit artikel is beoordeeld door Joe Medley , Honglin Yu en Jiewei Qian. Hero-afbeelding door Samir Bouaked op Unsplash .