Androidアプリ作成時、フラグメントにビューをできるだけコード上から配置する方法
こんにちは。ファイアーエムブレム風花雪月やりました。
あんな地獄を見せるくらいなら学園ものにするな。
さて、タイトルのようにandroidアプリ作成時にできるだけコードからビューを配置します。
0. 前提
Android Studioでアプリ開発するとき、アプリ画面はActivity⇒Fragment⇒Viewという順番に配置されます。
Activityが一番土台側で、Viewが実際に配置されるボタンとか写真とかそんなのですね。
Viewを宣言やら配置するのをxmlでやらずに、javaのコード上でしたいなと思ったんですが意外と手間取りました。
ActivityクラスにはViewを配置するメソッドがあるんですが、Fragmentにはそれらしいメソッドが見つからなかったんですね。
ちなみにBottom Navigation Activityというテンプレートを使っています。
0. 解決方法
上記の解決策が下記です。
①Fragmentのxmlに最低限のビューを配置(背景画像とスクロールビュー)
②FragmentのonCreatedメソッドでスクロールビュー上にボタンやらのViewを配置
どうということはなく、「スクロールビュー」という自分自身にViewを配置できるViewをxmlで置いといただけです。
以下コード。変数名が適当なのは許してください。
●Fragmentのxml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.home.HomeFragment"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" android:src="@drawable/bg" android:id="@+id/backGroundViewHome"/> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/scrollViewHome"> </ScrollView> </androidx.constraintlayout.widget.ConstraintLayout>
●Fragmentのクラス
package com.makty.bingogosyuin.ui.home; import android.content.Context; import android.graphics.Color; import android.os.Bundle; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; import android.widget.ScrollView; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import com.makty.bingogosyuin.R; import com.makty.bingogosyuin.databinding.FragmentHomeBinding; public class HomeFragment extends Fragment { private FragmentHomeBinding binding; private Context m_context = null; /*コンテキスト*/ private @ColorInt int mBackgroundColor = Color.TRANSPARENT; /**画面上のビュー**/ ScrollView m_scrollView = null; Button mButton0_0 = null; Button mButton0_1 = null; public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { HomeViewModel homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class); binding = FragmentHomeBinding.inflate(inflater, container, false); View root = binding.getRoot(); m_context = this.getContext(); /*バックグラウンドのスクロールビュー取得*/ m_scrollView = (ScrollView) root.findViewById(R.id.scrollViewHome); /*ボタン設置レイアウト作成*/ LinearLayout layout = new LinearLayout(m_context); layout.setOrientation(LinearLayout.HORIZONTAL); layout.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); layout.setGravity(Gravity.CENTER); /*ボタン作成*/ mButton0_0 = new Button(m_context); mButton0_0.setBackgroundColor(0xFFee7800); mButton0_0.setText("鹿島神社\n(茨城県)"); layout.addView(mButton0_0); mButton0_1 = new Button(m_context); mButton0_1.setBackgroundColor(0xFFee7800); mButton0_1.setGravity(Gravity.CENTER); mButton0_1.setText("鹿島神社\n(茨城県)"); layout.addView(mButton0_1); /*スクロールビューにレイアウトを追加*/ m_scrollView.addView(layout); return root; } @Override public void onDestroyView() { super.onDestroyView(); binding = null; } }
今回できた画面はこんな感じ。
めでたい。
言語の仕様や使い方をゆっくり自分のものにしていくときが一番心が落ち着きますね。
よいお年を。
KaiNDを作っていくぞ会 第5回 --MobileNetV2をGrad-CAMで可視化--
こんにちは。
開発メンバー、AI担当のKです。
最近、毎日簡易アエロバイクをこいでいるおかげか、6 [kg] 減量しました。
この調子でスマートになります。
目次
0. 前回まで
↓前回のブログ
https://tottorisnow33.hatenablog.com/entry/2022/07/23/094350
引き続き、KaiNDシリーズを拡張・改善しています。一緒にやっているメンバーで以下のように分担しています。
松原: KaiND Web
K : AI自体の改善
Y :スマホアプリのUIの改善
今回は、KaiNDを作っていくぞ会 第2回 --AI改善-- - 茨城エンジニアのPython開発日記 の続きで、可視化にチャレンジです。
1. 使用するNetwork
前回は可視化のためにResNetを使っていましたが、今回はKaiNDのアプリに載せているMobileNetV2で可視化します。
MobileNetV2は事前学習済みのものを用意して、転移学習でパグとブルドッグの識別をできるようにします。
MobileNetV2の転移学習については、以下を参考にしました。
www.tensorflow.org
2. Grad-CAMで可視化するために注意したこと
Grad-CAMで可視化する場合、Conv layerの出力が必要です。
値を取得するために、Conv layerの名前を指定する必要があります。
しかし、Tensorflow公式のやり方のように tf.keras.applications.MobileNetV2() の後に自分でlayerを追加すると、Conv layerを指定できません。
model.summary()で見ると分かるのですが、MobileNetV2のところが、"mobilenetv2_1.00_224"という1つの塊になってしまいます。
# Tensorflowの公式チュートリアルを参考にしたソース IMAGE_SIZE = (224, 224) IMG_SHAPE = IMAGE_SIZE + (3,) base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet') base_model.trainable = False inputs = tf.keras.Input(shape=IMG_SHAPE) x = base_model(inputs, training=False) # 層追加 x = tf.keras.layers.GlobalAveragePooling2D()(x) x = tf.keras.layers.Dropout(0.2)(x) outputs = tf.keras.layers.Dense(y_train.shape[-1], kernel_regularizer=tf.keras.regularizers.l2(0.0001), activation="softmax")(x) model = tf.keras.Model(inputs, outputs) model.summary()
Model: "model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_2 (InputLayer) [(None, 224, 224, 3)] 0
_________________________________________________________________
mobilenetv2_1.00_224 (Model) (None, 7, 7, 1280) 2257984
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280) 0
_________________________________________________________________
dropout (Dropout) (None, 1280) 0
_________________________________________________________________
dense (Dense) (None, 3) 3843
=================================================================
Total params: 2,261,827
Trainable params: 3,843
Non-trainable params: 2,257,984
_________________________________________________________________
そこで、ソースを以下のように少し書き換えて、MobileNetV2のconv layerの名前を取得できるようにしました。
ポイントは、base_model.outputを使って、MobileNetV2の出力を追加したい層の入力とすることと、tf.keras.Modelのinputにはbase_model.inputを使うことです。
# アレンジしたソース IMAGE_SIZE = (224, 224) IMG_SHAPE = IMAGE_SIZE + (3,) base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet') base_model.trainable = False x = base_model.output # 層追加 x = tf.keras.layers.GlobalAveragePooling2D()(x) x = tf.keras.layers.Dropout(0.2)(x) outputs = tf.keras.layers.Dense(y_train.shape[-1], kernel_regularizer=tf.keras.regularizers.l2(0.0001), activation="softmax")(x) model = tf.keras.Model(base_model.input, outputs) model.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
============================================================
input_1 (InputLayer) [(None, 224, 224, 3) 0
__________________________________________________________________________________________________
Conv1_pad (ZeroPadding2D) (None, 225, 225, 3) 0 input_1[0][0]
__________________________________________________________________________________________________
Conv1 (Conv2D) (None, 112, 112, 32) 864 Conv1_pad[0][0]
__________________________________________________________________________________________________
bn_Conv1 (BatchNormalization) (None, 112, 112, 32) 128 Conv1[0][0]
__________________________________________________________________________________________________
Conv1_relu (ReLU) (None, 112, 112, 32) 0 bn_Conv1[0][0]
__________________________________________________________________________________________________
expanded_conv_depthwise (Depthw (None, 112, 112, 32) 288 Conv1_relu[0][0]
__________________________________________________________________________________________________
expanded_conv_depthwise_BN (Bat (None, 112, 112, 32) 128 expanded_conv_depthwise[0][0]
__________________________________________________________________________________________________
expanded_conv_depthwise_relu (R (None, 112, 112, 32) 0 expanded_conv_depthwise_BN[0][0]
__________________________________________________________________________________________________
expanded_conv_project (Conv2D) (None, 112, 112, 16) 512 expanded_conv_depthwise_relu[0][0
__________________________________________________________________________________________________
expanded_conv_project_BN (Batch (None, 112, 112, 16) 64 expanded_conv_project[0][0]
__________________________________________________________________________________________________
block_1_expand (Conv2D) (None, 112, 112, 96) 1536 expanded_conv_project_BN[0][0]
__________________________________________________________________________________________________
block_1_expand_BN (BatchNormali (None, 112, 112, 96) 384 block_1_expand[0][0]
__________________________________________________________________________________________________
block_1_expand_relu (ReLU) (None, 112, 112, 96) 0 block_1_expand_BN[0][0]
__________________________________________________________________________________________________
block_1_pad (ZeroPadding2D) (None, 113, 113, 96) 0 block_1_expand_relu[0][0]
__________________________________________________________________________________________________
block_1_depthwise (DepthwiseCon (None, 56, 56, 96) 864 block_1_pad[0][0]
__________________________________________________________________________________________________
block_1_depthwise_BN (BatchNorm (None, 56, 56, 96) 384 block_1_depthwise[0][0]
__________________________________________________________________________________________________
block_1_depthwise_relu (ReLU) (None, 56, 56, 96) 0 block_1_depthwise_BN[0][0]
__________________________________________________________________________________________________
block_1_project (Conv2D) (None, 56, 56, 24) 2304 block_1_depthwise_relu[0][0]
__________________________________________________________________________________________________
block_1_project_BN (BatchNormal (None, 56, 56, 24) 96 block_1_project[0][0]
__________________________________________________________________________________________________
.....[中略]......
block_16_expand (Conv2D) (None, 7, 7, 960) 153600 block_15_add[0][0]
__________________________________________________________________________________________________
block_16_expand_BN (BatchNormal (None, 7, 7, 960) 3840 block_16_expand[0][0]
__________________________________________________________________________________________________
block_16_expand_relu (ReLU) (None, 7, 7, 960) 0 block_16_expand_BN[0][0]
__________________________________________________________________________________________________
block_16_depthwise (DepthwiseCo (None, 7, 7, 960) 8640 block_16_expand_relu[0][0]
__________________________________________________________________________________________________
block_16_depthwise_BN (BatchNor (None, 7, 7, 960) 3840 block_16_depthwise[0][0]
__________________________________________________________________________________________________
block_16_depthwise_relu (ReLU) (None, 7, 7, 960) 0 block_16_depthwise_BN[0][0]
__________________________________________________________________________________________________
block_16_project (Conv2D) (None, 7, 7, 320) 307200 block_16_depthwise_relu[0][0]
__________________________________________________________________________________________________
block_16_project_BN (BatchNorma (None, 7, 7, 320) 1280 block_16_project[0][0]
__________________________________________________________________________________________________
Conv_1 (Conv2D) (None, 7, 7, 1280) 409600 block_16_project_BN[0][0]
__________________________________________________________________________________________________
Conv_1_bn (BatchNormalization) (None, 7, 7, 1280) 5120 Conv_1[0][0]
__________________________________________________________________________________________________
out_relu (ReLU) (None, 7, 7, 1280) 0 Conv_1_bn[0][0]
__________________________________________________________________________________________________
global_average_pooling2d (Globa (None, 1280) 0 out_relu[0][0]
__________________________________________________________________________________________________
dropout (Dropout) (None, 1280) 0 global_average_pooling2d[0][0]
__________________________________________________________________________________________________
dense (Dense) (None, 3) 3843 dropout[0][0]
============================================================
Total params: 2,261,827
Trainable params: 3,843
Non-trainable params: 2,257,984
__________________________________________________________________________________________________
これで、Conv layerの名前を取得できるようになりました。
後は↓の参考ソースのように、Conv layerのLayer Name (とモデルのoutput) から勾配を取る感じです。
Grad CAM implementation with Tensorflow 2 · GitHub
3. MobileNetV2でGrad-CAMをやってみた結果
自作ResNetでやってみた結果は↓です。
【勉強会報告】
— ごまあぶら (@tsukuruiroirop1) July 6, 2022
MobileNetV2から転移学習したものでGrad-camの可視化をしました。
1枚目が入力画像。
2枚目が、前回のうまく学習できていないResNetを可視化。3枚目が、事前学習済みMobileNetV2を転移学習したもの。
2枚目は注目しているところがおかしい。
3枚目はパグの特徴的なところを注目してる。 pic.twitter.com/BT8rttOTKd
【勉強会報告】
— ごまあぶら (@tsukuruiroirop1) July 30, 2022
MobileNetV2から転移学習したものでGrad-camの可視化をブルドッグでも見てみました。
1枚目が入力画像。
2枚目が可視化画像です。
顔や耳、背中あたりを注目しているのがわかります。 pic.twitter.com/tUC6cfhjtm
CSVの列を入れ替えるソフト作ってみた
ブログ地図 - 茨城エンジニアのPython開発日記
CSVの列を入れ替えるソフト作りました。
CSVの列を入れ替えたがってる人たちがいたのでCSVの列を入れ替えるソフトを作りました pic.twitter.com/EIcp6Gfa1k
— ごまあぶら (@tsukuruiroirop1) 2022年7月25日
コードは美しくないけど内製便利化ツールならええだろという気持ち。
import os import csv #inputフォルダのファイル一覧取得 input_dir_path = "./input" files = os.listdir(input_dir_path) #inputフォルダ内のファイルでループ for file_name in files: input_file_path = input_dir_path + "/" + file_name output_file_path = "./output" + "/" + file_name #ファイルを開く with open(input_file_path) as f: #ファイル内を一行ずつループ for line in f: #改行文字消去 line = line.replace("\n", "") #行を,で分割 values = line.split(",") #出力する行を作成(ここで並び替える) output_values = [] output_values.append(values[9]) output_values.append(values[8]) output_values.append(values[7]) output_values.append(values[6]) output_values.append(values[5]) output_values.append(values[4]) output_values.append(values[3]) output_values.append(values[2]) output_values.append(values[1]) output_values.append(values[0]) #出力CSVに書き込み f_out = open(output_file_path, 'a') writer = csv.writer(f_out) writer.writerow(output_values)
特に語ることもないので終わり。
KaiNDを作っていくぞ会 第4回 --API作成--
ブログから記事を見つけたい場合はこちら
こんにちは松原です。
立ちながら人間と話す機会があって疲れ果てました。
ソフトウェアエンジニアは貧弱。
0. 前回まで
KaiNDを作っていくぞ会、以下の3つの柱で進めています。
・Webアプリ作成: 松原
・スマホアプリの改善: Y
・AI自体の改善: K
今のところ技術を学ぶだけで楽しいけど、
そのうちそれなりのシステムとして形にしたいなあ。
1. WebアプリUI
フロントの様子
アップロードボタン押下で画像選択
APIのあるフォルダ以下にアップロードされる
で、あとはAPI側に受け渡された画像をtensorflowで識別していきます。
2. システム構成図
割としょうもないですがシステムの構成図書いておきます。簡単ですね。
3. 今後の展望
API側に画像を渡すことができたので、golangでの識別処理を実装していきます。
最近はずっと自宅サーバ欲しいなあと思っています。
なんなら今のPCをサーバにするから仕方ないということでPC新調したい。
それではまた。
KaiNDを作っていくぞ会 第3回 --UI改善--
ブログから記事を見つけたい場合はこちら
こんにちは。
UI担当のYです。
0. 前回まで
KaiNDを作っていくぞ会、前回はAI改善でした。
KaiNDアプリは、前々回から以下のように担当を振って回しています。
松原: KaiND Web
K : AI自体の改善
Y :スマホアプリのUIの改善
今回は私YがスマホアプリのUI改善について書いていきたいと思います。
1. UI改善ポイント
リリース当初のアプリ画面です。
このままだと拡張性がないので、とりあえずハンバーガーメニューの実装をしていきたいと思います。
機能はあとで考えます。
画像保存したり、カメラロールの画像を識別できる機能とかつけたい。
2. ハンバーガーメニューの実装
一から作ると骨が折れるので、AndroidStudio内で用意されているテンプレートを使用していきます。
新しいプロジェクトを作成しようとすると、以下のようにテンプレートが選べます。
リリース時のアプリを作った時は、余計な機能を入れたくなかったのでEmpty Activity を使用していましたが、今回は Navigation Drawer Activity を使用していきます。
他にも地図作ったり、ログイン画面とかも簡単に作れるっぽい。
こんな感じですぐにハンバーガーメニューの実装ができました。
カメラ映像の取得でエラーが出てしまったので一旦アイコンの画像を貼り付けています。
【勉強会報告】
— ごまあぶら (@tsukuruiroirop1) 2022年6月25日
ナビゲーションウィンドウ実装しました。
あとはカメラ映像を描画するだけですが
カメラAPIの設定で再度手こずってます>< pic.twitter.com/2HzeR1GbjM
デフォルトだと、画面ごとのxmlファイルがあって、それに対応するjavaファイルがあるみたいです。
画面ごとのjavaファイルをいじって機能を実装していく感じですね。
3. 次回
とりあえず、過去ブログを参考にカメラ映像の取得をできるようにします。
そしたらタイトルウィンドウの色変えたりとかいろいろやっていきたいです。
来週はWebアプリ担当松原からの報告になります。
KaiNDを作っていくぞ会 第2回 --AI改善--
こんにちは。
開発メンバー、AI担当のKです。
テレワークが続いているので重度の運動不足です。
ここ最近、簡易的なエアロバイクをGETしたので、毎日使っています。
これで少しでも運動不足を解消したいです。
目次
0. 前回まで
↓前回のブログ
tottorisnow33.hatenablog.com
前回からKaiNDシリーズを拡張・改善しています。一緒にやっているメンバーで以下のように分担しています。
松原: KaiND Web
K : AI自体の改善
Y :スマホアプリのUIの改善
前回は松原くんがWebアプリについてブログに載せてくれました。今回は私KによるAI自体の改善について書いていきます。
1. AIがどこを注目しているか可視化したい
リリースしたアプリ(KaiNDシリーズ -Pug or Bulldog-)は、スマホのカメラでかざしたものがパグなのかブルドッグなのかその他なのかを識別するものです。
play.google.com
これからもっと性能を上げたいと思っています。
性能を上げるためには、学習データセットを増やす、Data Augmentationをもっとかける、もっと良いネットワークを使う(今はMobilnetV2を使用)などがあると思います。
何から試すべきか決めるためにも、まずはAIが画像上のどこに注目して、パグ or ブルドッグと判定しているのかを可視化することにしました。
可視化のために、Grad-CAMという論文の手法を使いました。
https://openaccess.thecvf.com/content_ICCV_2017/papers/Selvaraju_Grad-CAM_Visual_Explanations_ICCV_2017_paper.pdf
Grad-CAMは、"grad"という名前がついているように勾配を使います。
最終出力と最後のConv layerの出力で勾配を取る(微分する)ことで、conv layerの特徴マップのどの辺が最終出力に寄与しているかを計算しています。
"最終出力"と言っているのは、パグとかブルドッグとかの結果を出すクラス識別結果の部分です。
一方、"最後のConv layer"というのは、全結合層を適用する前(CNN部分のおしり)の部分です。
Grad-CAMの詳細は、論文や、以下のサイトを参考にしていただけると良いかと思います。
以下のサイトではGrad-CAMの手法を、図を使いながら説明しています。
dajiro.com
2. 参考にしたソース
私たちは Tensorflow の2系を使っています。
同じくtfの2系を使っていてGrad-CAMを実装している人はいないかなと探したところ、見つけました。
Grad CAM implementation with Tensorflow 2 · GitHub
↑こちらのソースを参考にGrad-CAMを実装しました。
ただ、実装するにあたり、アプリで使っているMobileNetV2のソースではうまくいかなそうなので、以前自作したResNetを使いました。
tottorisnow33.hatenablog.com
なぜMobileNetV2のソースではうまくいかなかったのか。
私たちのMobileNetV2のソースでは、以下のブログで書いたようにtensorflow hubを利用しています。
Section 1で書いたように、Grad-CAMではConv layerの出力が必要となります。
しかし、tensorflow hubで引っ張ってきたモデルは、画像の入力から全結合層による出力まで1つの箱のようになっているみたいでなのです。
私はtensorflow hubで引っ張ってきたモデルからconv layerのところだけ取り出せないかと試しましたが、ちょっとダメそうでした。
一方で、自作したResNetなら各layerを1つ1つ書いていて、conv layerの出力を取り出すのは容易だったのでResNetのソースでGrad-CAMを試しました。
3. 自作ResNetでGrad-CAMをやってみた結果
自作ResNetでやってみた結果は↓です。
【勉強会報告】
— ごまあぶら (@tsukuruiroirop1) June 11, 2022
識別結果に対して、特徴マップのどの辺が寄与しているか見たいので、Grad-camを使ってみた。
ただ、赤と青の部分で似たような値だった。。
学習がうまくいってないのかな?
そもそもgrad-camに使うconv layerの活性値は、BNやReLUの後でよいのだろうか?
次回はその辺を調べてみよう。 pic.twitter.com/kfCFoiCa1B
色の付き方だけ見ると、パグの耳、目、口元あたりに注目していることがわかります。
ただ、、、↑のツイートに書いたように、実際にGrad-CAMで計算した値は、青い部分と赤い部分で割と同じような値でした。
具体的に言うと、赤いところが1.09、青いところが1.06とかです。
(ちなみに、Grad-CAMの計算結果が大きいほど出力に寄与しています。可視化するときは、最大値を赤、最小値を青となるように正規化してます。)
値が近いので、結局赤いところも青いところも同じくらい注目しているのでは...?
と思いましたが。
これで良いのだろうか。。。。
今回Grad-CAMを初めて使ったので、Grad-CAMで出力した数値の勘所がわかりません。。
そもそも、今回のResNetは事前学習もしてないので、このモデルが微妙なだけかもしれません。。
KaiNDを作っていくぞ会 第1回 --Webアプリ--
ブログから記事を見つけたい場合はこちら
こんにちは。松原です。
埼京線はだめです。
0. 前回まで
さて、前回はアプリをリリースしました。
https://play.google.com/store/apps/details?id=com.KaiND_PugOrBulldog.android_with_opencv
こちらです。いいアプリですね。アイコンはないけど。
で、今回からはKaiNDシリーズを育てていきます。
割り当てはとりあえず以下です。
松原: KaiND Web
K : AI自体の改善
Y :スマホアプリのUIの改善
1. Webアプリ要件
仮だけど画面と機能はこんな感じ
画像をアップロード→識別をするWebアプリを作ってみてます。
— ごまあぶら (@tsukuruiroirop1) 2022年5月21日
自分でCSSを調整するとなぜか古風になる…
CSSマスターになりたい… pic.twitter.com/ELRzVM51Vh
2. Webアプリの構成
バックエンド: go
フロントエンド: React.ts
で行こうかなと思ってます。
golangでつくったapi叩くと識別結果がレスポンスで帰ってくる感じですね。
データベースはとりあえず要らないかな。
tensorflowをgoで使うことも可能らしい。多分簡単にできる。
さすがgoogle。
3. 今後の展望
はい。というわけでKaiNDのWebアプリ担当松原からのご報告でした。
来週はAI担当Kからの報告になります。
明日は焼肉です。