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

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

ما هي واجهة برمجة التطبيقات Handwriting Recognition API؟

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

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

حالات الاستخدام المقترَحة لواجهة برمجة التطبيقات Handwriting Recognition API

تشمل أمثلة الاستخدامات ما يلي:

  • تطبيقات لتدوين الملاحظات حيث يريد المستخدمون تسجيل ملاحظات مكتوبة بخط اليد وترجمتها إلى نصوص.
  • نماذج من التطبيقات يمكن للمستخدمين من خلالها استخدام الإدخال بالقلم أو الإصبع بسبب ضيق الوقت
  • الألعاب التي تتطلب ملء حروف أو أرقام، مثل الكلمات المتقاطعة، أو hangman، أو sudoku (سودوكو).

الوضع الحالي

تتوفّر واجهة برمجة التطبيقات Handwriting Recognition من (Chromium 99).

كيفية استخدام واجهة برمجة التطبيقات Handwriting Recognition API

اكتشاف الميزات

اكتشاف توافق المتصفّح من خلال التحقّق من وجود الطريقة createHandwritingRecognizer() على كائن المستكشف:

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

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

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

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

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

الكيانات الأساسية في واجهة برمجة التطبيقات للتعرف على الكتابة بخط اليد: يتم إنشاء رسم من خلال نقطة واحدة أو أكثر يتم إنشاء رسم من خلال أداة التعرف على نقطة أو أكثر. ويتم التعرّف الفعلي على مستوى الرسم.

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

للتعرّف على النص من الإدخال المكتوب بخط اليد، عليك الحصول على مثال 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)

تعمل واجهة برمجة تطبيقات التعرّف على الكتابة بخط اليد مع ميزة أحداث المؤشر التي توفّر واجهة مجردة للاطّلاع على الإدخالات من أي جهاز تأشير. تحتوي وسيطات حدث المؤشر على نوع المؤشر المستخدم. هذا يعني أنه يمكنك استخدام أحداث المؤشر لتحديد نوع الإدخال تلقائيًا. في المثال التالي، يتم إنشاء الرسم للتعرّف على الكتابة بخط اليد تلقائيًا عند أول موضع ورود لحدث 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));
});

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

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

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

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

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

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

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

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

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

الشفافية

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

استمرارية الحصول على الإذن

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

إضافة ملاحظات

يرغب فريق Chromium في معرفة معلومات عن تجاربك في استخدام واجهة برمجة التطبيقات Handwriting Recognition API.

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

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

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

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

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

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

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

شكر وتقدير

تمت مراجعة هذه المقالة من قِبل جو ميدلي وهونغلين يو وجيوي كيانان. صورة رئيسية من إعداد سمير بواكد على قناة Unsplash