Kenali tulisan tangan pengguna

Handwriting Recognition API memungkinkan Anda mengenali teks dari input tulisan tangan saat teks tersebut ditulis.

Apa itu Handwriting Recognition API?

Handwriting Recognition API memungkinkan Anda mengonversi tulisan tangan (tinta) dari pengguna menjadi teks. Beberapa sistem operasi telah lama menyertakan API tersebut, dan dengan kemampuan baru ini, aplikasi web Anda akhirnya dapat menggunakan fungsi ini. Konversi terjadi langsung di perangkat pengguna, berfungsi bahkan dalam mode offline, semuanya tanpa menambahkan library atau layanan pihak ketiga.

API ini menerapkan pengenalan "online" atau hampir real-time. Artinya, input tulisan tangan dikenali saat pengguna menggambarnya dengan merekam dan menganalisis goresan tunggal. Berbeda dengan prosedur "offline" seperti Optical Character Recognition (OCR), yang hanya diketahui produk akhirnya, algoritma online dapat memberikan tingkat akurasi yang lebih tinggi karena sinyal tambahan seperti urutan temporal dan tekanan setiap goresan tinta.

Kasus penggunaan yang disarankan untuk Handwriting Recognition API

Contoh penggunaan mencakup:

  • Aplikasi pencatatan yang memungkinkan pengguna mengambil catatan tulisan tangan dan menerjemahkannya menjadi teks.
  • Aplikasi formulir tempat pengguna dapat menggunakan input stilus atau jari karena batasan waktu.
  • Game yang mengharuskan pengisian huruf atau angka, seperti teka-teki silang, hangman, atau sudoku.

Status saat ini

Handwriting Recognition API tersedia mulai dari (Chromium 99).

Cara menggunakan Handwriting Recognition API

Deteksi fitur

Deteksi dukungan browser dengan memeriksa keberadaan metode createHandwritingRecognizer() pada objek navigator:

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

Konsep inti

Handwriting Recognition API mengonversi input tulisan tangan menjadi teks, terlepas dari metode inputnya (mouse, sentuhan, stilus). API ini memiliki empat entitas utama:

  1. Titik menunjukkan posisi pointer pada waktu tertentu.
  2. Goresan terdiri dari satu atau beberapa titik. Perekaman goresan dimulai saat pengguna meletakkan pointer (yaitu, mengklik tombol mouse utama, atau menyentuh layar dengan stylus atau jari) dan berakhir saat mereka mengangkat pointer kembali.
  3. Gambar terdiri dari satu atau beberapa goresan. Pengenalan sebenarnya terjadi di tingkat ini.
  4. Pengenal dikonfigurasi dengan bahasa input yang diharapkan. Objek ini digunakan untuk membuat instance gambar dengan konfigurasi pengenal yang diterapkan.

Konsep ini diimplementasikan sebagai antarmuka dan kamus tertentu, yang akan saya bahas sebentar lagi.

Entitas inti Handwriting Recognition API: Satu atau beberapa titik membentuk goresan, satu atau beberapa goresan membentuk gambar, yang dibuat oleh pengenal. Pengenalan sebenarnya dilakukan di tingkat gambar.

Membuat pengenal

Untuk mengenali teks dari input tulisan tangan, Anda perlu mendapatkan instance HandwritingRecognizer dengan memanggil navigator.createHandwritingRecognizer() dan meneruskan batasan kepadanya. Batasan menentukan model pengenalan tulisan tangan yang harus digunakan. Saat ini, Anda dapat menentukan daftar bahasa dalam urutan preferensi:

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

Metode ini menampilkan promise yang diselesaikan dengan instance HandwritingRecognizer saat browser dapat memenuhi permintaan Anda. Jika tidak, promise akan ditolak dengan error, dan pengenalan tulisan tangan tidak akan tersedia. Oleh karena itu, sebaiknya kueri dukungan pengenal untuk fitur pengenalan tertentu terlebih dahulu.

Mengueri dukungan pengenal

Dengan memanggil navigator.queryHandwritingRecognizer(), Anda dapat memeriksa apakah platform target mendukung fitur pengenalan tulisan tangan yang ingin Anda gunakan. Metode ini mengambil objek batasan yang sama dengan metode navigator.createHandwritingRecognizer(), yang berisi daftar bahasa yang diminta. Metode ini menampilkan promise yang diselesaikan dengan objek hasil jika pengenal yang kompatibel ditemukan. Jika tidak, promise akan diselesaikan ke null. Dalam contoh berikut, developer:

  • ingin mendeteksi teks dalam bahasa Inggris
  • mendapatkan prediksi alternatif yang lebih kecil kemungkinannya jika tersedia
  • mendapatkan akses ke hasil segmentasi, yaitu karakter yang dikenali, termasuk titik dan goresan yang membentuknya
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

