التعرف على الكتابة اليدوية للمستخدمين

تتيح لك واجهة برمجة التطبيقات Handwriting Recognition API التعرّف على النص من الإدخال المكتوب بخط اليد أثناء حدوثه.

ما هي Handwriting Recognition 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!
}

المفاهيم الأساسية

تحوّل واجهة برمجة التطبيقات Handwriting Recognition API الإدخال المكتوب بخط اليد إلى نص، بغض النظر عن طريقة الإدخال (الماوس أو اللمس أو قلم الشاشة). تتضمّن واجهة برمجة التطبيقات أربعة عناصر رئيسية:

  1. تمثّل النقطة موضع المؤشر في وقت معيّن.
  2. يتكوّن الخط من نقطة واحدة أو أكثر. يبدأ تسجيل الضربة عندما يضع المستخدم المؤشر على الشاشة (أي ينقر على زر الماوس الأساسي أو يلمس الشاشة بقلم أو إصبع) وينتهي عندما يرفع المؤشر مرة أخرى.
  3. يتكوّن الرسم من ضربة واحدة أو أكثر. ويتم التعرّف الفعلي على المحتوى على هذا المستوى.
  4. يتم ضبط أداة التعرّف باستخدام لغة الإدخال المتوقّعة. يُستخدَم لإنشاء مثيل لرسومات تم تطبيق إعدادات أداة التعرّف عليها.

يتم تنفيذ هذه المفاهيم كواجهات وقواميس محدّدة، وسأتناولها قريبًا.

الكيانات الأساسية في Handwriting Recognition API: تتألف الضربة من نقطة واحدة أو أكثر، ويتألف الرسم من ضربة واحدة أو أكثر، وينشئها أداة التعرّف. ويتم التعرّف على الرسم على مستوى الرسم.

إنشاء أداة للتعرّف

للتعرّف على نص من إدخال مكتوب بخط اليد، عليك الحصول على مثيل من HandwritingRecognizer من خلال استدعاء navigator.createHandwritingRecognizer() وتمرير القيود إليه. تحدّد القيود نموذج التعرّف على الكتابة بخط اليد الذي يجب استخدامه. يمكنك حاليًا تحديد قائمة باللغات حسب ترتيب الأفضلية:

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

تعرض الطريقة وعدًا يتم تنفيذه باستخدام مثيل من HandwritingRecognizer عندما يتمكّن المتصفّح من تلبية طلبك. وفي حال عدم توفّرها، سيتم رفض الوعد مع ظهور خطأ، ولن تكون ميزة التعرّف على الكتابة بخط اليد متاحة. لهذا السبب، ننصحك بالاستعلام أولاً عمّا إذا كان برنامج التعرّف على الصوت يتيح ميزات تعرّف معيّنة.

الاستعلام عن إمكانية استخدام أداة التعرّف

من خلال الاتصال بـ navigator.queryHandwritingRecognizer()، يمكنك التحقّق مما إذا كانت المنصة المستهدَفة تتيح ميزات التعرّف على الكتابة بخط اليد التي تنوي استخدامها. تتلقّى هذه الطريقة كائن القيود نفسه الذي تتلقّاه الطريقة navigator.createHandwritingRecognizer()، والذي يحتوي على قائمة باللغات المطلوبة. تعرض الطريقة وعدًا يتم تنفيذه باستخدام عنصر نتيجة إذا تم العثور على أداة تمييز متوافقة. بخلاف ذلك، يتم حلّ الوعد إلى 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. يمكنك استخدام هذه المعلومات لتفعيل ميزات معيّنة أو إيقافها ضِمن تطبيقك أو لإرسال طلب بحث جديد لمجموعة مختلفة من اللغات.

بدء رسم

يجب أن توفّر داخل تطبيقك مساحة إدخال يكتب فيها المستخدم بخط اليد. لأسباب تتعلّق بالأداء، يُنصح بتنفيذ ذلك بمساعدة عنصر لوحة الرسم. لا تتناول هذه المقالة طريقة التنفيذ الدقيقة لهذا الجزء، ولكن يمكنك الرجوع إلى العرض التوضيحي لمعرفة كيفية إجراء ذلك.

لبدء رسم جديد، استدعِ طريقة startDrawing() في أداة التعرّف. تستقبل هذه الطريقة عنصرًا يحتوي على تلميحات مختلفة لضبط خوارزمية التعرّف بدقة. جميع التلميحات اختيارية:

  • نوع النص الذي يتم إدخاله: نص أو عناوين بريد إلكتروني أو أرقام أو حرف فردي(recognitionType)
  • نوع جهاز الإدخال: الإدخال باستخدام الماوس أو اللمس أو قلم الشاشة (inputType)
  • النص السابق (textContext)
  • عدد التوقّعات البديلة الأقل احتمالاً التي يجب عرضها (alternatives)
  • قائمة بالأحرف التي يمكن التعرّف عليها من قِبل المستخدم ("وحدات الكتابة") والتي من المرجّح أن يدخلها المستخدم (graphemeSet)

