【YOLOv8】建築資材の物体検出 (カラーコーン編)

# はじめに
建築資材の物体検知の3回目となります。
初回はパイプの検出、前回はクランプの検出を行いました。

https://qiita.com/yutakam/items/848bb44831d3ee715452

https://qiita.com/yutakam/items/c0601bbea6c9d3ae53ae

# 検出対象の資材
今回の対象はカラーコーンです。
目的はカラーコーン自体の検出というよりも数を数えることですので、物体の全体ではなく重なったカラーコーンの画像を使用します。
こんな感じの画像です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351434/33354b7d-7159-ae05-b7cb-d638754bb5f0.jpeg" width=70%>

# 学習に使用するデータ
現場で撮影した画像 25 枚を使用します。
とりあえずいつも通り以下の手順で学習データを用意します。

- 元画像を圧縮 (今回は 1/4 にリサイズ)
- 640px × 640px にランダムに切り取る

# 検出の手法
前回と前々回は、対象の資材を矩形 (くけい) で囲む物体検出 (Object Detection) でしたが、今回はインスタンス セグメンテーション (Instance Segmentation) を行います。
セグメンテーションタスクはよく見るこんなやつです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351434/56d030d2-23d1-302c-d0f4-bd6e9272b0c3.jpeg" width=70%>

実際のカラーコーンの画像は色んな方向を向いていてバウンディングボックスで囲むのも難しいのと、折角ですので新しいことをやってみたいというだけの理由です。

# アノテーション
今回使用するアノテーションソフトは『labeme』です。
Windows 版は以下からダウンロードできます。

https://github.com/labelmeai/labelme/releases/tag/v5.3.1

また実際の作業は自分では実施せず、来年春に入社予定の K.O.さんに勉強がてらバイトで対応してもらいました。
勉強以外の目的として、アノテーションを初めて行う人の感想 (掛った時間や疲労度合い) も見てみたいという意図もあります。
K.O.さんにはスピードは求めず、正確なアノテーションをお願いしました。
こんな感じのアノテーションです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351434/638efdd9-39bb-77b4-3c25-6ade3228732a.png)

とりあえず 200 枚ちょっとアノテーションしてもらいました。
量としては全然足りませんが、時間は有限ですのでいったんこれで学習してみます。

# ラベルデータの変換
labelme で作成した JSON 形式のラベルを、labelme2yolo で YOLOv8 で使える形式に変換します。


https://github.com/GreatV/labelme2yolo

インストールします。
``` bash
pip install labelme2yolo
```

実行します。
結果は指定した JSON ディレクトリ配下に出力されます。
検証データを全体の 20%、今回はデータが少ないためテストデータは 0 を指定して用意しませんでした。後日撮った写真でテストします。
``` bash
labelme2yolo --json_dir ./json/ --val_size 0.2 --test_size 0
```

実行終了後は以下のようなディレクトリ構成となります。直下に「YOLODataset」ディレクトリが作成され、JSON から変換された txt ファイルが「labels」ディレクトリに配置されます。

``` bash
json
└── YOLODataset
    ├── images
    │   ├── train
    │   │   ├── **.jpg
    │   │   └── **.jpg
    │   ├── val
    │   │   ├── **.jpg
    │   │   └── **.jpg
    │   └── test
    │
    ├── labels
    │   ├── train
    │   │   ├── **.txt
    │   │   └── **.txt
    │   ├── val
    │   │   ├── **.txt
    │   │   └── **.txt
    │   └── test
    └── dataset.yaml
```

出来上がったデータを作業ディレクトリにコピーします。
``` bash
cp -r ./json/YOLODataset/* /PATH/TO/YOLOv8/datasets/pylon/v1/
```

# 学習
yaml ファイルを微修正します。

``` bash
# Path
path: /PATH/TO/YOLOv8/datasets/pylon/v1  # dataset root dir
train: images/train  # train images (relative to 'path') 
val: images/val  # val images (relative to 'path') 

# Classes
nc: 1
names: ['pylon']
```

学習してみます。
事前学習済みモデルは「yolov8l-seg.pt」としました。
``` py
from ultralytics import YOLO

model = YOLO('yolov8l-seg.pt')

model.train(
    data = '/PATH/TO/YOLOv8/datasets/pylon/v1/pylon.yaml',
    epochs = 100,
    batch = 16,
    workers = 8,
    imgsz = 640,
    project = 'runs/pylon/train',
    name = 'v1',
    )
```

学習に要した時間とメモリはこのような感じです。
| 学習時間 | VRAM 使用量 |
|:-:|:-:|
|0.152 hours | 約 12 ～ 13 GB / 1 epoch|

results.png は以下の通りです。
![results.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351434/478f74ef-0bc6-a637-50a0-53a8384d1493.png)

# 学習したモデルを使用して推論
"best.pt" で推論してみます。
いつも感じることですが、学習したモデルで推論する時がなんとも言えず一人で緊張してしまいます。
受験結果発表を受ける学生のような気分です。

さて
- うまく検出しているパターン
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351434/61fff0a6-5902-9d55-186b-227bc20c5a87.jpeg" width=70%>

- うまく検出していていないパターン
やはり数的にはうまく検出していなパターンが多いです。
学習データのバリエーションも少ないですし、想定内の結果ではありますが。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351434/56b20db8-5274-6d0b-14f5-2584842b6516.jpeg" width=70%>

# おわりに
今回は新しくインスタンス・セグメンテーションを試してみましたが、まずはどんな感じかつかむ意味では実施した良かったと思います。
K.O.さんは若いのでアノテーションはさほど苦痛ではないようでしたが、後で自分もやってみると、やはり面倒です。
現実的に使える状態までモデルを学習するのは、前回前々回のパイプとクランプでもそうですが、あらゆるパターンを想定しないといけないため、データの取得、アノテーションの実施方法をもっと研究しないといけないと切実に感じ始めています。