จดจำลายมือของผู้ใช้

Handwriting Recognition API ช่วยให้คุณจดจำข้อความจากอินพุตที่เขียนด้วยลายมือได้ทันที

Handwriting Recognition API คืออะไร

Handwriting Recognition API ช่วยให้คุณแปลงลายมือ (หมึก) จากผู้ใช้เป็นข้อความได้ ระบบปฏิบัติการบางระบบมี API ดังกล่าวมานานแล้ว และความสามารถใหม่นี้จะช่วยให้เว็บแอปของคุณใช้ฟังก์ชันการทำงานนี้ได้ในที่สุด Conversion จะเกิดขึ้นในอุปกรณ์ของผู้ใช้โดยตรง และทํางานได้แม้ในโหมดออฟไลน์ โดยไม่ต้องเพิ่มไลบรารีหรือบริการของบุคคลที่สาม

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 จะแปลงข้อมูลที่เขียนด้วยลายมือเป็นข้อความ ไม่ว่าจะเป็นวิธีการป้อนข้อมูลใดก็ตาม (เมาส์ สัมผัส สไตลัส) API มีเอนทิตีหลัก 4 อย่าง ได้แก่

  1. จุดแสดงตำแหน่งที่เคอร์เซอร์อยู่ ณ เวลาใดเวลาหนึ่ง
  2. เส้นประกอบด้วยจุดอย่างน้อย 1 จุด การบันทึกเส้นขีดจะเริ่มเมื่อผู้ใช้วางเคอร์เซอร์ลง (เช่น คลิกปุ่มเมาส์หลัก หรือแตะหน้าจอด้วยสไตลัสหรือนิ้ว) และสิ้นสุดเมื่อผู้ใช้ยกเคอร์เซอร์ขึ้น
  3. ภาพวาดประกอบด้วยเส้นอย่างน้อย 1 เส้น การจดจำจริงจะเกิดขึ้นในระดับนี้
  4. ตัวจดจำได้รับการกำหนดค่าด้วยภาษาอินพุตที่คาดไว้ ใช้เพื่อสร้างอินสแตนซ์ ของภาพวาดที่มีการกำหนดค่าตัวจดจำ

แนวคิดเหล่านี้ได้รับการติดตั้งใช้งานเป็นอินเทอร์เฟซและพจนานุกรมที่เฉพาะเจาะจง ซึ่งฉันจะกล่าวถึงในอีกไม่ช้า

เอนทิตีหลักของ Handwriting Recognition API: จุดอย่างน้อย 1 จุดประกอบกันเป็นลายเส้น ลายเส้นอย่างน้อย 1 เส้นประกอบกันเป็นภาพวาดที่ตัวจดจำสร้างขึ้น การจดจำจริงจะเกิดขึ้นที่ระดับภาพวาด

การสร้างตัวจดจำ

หากต้องการจดจำข้อความจากอินพุตลายมือ คุณต้องรับอินสแตนซ์ของ HandwritingRecognizer โดยการเรียก navigator.createHandwritingRecognizer() และส่งข้อจำกัด ไปยังอินสแตนซ์ ข้อจำกัดจะกำหนดโมเดลการจดจำลายมือที่ควรใช้ ปัจจุบันคุณ ระบุรายการภาษาตามลำดับความต้องการได้ดังนี้

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

เมธอดจะแสดงผล Promise ที่แก้ไขด้วยอินสแตนซ์ของ HandwritingRecognizer เมื่อเบราว์เซอร์ดำเนินการตามคำขอได้ มิเช่นนั้น ระบบจะปฏิเสธสัญญาด้วยข้อผิดพลาด และการจดจำลายมือจะไม่พร้อมใช้งาน ด้วยเหตุนี้ คุณจึงอาจต้องค้นหาการรองรับฟีเจอร์การจดจำที่เฉพาะเจาะจงของ ตัวจดจำก่อน

การค้นหาการรองรับตัวจดจำ

การเรียกใช้ navigator.queryHandwritingRecognizer() จะช่วยให้คุณตรวจสอบได้ว่าแพลตฟอร์มเป้าหมายรองรับฟีเจอร์การจดจำลายมือที่คุณต้องการใช้หรือไม่ เมธอดนี้ใช้ ออบเจ็กต์ข้อจำกัดเดียวกันกับเมธอด navigator.createHandwritingRecognizer() ซึ่งมีรายการภาษาที่ขอ เมธอดจะแสดงผล Promise ที่แก้ไขด้วยออบเจ็กต์ผลลัพธ์หากพบตัวจดจำที่เข้ากันได้ มิเช่นนั้น 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 ด้วย ดังนั้นตัวแฮนเดิล pointermove จะไม่เพิ่มจุดให้กับเส้นที่วาดเสร็จแล้ว

