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

ממשק ה-API לזיהוי כתב יד מאפשר לכם לזהות טקסט מקלט בכתב יד בזמן שהוא מתרחש.

כריסטיאן ליבל
כריסטיאן ליבל
תומאס סטיינר
תומאס סטיינר

מהו ממשק ה-API לזיהוי כתב יד?

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

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

תרחישי שימוש מוצעים עבור ממשק ה-API לזיהוי כתב יד

שימושים לדוגמה:

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

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

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

איך משתמשים בממשק ה-API לזיהוי כתב יד

זיהוי תכונות

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

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, כך שה-handler של 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, וניתן לקרוא לה רק מהקשר הגלישה ברמה העליונה.

שקיפות

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

עקביות הרשאה

בשלב זה, לא מוצגות בקשות להרשאות כלשהן בממשק ה-API של זיהוי כתב יד. כך שאין צורך לשמור את ההרשאות בשום צורה.

משוב

צוות Chromium רוצה לשמוע על החוויות שלך עם ממשק ה-API לזיהוי כתב יד.

ספרו לנו על תכנון ה-API

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

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

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

הבעת תמיכה ב-API

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

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

אישורים

המאמר הזה נכתב על ידי Joe Medley, Honglin Yu ו-Jiwei Qian. תמונה ראשית (Hero) מאת Samir Bouaked ב-UnFlood.