Android -- 开源库文本识别 ML Kit 的基本使用

Posted Kevin-Dev

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android -- 开源库文本识别 ML Kit 的基本使用相关的知识,希望对你有一定的参考价值。

前言

机器学习套件是一个移动 SDK,将 Google 的设备端机器学习专业知识运用于 androidios 应用。使用我们强大而易用的 Vision API 和 Natural Language API 解决应用中的常见挑战,或打造全新的用户体验。所有功能均由 Google 一流的机器学习模型提供支持,可免费使用。

学习指南:https://developers.google.cn/ml-kit/guides?hl=zh-cn

效果图

书写识别

准备工作

1. 在 app/build.gradle 添加如下依赖

implementation 'com.google.mlkit:digital-ink-recognition:18.0.0'

2. 创建 StrokeManager.java

/**
 * author: Kevin-Dev
 * date: 2023/2/2
 * desc:
 */
public class StrokeManager 
    /** Interface to register to be notified of changes in the recognized content. */
    public interface ContentChangedListener 

        /** This method is called when the recognized content changes. */
        void onContentChanged();
    

    /** Interface to register to be notified of changes in the status. */
    public interface StatusChangedListener 

        /** This method is called when the recognized content changes. */
        void onStatusChanged();
    

    /** Interface to register to be notified of changes in the downloaded model state. */
    public interface DownloadedModelsChangedListener 

        /** This method is called when the downloaded models changes. */
        void onDownloadedModelsChanged(Set<String> downloadedLanguageTags);
    

    @VisibleForTesting
    static final long CONVERSION_TIMEOUT_MS = 1000;
    private static final String TAG = "MLKD.StrokeManager";
    // This is a constant that is used as a message identifier to trigger the timeout.
    private static final int TIMEOUT_TRIGGER = 1;
    // For handling recognition and model downloading.
    private RecognitionTask recognitionTask = null;
    @VisibleForTesting ModelManager modelManager = new ModelManager();
    // Managing the recognition queue.
    private final List<RecognitionTask.RecognizedInk> content = new ArrayList<>();
    // Managing ink currently drawn.
    private Ink.Stroke.Builder strokeBuilder = Ink.Stroke.builder();
    private Ink.Builder inkBuilder = Ink.builder();
    private boolean stateChangedSinceLastRequest = false;
    @Nullable
    private ContentChangedListener contentChangedListener = null;
    @Nullable private StatusChangedListener statusChangedListener = null;
    @Nullable private DownloadedModelsChangedListener downloadedModelsChangedListener = null;

    private boolean triggerRecognitionAfterInput = true;
    private boolean clearCurrentInkAfterRecognition = true;
    private String status = "";

    public void setTriggerRecognitionAfterInput(boolean shouldTrigger) 
        triggerRecognitionAfterInput = shouldTrigger;
    

    public void setClearCurrentInkAfterRecognition(boolean shouldClear) 
        clearCurrentInkAfterRecognition = shouldClear;
    

    // Handler to handle the UI Timeout.
    // This handler is only used to trigger the UI timeout. Each time a UI interaction happens,
    // the timer is reset by clearing the queue on this handler and sending a new delayed message (in
    // addNewTouchEvent).
    private final Handler uiHandler =
            new Handler(
                    msg -> 
                        if (msg.what == TIMEOUT_TRIGGER) 
                            Log.i(TAG, "Handling timeout trigger.");
                            commitResult();
                            return true;
                        
                        // In the current use this statement is never reached because we only ever send
                        // TIMEOUT_TRIGGER messages to this handler.
                        // This line is necessary because otherwise Java's static analysis doesn't allow for
                        // compiling. Returning false indicates that a message wasn't handled.
                        return false;
                    );

    private void setStatus(String newStatus) 
        status = newStatus;
        if (statusChangedListener != null) 
            statusChangedListener.onStatusChanged();
        
    

    private void commitResult() 
        if (recognitionTask.done() && recognitionTask.result() != null) 
            content.add(recognitionTask.result());
            setStatus("Successful recognition: " + recognitionTask.result().text);
            if (clearCurrentInkAfterRecognition) 
                resetCurrentInk();
            
            if (contentChangedListener != null) 
                contentChangedListener.onContentChanged();
            
            reset();
        
    

    public void reset() 
        Log.i(TAG, "reset");
        resetCurrentInk();
        content.clear();
        if (recognitionTask != null && !recognitionTask.done()) 
            recognitionTask.cancel();
        
        setStatus("");
    

    private void resetCurrentInk() 
        inkBuilder = Ink.builder();
        strokeBuilder = Ink.Stroke.builder();
        stateChangedSinceLastRequest = false;
    

    public Ink getCurrentInk() 
        return inkBuilder.build();
    

    /**
     * This method is called when a new touch event happens on the drawing client and notifies the
     * StrokeManager of new content being added.
     *
     * <p>This method takes care of triggering the UI timeout and scheduling recognitions on the
     * background thread.
     *
     * @return whether the touch event was handled.
     */
    public boolean addNewTouchEvent(MotionEvent event) 
        int action = event.getActionMasked();
        float x = event.getX();
        float y = event.getY();
        long t = System.currentTimeMillis();

        // A new event happened -> clear all pending timeout messages.
        uiHandler.removeMessages(TIMEOUT_TRIGGER);

        switch (action) 
            case MotionEvent.ACTION_DOWN:
            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 = Ink.Stroke.builder();
                stateChangedSinceLastRequest = true;
                recognize();
       /* if (triggerRecognitionAfterInput) 
          recognize();
        */
                break;
            default:
                // Indicate touch event wasn't handled.
                return false;
        

        return true;
    

    // Listeners to update the drawing and status.
    public void setContentChangedListener(ContentChangedListener contentChangedListener) 
        this.contentChangedListener = contentChangedListener;
    

    public void setStatusChangedListener(StatusChangedListener statusChangedListener) 
        this.statusChangedListener = statusChangedListener;
    

    public void setDownloadedModelsChangedListener(
            DownloadedModelsChangedListener downloadedModelsChangedListener) 
        this.downloadedModelsChangedListener = downloadedModelsChangedListener;
    

    public List<RecognitionTask.RecognizedInk> getContent() 
        return content;
    

    public String getStatus() 
        return status;
    

    // Model downloading / deleting / setting.

    public void setActiveModel(String languageTag) 
        setStatus(modelManager.setModel(languageTag));
    

    public Task<Void> deleteActiveModel() 
        return modelManager
                .deleteActiveModel()
                .addOnSuccessListener(unused -> refreshDownloadedModelsStatus())
                .onSuccessTask(
                        status -> 
                            setStatus(status);
                            return Tasks.forResult(null);
                        );
    

    public Task<Void> download() 
        setStatus("Download started.");
        return modelManager
                .download()
                .addOnSuccessListener(unused -> refreshDownloadedModelsStatus())
                .onSuccessTask(
                        status -> 
                            setStatus(status);
                            return Tasks.forResult(null);
                        );
    

    // Recognition-related.

    public Task<String> recognize() 

        if (!stateChangedSinceLastRequest || inkBuilder.isEmpty()) 
            setStatus("No recognition, ink unchanged or empty");
            return Tasks.forResult(null);
        
        if (modelManager.getRecognizer() == null) 
            setStatus("Recognizer not set");
            return Tasks.forResult(null);
        

        return modelManager
                .checkIsModelDownloaded()
                .onSuccessTask(
                        result -> 
                            if (!result) 
                                setStatus("Model not downloaded yet");
                                return Tasks.forResult(null);
                            

                            stateChangedSinceLastRequest = false;
                            recognitionTask =
                                    new RecognitionTask(modelManager.getRecognizer(), inkBuilder.build());
                            uiHandler.sendMessageDelayed(
                                    uiHandler.obtainMessage(TIMEOUT_TRIGGER), CONVERSION_TIMEOUT_MS);
                            return recognitionTask.run();
                        );
    

    public void refreshDownloadedModelsStatus() 
        modelManager
                .getDownloadedModelLanguages()
                .addOnSuccessListener(
                        downloadedLanguageTags -> 
                            if (downloadedModelsChangedListener != null) 
                                downloadedModelsChangedListener.onDownloadedModelsChanged(downloadedLanguageTags);
                            
                        );
    

