アイキャッチ画像

【Android・iOS】.Net MAUIでOCRを実装する方法について

.Net MAUIでOCRを実装する方法について紹介していきます。

紹介環境

当記事は以下の環境で作成しています。

開発環境

  • Visual Studio 2022
  • .Net 9

対象プラットフォーム

  • Android 5.0以上
  • iOS 16以上

OCRとは?

OCR(Optical Character Recognition)は、画像データや印刷物などのテキストを文字データに変換する技術になります。

OCRを実装する方法について

OCRの実装方法を手順に沿って紹介していきます。

ファイル構造は以下で作成しています。良ければ参考にしてください。

サンプルのファイル構造

手順1:インターフェイスを準備します

DIで利用するためのインターフェイスを準備します。画像データにはMicrosoft.Maui.Graphics.IImageを利用して文字列を取得するようにします。

using IImage = Microsoft.Maui.Graphics.IImage;

// 名前空間はプロジェクトに合わせてください。
namespace SampleApp.DI;

/// <summary>
/// OCRに関することを定義します。
/// </summary>
public interface IOcrServiceDI
{
    /// <summary>
    /// 文字列を取得します。
    /// </summary>
    /// <param name="image"></param>
    /// <returns></returns>
    public Task<string?> GetTextAsync(IImage image);
}

今回はAndroid・iOSのみの実装になるため、それ以外のプラットフォームではnullを返却するようにします。

#if !ANDROID && !IOS
// 名前空間はプロジェクトに合わせてください。
namespace SampleApp.DI;

public class OcrServiceDI : IOcrServiceDI
{
    public async Task<string?> GetTextAsync(Microsoft.Maui.Graphics.IImage image)
    {
        await Task.CompletedTask;
        return null;
    }
}
#endif

手順2:Xamarin.Google.MLKit.TextRecognition.Japaneseをインストールします

Xamarin.Google.MLKit.TextRecognition.JapaneseのパッケージをNuGetから取得します。Xamarin.Google.MLKit.TextRecognition.Japaneseの詳細は以下を確認してください。

パッケージをNuGetから取得する方法は以下のようになります。

手順A:ソリューション エクスプローラーの「依存関係」を右クリックし、メニューから「NuGet パッケージの管理(N)」をクリックします。

手順A:Xamarin.Google.MLKit.TextRecognition.Japaneseのパッケージ

手順B:NuGet パッケージ マネージャーの検索欄に「Xamarin.Google.MLKit.TextRecognition.Japanese」と入力し、以下のようにインストールします。※この手順は参照タブで行います。

手順B:Xamarin.Google.MLKit.TextRecognition.Japaneseのパッケージ

手順C:ライセンスの同意が表示されますので「同意する(A)」をクリックします。これでパッケージのインストールは完了します。

手順C:Xamarin.Google.MLKit.TextRecognition.Japaneseのパッケージ

手順3:【Android】OCRを実装します

AndroidのOCRは以下のように実装します。

using Microsoft.Maui.Graphics.Platform;
using SampleApp.DI;
using Xamarin.Google.Android.Odml.Image;
using Xamarin.Google.MLKit.Vision.Text;
using Xamarin.Google.MLKit.Vision.Text.Japanese;
using IImage = Microsoft.Maui.Graphics.IImage;

// 名前空間はプロジェクトに合わせてください。
namespace SampleApp.Platforms.Android.DI;

public class OcrServiceDI : IOcrServiceDI
{
    /// <inheritdoc />
    public async Task<string?> GetTextAsync(IImage image)
    {
        TaskCompletionSource<string?> tcs = new();

        ITextRecognizer recognizer = TextRecognition.GetClient(new JapaneseTextRecognizerOptions.Builder().Build());
        MlImage mlImage = new BitmapMlImageBuilder(image.AsBitmap()).Build();
        recognizer.Process(mlImage).AddOnSuccessListener(new OnSuccessListener<Text>(
            text =>
            {
                tcs.TrySetResult(text.GetText());
            }));

        return await tcs.Task;
    }
}

手順3:【iOS】OCRを実装します

iOSのOCRは以下のように実装します。日本語に特化させるためにRecognitionLanguagesに「ja-JP」を指定しています。

using CoreGraphics;
using Foundation;
using Microsoft.Maui.Graphics.Platform;
using SampleApp.DI;
using System.Text;
using Vision;
using IImage = Microsoft.Maui.Graphics.IImage;

