Kullanıcılarınızın el yazısını tanıma

El Yazısı Tanıma API'si, el yazısı girişindeki metni yazıldığı sırada tanımanıza olanak tanır.

El Yazısı Tanıma API'si nedir?

El Yazısı Tanıma API'si, kullanıcılarınızın el yazısını (mürekkep) metne dönüştürmenize olanak tanır. Bazı işletim sistemleri bu tür API'leri uzun süredir içeriyordu. Bu yeni özellik sayesinde web uygulamalarınız da nihayet bu işlevi kullanabilir. Dönüşüm doğrudan kullanıcının cihazında gerçekleşir ve hiçbir üçüncü taraf kitaplığı veya hizmeti eklenmeden çevrimdışı modda bile çalışır.

Bu API, "online" veya neredeyse gerçek zamanlı tanıma olarak adlandırılan yöntemi uygular. Bu, kullanıcı çizerken tek tek vuruşları yakalayıp analiz ederek el yazısı girişinin tanındığı anlamına gelir. Yalnızca nihai ürünün bilinen Optik Karakter Tanıma (OCR) gibi "çevrimdışı" işlemlerin aksine, çevrimiçi algoritmalar, mürekkebin tek tek vuruşlarının zamansal sırası ve basıncı gibi ek sinyaller sayesinde daha yüksek düzeyde doğruluk sağlayabilir.

El Yazısı Tanıma API'si için önerilen kullanım alanları

Örnek kullanımlar:

  • Kullanıcıların el yazısıyla notlar alıp bunları metne dönüştürmek istediği not alma uygulamaları.
  • Kullanıcıların zaman kısıtlamaları nedeniyle kalem veya parmak girişi kullanabileceği Formlar uygulamaları.
  • Kelime bulmaca, asma adam veya sudoku gibi harf ya da sayı doldurmayı gerektiren oyunlar.

Mevcut durum

El Yazısı Tanıma API'si (Chromium 99) sürümünden itibaren kullanılabilir.

El Yazısı Tanıma API'sini kullanma

Özellik algılama

Gezgin nesnesinde createHandwritingRecognizer() yönteminin varlığını kontrol ederek tarayıcı desteğini algılayın:

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

Temel kavramlar

El Yazısı Tanıma API'si, giriş yönteminden (fare, dokunma, kalem) bağımsız olarak el yazısı girişini metne dönüştürür. API'nin dört ana öğesi vardır:

  1. Nokta, işaretçinin belirli bir zamandaki yerini temsil eder.
  2. Çizgi, bir veya daha fazla noktadan oluşur. Bir çizginin kaydı, kullanıcı işaretçiyi aşağı indirdiğinde (ör. birincil fare düğmesini tıkladığında veya ekrana kalemiyle ya da parmağıyla dokunduğunda) başlar ve işaretçiyi tekrar yukarı kaldırdığında sona erer.
  3. Çizim, bir veya daha fazla çizgiden oluşur. Gerçek tanıma işlemi bu düzeyde gerçekleşir.
  4. Tanıyıcı, beklenen giriş diliyle yapılandırılır. Tanımlayıcı yapılandırması uygulanmış bir çizim örneği oluşturmak için kullanılır.

Bu kavramlar, kısa süre içinde ele alacağım belirli arayüzler ve sözlükler olarak uygulanır.

El Yazısı Tanıma API'sinin temel öğeleri: Bir veya daha fazla nokta bir çizgiyi, bir veya daha fazla çizgi de tanımlayıcı tarafından oluşturulan bir çizimi oluşturur. Gerçek tanıma işlemi, çizim düzeyinde gerçekleşir.

Tanımlayıcı oluşturma

El yazısı girişindeki metni tanımak için navigator.createHandwritingRecognizer()'u çağırıp ona kısıtlamalar göndererek bir HandwritingRecognizer örneği elde etmeniz gerekir. Kısıtlamalar, kullanılacak el yazısı tanıma modelini belirler. Şu anda, tercih sırasına göre bir dil listesi belirtebilirsiniz:

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

