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

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

מה זה Handwriting Recognition API?

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

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

תרחישי שימוש מומלצים ב-Handwriting Recognition API

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

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

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

‫Handwriting Recognition API זמין מגרסה (Chromium 99).

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

זיהוי תכונות

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

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

מושגי ליבה

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

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

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

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

יצירת רכיב לזיהוי דיבור

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

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

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

שאילתה לגבי תמיכה במנגנון זיהוי

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

  • רוצה לזהות טקסטים באנגלית
  • לקבל חיזויים חלופיים עם סבירות נמוכה יותר, אם יש כאלה
  • לקבל גישה לתוצאת הפילוח, כלומר לתווים המזוהים, כולל הנקודות והקווים שמרכיבים אותם
const result =
  await navigator.queryHandwritingRecognizerSupport({
    languages: ['en']
  });

console.log(result?.textAlternatives); // true if alternatives are supported
console.log(result?.textSegmentation); // true if segmentation is supported

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

איך מתחילים לשרטט

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

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

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

‫Handwriting Recognition 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', 'stylus'].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.

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

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

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

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

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

שקיפות

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

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

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

משוב

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

נשמח לקבל מידע על עיצוב ה-API

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

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

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

תמיכה ב-API

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

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

תודות

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