Label Studioという素敵ツールに出会ったので紹介する（しない）

# 中小ーーーーーー！企業ーーーーーーーーー！！！



（この記事は [ちゅらデータ Advent Calendar 2021 15日目](https://qiita.com/advent-calendar/2021/churadata) の記事となります）

~~ネタすぎるのでQiitaに書くことじゃないって怒られたら個人ブログに移動します~~


# そういえば今年はこんなLTしました

https://speakerdeck.com/commander_aipa/sefalsetu-imasuguhazimeruemueruopusu


# ~~酒飲みすぎて~~ 忘れてました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/2a3d8285-330a-e2a7-aed3-2549af9d6d78.png)



# 無事フラグ回収しました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/3defa536-1eb1-e770-aebd-d7f2eeb0bba3.png)


# 思い出しながら書いていきます（今　2021/12/15 午前5:17）

## Label Studioってなんですか？

https://labelstud.io/

Label Studioは、OSSのデータラベリングツールです。Webアプリケーションとして利用することが可能です。個人的にはよくあるのは画像メインだなって思いますが、こちらは音や時系列、テキストデータにもラベリングすることができます。

※ページの画像スクショして貼り付け
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/3ae6e1db-612b-2ea0-0f80-9b14d1b1f6a5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/64319e45-3ebc-8836-e28d-09f5c9b0fe3b.png)

使ってみてとても便利だなって思ったのは、DockerやPython使ってたら導入が楽なのと、Web画面でサクサク動くのもそうなんですが、フロントがJavaScriptフレームワーク(React)で組まれていて、バックエンドがAPI(Python)で開発されています。また、APIとやり取りするためにtoken発行することができます。なので、Web画面でできないことは、ユーザー側で開発したコードをAPI経由で連携することができるので良いなって思いました。

~~とても良いおもちゃありがとうございます~~ これでデータ収集が無限にはかどりますね！


## クラウドサービスにも似たようなのあるよ？　

わかります、SageMakerのツールとか複数人で対応するとか便利そうでした。が、高いんです。企業はよくても個人はちょっときついなと。

## Label Studioにいるこいつはなんですか？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/945a61d4-4c98-77c6-1420-4e36951da33b.png)

~~たぬきです~~

たぬきと思ってたけどたぬきじゃないかも

https://twitter.com/hoto17296/status/1438825590186856453

## Label Studio、ぼ・・・ぼくにできるかなぁ？

~~ノープロブレム、トムの勝ちデース~~ DockerやPython使っている人は瞬殺です。


```bash
# Install the package
$ pip install -U label-studio
# Launch it!
$ label-studio
```


```bash
# Run latest Docker version
$ docker run -it -p 8080:8080 -v `pwd`/mydata:/label-studio/data heartexlabs/label-studio:latest
# Now visit http://localhost:8080/
```


## Label Studio使って何やりたいんですか？

死ぬまでにゴジラをAIで表現したいなって思っています。そのための準備です。

## 本題が迷子になっていますよ？

ｱｯﾊｲ。本題です。


## （本題） 利用のためにアクセスキーは必須なの？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/479c3e9f-3a9e-528e-6014-73e91439439c.png)

レースキュー！ソールジャー！！もっと速くーーーーーー！！！！！（思い出している）
危険なバトルつきぬけてゆくーーーー＼ｺﾞｰｺﾞﾌｧｲﾌﾞ／（思い出している）
レースキュー！ソールジャー！！もっと強くーーーーーー！！！！！（思い出している）
かがやく勇気をいま！とーがーらーせーーーてーーーーーー！！！！！（思い出している）

思い出した。

AWS上で検証しました。

EC2起動してDocker install後、Label Studioを起動してProjectを作成します。Object Detectionで作成しました。

また、S3に画像をおいて、Syncできるようにします。IAMでEC2にS3 Full Accessを付与すれば今回はおｋとします。（本当は絞ったほうがいい）

適当にアノテーション付与して、YOLOのデータセットをexportしようとすると。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/f01a1f34-8852-f028-acf3-e3dfd4aa8688.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/fed14845-79ff-f8be-73b7-dd0dbb2917e9.png)