Jika browser mendukung fitur yang diperlukan oleh developer, nilainya akan ditetapkan ke true dalam objek hasil. Jika tidak, nilai ini akan ditetapkan ke false. Anda dapat menggunakan informasi ini untuk mengaktifkan atau menonaktifkan fitur tertentu dalam aplikasi atau mengirim kueri baru untuk kumpulan bahasa yang berbeda.

Mulai menggambar

Dalam aplikasi, Anda harus menyediakan area input tempat pengguna membuat entri tulisan tangan. Untuk alasan performa, sebaiknya terapkan ini dengan bantuan objek kanvas. Penerapan yang tepat dari bagian ini berada di luar cakupan artikel ini, tetapi Anda dapat melihat demo untuk melihat cara melakukannya.

Untuk memulai gambar baru, panggil metode startDrawing() pada pengenal. Metode ini mengambil objek yang berisi berbagai petunjuk untuk menyesuaikan algoritma pengenalan. Semua petunjuk bersifat opsional:

  • Jenis teks yang dimasukkan: teks, alamat email, angka, atau karakter individual (recognitionType)
  • Jenis perangkat input: input mouse, sentuh, atau stilus (inputType)
  • Teks sebelumnya (textContext)
  • Jumlah prediksi alternatif yang kurang mungkin yang harus ditampilkan (alternatives)
  • Daftar karakter yang dapat diidentifikasi pengguna ("grafem") yang kemungkinan besar akan dimasukkan pengguna (graphemeSet)

Handwriting Recognition API berfungsi baik dengan Pointer Events yang menyediakan antarmuka abstrak untuk menggunakan input dari perangkat penunjuk apa pun. Argumen peristiwa pointer berisi jenis pointer yang digunakan. Artinya, Anda dapat menggunakan peristiwa penunjuk untuk menentukan jenis input secara otomatis. Pada contoh berikut, gambar untuk pengenalan tulisan tangan dibuat secara otomatis pada kemunculan pertama peristiwa pointerdown di area tulisan tangan. Karena pointerType mungkin kosong atau ditetapkan ke nilai eksklusif, saya memperkenalkan pemeriksaan konsistensi untuk memastikan hanya nilai yang didukung yang ditetapkan untuk jenis input gambar.

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

Menambahkan goresan

Peristiwa pointerdown juga merupakan tempat yang tepat untuk memulai goresan baru. Untuk melakukannya, buat instance HandwritingStroke baru. Selain itu, Anda harus menyimpan waktu saat ini sebagai titik referensi untuk titik berikutnya yang ditambahkan ke dalamnya:

function startStroke(event) {
  activeStroke = {
    stroke: new HandwritingStroke(),
    startTime: Date.now(),
  };
  addPoint(event);
}

Tambahkan titik

Setelah membuat goresan, Anda harus langsung menambahkan titik pertama ke goresan tersebut. Karena Anda akan menambahkan lebih banyak titik nanti, sebaiknya terapkan logika pembuatan titik dalam metode terpisah. Dalam contoh berikut, metode addPoint() menghitung waktu yang telah berlalu dari stempel waktu referensi. Informasi temporal bersifat opsional, tetapi dapat meningkatkan kualitas pengenalan. Kemudian, kode ini membaca koordinat X dan Y dari peristiwa pointer dan menambahkan titik ke goresan saat ini.

function addPoint(event) {
  const timeElapsed = Date.now() - activeStroke.startTime;
  activeStroke.stroke.addPoint({
    x: event.offsetX,
    y: event.offsetY,
    t: timeElapsed,
  });
}

Handler peristiwa pointermove dipanggil saat pointer dipindahkan di layar. Titik tersebut juga perlu ditambahkan ke goresan. Peristiwa juga dapat dimunculkan jika pointer tidak dalam status "turun", misalnya saat memindahkan kursor di layar tanpa menekan tombol mouse. Handler peristiwa dari contoh berikut memeriksa apakah ada goresan aktif, dan menambahkan titik baru ke goresan tersebut.

canvas.addEventListener('pointermove', (event) => {
  if (activeStroke) {
    addPoint(event);
  }
});

Mengenali teks

Saat pengguna mengangkat pointer lagi, Anda dapat menambahkan goresan ke gambar dengan memanggil metode addStroke()-nya. Contoh berikut juga mereset activeStroke, sehingga pengendali pointermove tidak akan menambahkan poin ke goresan yang selesai.

Selanjutnya, saatnya mengenali input pengguna dengan memanggil metode getPrediction() pada gambar. Pengenalan biasanya memerlukan waktu kurang dari beberapa ratus milidetik, sehingga Anda dapat menjalankan prediksi berulang kali jika diperlukan. Contoh berikut menjalankan prediksi baru setelah setiap goresan selesai.

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

Metode ini menampilkan promise yang di-resolve dengan array prediksi yang diurutkan berdasarkan kemungkinannya. Jumlah elemen bergantung pada nilai yang Anda teruskan ke petunjuk alternatives. Anda dapat menggunakan array ini untuk menyajikan pilihan kemungkinan kecocokan kepada pengguna, dan meminta mereka memilih opsi. Atau, Anda dapat memilih prediksi yang paling mungkin, seperti yang saya lakukan dalam contoh.

