AIをARに応用するぞ会 → (第4回) ~スマホでAI作動3~
こんにちは。
開発メンバーのYです。
最近は急な暑さにやられてダウンしてました。
さて前回はtffileをリードするところまではいきましたが、
エラーが解消しきれなかったので引き続きやっていきます。
以下、目次です。
1. 前回詰まったところ
前回、Android上でMappedByteBuffer 形式で学習モデルを読み込むことに成功しました。
リードには成功していそうでしたが以下のエラーが発生
E/libc: Access denied finding property "ro.hardware.chipname" A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 12027 (pplication_test), pid 12027 (pplication_test)
今回は↑のエラーを解決していきます。
↓前回のブログ
tottorisnow33.hatenablog.com
2. 読み込みエラーの解消
こちらはすぐに解決しました。
お恥ずかしい話ですが、画像サイズおよび入力のチャネル数が違ったことが原因だったようです、、、
基礎を疎かにしてはいけませんね、、、
3. ダミー画像での推論
モデルの読み込みエラーがなくなったらこっちのものなので、
ラーメンとパンケーキの画像を学習したモデルを使用してダミー画像で推論を行ってみました。
以下、推論の様子です。
Androidで学習モデルを読み込んで推論するアプリを作成中です!
— tsukuruiroiropython33 (@tsukuruiroirop1) 2021年6月12日
ダミー画像を使用してモデルの読み込みから推論まではいけました! pic.twitter.com/MZ7Z2XiQl5
どうやらちゃんと動いていそうです。
キャストしているので、パンケーキが0%で表示されていますが、実際は小数点以下で結果が出力されてます。
とりあえず推論実施が目的だったので、この辺は次回整理していく予定です。
今回作成したコードです。
メソッドMappedByteBuffer loadModelは以下をこちらを参考させてもらいました。
TenorFlow Lite 入門 / Androidによる画像分類|npaka|note
package com.example.myapplication_test; import androidx.appcompat.app.AppCompatActivity; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.media.MediaPlayer; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.ImageView; import android.graphics.Bitmap; import org.tensorflow.lite.Interpreter; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import java.io.FileInputStream; import java.io.IOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; public class MainActivity extends AppCompatActivity { private TextView textView; private boolean buttonTap = false; final int CLASS_NUM = 2; final int IMG_WIDTH = 224; final int IMG_HEIGHT = 224; MediaPlayer mp = null; private MappedByteBuffer loadModel(Context context, String modelPath) { try { AssetFileDescriptor fd = context.getAssets().openFd(modelPath); FileInputStream in = new FileInputStream(fd.getFileDescriptor()); FileChannel fileChannel = in.getChannel(); return fileChannel.map(FileChannel.MapMode.READ_ONLY, fd.getStartOffset(), fd.getDeclaredLength()); } catch (Exception e) { e.printStackTrace(); return null; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ボタンを設定 Button button1 = findViewById(R.id.button); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { /* ここがメイン関数 */ /*モデル取得*/ // String ModelPath = "mobilenet_v1_1.0_224_quant.tflite"; String ModelPath = "model_img_recog_ramen_pan_bn.tflite"; Context context = MainActivity.this; MappedByteBuffer myModel = loadModel(context, ModelPath); try (Interpreter interpreter = new Interpreter(myModel)){ /*****************************************/ /*********** 画像の取得 *******************/ /*****************************************/ /*********** 今回はダミー画像を入力 *******************/ /*画像を配列に格納(128*128*3)*/ byte[][][][] byteTestImg = new byte[1][IMG_HEIGHT][IMG_WIDTH][3]; for (int i = 0; i < IMG_WIDTH; i++) { for (int j = 0; j < IMG_HEIGHT; j++) { byteTestImg[0][i][j][0] = (byte) 255; // R byteTestImg[0][i][j][1] = (byte) 128; // G byteTestImg[0][i][j][2] = (byte) 0; // B } } /*****************************************/ /*********** 画像の表示 *******************/ /*****************************************/ ImageView inferenceImageView = (ImageView) findViewById (R.id.image_view_01); /* pixelごとのRGBに値を格納 */ int[] pixels = new int [IMG_WIDTH * IMG_HEIGHT]; for (int i = 0; i < IMG_HEIGHT; i++) { for (int j = 0; j < IMG_WIDTH; j++) { int c = j + i * IMG_WIDTH; int red = (int)byteTestImg[0][i][j][0]; int green = (int)byteTestImg[0][i][j][1]; int blue = (int)byteTestImg[0][i][j][2];; pixels [c] = Color.argb (255, red, green, blue); } } /* bitmap配列にrgb値を格納 */ Bitmap bitmapImage = Bitmap.createBitmap (IMG_WIDTH, IMG_HEIGHT, Bitmap.Config.ARGB_8888); bitmapImage.setPixels (pixels, 0, IMG_WIDTH, 0, 0, IMG_WIDTH, IMG_HEIGHT); /* 画像の表示 */ inferenceImageView.setImageBitmap (bitmapImage); /*****************************************/ /*********** 推論の実施 *******************/ /*****************************************/ /* [0,255]の画像を[0.0, 1.0]に正規化 */ float[][][][] testImg = new float[1][IMG_HEIGHT][IMG_WIDTH][3]; for (int i = 0; i < IMG_WIDTH; i++) { for (int j = 0; j < IMG_HEIGHT; j++) { for (int ch = 0; ch < 3; ch++) { // testImg[0][i][j][ch] = (float) 0.1; testImg[0][i][j][ch] = (float)byteTestImg[0][i][j][ch] / 255.0f; } } } /*推論実施*/ float[][] output = new float[1][CLASS_NUM]; interpreter.run(testImg, output); /*****************************************/ /*********** 結果の出力 *******************/ /*****************************************/ /* 結果の描画 */ String cate1 = "PanCake: " + String.valueOf((int)(output[0][0]*100)) + "%"; String cate2 = "Ramen: " + String.valueOf((int)(output[0][1]*100)) + "%"; ((TextView) findViewById(R.id.category1)).setText(cate1); ((TextView) findViewById(R.id.category2)).setText(cate2); } catch (IllegalArgumentException e){ System.out.println("IllegalArgumentException!!!!!!!!!!!!!!!!!!!!!!!"); System.out.println(e); } } }); } }
4. まとめ
今回は、ダミー画像を用いて推論実施までいけました。
次回は、実際に写真を使って推論させてみようと思います。
それではまた次回。