تتكامل واجهة برمجة التطبيقات Handwriting Recognition 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', '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، وبالتالي لن يضيف معالج 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> تحسينًا تدريجيًا، وهو عنصر تحكّم في التعديل قادر على التعرّف على الكتابة بخط اليد. من خلال النقر على الزر في أسفل يسار عنصر التحكّم في التعديل، يمكنك تفعيل وضع الرسم. عند الانتهاء من الرسم، سيبدأ مكوّن الويب تلقائيًا في التعرّف على النص وإضافته إلى عنصر التحكّم في التعديل. إذا لم يكن بإمكانك استخدام واجهة برمجة التطبيقات Handwriting Recognition API على الإطلاق، أو إذا كانت المنصة لا تتوافق مع الميزات المطلوبة، سيتم إخفاء زر التعديل. ومع ذلك، يظل عنصر التحكّم الأساسي في التعديل قابلاً للاستخدام كـ <textarea>.

يوفّر مكوّن الويب سمات وخصائص لتحديد سلوك التعرّف من الخارج، بما في ذلك languages وrecognitiontype. يمكنك ضبط محتوى عنصر التحكّم من خلال السمة value:

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

للاطّلاع على أي تغييرات في القيمة، يمكنك الاستماع إلى حدث input.

يمكنك تجربة المكوّن باستخدام هذا العرض التوضيحي على GitHub. ننصحك أيضًا بإلقاء نظرة على رمز المصدر. لاستخدام عنصر التحكّم في تطبيقك، احصل عليه من npm.

الأمان والأذونات

صمّم فريق Chromium واجهة Handwriting Recognition API ونفّذها باستخدام المبادئ الأساسية المحدّدة في التحكّم في الوصول إلى الميزات الفعّالة لمنصة الويب، بما في ذلك تحكّم المستخدم والشفافية وبيئة العمل المريحة.

تحكّم المستخدم

لا يمكن للمستخدم إيقاف واجهة برمجة التطبيقات للتعرّف على الكتابة بخط اليد. ولا تتوفّر إلا للمواقع الإلكترونية التي يتم عرضها عبر HTTPS، ولا يمكن استدعاؤها إلا من سياق التصفّح على المستوى الأعلى.

الشفافية

لا يتوفّر أي مؤشر لمعرفة ما إذا كانت ميزة التعرّف على الكتابة بخط اليد مفعَّلة. لمنع التتبّع الرقمي، يتّخذ المتصفّح إجراءات مضادة، مثل عرض طلب الحصول على إذن للمستخدم عند رصد احتمال إساءة الاستخدام.

استمرار الإذن

لا تعرض واجهة برمجة التطبيقات Handwriting Recognition API حاليًا أي طلبات للحصول على أذونات. وبالتالي، لا حاجة إلى حفظ الإذن بأي شكل من الأشكال.

الملاحظات

يريد فريق Chromium معرفة رأيك في تجربة استخدام Handwriting Recognition API.

أخبِرنا عن تصميم واجهة برمجة التطبيقات

هل هناك أي شيء في واجهة برمجة التطبيقات لا يعمل على النحو المتوقع؟ أو هل هناك طرق أو سمات مفقودة تحتاج إليها لتنفيذ فكرتك؟ هل لديك سؤال أو تعليق بشأن نموذج الأمان؟ يمكنك الإبلاغ عن مشكلة في المواصفات في مستودع GitHub ذي الصلة، أو إضافة أفكارك إلى مشكلة حالية.

الإبلاغ عن مشكلة في عملية التنفيذ

هل عثرت على خطأ في تنفيذ Chromium؟ أو هل يختلف التنفيذ عن المواصفات؟ يمكنك الإبلاغ عن خطأ على new.crbug.com. احرص على تضمين أكبر قدر ممكن من التفاصيل، وتقديم تعليمات بسيطة لإعادة إنتاج الخطأ، وأدخِل Blink>Handwriting في مربّع المكوّنات.

إظهار الدعم لواجهة برمجة التطبيقات

هل تخطّط لاستخدام Handwriting Recognition API؟ يساعد دعمك العلني فريق Chromium في تحديد أولويات الميزات، ويوضّح لمورّدي المتصفّحات الآخرين مدى أهمية توفيرها.

شارِك كيف تخطّط لاستخدامه في سلسلة محادثات WICG Discourse. يمكنك إرسال تغريدة إلى ‎@ChromiumDev باستخدام الهاشتاغ #HandwritingRecognition وإخبارنا بمكان استخدامك لهذه الميزة وكيفية استخدامها.

الإقرارات

تمت مراجعة هذا المستند من قِبل جو ميدلي و&quot;هونغلين يو&quot; و&quot;جيويه وي تشيان&quot;.