画像は空っぽで、labelだけ落ちてきます。なぜ？？？


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/2ce6178a-1156-79b4-cb27-4bb658ac8fbb.png)

最悪S3から落とせばいいのですが、紐付けるのが面倒なのでできればLabel Studioからまとめて落としたいです。

そこでなぜ失敗するのか調査したところ、Docker側でエラーが出力されていることに気が付きました。


```
[2021-12-14 21:38:21,497] [label_studio_converter.converter::convert_to_yolo::580] [ERROR] Unable to download s3://${aipa-images}/input/f81eab74-1750-11ec-bfb8-916757824fb9_2002_0_480_640.jpg. The item {'id': 1, 'input': {'image': 's3://${aipa-images}/input/f81eab74-1750-11ec-bfb8-916757824fb9_2002_0_480_640.jpg'}, 'output': defaultdict(<class 'list'>, {'label': [{'x': 6.406250000000003, 'y': 13.124999999999995, 'width': 77.49999999999997, 'height': 86.87499999999982, 'rotation': 0, 'rectanglelabels': ['Godzilla'], 'type': 'RectangleLabels', 'original_width': 640, 'original_height': 480}]}), 'completed_by': 1, 'annotation_id': 1, 'created_at': '2021-12-14T21:17:45.591190Z', 'updated_at': '2021-12-14T21:17:45.591222Z', 'lead_time': 15.235} will be skipped
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/label_studio_converter/converter.py", line 576, in convert_to_yolo
    image_path = download(image_path, output_image_dir, project_dir=self.project_dir,
  File "/usr/local/lib/python3.8/dist-packages/label_studio_converter/utils.py", line 129, in download
    r = requests.get(url)
  File "/usr/local/lib/python3.8/dist-packages/requests/api.py", line 75, in get
    return request('get', url, params=params, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/requests/api.py", line 61, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/requests/sessions.py", line 542, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python3.8/dist-packages/requests/sessions.py", line 649, in send
    adapter = self.get_adapter(url=request.url)
  File "/usr/local/lib/python3.8/dist-packages/requests/sessions.py", line 742, in get_adapter
    raise InvalidSchema("No connection adapters were found for {!r}".format(url))
requests.exceptions.InvalidSchema: No connection adapters were found for 's3://${aipa-images}/input/f81eab74-1750-11ec-bfb8-916757824fb9_2002_0_480_640.jpg'
```

Pythonのrequestsモジュールがだいたい締めていますが、スタックトレースの上部分を確認すると、エラーの元はこちらのようです。

```
  File "/usr/local/lib/python3.8/dist-packages/label_studio_converter/converter.py", line 576, in convert_to_yolo
    image_path = download(image_path, output_image_dir, project_dir=self.project_dir,
  File "/usr/local/lib/python3.8/dist-packages/label_studio_converter/utils.py", line 129, in download
    r = requests.get(url)
```

Label StudioはOSSでGithubで公開されているので、該当のコードを見に行きます。


https://github.com/heartexlabs/label-studio-converter/blob/master/label_studio_converter/utils.py#L129

なるほど？？？

よくわかりませんが、s3に対してrequests moduleでGETを投げているようですね。

はて？？？そもそもできるんだっけ？通常はboto3を使いそうですが、なぜだろう？？？

当時、Web画面からでしか状況を確認してなかったので、アクセスキーが足りないからだ！って思っていたのですが、LT直前になって動かないから、コードみてみると？？？となったことを思い出しました。そうだ。そうだった。

話し戻ります。何かしら設定違いがあって、分岐に失敗しているのかしら？とboto3を検索キーワードにして、リポジトリを検索するのですが、boto3が使われていない。なるほど？

とりあえず、コンテナ上からboto3でs3からダウンロードできそうか試してみます。


```bash
ubuntu@ip-1-1-1-1:~$ docker exec -it ${container_id} /bin/bash
root@aaa:/label-studio# pip3 install boto3
Requirement already satisfied: boto3 in /usr/local/lib/python3.8/dist-packages (1.16.28)
```

なるほど？

```
root@aaa:/label-studio# python3
Python 3.8.10 (default, Sep 28 2021, 16:10:42)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import boto3
>>>
```