Tarayıcı isteğinizi yerine getirebiliyorsa yöntem, HandwritingRecognizer örneğiyle çözülen bir promise döndürür. Aksi takdirde, söz konusu vaat bir hatayla reddedilir ve el yazısı tanıma kullanılamaz. Bu nedenle, önce tanımlayıcının belirli tanıma özellikleri için destek sunup sunmadığını sorgulamak isteyebilirsiniz.

Tanımlayıcı desteğini sorgulayın

navigator.queryHandwritingRecognizerSupport() işlevini çağırarak hedef platformun, kullanmak istediğiniz el yazısı tanıma özelliklerini destekleyip desteklemediğini kontrol edebilirsiniz. Aşağıdaki örnekte geliştirici:

  • İngilizce metinleri algılamak istiyor
  • Mümkün olduğunda alternatif, daha az olasılık içeren tahminler alın
  • Segmentasyon sonucuna (ör. onları oluşturan noktalar ve çizgiler dahil olmak üzere tanınan karakterler) erişme
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

Yöntem, bir sonuç nesnesi ile çözülen bir promise döndürür. Tarayıcı, geliştirici tarafından belirtilen özelliği destekliyorsa özelliğin değeri true olarak ayarlanır. Aksi takdirde false olarak ayarlanır. Bu bilgileri, uygulamanızdaki belirli özellikleri etkinleştirmek veya devre dışı bırakmak ya da sorgunuzu ayarlamak ve yeni bir sorgu göndermek için kullanabilirsiniz.

Çizim başlatma

Uygulamanızda, kullanıcının el yazısıyla giriş yapabileceği bir giriş alanı sunmanız gerekir. Performans nedeniyle bunu bir tuval nesnesi yardımıyla uygulamanız önerilir. Bu bölümün tam olarak uygulanması bu makalenin kapsamı dışındadır ancak nasıl yapılabileceğini görmek için demo bölümüne göz atabilirsiniz.

Yeni bir çizim başlatmak için tanımlayıcıdaki startDrawing() yöntemini çağırın. Bu yöntem, tanıma algoritmasında ince ayar yapmak için farklı ipuçları içeren bir nesne alır. Tüm ipuçları isteğe bağlıdır:

  • Girilecek metin türü: metin, e-posta adresleri, sayılar veya tek bir karakter (recognitionType)
  • Giriş cihazının türü: fare, dokunma veya kalem girişi (inputType)
  • Önceki metin (textContext)
  • Döndürülmesi gereken daha az olası alternatif tahminlerin sayısı (alternatives)
  • Kullanıcının büyük olasılıkla gireceği, kullanıcı tarafından tanımlanabilen karakterlerin ("grafemler") listesi (graphemeSet)

El Yazısı Tanıma API'si, herhangi bir işaretçi cihazdan giriş almak için soyut bir arayüz sağlayan İşaretçi Etkinlikleri ile iyi çalışır. İşaretçi etkinliği bağımsız değişkenleri, kullanılan işaretçi türünü içerir. Bu, giriş türünü otomatik olarak belirlemek için işaretçi etkinliklerini kullanabileceğiniz anlamına gelir. Aşağıdaki örnekte, el yazısı tanıma için çizim, el yazısı alanında bir pointerdown etkinliğinin ilk oluşumunda otomatik olarak oluşturulur. pointerType boş olabilir veya özel bir değere ayarlanmış olabilir. Bu nedenle, çizimin giriş türü için yalnızca desteklenen değerlerin ayarlandığından emin olmak amacıyla bir tutarlılık kontrolü ekledim.

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

Kontur ekleme

pointerdown etkinliği, yeni bir vuruş başlatmak için de doğru yerdir. Bunun için yeni bir HandwritingStroke örneği oluşturun. Ayrıca, mevcut zamanı, ona eklenen sonraki noktalar için referans noktası olarak saklamanız gerekir:

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

