Lobeでラベル付けした結果を抽出する

## Lobeとは
Microsoftが突如10月に公開した[Lobe](https://lobe.ai)というアプリケーションがあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/28077b86-7fb6-d695-f1ae-fa2b657128fe.png)

現在はImage Classificationのみ対応ですが、Lobe上でアノテーションした後に、`ResNet-50 V2`と`MobileNet V2` でモデル学習させることができます。しかも学習させたモデルを様々な形式でエクスポートできます。CodeMLとかTensorflowとかONNXとか幅広く対応。すごい。

## Lobeでアノテーションするのがすごい簡単
そんなLobeですが、僕の一番の推しポイントはラベリングのアノテーションが非常に簡単というところです。

![nuo-upa-.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/abd23ba1-62bd-87fc-7e1f-4432b2c596ee.gif)

これはなぜかポケモンのウパーとヌオーを分類しているところですが(なんで？)、ワンクリックでラベルを振ることができています。キーボード入力でも勿論よく補完が効くので非常に楽です。enterを押すと次の画像に行きます。
ラベルの振り直しをするたびにTrain Modelが走るため、一度に大量に変更すると若干スタックするような挙動になりますが、最近のバージョンでは結構安定してきています。

## Lobeで行ったアノテーション結果を他で使いたい

さて、この便利なラベルアノテーションした結果、他でも使いたいと思いませんか。もちろんLobeが学習してくれるのでそのモデルをエクスポートすればいいだけなのかもしれないですが、学習する部分は自分でハンドリングしたい場合もそりゃあたくさんありますよね。

しかしこのLobeさん、モデルのエクスポートはできますが、アノテーション結果をエクスポートすることができません。悲報です。Lobeを使ってウキウキでアノテーションを進めていた僕はこの事実を知ったときに膝から崩れ落ちました。

なんとしてもデータを保存しているところを見つけなくてはならない。どうせElectronだし、sqliteかなにかがあるだろうと思ってそれを探す旅に出ました。

### Lobeがデータを保存しているsqliteを見つけ出す

LobeはRedditに公式に聞けるよ！という場を用意してくれており、Lobe公式が一般の皆さんに対してバンバン回答をしてくれています。ここでバッチリの質問をしているひとがいました。

https://www.reddit.com/r/Lobe/comments/jpx5uc/where_does_lobe_stores_data_and_can_you_change_it/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/1f4ae0b9-53ba-891e-ac79-1f5c5ded92b7.png)

> Where does Lobe stores data and can you change it?

僕と全く同じ悩みを抱えていて100回頷きました。

しかもこれは回答が帰ってきています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/87a949ea-dc41-4719-d066-9e76eada966a.png)

> Thanks for the feedback! 
> Right now Lobe keeps all your data in the Application Data location, so for Windows that is %appdata%\lobe and for Mac it is ~/Library/Application Support/lobe.

いやー、すぐにわかってよかったよかった。ということで、↑の位置に目的のsqliteがあるようです。

### Pandasに展開する

場所がわかれば後はpythonに渡してごにょごにょするだけ、ということですね。実際に読んでみるコードは以下のような感じです。

```python
import pandas as pd
import sqlite3

# Read sqlite query results into a pandas DataFrame
dir = "/Users/{user_name}/Library/Application Support/Lobe/projects/9bc85ec7e2b24b16b6f243fb3c1c0121"
con = sqlite3.connect(f"{dir}/db.sqlite")
df = pd.read_sql_query("SELECT * from data_items", con)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/2e92089a-7dc7-9be9-31cd-7af6e2157541.png)

データ構造はこんな感じになっています。imageにはhash nameが振られているみたいですね。どの画像にどのラベルが振られているかは、example_idを見て一致していれば、という感じで行けそうです。

### どの画像がどのラベルなのかを振る

```python
# ラベル列を作る
label_dict = (df.query("type == 'text'")[["item", "example_id"]]
.set_index("example_id")["item"]).to_dict()

# 画像だけ抜き出す
image_df = df.query("type=='image'")
# ラベルを振る
image_df["label"] = image_df["example_id"].map(label_dict)
image_df["img_path"] = image_df["hash"].apply(lambda x: f"{dir}/data/blobs/{x}")
```

### ラベルごとに対応するディレクトリへ書き出す

これで各画像について、そのラベルと画像へのパスがつきました。あとは煮るなり焼くなりですが、kerasとかでよく使う形式で言うと、画像のラベルごとにディレクトリが分かれている事が多いと思いますので、そのようにしてみましょう。

```python
import os
import shutil

os.makedirs("path/to/data/annotated/ヌオー")
os.makedirs("path/to/data/annotated/ウパー")

# image_dfに従って画像を格納する
image_df.apply(lambda x: shutil.copy(x["img_path"], f"path/to/data/annotated/{x['label']}/{x['hash']}.jpg"), axis=1)
```

## Lobeはアノテーションがしやすいしその結果のエクスポートも割と簡単にできるよ

ということで、Lobeでアノテーションした結果を他で使う方法について、具体例とともに紹介しました。どなたかの役に立てば幸いです。