เทคโนโลยีการจดจำหมึกดิจิทัลของ ML Kit ให้คุณจดจำข้อความที่เขียนด้วยลายมือบน ดิจิทัลในหลายร้อยภาษา และจำแนกภาพร่างได้
ลองเลย
- ลองใช้แอปตัวอย่างเพื่อ ดูตัวอย่างการใช้ API นี้
ก่อนเริ่มต้น
- ในไฟล์
build.gradle
ระดับโปรเจ็กต์ อย่าลืมรวมที่เก็บ Maven ของ Google ไว้ทั้งในส่วนbuildscript
และallprojects
- เพิ่มทรัพยากร Dependency สำหรับไลบรารี ML Kit Android ลงในไฟล์ Gradle ระดับแอปของโมดูล ซึ่งปกติคือ
app/build.gradle
dependencies {
// ...
implementation 'com.google.mlkit:digital-ink-recognition:18.1.0'
}
คุณพร้อมที่จะเริ่มจดจำข้อความในออบเจ็กต์ Ink
แล้ว
สร้างออบเจ็กต์ Ink
วิธีหลักในการสร้างวัตถุ Ink
คือการวาดวัตถุบนหน้าจอสัมผัส เปิด
Android คุณสามารถใช้
Canvas สำหรับ
วัตถุประสงค์นี้ บัญชี
เครื่องจัดการเหตุการณ์การแตะ
ควรเรียก addNewTouchEvent()
แสดงข้อมูลโค้ดต่อไปนี้เพื่อจัดเก็บจุดในเส้น
ผู้ใช้ดึงเข้าสู่ออบเจ็กต์ Ink
รูปแบบทั่วไปนี้จะแสดงในข้อมูลโค้ดต่อไปนี้ โปรดดู ตัวอย่างการเริ่มต้นอย่างรวดเร็วสำหรับ ML Kit เพื่อดูตัวอย่างที่สมบูรณ์ขึ้น
Kotlin
var inkBuilder = Ink.builder() lateinit var strokeBuilder: Ink.Stroke.Builder // Call this each time there is a new event. fun addNewTouchEvent(event: MotionEvent) { val action = event.actionMasked val x = event.x val y = event.y var t = System.currentTimeMillis() // If your setup does not provide timing information, you can omit the // third paramater (t) in the calls to Ink.Point.create when (action) { MotionEvent.ACTION_DOWN -> { strokeBuilder = Ink.Stroke.builder() strokeBuilder.addPoint(Ink.Point.create(x, y, t)) } MotionEvent.ACTION_MOVE -> strokeBuilder!!.addPoint(Ink.Point.create(x, y, t)) MotionEvent.ACTION_UP -> { strokeBuilder.addPoint(Ink.Point.create(x, y, t)) inkBuilder.addStroke(strokeBuilder.build()) } else -> { // Action not relevant for ink construction } } } ... // This is what to send to the recognizer. val ink = inkBuilder.build()
Java
Ink.Builder inkBuilder = Ink.builder(); Ink.Stroke.Builder strokeBuilder; // Call this each time there is a new event. public void addNewTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); long t = System.currentTimeMillis(); // If your setup does not provide timing information, you can omit the // third paramater (t) in the calls to Ink.Point.create int action = event.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: strokeBuilder = Ink.Stroke.builder(); strokeBuilder.addPoint(Ink.Point.create(x, y, t)); break; case MotionEvent.ACTION_MOVE: strokeBuilder.addPoint(Ink.Point.create(x, y, t)); break; case MotionEvent.ACTION_UP: strokeBuilder.addPoint(Ink.Point.create(x, y, t)); inkBuilder.addStroke(strokeBuilder.build()); strokeBuilder = null; break; } } ... // This is what to send to the recognizer. Ink ink = inkBuilder.build();
รับอินสแตนซ์ของ DigitalInkRecognizer
หากต้องการจดจำ ให้ส่งอินสแตนซ์ Ink
ไปยัง
DigitalInkRecognizer
ออบเจ็กต์ โค้ดด้านล่างแสดงวิธีสร้างอินสแตนซ์
ระบบจดจำจากแท็ก BCP-47
Kotlin
// Specify the recognition model for a language var modelIdentifier: DigitalInkRecognitionModelIdentifier try { modelIdentifier = DigitalInkRecognitionModelIdentifier.fromLanguageTag("en-US") } catch (e: MlKitException) { // language tag failed to parse, handle error. } if (modelIdentifier == null) { // no model was found, handle error. } var model: DigitalInkRecognitionModel = DigitalInkRecognitionModel.builder(modelIdentifier).build() // Get a recognizer for the language var recognizer: DigitalInkRecognizer = DigitalInkRecognition.getClient( DigitalInkRecognizerOptions.builder(model).build())
Java
// Specify the recognition model for a language DigitalInkRecognitionModelIdentifier modelIdentifier; try { modelIdentifier = DigitalInkRecognitionModelIdentifier.fromLanguageTag("en-US"); } catch (MlKitException e) { // language tag failed to parse, handle error. } if (modelIdentifier == null) { // no model was found, handle error. } DigitalInkRecognitionModel model = DigitalInkRecognitionModel.builder(modelIdentifier).build(); // Get a recognizer for the language DigitalInkRecognizer recognizer = DigitalInkRecognition.getClient( DigitalInkRecognizerOptions.builder(model).build());
ประมวลผลออบเจ็กต์ Ink
Kotlin
recognizer.recognize(ink) .addOnSuccessListener { result: RecognitionResult -> // `result` contains the recognizer's answers as a RecognitionResult. // Logs the text from the top candidate. Log.i(TAG, result.candidates[0].text) } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error during recognition: $e") }
Java
recognizer.recognize(ink) .addOnSuccessListener( // `result` contains the recognizer's answers as a RecognitionResult. // Logs the text from the top candidate. result -> Log.i(TAG, result.getCandidates().get(0).getText())) .addOnFailureListener( e -> Log.e(TAG, "Error during recognition: " + e));
โค้ดตัวอย่างด้านบนถือว่าโมเดลการจดจำได้รับการ ดาวน์โหลดแล้ว ดังที่อธิบายไว้ในส่วนถัดไป
การจัดการการดาวน์โหลดโมเดล
แม้ว่า API การรู้จำหมึกดิจิทัลจะรองรับภาษาหลายร้อยภาษา แต่ละภาษา
ภาษาจำเป็นต้องมีการดาวน์โหลดข้อมูลบางอย่างก่อนการจดจำ รอบๆ
ต้องใช้พื้นที่เก็บข้อมูล 20 MB ต่อภาษา ซึ่งจัดการโดย
ออบเจ็กต์ RemoteModelManager
รายการ
ดาวน์โหลดโมเดลใหม่
Kotlin
import com.google.mlkit.common.model.DownloadConditions import com.google.mlkit.common.model.RemoteModelManager var model: DigitalInkRecognitionModel = ... val remoteModelManager = RemoteModelManager.getInstance() remoteModelManager.download(model, DownloadConditions.Builder().build()) .addOnSuccessListener { Log.i(TAG, "Model downloaded") } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error while downloading a model: $e") }
Java
import com.google.mlkit.common.model.DownloadConditions; import com.google.mlkit.common.model.RemoteModelManager; DigitalInkRecognitionModel model = ...; RemoteModelManager remoteModelManager = RemoteModelManager.getInstance(); remoteModelManager .download(model, new DownloadConditions.Builder().build()) .addOnSuccessListener(aVoid -> Log.i(TAG, "Model downloaded")) .addOnFailureListener( e -> Log.e(TAG, "Error while downloading a model: " + e));
ตรวจสอบว่าดาวน์โหลดโมเดลแล้วหรือไม่
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.isModelDownloaded(model)
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.isModelDownloaded(model);
ลบโมเดลที่ดาวน์โหลด
การนำรุ่นออกจากพื้นที่เก็บข้อมูลของอุปกรณ์จะช่วยเพิ่มพื้นที่ว่าง
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.deleteDownloadedModel(model) .addOnSuccessListener { Log.i(TAG, "Model successfully deleted") } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error while deleting a model: $e") }
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.deleteDownloadedModel(model) .addOnSuccessListener( aVoid -> Log.i(TAG, "Model successfully deleted")) .addOnFailureListener( e -> Log.e(TAG, "Error while deleting a model: " + e));
เคล็ดลับในการปรับปรุงความแม่นยำในการจดจำข้อความ
ความถูกต้องของการจดจำข้อความอาจแตกต่างกันไปตามภาษาต่างๆ ความแม่นยำก็ขึ้นอยู่กับ เกี่ยวกับสไตล์การเขียน แม้ว่า Digital Ink Recognition จะได้รับการฝึกให้จัดการกับรูปแบบการเขียนที่หลากหลาย ผลลัพธ์อาจแตกต่างกันไปตามผู้ใช้แต่ละคน
วิธีปรับปรุงความแม่นยำของโปรแกรมจดจำข้อความมีดังนี้ โปรดทราบว่าเทคนิคเหล่านี้จะ ไม่ใช้กับตัวแยกประเภทภาพวาดสำหรับอีโมจิ การวาดอัตโนมัติ และรูปร่าง
พื้นที่สำหรับเขียน
แอปพลิเคชันจำนวนมากมีพื้นที่การเขียนที่กำหนดไว้อย่างชัดเจนสำหรับป้อนข้อมูลของผู้ใช้ ความหมายของสัญลักษณ์คือ กำหนดบางส่วนโดยขนาดที่สัมพันธ์กับขนาดพื้นที่การเขียนที่มีข้อมูลอยู่ เช่น ความแตกต่างระหว่างอักษรตัวพิมพ์เล็กหรือตัวพิมพ์ใหญ่ "o" หรือ "c" และเครื่องหมายคอมมาเทียบกับ เครื่องหมายทับ
การบอกโปรแกรมจดจำว่าความกว้างและความสูงของพื้นที่การเขียนจะช่วยเพิ่มความแม่นยำได้ อย่างไรก็ตาม เครื่องมือจดจำจะถือว่าพื้นที่สำหรับการเขียนมีเพียงบรรทัดเดียว หากรูปภาพ พื้นที่การเขียนมีขนาดใหญ่พอที่จะให้ผู้ใช้เขียนได้ตั้งแต่ 2 บรรทัดขึ้นไป คุณอาจเขียนได้ดีกว่า โดยการส่งผ่านในบริเวณการเขียนที่มีความสูงซึ่งเป็นค่าประมาณที่ดีที่สุดของความสูง บรรทัดเดียวก็ได้ ออบเจ็กต์ WritingArea ที่คุณส่งไปยังเครื่องมือจำแนกไม่จำเป็นต้องสอดคล้องกัน ตรงตามพื้นที่เขียนจริงบนหน้าจอ การเปลี่ยนความสูงของพื้นที่การเขียนในลักษณะนี้ ทำงานในบางภาษาได้ดีกว่าภาษาอื่นๆ
เมื่อคุณระบุพื้นที่การเขียน ให้ระบุความกว้างและความสูงเป็นหน่วยเดียวกับเส้นโครงร่าง พิกัด อาร์กิวเมนต์ x,y พิกัดไม่มีข้อกำหนดหน่วย - API จะปรับทั้งหมดให้เป็นค่ามาตรฐาน หน่วย ดังนั้นสิ่งเดียวที่สำคัญคือขนาดและตำแหน่งของเส้นโครงร่าง คุณมีอิสระที่จะ ส่งพิกัดในขนาดใดก็ได้ที่เหมาะสมกับระบบของคุณ
ก่อนบริบท
ก่อนบริบทคือข้อความที่อยู่ก่อนเส้นโครงร่างใน Ink
ที่คุณ
กำลังพยายามจดจำ คุณช่วยเครื่องมือจดจำได้โดยบอกเกี่ยวกับบริบทเบื้องต้น
เช่น ตัวอักษรแบบคัดลายมือ "n" และ "u" ก็มักเข้าใจผิดว่าเป็นกันและกัน หากผู้ใช้มี ป้อนคำว่า "อาร์กิวเมนต์" บางส่วนไปแล้ว พวกเขาอาจใช้คำนี้ต่อไปอีก เช่น "ument" หรือ "nment" การระบุ "อาร์กิวเมนต์" ก่อนบริบท แก้ไขความคลุมเครือ เนื่องจากคำว่า "อาร์กิวเมนต์" จะเป็นไปได้มากกว่า "อาร์กิวเมนต์"
นอกจากนี้ บริบทเบื้องต้นยังช่วยให้ระบบจดจำระบุตัวแบ่งคำหรือการเว้นวรรคระหว่างคำได้ด้วย คุณสามารถ พิมพ์อักขระเว้นวรรคแต่คุณไม่สามารถวาดได้ 1 คำ ดังนั้น โปรแกรมรู้จำจะระบุได้อย่างไรว่า 1 คำสิ้นสุดเมื่อใด แล้วเพลงถัดไปก็เริ่มล่ะ หากผู้ใช้เคยเขียนคำว่า "สวัสดี" ไว้แล้ว และต่อด้วยคำว่า "world" โดยไม่มีบริบทล่วงหน้า เครื่องมือจดจำจะแสดงผลสตริง "world" แต่ถ้าคุณระบุ บริบท "สวัสดี" ล่วงหน้า โมเดลจะส่งกลับสตริง " โลก" โดยเว้นวรรคนำหน้า โลก" เหมาะสมกว่าคำว่า "Heyword"
คุณควรระบุสตริงก่อนบริบทที่ยาวที่สุดเท่าที่จะเป็นไปได้ โดยไม่เกิน 20 อักขระโดยรวมถึง พื้นที่ทำงาน หากสตริงยาวกว่า เครื่องมือจดจำจะใช้เพียง 20 อักขระสุดท้าย
ตัวอย่างโค้ดด้านล่างแสดงวิธีกำหนดพื้นที่การเขียนและใช้
RecognitionContext
ที่จะระบุบริบทล่วงหน้า
Kotlin
var preContext : String = ...; var width : Float = ...; var height : Float = ...; val recognitionContext : RecognitionContext = RecognitionContext.builder() .setPreContext(preContext) .setWritingArea(WritingArea(width, height)) .build() recognizer.recognize(ink, recognitionContext)
Java
String preContext = ...; float width = ...; float height = ...; RecognitionContext recognitionContext = RecognitionContext.builder() .setPreContext(preContext) .setWritingArea(new WritingArea(width, height)) .build(); recognizer.recognize(ink, recognitionContext);
การจัดเรียงสโตรก
ความถูกต้องในการจดจำจะคำนึงถึงลำดับของเส้น โปรแกรมรู้จำคาดหวังให้สโตรก เกิดขึ้นตามลำดับที่โดยปกติแล้วผู้คนจะเขียน เช่น อ่านซ้ายไปขวาสำหรับภาษาอังกฤษ ทุกกรณี ที่ออกจากรูปแบบนี้ เช่น การเขียนประโยคภาษาอังกฤษโดยเริ่มต้นด้วยคำสุดท้าย จะให้ผลลัพธ์ที่แม่นยำน้อยลง
อีกตัวอย่างหนึ่งคือเมื่อนำคำที่อยู่ตรงกลาง Ink
ออกและแทนที่ด้วย
คำอื่น การแก้ไขอาจอยู่ระหว่างกลางประโยค แต่เส้นแบ่งการแก้ไข
จะอยู่ท้ายสุดของลำดับเส้นโครงร่าง
ในกรณีนี้ เราขอแนะนำให้ส่งคำที่เขียนใหม่แยกต่างหากไปยัง API และรวม
ผลลัพธ์ด้วยการจดจำก่อนหน้าโดยใช้ตรรกะของคุณเอง
การจัดการกับรูปร่างที่ไม่ชัดเจน
มีบางกรณีที่ความหมายของรูปร่างที่ให้ไว้กับเครื่องมือจดจำนั้นไม่ชัดเจน สำหรับ เช่น สี่เหลี่ยมผืนผ้าที่มีขอบมนมากอาจมองว่าเป็นสี่เหลี่ยมผืนผ้าหรือวงรี
คุณใช้คะแนนการจดจำ เมื่อมีเคสที่ไม่ชัดเจนเหล่านี้จัดการได้ เฉพาะ
ตัวแยกประเภทรูปร่างจะให้คะแนน หากโมเดลมั่นใจมาก คะแนนของผลลัพธ์อันดับต้นๆ จะเป็น
ดีกว่าโซลูชันที่ 2 อย่างมาก หากไม่แน่นอน คะแนนสำหรับผลลัพธ์ 2 อันดับแรกจะ
ใกล้ๆ นอกจากนี้ โปรดทราบว่าตัวแยกประเภทรูปร่างจะตีความ Ink
ทั้งหมดว่าเป็น
รูปร่างเดียว เช่น หาก Ink
มีสี่เหลี่ยมผืนผ้าและวงรีอยู่ข้างแต่ละจุด
เครื่องมือจดจำอาจแสดงผลอย่างใดอย่างหนึ่ง (หรือบางอย่างที่แตกต่างออกไปโดยสิ้นเชิง) เป็น
เนื่องจากตัวเลือกการจดจำเดี่ยวไม่สามารถแสดงรูปร่างสองรูป