3. 创建 ModelManager.java

/**
 * author: Kevin-Dev
 * date: 2023/2/2
 * desc:
 */
public class ModelManager 
    private static final String TAG = "MLKD.ModelManager";
    private DigitalInkRecognitionModel model;
    private DigitalInkRecognizer recognizer;
    final RemoteModelManager remoteModelManager = RemoteModelManager.getInstance();

    public String setModel(String languageTag) 
        // Clear the old model and recognizer.
        model = null;
        if (recognizer != null) 
            recognizer.close();
        
        recognizer = null;

        // Try to parse the languageTag and get a model from it.
        DigitalInkRecognitionModelIdentifier modelIdentifier;
        try 
            modelIdentifier = DigitalInkRecognitionModelIdentifier.fromLanguageTag(languageTag);
         catch (MlKitException e) 
            Log.e(TAG, "Failed to parse language '" + languageTag + "'");
            return "";
        
        if (modelIdentifier == null) 
            return "No model for language: " + languageTag;
        

        // Initialize the model and recognizer.
        model = DigitalInkRecognitionModel.builder(modelIdentifier).build();
        recognizer =
                DigitalInkRecognition.getClient(DigitalInkRecognizerOptions.builder(model).build());
        Log.i(
                TAG,
                "Model set for language '"
                        + languageTag
                        + "' ('"
                        + modelIdentifier.getLanguageTag()
                        + "').");
        return "Model set for language: " + languageTag;
    

    public DigitalInkRecognizer getRecognizer() 
        return recognizer;
    

    public Task<Boolean> checkIsModelDownloaded() 
        return remoteModelManager.isModelDownloaded(model);
    

    public Task<String> deleteActiveModel() 
        if (model == null) 
            Log.i(TAG, "Model not set");
            return Tasks.forResult("Model not set");
        
        return checkIsModelDownloaded()
                .onSuccessTask(
                        result -> 
                            if (!result) 
                                return Tasks.forResult("Model not downloaded yet");
                            
                            return remoteModelManager
                                    .deleteDownloadedModel(model)
                                    .onSuccessTask(
                                            aVoid -> 
                                                Log.i(TAG, "Model successfully deleted");
                                                return Tasks.forResult("Model successfully deleted");
                                            );
                        )
                .addOnFailureListener(e -> Log.e(TAG, "Error while model deletion: " + e));
    

    public Task<Set<String>> getDownloadedModelLanguages() 
        return remoteModelManager
                .getDownloadedModels(DigitalInkRecognitionModel.class)
                .onSuccessTask(
                        (remoteModels) -> 
                            Set<String> result = new HashSet<>();
                            for (DigitalInkRecognitionModel model : remoteModels) 
                                result.add(model.getModelIdentifier().getLanguageTag());
                            
                            Log.i(TAG, "Downloaded models for languages:" + result);
                            return Tasks.forResult(result);
                        );
    

    public Task<String> download() 
        if (model == null) 
            return Tasks.forResult("Model not selected.");
        
        return remoteModelManager
                .download(model, new DownloadConditions.Builder().build())
                .onSuccessTask(
                        aVoid -> 
                            Log.i(TAG, "Model download succeeded.");
                            return Tasks.forResult("Downloaded model successfully");
                        )
                .addOnFailureListener(e -> Log.e(TAG, "Error while downloading the model: " + e));
    

  1. 创建 RecognitionTask.java
用华为HMS ML kit人体骨骼识别技术,Android快速实现人体姿势动作抓拍

使用 CameraView 在 Android 上使用 ML Kit 检测人脸

Android实战图像识别:Compose + MLKit + CameraX

用于 android 的 ML-Kit 人脸检测是不是支持 GPU 加速?

MLKit 是一个强大易用的工具包。通过 ML Kit 您可以很轻松的实现文字识别条码识别图像标记人脸检测对象检测等功能

MLKit 是一个强大易用的工具包。通过 ML Kit 您可以很轻松的实现文字识别条码识别图像标记人脸检测对象检测等功能