![たったの20分で誰でも画像分類の実装ができる方法をわかりやすく解説](https://i.ytimg.com/vi/VMH05DbIzvg/hqdefault.jpg)
コンテンツ
- デバイス上またはクラウド内で?
- MLキットを使用したテキスト認識アプリの作成
- Googleの事前学習済みの機械学習モデルをダウンロードする
- レイアウトの構築
- アクションバーのアイコン:ギャラリーアプリの起動
- 許可リクエストとクリックイベントの処理
- createTempFileを使用した画像のサイズ変更
- 画像をImageViewに設定します
- テキストを認識するアプリを教える
- プロジェクトのテスト
- まとめ
また、テキスト認識APIを翻訳アプリの基礎として使用したり、ユーザーが苦労しているテキストにカメラを向けて読み上げさせるアクセシビリティサービスを使用することもできます。
このチュートリアルでは、ユーザーのギャラリー内の任意の画像からテキストを抽出できるアプリを作成することにより、幅広い革新的な機能の基礎を築きます。このチュートリアルでは取り上げませんが、このアプリケーションをデバイスのカメラに接続することで、ユーザーの周囲からリアルタイムでテキストをキャプチャすることもできます。
デバイス上またはクラウド内で?
MLキットAPIの一部はデバイス上でのみ使用できますが、テキスト認識APIなど、一部はデバイス上およびクラウドで使用できます。
クラウドベースのText APIは、より広い範囲の言語と文字を識別でき、デバイス上の対応するものよりも高い精度を約束します。しかし、それ する アクティブなインターネット接続が必要であり、Blazeレベルのプロジェクトでのみ使用できます。
この記事では、テキスト認識APIをローカルで実行するため、Blazeにアップグレードしたか、無料のFirebase Sparkプランを使用しているかに関係なくフォローできます。
MLキットを使用したテキスト認識アプリの作成
選択した設定でアプリケーションを作成しますが、プロンプトが表示されたら「空のアクティビティ」テンプレートを選択します。
ML Kit SDKはFirebaseの一部であるため、SHA-1署名証明書を使用して、プロジェクトをFirebaseに接続する必要があります。プロジェクトのSHA-1を取得するには:
- Android Studioの[Gradle]タブを選択します。
- 「Gradle projects」パネルで、プロジェクトの「root」をダブルクリックして展開し、「Tasks> Android> Signing Report」を選択します。
- Android Studioウィンドウの下部にあるパネルが更新され、SHA-1署名証明書など、このプロジェクトに関する情報が表示されます。
プロジェクトをFirebaseに接続するには:
- Webブラウザーで、Firebase Consoleを起動します。
- 「プロジェクトを追加」を選択します。
- プロジェクトに名前を付けます。 「MLテスト」を使用しています。
- 利用規約を読み、続行してよければ「同意します...」を選択してから「プロジェクトを作成」を選択します。
- 「FirebaseをAndroidアプリに追加」を選択します。
- プロジェクトのパッケージ名を入力します。これは、MainActivityファイルの上部とマニフェスト内にあります。
- プロジェクトのSHA-1署名証明書を入力します。
- [アプリの登録]をクリックします。
- [Download google-services.json]を選択します。このファイルには、APIキーを含む、プロジェクトに必要なすべてのFirebaseメタデータが含まれています。
- Android Studioで、google-services.jsonファイルをプロジェクトの「app」ディレクトリにドラッグアンドドロップします。
- プロジェクトレベルのbuild.gradleファイルを開き、Googleサービスのクラスパスを追加します。
classpath com.google.gms:google-services:4.0.1
- アプリレベルのbuild.gradleファイルを開き、Firebase Core、Firebase ML Vision、モデルインタープリター、およびGoogleサービスプラグインの依存関係を追加します。
プラグインの適用:com.google.gms.google-services ... ... ...依存関係{implementation fileTree(dir:libs、include:)implementation com.google.firebase:firebase-core:16.0.1 implementation com。 google.firebase:firebase-ml-vision:16.0.0実装com.google.firebase:firebase-ml-model-interpreter:16.0.0
この時点で、Firebaseサーバーに接続できるようにプロジェクトを実行する必要があります。
- 物理的なAndroidスマートフォンまたはタブレット、またはAndroid Virtual Device(AVD)にアプリをインストールします。
- Firebase Consoleで、[アプリを実行してインストールを確認する]を選択します。
- しばらくすると、「おめでとうございます」と表示されます。 [コンソールに進む]を選択します。
Googleの事前学習済みの機械学習モデルをダウンロードする
デフォルトでは、ML Kitは必要なときにのみモデルをダウンロードするため、ユーザーが初めてテキストを抽出しようとすると、アプリはOCRモデルをダウンロードします。
これは、ユーザーエクスペリエンスに悪影響を及ぼす可能性があります。機能にアクセスしようとすると、アプリが実際にこの機能を提供する前に、より多くのリソースをダウンロードする必要があることがわかります。最悪のシナリオでは、たとえばデバイスがインターネットに接続していない場合など、必要なときにアプリが必要なリソースをダウンロードできないこともあります。
これがアプリで発生しないように、インストール時に必要なOCRモデルをダウンロードします。これには、マニエストにいくつかの変更が必要です。
マニフェストを開いたまま、WRITE_EXTERNAL_STORAGE権限も追加します。これは、このチュートリアルで後ほど使用します。
レイアウトの構築
簡単なものを邪魔にならないようにして、次の要素で構成されるレイアウトを作成しましょう。
- ImageView。最初はプレースホルダーが表示されますが、ユーザーがギャラリーから画像を選択すると更新されます。
- テキスト抽出をトリガーするボタン。
- TextView。抽出されたテキストを表示します。
- ScrollView。抽出されたテキストが画面にきちんと収まるという保証はないため、TextViewをScrollView内に配置します。
完成したactivity_main.xmlファイルは次のとおりです。
このレイアウトは「ic_placeholder」ドロウアブルを参照するので、今すぐ作成してみましょう。
- Android Studioツールバーから[ファイル]> [新規]> [画像アセット]を選択します。
- [アイコンの種類]プルダウンを開き、[アクションバーとタブアイコン]を選択します。
- [クリップアート]ラジオボタンが選択されていることを確認します。
- [クリップアート]ボタンをクリックします。
- プレースホルダーとして使用する画像を選択します。 「写真に追加」を使用しています。
- [OK]をクリックします。
- 「テーマ」ドロップダウンを開き、「HOLO_LIGHT」を選択します。
- [名前]フィールドに「ic_placeholder」と入力します。
- [次へ]をクリックします。情報を読み、続行してよければ[完了]をクリックします。
アクションバーのアイコン:ギャラリーアプリの起動
次に、ユーザーのギャラリーを起動するアクションバーアイテムを作成し、ユーザーが画像を選択できるようにします。
「res / menu」ディレクトリ内にあるメニューリソースファイル内にアクションバーアイコンを定義します。プロジェクトにこのディレクトリが含まれていない場合は、作成する必要があります。
- Controlキーを押しながらプロジェクトの「res」ディレクトリをクリックし、「新規」>「Androidリソースディレクトリ」を選択します。
- 「リソースタイプ」ドロップダウンを開き、「メニュー」を選択します。
- 「ディレクトリ名」は「メニュー」に自動的に更新されますが、更新されない場合は手動で名前を変更する必要があります。
- [OK]をクリックします。
これで、メニューリソースファイルを作成する準備ができました。
- Controlキーを押しながらプロジェクトの「メニュー」ディレクトリをクリックし、「新規」>「メニューリソースファイル」を選択します。
- このファイルに「my_menu」という名前を付けます。
- [OK]をクリックします。
- 「my_menu.xml」ファイルを開き、次を追加します。
メニューファイルは「action_gallery」文字列を参照するため、プロジェクトのres / values / strings.xmlファイルを開き、このリソースを作成します。ここにいる間、このプロジェクト全体で使用する他の文字列も定義しています。
次に、Image Asset Studioを使用して、アクションバーの「ic_gallery」アイコンを作成します。
- [ファイル]> [新規]> [画像アセット]を選択します。
- [アイコンの種類]ドロップダウンを[アクションバーとタブアイコン]に設定します。
- [クリップアート]ボタンをクリックします。
- ドロアブルを選択してください。私は「画像」を使用しています。
- 「OK」をクリックします。
- このアイコンがアクションバーにはっきりと表示されるようにするには、[テーマ]ドロップダウンを開いて[HOLO_DARK]を選択します。
- このアイコンに「ic_gallery」という名前を付けます。
- 「「次へ」をクリックして、「完了」をクリックします。
許可リクエストとクリックイベントの処理
メニューのインスタンス化、アクションバーのクリックイベントの処理、デバイスのストレージへのアクセスのリクエストなど、個別のBaseActivityクラスでテキスト認識APIに直接関連しないすべてのタスクを実行します。
- Android Studioのツールバーから[ファイル]> [新規]> [Javaクラス]を選択します。
- このクラスに「BaseActivity」という名前を付けます。
- 「OK」をクリックします。
- BaseActivityを開き、次を追加します。
import android.app.Activity; import android.support.v4.app.ActivityCompat; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.content.DialogInterface; import android.content.Intent; import android.Manifest; import android.provider.MediaStore; import android.view.Menu; import android.view.MenuItem; import android.content.pm.PackageManager; import android.net.Uri; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.io.File;パブリッククラスBaseActivityはAppCompatActivityを拡張します{public static final int WRITE_STORAGE = 100; public static final int SELECT_PHOTO = 102; public static final String ACTION_BAR_TITLE = "action_bar_title";公開ファイルの写真。 @Override protected void onCreate(@Nullable Bundle savedInstanceState){super.onCreate(savedInstanceState); ActionBar actionBar = getSupportActionBar(); if(actionBar!= null){actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setTitle(getIntent()。getStringExtra(ACTION_BAR_TITLE)); }} @Override public boolean onCreateOptionsMenu(Menu menu){getMenuInflater()。inflate(R.menu.my_menu、menu); trueを返します。 } @Override public boolean onOptionsItemSelected(MenuItem item){switch(item.getItemId()){//「gallery_action」が選択されている場合、... // case R.id.gallery_action:// ...確認してくださいWRITE_STORAGEパーミッション// checkPermission(WRITE_STORAGE);ブレーク; } return super.onOptionsItemSelected(item); } @Override public void onRequestPermissionsResult(int requestCode、@NonNull String permissions、@NonNull int grantResults){super.onRequestPermissionsResult(requestCode、permissions、grantResults); switch(requestCode){case WRITE_STORAGE://許可リクエストが許可された場合、... // if(grantResults.length> 0 && grantResults == PackageManager.PERMISSION_GRANTED){//...selectPicture// selectPicture( ); //許可リクエストが拒否された場合、... //} else {//...「permission_request」文字列を表示// requestPermission(this、requestCode、R.string.permission_request); } break; }} //許可要求ダイアログを表示する// public static void requestPermission(final Activity activity、final int requestCode、int msg){AlertDialog.Builder alert = new AlertDialog.Builder(activity); alert.set(msg); alert.setPositiveButton(android.R.string.ok、new DialogInterface.OnClickListener(){@Override public void onClick(DialogInterface dialogInterface、int i){dialogInterface.dismiss(); Intent permissonIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); permissonIntent .setData(Uri.parse( "package:" + activity.getPackageName())); activity.startActivityForResult(permissonIntent、requestCode);}}); alert.setNegativeButton(android.R.string.cancel、new DialogInterface.OnClickListener(){@Override public void onClick(DialogInterface dialogInterface、int i){dialogInterface.dismiss();}}); alert.setCancelable(false); alert.show(); } //ユーザーがWRITE_STORAGEパーミッションを付与したかどうかを確認します// public void checkPermission(int requestCode){switch(requestCode){case WRITE_STORAGE:int hasWriteExternalStoragePermission = ActivityCompat.checkSelfPermission(this、Manifest.permission.WRITE_EXTERNAL_STORAGE); //外部ストレージにアクセスできる場合... // if(hasWriteExternalStoragePermission == PackageManager.PERMISSION_GRANTED){//...selectPictureを呼び出します。ユーザーが画像を選択できるアクティビティを起動します// selectPicture(); //権限が付与されていない場合は、... //} else {// ...権限をリクエスト// ActivityCompat.requestPermissions(this、new String {Manifest.permission.WRITE_EXTERNAL_STORAGE}、requestCode); } break; }} private void selectPicture(){photo = MyHelper.createTempFile(photo); Intent intent = new Intent(Intent.ACTION_PICK、MediaStore.Images.Media.EXTERNAL_CONTENT_URI); //ユーザーが画像を選択できるアクティビティを開始// startActivityForResult(intent、SELECT_PHOTO); }}
この時点で、プロジェクトはMyHelper.createTempFileを解決できないと不平を言っているはずです。これを今すぐ実装しましょう!
createTempFileを使用した画像のサイズ変更
新しい「MyHelper」クラスを作成します。このクラスでは、ユーザーが選択した画像のサイズを変更し、Text Recognition APIで処理できるようにします。
import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.content.Context; import android.database.Cursor; import android.os.Environment; import android.widget.ImageView; import android.provider.MediaStore; import android.net.Uri; import static android.graphics.BitmapFactory.decodeFile; import static android.graphics.BitmapFactory.decodeStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class MyHelper {public static String getPath(Context context、Uri uri){String path = "";文字列投影= {MediaStore.Images.Media.DATA};カーソルcursor = context.getContentResolver()。query(uri、projection、null、null、null); int column_index; if(cursor!= null){column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); path = cursor.getString(column_index); cursor.close(); } 復路; } public static File createTempFile(File file){File directory = new File(Environment.getExternalStorageDirectory()。getPath()+ "/com.jessicathornsby.myapplication"); if(!directory.exists()||!directory.isDirectory()){directory.mkdirs(); } if(file == null){file = new File(directory、 "orig.jpg"); }戻りファイル。 } public static Bitmap resizePhoto(ファイルimageFile、コンテキストコンテキスト、Uri uri、ImageViewビュー){BitmapFactory.Options newOptions = new BitmapFactory.Options(); try {decodeStream(context.getContentResolver()。openInputStream(uri)、null、newOptions); int photoHeight = newOptions.outHeight; int photoWidth = newOptions.outWidth; newOptions.inSampleSize = Math.min(photoWidth / view.getWidth()、photoHeight / view.getHeight()); return compressPhoto(imageFile、BitmapFactory.decodeStream(context.getContentResolver()。openInputStream(uri)、null、newOptions)); } catch(FileNotFoundException例外){exception.printStackTrace(); nullを返します。 }} public static Bitmap resizePhoto(File imageFile、String path、ImageView view){BitmapFactory.Options options = new BitmapFactory.Options(); decodeFile(path、options); int photoHeight = options.outHeight; int photoWidth = options.outWidth; options.inSampleSize = Math.min(photoWidth / view.getWidth()、photoHeight / view.getHeight()); return compressPhoto(imageFile、BitmapFactory.decodeFile(path、options)); } private static Bitmap compressPhoto(File photoFile、Bitmap bitmap){try {FileOutputStream fOutput = new FileOutputStream(photoFile); bitmap.compress(Bitmap.CompressFormat.JPEG、70、fOutput); fOutput.close(); } catch(IOException例外){exception.printStackTrace(); }ビットマップを返します。 }}
画像をImageViewに設定します
次に、MainActivityクラスにonActivityResult()を実装し、ユーザーが選択した画像をImageViewに設定する必要があります。
import android.graphics.Bitmap; import android.os.Bundle; import android.widget.ImageView; import android.content.Intent; import android.widget.TextView; import android.net.Uri;パブリッククラスMainActivityはBaseActivityを拡張します{private Bitmap myBitmap;プライベートImageView myImageView; private TextView myTextView; @Override protected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myTextView = findViewById(R.id.textView); myImageView = findViewById(R.id.imageView); } @Override protected void onActivityResult(int requestCode、int resultCode、Intent data){super.onActivityResult(requestCode、resultCode、data); if(resultCode == RESULT_OK){switch(requestCode){case WRITE_STORAGE:checkPermission(requestCode);ブレーク; case SELECT_PHOTO:Uri dataUri = data.getData();文字列パス= MyHelper.getPath(this、dataUri); if(path == null){myBitmap = MyHelper.resizePhoto(photo、this、dataUri、myImageView); } else {myBitmap = MyHelper.resizePhoto(photo、path、myImageView); } if(myBitmap!= null){myTextView.setText(null); myImageView.setImageBitmap(myBitmap); } break; }}}}
物理的なAndroidデバイスまたはAVDでこのプロジェクトを実行し、アクションバーアイコンをクリックします。プロンプトが表示されたら、WRITE_STORAGE権限を付与し、ギャラリーから画像を選択します。これで、この画像がアプリのUIに表示されます。
これで基礎ができました。テキストの抽出を開始する準備ができました。
テキストを認識するアプリを教える
クリックイベントに応答してテキスト認識をトリガーするため、OnClickListenerを実装する必要があります。
import android.graphics.Bitmap; import android.os.Bundle; import android.widget.ImageView; import android.content.Intent; import android.widget.TextView; import android.view.View; import android.net.Uri;パブリッククラスMainActivityはBaseActivityを拡張し、View.OnClickListenerを実装します{private Bitmap myBitmap;プライベートImageView myImageView; private TextView myTextView; @Override protected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myTextView = findViewById(R.id.textView); myImageView = findViewById(R.id.imageView); findViewById(R.id.checkText).setOnClickListener(this); } @Override public void onClick(View view){switch(view.getId()){case R.id.checkText:if(myBitmap!= null){//次のステップでrunTextRecogを実装する// runTextRecog (); } break; }}
MLキットは、FirebaseVisionImage形式の画像のみを処理できるため、画像をFirebaseVisionImageオブジェクトに変換する必要があります。 FirebaseVisionImageは、Bitmap、media.Image、ByteBuffer、またはバイト配列から作成できます。ビットマップを使用しているため、FirebaseVisionImageクラスのfromBitmap()ユーティリティメソッドを呼び出して、ビットマップを渡す必要があります。
private void runTextRecog(){FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(myBitmap);
MLキットには、画像認識操作ごとに異なる検出器クラスがあります。テキストの場合、FirebaseVisionTextDetectorクラスを使用する必要があります。このクラスは、画像に対して光学文字認識(OCR)を実行します。
getVisionTextDetectorを使用して、FirebaseVisionTextDetectorのインスタンスを作成します。
FirebaseVisionTextDetectorディテクター= FirebaseVision.getInstance()。getVisionTextDetector();
次に、detectInImage()メソッドを呼び出してFirebaseVisionImageオブジェクトを渡すことにより、FirebaseVisionImageのテキストを確認する必要があります。また、結果が利用可能になるたびにアプリに通知されるように、onSuccessコールバックとonFailureコールバック、さらに対応するリスナーを実装する必要があります。
detector.detectInImage(image).addOnSuccessListener(new OnSuccessListener この操作が失敗した場合、トーストを表示しますが、操作が成功した場合は、応答とともにprocessExtractedTextを呼び出します。 この時点で、テキスト検出コードは次のようになります。 // FirebaseVisionImage //を作成しますprivate void runTextRecog(){FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(myBitmap); // FirebaseVisionCloudTextDetector // FirebaseVisionTextDetector Detectorのインスタンスを作成します= FirebaseVision.getInstance()。getVisionTextDetector(); // OnSuccessListener // Detector.detectInImage(image).addOnSuccessListener(new OnSuccessListenerを登録します アプリがonSuccess通知を受信するたびに、結果を解析する必要があります。 FirebaseVisionTextオブジェクトには、要素、行、ブロックを含めることができます。通常、各ブロックはテキストの単一の段落に相当します。 FirebaseVisionTextが0ブロックを返す場合、「no_text」文字列を表示しますが、1つ以上のブロックが含まれている場合、取得したテキストをTextViewの一部として表示します。 private void processExtractedText(FirebaseVisionText firebaseVisionText){myTextView.setText(null); if(firebaseVisionText.getBlocks()。size()== 0){myTextView.setText(R.string.no_text);戻り; } for(FirebaseVisionText.Block block:firebaseVisionText.getBlocks()){myTextView.append(block.getText()); }}} 完成したMainActivityコードは次のとおりです。 import android.graphics.Bitmap; import android.os.Bundle; import android.widget.ImageView; import android.content.Intent; import android.widget.TextView; import android.widget.Toast; import android.view.View; import android.net.Uri; import android.support.annotation.NonNull; import com.google.firebase.ml.vision.common.FirebaseVisionImage; import com.google.firebase.ml.vision.text.FirebaseVisionText; import com.google.firebase.ml.vision.text.FirebaseVisionTextDetector; import com.google.firebase.ml.vision.FirebaseVision; import com.google.android.gms.tasks.OnSuccessListener; import com.google.android.gms.tasks.OnFailureListener;パブリッククラスMainActivityはBaseActivityを拡張し、View.OnClickListenerを実装します{private Bitmap myBitmap;プライベートImageView myImageView; private TextView myTextView; @Override protected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myTextView = findViewById(R.id.textView); myImageView = findViewById(R.id.imageView); findViewById(R.id.checkText).setOnClickListener(this); } @override public void onClick(View view){switch(view.getId()){case R.id.checkText:if(myBitmap!= null){runTextRecog(); } break; }} @Override protected void onActivityResult(int requestCode、int resultCode、Intent data){super.onActivityResult(requestCode、resultCode、data); if(resultCode == RESULT_OK){switch(requestCode){case WRITE_STORAGE:checkPermission(requestCode);ブレーク; case SELECT_PHOTO:Uri dataUri = data.getData();文字列パス= MyHelper.getPath(this、dataUri); if(path == null){myBitmap = MyHelper.resizePhoto(photo、this、dataUri、myImageView); } else {myBitmap = MyHelper.resizePhoto(photo、path、myImageView); } if(myBitmap!= null){myTextView.setText(null); myImageView.setImageBitmap(myBitmap); } break; }}} private void runTextRecog(){FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(myBitmap); FirebaseVisionTextDetectorディテクター= FirebaseVision.getInstance()。getVisionTextDetector(); detector.detectInImage(image).addOnSuccessListener(new OnSuccessListener MLキットのテキスト認識の動作を確認しましょう!このプロジェクトをAndroidデバイスまたはAVDにインストールし、ギャラリーから画像を選択して、「テキストを確認」ボタンをタップします。アプリは、画像からすべてのテキストを抽出し、それをTextViewに表示して応答する必要があります。 画像のサイズとそれに含まれるテキストの量によっては、抽出されたテキストをすべて表示するためにスクロールする必要がある場合があります。 GitHubから完成したプロジェクトをダウンロードすることもできます。 これで、MLキットを使用して、画像からテキストを検出および抽出する方法がわかりました。 テキスト認識APIは、MLキットの一部にすぎません。このSDKは、バーコードスキャン、顔検出、画像ラベリング、ランドマーク認識も提供し、Smart Replyや高密度の顔輪郭APIなど、一般的なモバイルユースケース向けのAPIを追加する予定です。 どのMLキットAPIを試してみたいですか?以下のコメントでお知らせください!プロジェクトのテスト
まとめ