はじめに
新しく画像分類モデルを作成したいと思ったのですが、前回、学習データを作成したときは画像に対するラベリングをMac標準のFinderを使いドラッグアンドドロップ操作で行ったため、手が疲れました。そこで今回は画像に対する手動のラベリングを疲れにくい操作で行えるツールをFlutterで新たに作成しました。
ソースコード tfandkusu/ikut_annotation
前回の作成の様子
スプラトゥーン2のプレイ動画から、やられたシーンだけをディープラーニングで自動抽出する
なぜFlutter Desktopか
- 学習のための画像約5万枚が手元のPCにあり、デスクトップアプリならば他に動かさなくて良いです。
- タッチパネルよりキーボードの方が速く操作できそうです。
- ブラウザで動くものだとサーバーサイドアプリが必要で工数が大きいです。
- 個人的にデスクトップアプリを作れる手段としてFlutterが一番馴染んでいました。
ツールの要件
私が作った画像分類モデルはスプラトゥーン2のシーン分類でしたが、プレイヤー以外には分かりにくいので、今回はFood 101データセットをお借りして、料理画像の分類モデルのための学習データを作成するためのツールを作成します。
ラベル
今回は画像に
- takoyaki
- sushi
- gyoza
- other (ラベリング不可能)
の何れかのラベルを設定します。
ラベル一覧はlabel.txtファイルに設定します。
takoyaki
sushi
gyoza
other
ラベル付き画像
画像は実行ファイル直下のimageに置き、result.csvにファイル名とラベル名を設定します。後述する操作で、このCSVの2列目は書き換わります。
1003289.jpg,takoyaki
100332.jpg,sushi
1003912.jpg,gyoza
1005352.jpg,sushi
1009005.jpg,gyoza
1010258.jpg,takoyaki
1012499.jpg,sushi
1012802.jpg,takoyaki
1013418.jpg,sushi
101625.jpg,sushi
操作方法
キーボードで操作します。
操作 | キー |
---|---|
次の画像を表示/選択する | ]キー |
前の画像を表示/選択する | [キー |
選択画像にラベル1を設定する | Zキー |
選択画像にラベル2を設定する | Xキー |
選択画像にラベル3を設定する | Cキー |
選択画像にラベル4を設定する | Vキー |
100枚先の画像を表示/選択する | Pキー |
100枚前の画像を表示/選択する | Oキー |
デバッグ実行する
ほぼ公式のDesktop support for Flutterに記載されている内容です。
Macでデバッグ実行する
(インストールされていなければ)XcodeとCocoaPodsをインストールします。
Flutter SDKに対してMacOSを有効可します。
flutter config --enable-macos-desktop
プロジェクトに対してMacOS用のビルドをできるようにします。
flutter create --platforms=macos .
Android StudioからmacOS(desktop)を選んで起動します。
または
flutter run -d mac
コマンドで実行します。
app-sandboxの無効化
AppStoreで配布されるアプリがローカルファイルの読み書きをするためには、権限の管理が必要です。しかし今回はAppStoreで配布しないので、app-sandboxは無効化して、特に権限の管理は行いません。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!--
<key>com.apple.security.app-sandbox</key>
<true/>
-->
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
参考
Entitlements and the App Sandbox
App Sandbox Entitlement
Windowsでデバッグ実行する
Windowsでデバッグ実行および後述する配布用ビルドを作成するときは、Windows PCで行う必要があります。
Visual Studio 2019をインストールします。
Flutter SDKに対してWindowsを有効可します。
flutter config --enable-windows-desktop
プロジェクトに対してWindows用のビルドをできるようにします。
flutter create --platforms=windows .
Android StudioからWindows(desktop)を選んで起動します。
または
flutter run -d windows
コマンドで実行します。
Windows向け配布用ビルドを作成する
Windows向けに配布用ビルドを作成しました。作成はWindows PC上で行う必要があります。
flutter build windows
ちなみに、普段Macで開発しているのでクロスコンパイルができると便利なのですが、Macでビルドしようとすると、このようなメッセージが出ます。
"build windows" only supported on Windows hosts.
配布用ビルド作成結果はプロジェクト直下の build/windows/runner/Release ディレクトリに作成されます。flutter_windows.dll も data ディレクトリも必要な物です。
それらのファイルに依存するdllファイルと、アプリが必要とするファイルやディレクトリを合わせて、配布パッケージとします。
- 依存するdll
- msvcrp140.dll
- vcruntime140.dll
- vcruntime140_1.dll
- アプリが必要とするファイルやディレクトリ
- image/ 画像ディレクトリ
- label.txt ラベル一覧
- result.csv ラベル付け結果
依存するdllファイルはVisual Studio 2019のインストールディレクトリからコピーします。
プログラムの解説
今回作成したFlutterプロジェクトはこちらで公開されています。
この節ではデスクトップアプリならではの部分をかいつまんで紹介します。
ローカルファイルの読み書き
ローカルファイルの読み書きにはFileクラスを使用しました。また、CSVの読み書きにはcsvパッケージを利用しました。
label.txtファイルからラベル一覧を取得するコードはこのようになりました。
Future<List<String>> _loadLabels() async {
final dir = Directory.current.path;
final file = new File('$dir/label.txt');
// ファイルを読む
final csvString = await file.readAsString();
// CSVと解釈して各行をList<String>に格納する。
final fields = CsvToListConverter().convert(csvString);
final labels = fields.map((items) => items[0] as String).toList();
return labels;
}
ラベル付けの結果をCSVファイルに書き出すコードはこのようになりました。
// CSV文字列を得る
final csvString = ListToCsvConverter().convert(state.images.map((image) {
// 二次元配列に変換する
final name = basename(image.path);
return [name, image.label];
}).toList());
_file.writeAsString(csvString);
キーボード入力
キーボード入力はこのようにFocusを使用して取得します。
class MainPage extends HookWidget {
@override
Widget build(BuildContext context) {
final mainStateNotifier = useProvider(mainStateNotifierProvider);
return Focus(
autofocus: true,
onKey: (node, event) {
if (event is RawKeyDownEvent) {
if (event.logicalKey.keyLabel == ']') {
mainStateNotifier.move(1);
} else if (event.logicalKey.keyLabel == '[') {
mainStateNotifier.move(-1);
} else if (event.logicalKey.keyLabel == 'p') {
mainStateNotifier.move(100);
} else if (event.logicalKey.keyLabel == 'o') {
mainStateNotifier.move(-100);
} else if (event.logicalKey.keyLabel == 'z') {
mainStateNotifier.update(0);
} else if (event.logicalKey.keyLabel == 'x') {
mainStateNotifier.update(1);
} else if (event.logicalKey.keyLabel == 'c') {
mainStateNotifier.update(2);
} else if (event.logicalKey.keyLabel == 'v') {
mainStateNotifier.update(3);
}
}
// ここをfalseにすると、MacOSでペコペコ音が鳴る
return true;
},
child: Scaffold(
appBar: AppBar(
title: Text('iKut annotation'),
),
body: Stack(children: [
_buildPreviousImage(),
_buildImage(),
_buildLabels()
])),
);
}
// 略
}
まとめ
FlutterでスマホやWebのアプリを作っていて、機械学習のための学習データ作成のように、開発のためのツールが必要になったときは、Flutter DesktopでPC向けのアプリケーションを作成する選択肢もあると思いました。