なるほど。インストールされているようです。はて？


時間がないので、とりあえず直接書き換えてみます。


```bash
root@aaa:/label-studio# vim /usr/local/lib/python3.8/dist-packages/label_studio_converter/utils.py
bash: vim: command not found
```

vimがインストールされていない重大な問題がでたのでインストールします。

```bash
root@aaa:/label-studio# apt update -y && apt install -y vim
```


```
root@aaa:/label-studio# vim /usr/local/lib/python3.8/dist-packages/label_studio_converter/utils.py
```

書き込めるようになったので、こんな感じで書き換えます。

```python
128         if download_resources:
129             if url.find('s3') > -1:
130                 import boto3
131                 client = boto3.client('s3')
132                 a = url.split('/')
133                 bucket = a[2]
134                 key = '/'.join(a[3:])
135                 res = client.get_object(Bucket=bucket, Key=key)
136                 r = res['Body'].read()
137                 with io.open(filepath, mode='wb') as fout:
138                     fout.write(r)
139             else:
140                 r = requests.get(url)  # 元のコード
141                 r.raise_for_status()
142                 with io.open(filepath, mode='wb') as fout:
143                     fout.write(r.content)
```

で、サーバを再起動したいですが。。。再起動の手順がない・・・？なるほど？


コード読んでいるとどうやらDjangoで動いているようです。Djangoあんまり詳しくないけど、hot reloadみたいなことができたような。。。


https://github.com/heartexlabs/label-studio/blob/3e28c167765c5a4030fbd309955d4eb64baf1841/label_studio/server.py#L45

まじかー、めんどい。

よくわからないですが、コンテナ内でserverプロセス殺しても死ななかったので、一旦諦めてリポジトリをcloneしてきます。

```bash
# コンテナ停止後
$ sudo apt install python3-pip
$ git clone https://github.com/heartexlabs/label-studio.git

# 起動後、初期設定からスタートしてしまうので、設定済みのデータは移動
$ sudo mv mydata label-studio/

$ cd label-studio/
$ vim deploy/requirements.txt
```

label-studio-converterを書き換えたいのですが、こいつは外部モジュールとして分離されていたので `requirements.txt` ではコメントアウトして、こいつも持ってきます。

```
# label-studio-converter==0.0.36
```

```bash
$ git clone https://github.com/heartexlabs/label-studio-converter.git
$ cd label-studio-converter/
$ vim label_studio_converter/utils.py
```

さっきと同じように書き換える。書き換えたら、pipを叩く。

```bash
$ pip3 install -e .
$ cd ../
$ pip3 install -e .

# あとでエラーになるので事前対応
$ pip3 uninstall attr

# あとはlabel-studioのdocumentに沿って起動する
$ python3 label_studio/manage.py migrate

# migrationしたけど、パスがhomeディレクトリになっている。。。
# そして、パスの指定方法がわからなかった。。。
# ので、権限変更して上書きすることにする
$ ls /home/ubuntu/.local/share/label-studio/
export  label_studio.sqlite3  media

$ sudo chown ubuntu:ubuntu -R mydata/
$ cp -rf mydata/* /home/ubuntu/.local/share/label-studio/

$ python3 label_studio/manage.py runserver 0.0.0.0:8080
```

起動したらブラウザから、再度エクスポートしてみる。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/31d28ddd-a81b-0047-4436-8bfc375e034a.png)

わーいできた。疲れた。

そのまま使えないと思うのと、メンテナになぜこんな事になっているの？と聞く必要がありますが、皆様PRチャンスです。（僕がやれ？ｱｯﾊｲ）


## （本題） 結局どうやってAPIと連携したの？

TODO: 疲れたので、今年中にあげる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/5a7809a4-96a3-378a-8686-404109a245d2.png)


# 以上です。

子供が起きたので以上とさせていただきます。ありがとうございました。

[ちゅらデータ Advent Calendar 2021 16日目](https://qiita.com/advent-calendar/2021/churadata) は aipa さんによる 「Python VS Anaconda VS ちゅらデータ 65535(1GB)　~呪われたプロジェクトの仕様変更~ 」（たぶん）です！来週もまたみてね。