LoginSignup
8
2

More than 3 years have passed since last update.

Flutter Desktopで簡易アノテーションツールを作る

Last updated at Posted at 2021-03-09

はじめに

新しく画像分類モデルを作成したいと思ったのですが、前回、学習データを作成したときは画像に対するラベリングをMac標準のFinderを使いドラッグアンドドロップ操作で行ったため、手が疲れました。そこで今回は画像に対する手動のラベリングを疲れにくい操作で行えるツールをFlutterで新たに作成しました。

annotation.gif

ソースコード tfandkusu/ikut_annotation

前回の作成の様子

スプラトゥーン2のプレイ動画から、やられたシーンだけをディープラーニングで自動抽出する

なぜFlutter Desktopか

  • 学習のための画像約5万枚が手元のPCにあり、デスクトップアプリならば他に動かさなくて良いです。
  • タッチパネルよりキーボードの方が速く操作できそうです。
  • ブラウザで動くものだとサーバーサイドアプリが必要で工数が大きいです。
  • 個人的にデスクトップアプリを作れる手段としてFlutterが一番馴染んでいました。

ツールの要件

私が作った画像分類モデルはスプラトゥーン2のシーン分類でしたが、プレイヤー以外には分かりにくいので、今回はFood 101データセットをお借りして、料理画像の分類モデルのための学習データを作成するためのツールを作成します。

ラベル

今回は画像に

  • takoyaki
  • sushi
  • gyoza
  • other (ラベリング不可能)

の何れかのラベルを設定します。

ラベル一覧はlabel.txtファイルに設定します。

label.txt
takoyaki
sushi
gyoza
other

ラベル付き画像

画像は実行ファイル直下のimageに置き、result.csvにファイル名とラベル名を設定します。後述する操作で、このCSVの2列目は書き換わります。

result.csv
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でデバッグ実行する

(インストールされていなければ)XcodeCocoaPodsをインストールします。

Flutter SDKに対してMacOSを有効可します。

flutter config --enable-macos-desktop

プロジェクトに対してMacOS用のビルドをできるようにします。

flutter create --platforms=macos .

Android StudioからmacOS(desktop)を選んで起動します。

スクリーンショット 2021-03-08 5.31.56.png

または

flutter run -d mac

コマンドで実行します。

app-sandboxの無効化

AppStoreで配布されるアプリがローカルファイルの読み書きをするためには、権限の管理が必要です。しかし今回はAppStoreで配布しないので、app-sandboxは無効化して、特に権限の管理は行いません。

macos/Runnner/DebugProfile.entitlements
<?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)を選んで起動します。

image.png

または

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 ディレクトリも必要な物です。

image.png

それらのファイルに依存するdllファイルと、アプリが必要とするファイルやディレクトリを合わせて、配布パッケージとします。

  • 依存するdll
    • msvcrp140.dll
    • vcruntime140.dll
    • vcruntime140_1.dll
  • アプリが必要とするファイルやディレクトリ
    • image/ 画像ディレクトリ
    • label.txt ラベル一覧
    • result.csv ラベル付け結果

image.png

依存するdllファイルはVisual Studio 2019のインストールディレクトリからコピーします。

image.png

マイクロソフト公式情報

プログラムの解説

今回作成したFlutterプロジェクトはこちらで公開されています。

この節ではデスクトップアプリならではの部分をかいつまんで紹介します。

ローカルファイルの読み書き

ローカルファイルの読み書きにはFileクラスを使用しました。また、CSVの読み書きにはcsvパッケージを利用しました。

label.txtファイルからラベル一覧を取得するコードはこのようになりました。

main_state_notifier_provider.dart
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ファイルに書き出すコードはこのようになりました。

main_state_notifier_provider.dart
// CSV文字列を得る
final csvString = ListToCsvConverter().convert(state.images.map((image) {
  // 二次元配列に変換する
  final name = basename(image.path);
  return [name, image.label];
}).toList());
_file.writeAsString(csvString);

キーボード入力

キーボード入力はこのようにFocusを使用して取得します。

main.dart
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向けのアプリケーションを作成する選択肢もあると思いました。

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2