// 名前空間はプロジェクトに合わせてください。
namespace SampleApp.Platforms.iOS.DI;

public class OcrServiceDI : IOcrServiceDI
{
    /// <inheritdoc/>
    public async Task<string?> GetTextAsync(IImage image)
    {
        TaskCompletionSource<string?> tcs = new();

        VNRecognizeTextRequest textRequest = new(new((request, error) =>
        {
            StringBuilder stringBuilder = new();

            VNRecognizedTextObservation[] results = request.GetResults<VNRecognizedTextObservation>();
            foreach (VNRecognizedTextObservation result in results)
            {
                VNRecognizedText[] recognizedTexts = result.TopCandidates(1);

                foreach (VNRecognizedText recognizedText in recognizedTexts)
                {
                    stringBuilder.AppendLine(recognizedText.String);
                }
            }

            tcs.TrySetResult(stringBuilder.ToString());
        }))
        {
            RecognitionLevel = VNRequestTextRecognitionLevel.Accurate,
            RecognitionLanguages = ["ja-JP"],
        };

        if (image.AsUIImage().CGImage is CGImage cgImage)
        {
            VNImageRequestHandler requestHandler = new(cgImage, new VNImageOptions());
            requestHandler.Perform([textRequest], out NSError _);
            return await tcs.Task;
        }
        return null;
    }
}

VNRecognizeTextRequestのサポートしている言語はiOSのバージョンによって異なります。iOS16以上の場合、サポートしている言語は以下で確認できます。

string[] supportedLanguages = VNRecognizeTextRequest.GetSupportedRecognitionLanguages(VNRequestTextRecognitionLevel.Accurate, VNRecognizeTextRequestRevision.Three, out NSError error);

// サポートしている言語
// en-US,fr-FR,it-IT,de-DE,es-ES
// pt-BR,zh-Hans,zh-Hant,yue-Hans,yue-Hant
// ko-KR,ja-JP,ru-RU,uk-UA,th-TH
// vi-VT,ar-SA,ars-SA

手順4:DIを登録します

作成したDIを以下のように登録していきます。

using SampleApp.DI;

#if ANDROID

using SampleApp.Platforms.Android.DI;

#elif IOS

using SampleApp.Platforms.iOS.DI;

#endif

// 名前空間はプロジェクトに合わせてください。
namespace SampleApp;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        MauiAppBuilder builder = MauiApp.CreateBuilder();
        builder.UseMauiApp<App>();

        builder.Services.AddSingleton<IOcrServiceDI, OcrServiceDI>();

        return builder.Build();
    }
}

手順5:実装します

実装例は以下のようになります。実装例ではメディアピッカーで選択された画像データから文字列を取得します。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Class="SampleApp.MainPage">
    <ScrollView>
        <VerticalStackLayout Padding="10" Spacing="20">
            <Image
                x:Name="img"
                HeightRequest="300"
                Aspect="AspectFit" />
            <Label x:Name="lblText" />
            <Button
                Clicked="Button_Clicked"
                Text="開始" />
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>
using Microsoft.Maui.Graphics.Platform;
using SampleApp.DI;
using IImage = Microsoft.Maui.Graphics.IImage;

// 名前空間はプロジェクトに合わせてください。
namespace SampleApp;

public partial class MainPage : ContentPage
{
    private IOcrServiceDI _ocrServiceDI;

    public MainPage(IOcrServiceDI ocrServiceDI)
    {
        InitializeComponent();
        _ocrServiceDI = ocrServiceDI;
    }

    private async void Button_Clicked(object sender, EventArgs e)
    {
        FileResult? fileResult = await MediaPicker.Default.PickPhotoAsync();
        if (fileResult is null) return;
        IImage image;
        using (Stream stream = await fileResult.OpenReadAsync())
        {
            image = PlatformImage.FromStream(stream);
        }
        img.Source = fileResult.FullPath;
        lblText.Text = await _ocrServiceDI.GetTextAsync(image);
    }
}

動作イメージ

動作イメージは以下のようになります。

Androidの動作イメージ
Androidの場合
iOSの動作イメージ
iOSの場合

終わりに

OCRの実装は有料だと思っていましたが、無料で実装できましたので紹介してみました。良ければ参考にしてみてください。