คุณใช้ ML Kit เพื่อจดจำข้อความในรูปภาพหรือวิดีโอได้ เช่น ข้อความบนป้ายถนน ลักษณะหลักๆ ของฟีเจอร์นี้ ได้แก่
ฟีเจอร์ | ไม่ได้จัดกลุ่ม | รวมกลุ่ม |
---|---|---|
ชื่อห้องสมุด | com.google.android.gms:play-services-mlkit-text-recognition
com.google.android.gms:play-services-mlkit-text-recognition-chinese com.google.android.gms:play-services-mlkit-text-recognition-devanagari com.google.android.gms:play-services-mlkit-text-recognition-japanese com.google.android.gms:play-services-mlkit-text-recognition-korean |
com.google.mlkit:text-recognition
com.google.mlkit:text-recognition-chinese com.google.mlkit:text-recognition-devanagari com.google.mlkit:text-recognition-japanese com.google.mlkit:text-recognition-korean |
การใช้งาน | โมเดลจะดาวน์โหลดแบบไดนามิกผ่านบริการ Google Play | โมเดลลิงก์กับแอปของคุณแบบคงที่ ณ เวลาบิลด์ |
ขนาดแอป | ขนาดที่เพิ่มขึ้นประมาณ 260 KB ต่อสถาปัตยกรรมสคริปต์ | ขนาดเพิ่มขึ้นประมาณ 4 MB ต่อสคริปต์ต่อสถาปัตยกรรม |
เวลาในการเริ่มต้น | อาจต้องรอให้โมเดลดาวน์โหลดก่อนใช้งานครั้งแรก | โมเดลจะพร้อมใช้งานทันที |
การแสดง | เรียลไทม์บนอุปกรณ์ส่วนใหญ่สำหรับไลบรารีสคริปต์ภาษาละติน และช้ากว่าสำหรับอุปกรณ์อื่นๆ | เรียลไทม์บนอุปกรณ์ส่วนใหญ่สำหรับไลบรารีสคริปต์ภาษาละติน และช้ากว่าสำหรับอุปกรณ์อื่นๆ |
ลองเลย
- ลองใช้แอปตัวอย่างเพื่อดูตัวอย่างการใช้ API นี้
- ลองเขียนโค้ดเองด้วย Codelab
ก่อนเริ่มต้น
- ในไฟล์
build.gradle
ระดับโปรเจ็กต์ อย่าลืมรวมที่เก็บ Maven ของ Google ไว้ทั้งในส่วนbuildscript
และallprojects
เพิ่มทรัพยากร Dependency สำหรับไลบรารี ML Kit Android ลงในไฟล์ Gradle ระดับแอปของโมดูล ซึ่งปกติคือ
app/build.gradle
สำหรับการรวมโมเดลกับแอป
dependencies { // To recognize Latin script implementation 'com.google.mlkit:text-recognition:16.0.0' // To recognize Chinese script implementation 'com.google.mlkit:text-recognition-chinese:16.0.0' // To recognize Devanagari script implementation 'com.google.mlkit:text-recognition-devanagari:16.0.0' // To recognize Japanese script implementation 'com.google.mlkit:text-recognition-japanese:16.0.0' // To recognize Korean script implementation 'com.google.mlkit:text-recognition-korean:16.0.0' }
สำหรับการใช้โมเดลในบริการ Google Play
dependencies { // To recognize Latin script implementation 'com.google.android.gms:play-services-mlkit-text-recognition:19.0.0' // To recognize Chinese script implementation 'com.google.android.gms:play-services-mlkit-text-recognition-chinese:16.0.0' // To recognize Devanagari script implementation 'com.google.android.gms:play-services-mlkit-text-recognition-devanagari:16.0.0' // To recognize Japanese script implementation 'com.google.android.gms:play-services-mlkit-text-recognition-japanese:16.0.0' // To recognize Korean script implementation 'com.google.android.gms:play-services-mlkit-text-recognition-korean:16.0.0' }
หากเลือกใช้โมเดลในบริการ Google Play คุณสามารถกำหนดค่าแอปให้ดาวน์โหลดโมเดลลงในอุปกรณ์โดยอัตโนมัติหลังจากที่ติดตั้งแอปจาก Play Store แล้ว ซึ่งทำได้โดยเพิ่มการประกาศต่อไปนี้ลงในไฟล์
AndroidManifest.xml
ของแอป<application ...> ... <meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="ocr" > <!-- To use multiple models: android:value="ocr,ocr_chinese,ocr_devanagari,ocr_japanese,ocr_korean,..." --> </application>
นอกจากนี้ คุณยังตรวจสอบความพร้อมใช้งานของโมเดลและส่งคำขอดาวน์โหลดผ่าน ModuleInstallClient API ของบริการ Google Play อย่างชัดแจ้งได้ด้วย หากไม่เปิดใช้การดาวน์โหลดโมเดลเวลาติดตั้งหรือขอการดาวน์โหลดอย่างชัดแจ้ง ระบบจะดาวน์โหลดโมเดลในครั้งแรกที่คุณเรียกใช้ตัวสแกน คำขอที่คุณสร้างก่อนดาวน์โหลดเสร็จ จะไม่สร้างผลลัพธ์ใดๆ
1. สร้างอินสแตนซ์ของ TextRecognizer
สร้างอินสแตนซ์ของ TextRecognizer
โดยส่งตัวเลือกที่เกี่ยวข้องกับไลบรารีที่คุณประกาศทรัพยากร Dependency ไว้ด้านบน ดังนี้
Kotlin
// When using Latin script library val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS) // When using Chinese script library val recognizer = TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build()) // When using Devanagari script library val recognizer = TextRecognition.getClient(DevanagariTextRecognizerOptions.Builder().build()) // When using Japanese script library val recognizer = TextRecognition.getClient(JapaneseTextRecognizerOptions.Builder().build()) // When using Korean script library val recognizer = TextRecognition.getClient(KoreanTextRecognizerOptions.Builder().build())
Java
// When using Latin script library TextRecognizer recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS); // When using Chinese script library TextRecognizer recognizer = TextRecognition.getClient(new ChineseTextRecognizerOptions.Builder().build()); // When using Devanagari script library TextRecognizer recognizer = TextRecognition.getClient(new DevanagariTextRecognizerOptions.Builder().build()); // When using Japanese script library TextRecognizer recognizer = TextRecognition.getClient(new JapaneseTextRecognizerOptions.Builder().build()); // When using Korean script library TextRecognizer recognizer = TextRecognition.getClient(new KoreanTextRecognizerOptions.Builder().build());
2. เตรียมรูปภาพอินพุต
หากต้องการจดจำข้อความในรูปภาพ ให้สร้างออบเจ็กต์ InputImage
จาก Bitmap
, media.Image
, ByteBuffer
, ไบต์อาร์เรย์ หรือไฟล์ในอุปกรณ์ จากนั้นส่งออบเจ็กต์ InputImage
ไปยังเมธอด processImage
ของ TextRecognizer
คุณสร้างออบเจ็กต์ InputImage
จากแหล่งที่มาต่างๆ ได้ตามที่อธิบายไว้ด้านล่าง
กำลังใช้media.Image
หากต้องการสร้างออบเจ็กต์ InputImage
จากออบเจ็กต์ media.Image
เช่น เมื่อคุณจับภาพจากกล้องของอุปกรณ์ ให้ส่งออบเจ็กต์ media.Image
และการหมุนของรูปภาพไปยัง InputImage.fromMediaImage()
หากคุณใช้ไลบรารี
CameraX คลาส OnImageCapturedListener
และ
ImageAnalysis.Analyzer
จะคำนวณค่าการหมุนเวียนให้คุณ
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null) { val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) // Pass image to an ML Kit Vision API // ... } } }
Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { @Override public void analyze(ImageProxy imageProxy) { Image mediaImage = imageProxy.getImage(); if (mediaImage != null) { InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees()); // Pass image to an ML Kit Vision API // ... } } }
หากคุณไม่ได้ใช้ไลบรารีของกล้องที่ให้ระดับการหมุนของภาพ คุณสามารถคำนวณได้จากระดับการหมุนของอุปกรณ์และการวางแนวของเซ็นเซอร์กล้องในอุปกรณ์
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 0) ORIENTATIONS.append(Surface.ROTATION_90, 90) ORIENTATIONS.append(Surface.ROTATION_180, 180) ORIENTATIONS.append(Surface.ROTATION_270, 270) } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Throws(CameraAccessException::class) private fun getRotationCompensation(cameraId: String, activity: Activity, isFrontFacing: Boolean): Int { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. val deviceRotation = activity.windowManager.defaultDisplay.rotation var rotationCompensation = ORIENTATIONS.get(deviceRotation) // Get the device's sensor orientation. val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360 } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360 } return rotationCompensation }
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 0); ORIENTATIONS.append(Surface.ROTATION_90, 90); ORIENTATIONS.append(Surface.ROTATION_180, 180); ORIENTATIONS.append(Surface.ROTATION_270, 270); } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private int getRotationCompensation(String cameraId, Activity activity, boolean isFrontFacing) throws CameraAccessException { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int rotationCompensation = ORIENTATIONS.get(deviceRotation); // Get the device's sensor orientation. CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360; } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360; } return rotationCompensation; }
จากนั้นส่งออบเจ็กต์ media.Image
และค่าระดับการหมุนไปยัง InputImage.fromMediaImage()
ดังนี้
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
การใช้ URI ของไฟล์
หากต้องการสร้างออบเจ็กต์ InputImage
จาก URI ของไฟล์ ให้ส่งบริบทของแอปและ URI ของไฟล์ไปยัง InputImage.fromFilePath()
ซึ่งจะเป็นประโยชน์เมื่อคุณใช้ Intent ACTION_GET_CONTENT
เพื่อแจ้งให้ผู้ใช้เลือกรูปภาพจากแอปแกลเลอรี
Kotlin
val image: InputImage try { image = InputImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
Java
InputImage image; try { image = InputImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
กำลังใช้ByteBuffer
หรือByteArray
หากต้องการสร้างออบเจ็กต์ InputImage
จาก ByteBuffer
หรือ ByteArray
ให้คำนวณระดับการหมุนรูปภาพตามที่อธิบายไว้ก่อนหน้านี้สำหรับอินพุต media.Image
ก่อน
จากนั้นสร้างออบเจ็กต์ InputImage
ด้วยบัฟเฟอร์หรืออาร์เรย์ ร่วมกับความสูง ความกว้าง รูปแบบการเข้ารหัสสี และระดับการหมุนของรูปภาพ
Kotlin
val image = InputImage.fromByteBuffer( byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ) // Or: val image = InputImage.fromByteArray( byteArray, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 )
Java
InputImage image = InputImage.fromByteBuffer(byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ); // Or: InputImage image = InputImage.fromByteArray( byteArray, /* image width */480, /* image height */360, rotation, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 );
กำลังใช้Bitmap
หากต้องการสร้างออบเจ็กต์ InputImage
จากออบเจ็กต์ Bitmap
ให้ประกาศดังต่อไปนี้
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
รูปภาพจะแสดงเป็นวัตถุ Bitmap
ร่วมกับองศาการหมุน
3. ประมวลผลรูปภาพ
ส่งรูปภาพไปยังเมธอด process
:
Kotlin
val result = recognizer.process(image) .addOnSuccessListener { visionText -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
Task<Text> result = recognizer.process(image) .addOnSuccessListener(new OnSuccessListener<Text>() { @Override public void onSuccess(Text visionText) { // Task completed successfully // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
4. ดึงข้อความจากบล็อกข้อความที่รู้จัก
หากการดำเนินการจดจำข้อความสำเร็จ ระบบจะส่งออบเจ็กต์ Text
ไปยัง Listener ที่สำเร็จ ออบเจ็กต์ Text
มีข้อความแบบเต็มที่รู้จักในรูปภาพและออบเจ็กต์ TextBlock
อย่างน้อย 0 รายการ
TextBlock
แต่ละรายการแสดงบล็อกข้อความสี่เหลี่ยมผืนผ้า ซึ่งมีออบเจ็กต์ Line
0 รายการหรือมากกว่า ออบเจ็กต์ Line
แต่ละรายการจะแสดงบรรทัดข้อความ ซึ่งมีออบเจ็กต์ Element
0 รายการหรือมากกว่า ออบเจ็กต์ Element
แต่ละรายการแสดงถึงคำหรือเอนทิตีที่มีลักษณะคล้ายคำ ซึ่งมีออบเจ็กต์ Symbol
อยู่ 0 รายการขึ้นไป ออบเจ็กต์ Symbol
แต่ละรายการแสดงอักขระ ตัวเลข หรือเอนทิตีที่มีลักษณะคล้ายคำ
สำหรับออบเจ็กต์ TextBlock
, Line
, Element
และ Symbol
แต่ละรายการ คุณจะได้รับข้อความที่รู้จักในภูมิภาค พิกัดขอบเขตของภูมิภาค และแอตทริบิวต์อื่นๆ อีกมากมาย เช่น ข้อมูลการหมุน คะแนนความเชื่อมั่น ฯลฯ
เช่น
Kotlin
val resultText = result.text for (block in result.textBlocks) { val blockText = block.text val blockCornerPoints = block.cornerPoints val blockFrame = block.boundingBox for (line in block.lines) { val lineText = line.text val lineCornerPoints = line.cornerPoints val lineFrame = line.boundingBox for (element in line.elements) { val elementText = element.text val elementCornerPoints = element.cornerPoints val elementFrame = element.boundingBox } } }
Java
String resultText = result.getText(); for (Text.TextBlock block : result.getTextBlocks()) { String blockText = block.getText(); Point[] blockCornerPoints = block.getCornerPoints(); Rect blockFrame = block.getBoundingBox(); for (Text.Line line : block.getLines()) { String lineText = line.getText(); Point[] lineCornerPoints = line.getCornerPoints(); Rect lineFrame = line.getBoundingBox(); for (Text.Element element : line.getElements()) { String elementText = element.getText(); Point[] elementCornerPoints = element.getCornerPoints(); Rect elementFrame = element.getBoundingBox(); for (Text.Symbol symbol : element.getSymbols()) { String symbolText = symbol.getText(); Point[] symbolCornerPoints = symbol.getCornerPoints(); Rect symbolFrame = symbol.getBoundingBox(); } } } }
หลักเกณฑ์เกี่ยวกับรูปภาพที่ป้อน
-
เพื่อให้ ML Kit จดจำข้อความได้อย่างถูกต้อง รูปภาพที่ป้อนต้องมีข้อความที่นำเสนอด้วยข้อมูลพิกเซลที่เพียงพอ โดยหลักการแล้ว อักขระแต่ละตัวควรมีขนาดอย่างน้อย 16x16 พิกเซล โดยทั่วไปจะไม่ได้รับประโยชน์ด้านความแม่นยำสำหรับอักขระที่มีขนาดใหญ่กว่า 24x24 พิกเซล
ตัวอย่างเช่น รูปภาพขนาด 640x480 อาจเหมาะสำหรับการสแกนนามบัตรที่ใช้พื้นที่เต็มความกว้างของรูปภาพ หากต้องการสแกนเอกสารที่พิมพ์บนกระดาษขนาดตัวอักษร อาจต้องใช้รูปภาพขนาด 720x1280 พิกเซล
-
การโฟกัสของรูปภาพไม่ดีอาจส่งผลต่อความถูกต้องในการจดจำข้อความ หากไม่ได้ผลลัพธ์ที่ยอมรับได้ ให้ลองขอให้ผู้ใช้จับภาพอีกครั้ง
-
ถ้าคุณกำลังจดจำข้อความในแอปพลิเคชันแบบเรียลไทม์ คุณควรพิจารณาขนาดโดยรวมของรูปภาพที่ป้อน ระบบจะประมวลผลรูปภาพขนาดเล็กได้เร็วขึ้น หากต้องการลดเวลาในการตอบสนอง ให้ตรวจสอบว่าข้อความใช้พื้นที่ในรูปภาพมากที่สุดเท่าที่จะเป็นไปได้ และจับภาพด้วยความละเอียดที่ต่ำลง (โปรดคำนึงถึงข้อกำหนดเกี่ยวกับความถูกต้องที่กล่าวถึงข้างต้น) ดูข้อมูลเพิ่มเติมได้ที่เคล็ดลับในการปรับปรุงประสิทธิภาพ
เคล็ดลับในการปรับปรุงประสิทธิภาพ
- หากคุณใช้
Camera
หรือcamera2
API ให้ควบคุมการเรียกไปยังตัวตรวจจับ หากมีเฟรมวิดีโอใหม่ขณะที่ตัวตรวจจับทำงานอยู่ ให้วางเฟรมดังกล่าว โปรดดูคลาสVisionProcessorBase
ในแอปตัวอย่างการเริ่มต้นอย่างรวดเร็วสำหรับตัวอย่าง - หากใช้
CameraX
API โปรดตรวจสอบว่ามีการตั้งค่ากลยุทธ์ Backpress เป็นค่าเริ่มต้นImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
ซึ่งทำให้ระบบนำส่งรูปภาพเพียง 1 รูปเพื่อทำการวิเคราะห์ต่อครั้งเท่านั้น หากมีการสร้างรูปภาพเพิ่มเติมขณะที่เครื่องมือวิเคราะห์ไม่ว่าง รูปภาพจะถูกตัดออกโดยอัตโนมัติและไม่ได้เข้าคิวรอการนำส่ง เมื่อระบบปิดรูปภาพที่วิเคราะห์ด้วยการเรียกใช้ ImageProxy.close() ระบบจะส่งรูปภาพล่าสุดถัดไป - หากคุณใช้เอาต์พุตของเครื่องมือตรวจจับเพื่อวางซ้อนกราฟิกบนรูปภาพอินพุต ให้รับผลลัพธ์จาก ML Kit ก่อน จากนั้นแสดงผลรูปภาพและการวางซ้อนในขั้นตอนเดียว ซึ่งจะแสดงผลบนพื้นที่แสดงผล
เพียงครั้งเดียวต่อเฟรมอินพุตแต่ละเฟรม โปรดดูชั้นเรียน
CameraSourcePreview
และGraphicOverlay
ในแอปตัวอย่างการเริ่มต้นอย่างรวดเร็ว - หากคุณใช้ Camera2 API ให้จับภาพในรูปแบบ
ImageFormat.YUV_420_888
หากคุณใช้ Camera API เวอร์ชันเก่า ให้จับภาพในรูปแบบImageFormat.NV21
- ลองจับภาพที่ความละเอียดต่ำลง อย่างไรก็ตาม โปรดคำนึงถึงข้อกำหนดเกี่ยวกับขนาดรูปภาพของ API นี้ด้วย