تشخیص متن در تصاویر با ML Kit در اندروید

می‌توانید از کیت ML برای تشخیص متن در تصاویر یا ویدیو، مانند متن تابلوهای خیابان، استفاده کنید. ویژگی های اصلی این ویژگی عبارتند از:

ویژگی تفکیک شده همراه
نام کتابخانه 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 کیلوبایت افزایش اندازه در هر معماری اسکریپت. حدود 4 مگابایت افزایش اندازه در هر اسکریپت در هر معماری.
زمان اولیه سازی ممکن است قبل از اولین استفاده باید منتظر بمانید تا مدل دانلود شود. مدل فورا موجود است
کارایی زمان واقعی در اکثر دستگاه‌ها برای کتابخانه اسکریپت لاتین، برای دیگران کندتر. زمان واقعی در اکثر دستگاه‌ها برای کتابخانه اسکریپت لاتین، برای دیگران کندتر.

آن را امتحان کنید

  • با برنامه نمونه بازی کنید تا نمونه استفاده از این API را ببینید.
  • کد را خودتان با Codelab امتحان کنید.

قبل از اینکه شروع کنی

  1. در فایل build.gradle در سطح پروژه خود، مطمئن شوید که مخزن Maven Google را در هر دو بخش buildscript و allprojects خود قرار دهید.
  2. وابستگی های کتابخانه های اندروید ML Kit را به فایل 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'
    }
    
  3. اگر انتخاب کردید که از مدل در خدمات Google Play استفاده کنید ، می‌توانید برنامه خود را طوری پیکربندی کنید که پس از نصب برنامه از فروشگاه Play، مدل را به‌طور خودکار در دستگاه دانلود کنید. برای انجام این کار، اعلان زیر را به فایل 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>
    

    همچنین می‌توانید صریحاً در دسترس بودن مدل را بررسی کنید و از طریق سرویس‌های Google Play ModuleInstallClient API درخواست دانلود کنید. اگر دانلودهای مدل در زمان نصب را فعال نکنید یا درخواست دانلود صریح نداشته باشید، اولین باری که اسکنر را اجرا می کنید، مدل دانلود می شود. درخواست‌هایی که قبل از تکمیل دانلود ارائه می‌کنید، نتیجه‌ای ندارند.

1. یک نمونه از TextRecognizer ایجاد کنید

یک نمونه از TextRecognizer ایجاد کنید و گزینه‌های مربوط به کتابخانه‌ای را که در بالا به آن وابستگی اعلام کرده‌اید ارسال کنید:

کاتلین

// 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())

جاوا

// 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 مقدار چرخش را برای شما محاسبه می کنند.

کاتلین

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
            // ...
        }
    }
}

جاوا

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
          // ...
        }
    }
}

اگر از کتابخانه دوربینی که درجه چرخش تصویر را به شما می دهد استفاده نمی کنید، می توانید آن را از روی درجه چرخش دستگاه و جهت سنسور دوربین در دستگاه محاسبه کنید:

کاتلین

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
}

جاوا

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() منتقل کنید:

کاتلین

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

InputImage image = InputImage.fromMediaImage(mediaImage, rotation);

استفاده از URI فایل

برای ایجاد یک شی InputImage از URI فایل، زمینه برنامه و فایل URI را به InputImage.fromFilePath() ارسال کنید. این زمانی مفید است که از یک هدف ACTION_GET_CONTENT استفاده می کنید تا از کاربر بخواهید تصویری را از برنامه گالری خود انتخاب کند.

کاتلین

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 را با بافر یا آرایه به همراه ارتفاع، عرض، فرمت کدگذاری رنگ و درجه چرخش تصویر ایجاد کنید:

کاتلین

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
)

جاوا

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 ، اعلان زیر را انجام دهید:

کاتلین

val image = InputImage.fromBitmap(bitmap, 0)

Java

InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);

تصویر با یک شی Bitmap همراه با درجه چرخش نمایش داده می شود.

3. تصویر را پردازش کنید

ارسال تصویر به روش process :

کاتلین

val result = recognizer.process(image)
        .addOnSuccessListener { visionText ->
            // Task completed successfully
            // ...
        }
        .addOnFailureListener { e ->
            // Task failed with an exception
            // ...
        }

جاوا

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 به شنونده موفق ارسال می شود. یک شیء Text حاوی متن کامل شناسایی شده در تصویر و صفر یا چند شیء TextBlock است.