Nokta ekleyin

Kalın çizgiyi oluşturduktan sonra doğrudan ilk noktayı eklemeniz gerekir. Daha sonra daha fazla puan ekleyeceğiniz için puan oluşturma mantığını ayrı bir yöntemde uygulamak mantıklı olacaktır. Aşağıdaki örnekte addPoint() yöntemi, referans zaman damgasından itibaren geçen süreyi hesaplar. Zamansal bilgiler isteğe bağlıdır ancak tanıma kalitesini iyileştirebilir. Ardından, işaretçi etkinliğindeki X ve Y koordinatlarını okur ve noktayı mevcut çizgiye ekler.

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

İşaretçi ekranda hareket ettirildiğinde pointermove etkinlik işleyici çağrılır. Bu noktaların çizgiye de eklenmesi gerekir. İşaretçi "aşağı" durumda değilse de (ör. fare düğmesine basmadan imleci ekranda hareket ettirirken) etkinlik tetiklenebilir. Aşağıdaki örnekteki etkinlik işleyici, etkin bir çizgi olup olmadığını kontrol eder ve yeni noktayı buna ekler.

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

Metni tanıma

Kullanıcı işaretçiyi tekrar kaldırdığında, addStroke() yöntemini çağırarak çizgiyi çiziminize ekleyebilirsiniz. Aşağıdaki örnekte activeStroke da sıfırlanır. Böylece pointermove işleyicisi, tamamlanan çizgiye nokta eklemez.

Ardından, çizimde getPrediction() yöntemini çağırarak kullanıcının girişini tanıma zamanı gelmiştir. Tanımlama işlemi genellikle birkaç yüz milisaniyeden kısa sürer. Bu nedenle, gerekirse tahminleri tekrar tekrar çalıştırabilirsiniz. Aşağıdaki örnekte, her tamamlanan vuruştan sonra yeni bir tahmin çalıştırılır.

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

Bu yöntem, olasılıklarına göre sıralanmış bir tahmin dizisiyle çözülen bir promise döndürür. Öğe sayısı, alternatives ipucuna ilettiğiniz değere bağlıdır. Kullanıcıya olası eşleşmeler arasından seçim yapması için bu diziyi kullanabilir ve bir seçenek seçmesini isteyebilirsiniz. Alternatif olarak, en olası tahmini kullanabilirsiniz. Örnekte bunu yapıyorum.

Tahmin nesnesi, tanınan metni ve isteğe bağlı bir segmentasyon sonucunu içerir. Bu konuyu aşağıdaki bölümde ele alacağım.

Segmentasyon sonuçlarıyla ayrıntılı analizler

Hedef platform tarafından destekleniyorsa tahmin nesnesi bir segmentasyon sonucu da içerebilir. Bu, tanınan tüm el yazısı segmentini içeren bir dizidir. Tanınan kullanıcı tarafından tanımlanabilir karakterin (grapheme) tanınan metindeki konumuyla (beginIndex, endIndex) ve onu oluşturan vuruş ve noktalarla birlikte bir kombinasyondur.

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

Bu bilgileri kullanarak, tuvalde tanınan grafemleri tekrar bulabilirsiniz.

Tanınan her bir grafemin etrafına kutular çizilir.

Tam tanıma

Tanımlama işlemi tamamlandıktan sonra HandwritingDrawing üzerinde clear() yöntemini ve HandwritingRecognizer üzerinde finish() yöntemini çağırarak kaynakları serbest bırakabilirsiniz:

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

Demo

<handwriting-textarea> web bileşeni, el yazısı tanıma özelliğine sahip kademeli olarak geliştirilmiş bir düzenleme denetimi uygular. Düzenleme denetiminin sağ alt köşesindeki düğmeyi tıklayarak çizim modunu etkinleştirebilirsiniz. Çizimi tamamladığınızda web bileşeni otomatik olarak tanıma işlemini başlatır ve tanınan metni düzenleme denetimine geri ekler. El yazısı tanıma API'si hiç desteklenmiyorsa veya platform istenen özellikleri desteklemiyorsa düzenleme düğmesi gizlenir. Ancak temel düzenleme denetimi <textarea> olarak kullanılabilir durumda kalır.