จากนั้นก็ถึงเวลาจดจำอินพุตของผู้ใช้โดยการเรียกใช้เมธอด getPrediction() ใน drawing โดยปกติแล้ว การจดจำจะใช้เวลาน้อยกว่า 200 มิลลิวินาที คุณจึงเรียกใช้การคาดการณ์ซ้ำๆ ได้หากจำเป็น ตัวอย่างต่อไปนี้จะเรียกใช้การคาดคะเนใหม่หลังจากที่วาดแต่ละครั้งเสร็จสมบูรณ์

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);
      });
    },
  );
}

คุณสามารถใช้ข้อมูลนี้เพื่อติดตามกราฟีมที่ระบบจดจำได้บน Canvas อีกครั้ง

ระบบจะวาดกรอบรอบกราฟีมแต่ละตัวที่รู้จัก

การจดจำที่สมบูรณ์

หลังจากที่การจดจำเสร็จสมบูรณ์แล้ว คุณจะปล่อยทรัพยากรได้โดยเรียกใช้เมธอด 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 โดยใช้หลักการพื้นฐาน ที่กำหนดไว้ในการควบคุมการเข้าถึงฟีเจอร์ที่มีประสิทธิภาพของแพลตฟอร์มเว็บ ซึ่งรวมถึงการควบคุมของผู้ใช้ ความโปร่งใส และการยศาสตร์

การควบคุมของผู้ใช้

ผู้ใช้ปิด Handwriting Recognition API ไม่ได้ โดยจะใช้ได้กับเว็บไซต์ที่แสดงผ่าน HTTPS เท่านั้น และเรียกใช้ได้จากบริบทการท่องเว็บระดับบนสุดเท่านั้น

ความโปร่งใส

ไม่มีข้อบ่งชี้ว่าการจดจำลายมือทำงานอยู่หรือไม่ เบราว์เซอร์จะใช้มาตรการตอบโต้เพื่อป้องกันการเก็บลายนิ้วมือ เช่น การแสดงข้อความแจ้งขอสิทธิ์ต่อผู้ใช้เมื่อตรวจพบการละเมิดที่อาจเกิดขึ้น

การคงอยู่ของสิทธิ์

ปัจจุบัน Handwriting Recognition API ไม่แสดงข้อความแจ้งขอสิทธิ์ใดๆ ดังนั้นจึงไม่จำเป็นต้องบันทึกสิทธิ์ในลักษณะใดๆ

ความคิดเห็น

ทีม Chromium ต้องการทราบความคิดเห็นของคุณเกี่ยวกับประสบการณ์การใช้งาน Handwriting Recognition API

บอกเราเกี่ยวกับการออกแบบ API

มีอะไรเกี่ยวกับ API ที่ไม่ทำงานตามที่คุณคาดหวังไว้ไหม หรือมีเมธอด หรือพร็อพเพอร์ตี้ที่ขาดหายไปซึ่งคุณต้องใช้เพื่อนำแนวคิดไปใช้ไหม หากมีคำถามหรือความคิดเห็นเกี่ยวกับโมเดลความปลอดภัย แจ้งปัญหาเกี่ยวกับข้อกำหนดในที่เก็บ GitHub ที่เกี่ยวข้อง หรือแสดงความคิดเห็นในปัญหาที่มีอยู่

รายงานปัญหาเกี่ยวกับการติดตั้งใช้งาน

หากพบข้อบกพร่องในการใช้งาน Chromium หรือการติดตั้งใช้งานแตกต่างจากข้อกำหนด ยื่นข้อบกพร่องที่ new.crbug.com โปรดใส่รายละเอียดให้มากที่สุดเท่าที่จะทำได้ วิธีการง่ายๆ ในการทำซ้ำ และป้อน Blink>Handwriting ในช่องคอมโพเนนต์

แสดงการสนับสนุน API

คุณวางแผนที่จะใช้ Handwriting Recognition API ไหม การสนับสนุนแบบสาธารณะของคุณจะช่วยให้ทีม Chromium จัดลําดับความสําคัญของฟีเจอร์และแสดงให้ผู้ให้บริการเบราว์เซอร์รายอื่นๆ เห็นว่าการสนับสนุนฟีเจอร์เหล่านี้มีความสําคัญเพียงใด

แชร์วิธีที่คุณวางแผนจะใช้ในเธรด Discourse ของ WICG ทวีตถึง @ChromiumDev โดยใช้แฮชแท็ก #HandwritingRecognition และแจ้งให้เราทราบว่าคุณใช้ฟีเจอร์นี้ที่ใดและอย่างไร

คำขอบคุณ

เอกสารนี้ได้รับการตรวจสอบโดย Joe Medley Honglin Yu และ Jiewei Qian