هر TextBlock یک بلوک مستطیل شکل از متن را نشان می دهد که شامل صفر یا چند شی Line است. هر شی Line نشان دهنده یک خط متن است که حاوی صفر یا چند شی Element است. هر شی Element یک کلمه یا یک موجودیت کلمه مانند را نشان می دهد که حاوی صفر یا چند شی Symbol است. هر شی Symbol نشان دهنده یک کاراکتر، یک رقم یا یک موجودیت کلمه مانند است.

برای هر شیء TextBlock ، Line ، Element و Symbol ، می‌توانید متن شناسایی شده در منطقه، مختصات مرزی منطقه و بسیاری از ویژگی‌های دیگر مانند اطلاعات چرخش، امتیاز اطمینان و غیره را دریافت کنید.

مثلا:

کاتلین

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

جاوا

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 بتواند متن را به طور دقیق تشخیص دهد، تصاویر ورودی باید حاوی متنی باشند که با داده پیکسلی کافی نشان داده شود. در حالت ایده آل، هر کاراکتر باید حداقل 16x16 پیکسل باشد. به طور کلی هیچ مزیت دقت برای کاراکترهای بزرگتر از 24x24 پیکسل وجود ندارد.

    بنابراین، برای مثال، یک تصویر 640x480 ممکن است برای اسکن کارت ویزیتی که تمام عرض تصویر را اشغال می کند، به خوبی کار کند. برای اسکن یک سند چاپ شده روی کاغذ با اندازه حرف، ممکن است به یک تصویر 720x1280 پیکسل نیاز باشد.

  • فوکوس ضعیف تصویر می تواند بر دقت تشخیص متن تأثیر بگذارد. اگر نتایج قابل قبولی دریافت نکردید، از کاربر بخواهید که تصویر را دوباره بگیرد.

  • اگر متن را در یک برنامه بلادرنگ تشخیص می دهید، باید ابعاد کلی تصاویر ورودی را در نظر بگیرید. تصاویر کوچکتر را می توان سریعتر پردازش کرد. برای کاهش تأخیر، اطمینان حاصل کنید که متن تا آنجا که ممکن است از تصویر را اشغال می کند و تصاویر را با وضوح پایین تر ثبت کنید (با در نظر گرفتن الزامات دقت ذکر شده در بالا). برای اطلاعات بیشتر، نکاتی برای بهبود عملکرد را ببینید.

نکاتی برای بهبود عملکرد

  • اگر از Camera یا camera2 API استفاده می کنید، دریچه گاز با آشکارساز تماس می گیرد. اگر یک قاب ویدیویی جدید در حین کار کردن آشکارساز در دسترس قرار گرفت، قاب را رها کنید. برای مثال، کلاس VisionProcessorBase را در برنامه نمونه سریع شروع کنید.
  • اگر از CameraX API استفاده می‌کنید، مطمئن شوید که استراتژی فشار برگشتی روی مقدار پیش‌فرض ImageAnalysis تنظیم شده است ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST . این تضمین می کند که هر بار فقط یک تصویر برای تجزیه و تحلیل تحویل داده می شود. اگر در زمانی که آنالایزر مشغول است، تصاویر بیشتری تولید شود، به طور خودکار حذف می شوند و برای تحویل در صف قرار نمی گیرند. هنگامی که تصویر مورد تجزیه و تحلیل با فراخوانی ImageProxy.close بسته شد، آخرین تصویر بعدی تحویل داده می شود.
  • اگر از خروجی آشکارساز برای همپوشانی گرافیک روی تصویر ورودی استفاده می‌کنید، ابتدا نتیجه را از کیت ML دریافت کنید، سپس تصویر را در یک مرحله رندر و همپوشانی کنید. این تنها یک بار برای هر فریم ورودی به سطح نمایشگر نمایش داده می شود. برای مثال، کلاس‌های CameraSourcePreview و GraphicOverlay را در برنامه نمونه شروع سریع ببینید.
  • اگر از Camera2 API استفاده می کنید، تصاویر را با فرمت ImageFormat.YUV_420_888 بگیرید. اگر از دوربین API قدیمی استفاده می کنید، تصاویر را با فرمت ImageFormat.NV21 بگیرید.
  • گرفتن تصاویر با وضوح کمتر را در نظر بگیرید. با این حال، الزامات ابعاد تصویر این API را نیز در نظر داشته باشید.