זיהוי המשתמשים שלך' כתב יד

Handwriting Recognition API מאפשר לזהות טקסט מקלט בכתב יד בזמן שהוא מתבצע.

מהו Handwriting Recognition API?

באמצעות Handwriting Recognition API אפשר להמיר כתב יד (דיו) של משתמשים לטקסט. מערכת הפעלה מסוימות כללו ממשקי API כאלה כבר מזמן, והיכולת החדשה הזו תאפשר לאפליקציות האינטרנט שלכם להשתמש בפונקציונליות הזו סוף סוף. ההמרה מתרחשת ישירות במכשיר של המשתמש, ופועלת גם במצב אופליין, בלי להוסיף ספריות או שירותים של צד שלישי.

ממשק ה-API הזה מיישם זיהוי שנקרא 'אונליין' או זיהוי כמעט בזמן אמת. כלומר, הקלט בכתב יד מזוהה בזמן שהמשתמש מצייר אותו, על ידי תיעוד וניתוח של הקווים הבודדים. בניגוד לפעולות 'אופליין' כמו זיהוי תווים אופטי (OCR), שבהן ידוע רק המוצר הסופי, אלגוריתמים אונליין יכולים לספק רמה גבוהה יותר של דיוק בגלל אותות נוספים כמו הרצף הזמני והלחץ של משיכות הדיו הנפרדות.

תרחישים לדוגמה לשימוש ב-Handwriting Recognition API

דוגמאות לשימושים:

  • אפליקציות לרישום הערות שבהן המשתמשים רוצים לצלם הערות בכתב יד ולתרגם אותן לטקסט.
  • אפליקציות של טפסים שבהן המשתמשים יכולים להשתמש בקלט באמצעות עט או אצבע בגלל מגבלות זמן.
  • משחקים שבהם צריך למלא אותיות או מספרים, כמו תשבצים, דמעות או סודוקו.

הסטטוס הנוכחי

ממשק ה-API של זיהוי כתב היד זמין מ-Chromium 99 ואילך.

איך משתמשים ב-Handwriting Recognition API

זיהוי תכונות

כדי לזהות את התמיכה בדפדפן, בודקים אם השיטה createHandwritingRecognizer() קיימת באובייקט navigator:

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

מושגי ליבה

ממשק Handwriting Recognition API ממיר קלט בכתב יד לטקסט, ללא קשר לשיטת הקלט (עכבר, מגע, עט). ל-API יש ארבע ישויות עיקריות:

  1. נקודה מייצגת את המיקום שבו הסמן היה בזמן מסוים.
  2. קו מורכב מנקודה אחת או יותר. ההקלטה של הקו מתחילה כשהמשתמש מניח את הסמן (כלומר לוחץ על לחצן העכבר הראשי או נוגע במסך בעזרת העט או האצבע) ומסתיימת כשהמשתמש מרים את הסמן חזרה.
  3. ציור מורכב מקשת אחת או יותר. הזיהוי בפועל מתבצע ברמה הזו.
  4. המזהה מוגדר עם שפת הקלט הצפויה. הוא משמש ליצירת מופע של ציור עם הגדרות המזהה.

המושגים האלה מיושמים כממשקים ומילונים ספציפיים, ועליהם ארחיב בהמשך.

ישויות הליבה של Handwriting Recognition API: נקודה אחת או יותר יוצרות קו, וקו אחד או יותר יוצרים ציור, שהמערכת לזיהוי יוצרת. הזיהוי בפועל מתבצע ברמת הציור.

יצירת מכשיר זיהוי

כדי לזהות טקסט ממקור ידני, צריך לקבל מופע של HandwritingRecognizer על ידי קריאה ל-navigator.createHandwritingRecognizer() והעברת אילוצים אליו. האילוצים קובעים את מודל זיהוי הכתב היד שבו צריך להשתמש. בשלב הזה אפשר לציין רשימה של שפות לפי סדר ההעדפה:

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

השיטה מחזירה הבטחה (promise) שמתקבלת עם מופע של 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

השיטה מחזירה הבטחה (promise) שמתקבלת עם אובייקט תוצאה. אם הדפדפן תומך בתכונה שצוינה על ידי המפתח, הערך שלה יוגדר כ-true. אחרת, הוא יוגדר כ-false. אפשר להשתמש במידע הזה כדי להפעיל או להשבית תכונות מסוימות באפליקציה, או כדי לשנות את השאילתה ולשלוח שאילתה חדשה.

התחלת שרטוט

באפליקציה, צריך להציע אזור קלט שבו המשתמש מזין את הפרטים בכתב יד. מטעמי ביצועים, מומלץ להטמיע את הקוד הזה בעזרת אובייקט לוח. ההטמעה המדויקת של החלק הזה לא נכללת במאמר הזה, אבל אפשר לעיין בהדגמה כדי לראות איך עושים את זה.