Web bileşeni, languages ve recognitiontype dahil olmak üzere tanıma davranışını dışarıdan tanımlamak için özellikler ve özellikler sunar. Kontrolün içeriğini value özelliği aracılığıyla ayarlayabilirsiniz:

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

Değerdeki değişiklikler hakkında bilgi edinmek için input etkinliğini dinleyebilirsiniz.

Bileşeni Glitch'teki bu demoyu kullanarak deneyebilirsiniz. Ayrıca kaynak koda da göz atın. Kontrolü uygulamanızda kullanmak için npm'den edinin.

Güvenlik ve izinler

Chromium ekibi, kullanıcı kontrolü, şeffaflık ve ergonomi gibi Güçlü Web Platformu Özelliklerine Erişimi Kontrol Etme başlıklı makalede tanımlanan temel ilkeleri kullanarak El Yazısı Tanıma API'sini tasarlayıp uyguladı.

Kullanıcı denetimi

El yazısı tanıma API'si kullanıcı tarafından devre dışı bırakılamaz. Yalnızca HTTPS üzerinden yayınlanan web sitelerinde kullanılabilir ve yalnızca üst düzey tarama bağlamından çağrılabilir.

Şeffaflık

El yazısı tanıma özelliğinin etkin olup olmadığı gösterilmez. Tarayıcı, parmak izi oluşturmayı önlemek için olası bir kötüye kullanım algıladığında kullanıcıya izin istemi göstermek gibi karşı önlemler uygular.

İzin kalıcılığı

El Yazısı Tanıma API'si şu anda herhangi bir izin istemi göstermiyor. Bu nedenle, iznin herhangi bir şekilde devam ettirilmesi gerekmez.

Geri bildirim

Chromium ekibi, El Yazısı Tanıma API'si ile ilgili deneyimlerinizi öğrenmek istiyor.

API tasarımı hakkında bilgi verin

API ile ilgili olarak beklediğiniz gibi çalışmayan bir şey var mı? Yoksa fikrinizi uygulamak için ihtiyaç duyduğunuz yöntemler veya özellikler eksik mi? Güvenlik modeliyle ilgili sorunuz veya yorumunuz mu var? İlgili GitHub deposunda spesifikasyon sorunu oluşturun veya mevcut bir soruna düşüncelerinizi ekleyin.

Uygulamayla ilgili sorunları bildirme

Chromium'un uygulamasında bir hata mı buldunuz? Yoksa uygulama, spesifikasyondan farklı mı? new.crbug.com adresinden hata kaydı oluşturun. Mümkün olduğunca fazla ayrıntı ekleyin, hatayı yeniden oluşturmayla ilgili basit talimatlar verin ve Bileşenler kutusuna Blink>Handwriting yazın. Glitch, hızlı ve kolay yeniden oluşturma işlemlerini paylaşmak için idealdir.

API'yi destekleme

El yazısı tanıma API'sini kullanmayı planlıyor musunuz? Herkese açık desteğiniz, Chromium ekibinin özelliklere öncelik vermesine yardımcı olur ve diğer tarayıcı tedarikçi firmalarına bu özellikleri desteklemenin ne kadar önemli olduğunu gösterir.

WICG Discourse mesaj dizisinde bu özelliği nasıl kullanmayı planladığınızı paylaşın. #HandwritingRecognition hashtag'ini kullanarak @ChromiumDev hesabına tweet gönderin ve bu özelliği nerede ve nasıl kullandığınızı bize bildirin.

Teşekkür ederiz

Bu doküman Joe Medley, Honglin Yu ve Jiewei Qian tarafından incelenmiştir.