Objek prediksi berisi teks yang dikenali dan hasil segmentasi opsional, yang akan saya bahas di bagian berikut.

Insight mendetail dengan hasil segmentasi

Jika didukung oleh platform target, objek prediksi juga dapat berisi hasil segmentasi. Ini adalah array yang berisi semua segmen tulisan tangan yang dikenali, kombinasi karakter yang dapat diidentifikasi pengguna yang dikenali (grapheme) beserta posisinya dalam teks yang dikenali (beginIndex, endIndex), serta goresan dan titik yang membuatnya.

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

Anda dapat menggunakan informasi ini untuk melacak kembali grafem yang dikenali di kanvas.

Kotak digambar di sekitar setiap grafem yang dikenali

Pengenalan lengkap

Setelah pengenalan selesai, Anda dapat membebaskan resource dengan memanggil metode clear() di HandwritingDrawing, dan metode finish() di HandwritingRecognizer:

drawing.clear();
recognizer.finish();

Demo

Komponen web <handwriting-textarea> menerapkan kontrol pengeditan yang ditingkatkan secara progresif dan mampu melakukan pengenalan tulisan tangan. Dengan mengklik tombol di sudut kanan bawah kontrol pengeditan, Anda mengaktifkan mode menggambar. Saat Anda menyelesaikan gambar, komponen web akan otomatis memulai pengenalan dan menambahkan kembali teks yang dikenali ke kontrol pengeditan. Jika Handwriting Recognition API tidak didukung sama sekali, atau platform tidak mendukung fitur yang diminta, tombol edit akan disembunyikan. Namun, kontrol pengeditan dasar tetap dapat digunakan sebagai <textarea>.

Komponen web menawarkan properti dan atribut untuk menentukan perilaku pengenalan dari luar, termasuk languages dan recognitiontype. Anda dapat menyetel konten kontrol melalui atribut value:

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

Untuk mengetahui perubahan nilai, Anda dapat memproses peristiwa input.

Anda dapat mencoba komponen menggunakan demo ini di GitHub. Pastikan juga untuk melihat kode sumber. Untuk menggunakan kontrol di aplikasi Anda, dapatkan dari npm.

Keamanan dan izin

Tim Chromium mendesain dan menerapkan Handwriting Recognition API menggunakan prinsip inti yang ditentukan dalam Mengontrol Akses ke Fitur Platform Web yang Canggih, termasuk kontrol pengguna, transparansi, dan ergonomi.

Kontrol pengguna

Handwriting Recognition API tidak dapat dinonaktifkan oleh pengguna. API ini hanya tersedia untuk situs yang ditayangkan melalui HTTPS, dan hanya dapat dipanggil dari konteks penjelajahan tingkat teratas.

Transparansi

Tidak ada indikasi apakah pengenalan tulisan tangan aktif. Untuk mencegah pembuatan sidik jari, browser menerapkan tindakan pencegahan, seperti menampilkan dialog izin kepada pengguna saat mendeteksi kemungkinan penyalahgunaan.

Persistensi izin

Handwriting Recognition API saat ini tidak menampilkan perintah izin apa pun. Oleh karena itu, izin tidak perlu dipertahankan dengan cara apa pun.

Masukan

Tim Chromium ingin mengetahui pengalaman Anda saat menggunakan Handwriting Recognition API.

Beri tahu kami tentang desain API

Apakah ada sesuatu tentang API yang tidak berfungsi seperti yang Anda harapkan? Atau, apakah ada metode atau properti yang tidak ada dan perlu Anda terapkan untuk mewujudkan ide Anda? Ada pertanyaan atau komentar tentang model keamanan? Laporkan masalah spesifikasi di repo GitHub yang sesuai, atau tambahkan pendapat Anda ke masalah yang sudah ada.

Melaporkan masalah terkait penerapan

Apakah Anda menemukan bug pada penerapan Chromium? Atau apakah implementasinya berbeda dengan spesifikasi? Laporkan bug di new.crbug.com. Pastikan untuk menyertakan detail sebanyak mungkin, petunjuk sederhana untuk mereproduksi, dan masukkan Blink>Handwriting di kotak Komponen.

Menunjukkan dukungan untuk API

Apakah Anda berencana menggunakan Handwriting Recognition API? Dukungan publik Anda membantu tim Chromium memprioritaskan fitur dan menunjukkan kepada vendor browser lain betapa pentingnya dukungan untuk fitur tersebut.

Bagikan rencana penggunaan Anda di thread WICG Discourse. Kirim tweet ke @ChromiumDev menggunakan hashtag #HandwritingRecognition dan beri tahu kami di mana dan bagaimana Anda menggunakannya.

Ucapan terima kasih

Dokumen ini ditinjau oleh Joe Medley, Honglin Yu, dan Jiewei Qian.