כדי להתחיל ציור חדש, צריך להפעיל את השיטה startDrawing() במזהה. בשיטה הזו נעשה שימוש באובייקט שמכיל רמזים שונים כדי לשפר את אלגוריתם הזיהוי. כל ההנחיות הן אופציונליות:

  • סוג הטקסט שמוזן: טקסט, כתובות אימייל, מספרים או תו בודד (recognitionType)
  • סוג מכשיר הקלט: עכבר, מגע או קלט בעט (inputType)
  • הטקסט הקודם (textContext)
  • מספר התחזיות החלופיות פחות הסבירות שצריך להחזיר (alternatives)
  • רשימה של תווים מזהים של משתמשים ('גרפימות') שהמשתמשים צפויים להזין (graphemeSet)

ממשק ה-API לזיהוי כתב יד עובד היטב עם Pointer Events, שמספקים ממשק מופשט לצריכת קלט מכל אמצעי הצבעה. הארגומנטים של אירוע הסמן מכילים את סוג הסמן שבו נעשה שימוש. כלומר, אפשר להשתמש באירועי סמן כדי לקבוע את סוג הקלט באופן אוטומטי. בדוגמה הבאה, הציור לזיהוי כתב יד נוצר באופן אוטומטי במופע הראשון של אירוע 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) יחד עם המיקום שלו בטקסט שזוהה (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> מטמיע אמצעי בקרה לעריכה שמתפתח בהדרגה, שיכול לזהות כתב יד. לוחצים על הלחצן בפינה השמאלית התחתונה של חלונית הבקרה של העריכה כדי להפעיל את מצב הציור. כשמסיימים את הציור, רכיב האינטרנט מתחיל את הזיהוי באופן אוטומטי ומוסיף את הטקסט שזוהה חזרה לכלי העריכה. אם אין תמיכה ב-Handwriting Recognition API בכלל, או שהפלטפורמה לא תומכת בתכונות המבוקשות, לחצן העריכה יהיה מוסתר. עם זאת, עדיין אפשר להשתמש בלחצן הבקרה הבסיסי לעריכה כ-<textarea>.

רכיב האינטרנט מציע מאפיינים ומאפיינים משניים כדי להגדיר את התנהגות הזיהוי מבחוץ, כולל languages ו-recognitiontype. אפשר להגדיר את התוכן של הרכיב באמצעות המאפיין value:

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

כדי לקבל עדכונים על שינויים בערך, אפשר להאזין לאירוע input.

אתם יכולים לנסות את הרכיב באמצעות הדגמה הזו ב-Glitch. כדאי גם לעיין בקוד המקור. כדי להשתמש באמצעי הבקרה באפליקציה, צריך לקבל אותו מ-npm.

אבטחה והרשאות

צוות Chromium תכנן ויישם את Handwriting Recognition API על סמך העקרונות המרכזיים שמוגדרים במאמר בקרת הגישה לתכונות מתקדמות של פלטפורמת האינטרנט, כולל בקרת משתמשים, שקיפות וארגונומיה.

שליטת משתמשים

המשתמש לא יכול להשבית את Handwriting Recognition API. הוא זמין רק לאתרים שמוצגים באמצעות HTTPS, וניתן להפעיל אותו רק מההקשר של הגלישה ברמה העליונה.

שקיפות

אין אינדיקציה לכך שזיהוי כתב היד פעיל. כדי למנוע יצירה של טביעת אצבע, הדפדפן מטמיע אמצעי נגד, כמו הצגת בקשה להרשאה למשתמש כשהוא מזהה ניצול לרעה אפשרי.

התמדת ההרשאות

בשלב הזה, ממשק Handwriting Recognition API לא מציג הנחיות לגבי הרשאות. לכן, אין צורך לשמור את ההרשאה בשום צורה.

משוב

צוות Chromium רוצה לשמוע על החוויה שלכם עם Handwriting Recognition API.

תיאור של עיצוב ה-API

האם יש משהו ב-API שלא פועל כצפוי? או אולי חסרות שיטות או מאפיינים שאתם צריכים כדי להטמיע את הרעיון? יש לכם שאלות או הערות לגבי מודל האבטחה? אתם יכולים לשלוח דיווח על בעיה במפרט במאגר GitHub המתאים, או להוסיף את המחשבות שלכם לבעיה קיימת.

דיווח על בעיה בהטמעה

מצאתם באג בהטמעה של Chromium? או שההטמעה שונה מהמפרט? שולחים דיווח על באג בכתובת new.crbug.com. חשוב לכלול כמה שיותר פרטים, הוראות פשוטות לשחזור הבעיה ולהזין את הערך Blink>Handwriting בתיבה Components. Glitch הוא כלי מצוין לשיתוף שחזור מהיר וקל של באגים.

תמיכה ב-API

אתם מתכננים להשתמש ב-Handwriting Recognition API? התמיכה הציבורית שלכם עוזרת לצוות Chromium לתת עדיפות לתכונות, ומראה לספקי דפדפנים אחרים כמה חשובה התמיכה בהן.

אתם יכולים לספר לנו איך אתם מתכננים להשתמש בו בשרשור ב-Discourse של WICG. אפשר לשלוח ציוץ אל @ChromiumDev עם ההאשטאג #HandwritingRecognition ולספר לנו איפה ואיך אתם משתמשים בו.

תודות

המסמך הזה נבדק על ידי Joe Medley,‏ Honglin Yu ו-Jiewei Qian.