【DockerでYOLOv5を動かす】UbuntuでYOLOv5をCPUで実行する方法とトラブルシューティング

## はじめに
YOLOv5は物体検出に強力なモデルですが、GPUがない環境でもDockerを使って動かすことが可能です。この記事では、NVIDIAのGPUがないUbuntu環境で、Dockerを使ってYOLOv5をCPUで動かす手順を詳しく説明し、各ステップで発生しうるエラーと解決策についても詳しく解説します。

## 環境と要件
- **OS**: UbuntuServer 22.04
- **Docker**: インストール済み
- **CPU**: IntelCorei37500
- **メモリ**: 4GB（後に8GB増強して12GBにした）
- **SSD**: 500GB
- **GPU**: なし (CPUでYOLOv5を実行)

**必要なリソース**:
- Dockerイメージ: `ultralytics/yolov5:v6.2-cpu` (CPU環境向け)

## ステップ1: Dockerイメージの準備
まず、YOLOv5のDockerイメージをDockerHubから取得します。

```bash
docker pull ultralytics/yolov5:v6.2-cpu
```

イメージのダウンロードが完了したら、コンテナを起動します。

```bash
docker run --rm -it \
  --device /dev/video0:/dev/video0 \
  --device /dev/video1:/dev/video1 \
  -v $(pwd):/usr/src/app \
  ultralytics/yolov5:v6.2-cpu
```

- `--device /dev/video0:/dev/video0` はホストマシンのカメラをコンテナにマウントしています。
- `-v $(pwd):/usr/src/app` でホストのカレントディレクトリをコンテナにマウントし、結果ファイルを直接ホスト側で確認できるようにしています。

## ステップ2: 推論の実行
### カメラ映像を使ったリアルタイム推論
まずカメラデバイスが認識されているか確認します。

```bash
ls /dev/video*
```

カメラが認識されていれば、YOLOv5でリアルタイム推論を実行できます。

```bash
python3 detect.py --source 0 --weights yolov5s.pt --conf 0.5 --device cpu
```

しかし、Docker環境では通常、GUIがサポートされないため、`cv2.imshow()` や `PIL Image.show()` は使用できません。代わりに、推論結果をファイルとして保存して確認する方法を利用します。

## ステップ3: 結果の保存と確認
推論結果をファイルとして保存するには、以下のオプションを使用します。

```bash
python3 detect.py --source 0 --weights yolov5s.pt --conf 0.5 --device cpu --save-txt --save-conf
```

- `--save-txt`: 検出されたオブジェクトの座標をテキストファイルに保存
- `--save-conf`: 検出結果に信頼度を追加

推論結果はデフォルトで `runs/detect/exp` ディレクトリに保存されます。コンテナ内の結果をホスト側にコピーするには、以下のコマンドを使用します。

```bash
docker cp <container_id>:/usr/src/app/runs/detect ./detect_results
```

## ステップ4: Flaskを使ったリアルタイム表示
Docker環境内でリアルタイム表示が難しい場合は、Flaskを利用して、推論結果をWebブラウザにストリーミングする方法があります。

### Flaskアプリケーションのセットアップ
Flaskとその他必要なライブラリをインストールします。

```bash
pip install flask opencv-python
```

Flaskアプリケーションを作成します。以下のコードを `app.py` として保存します。

```python
from flask import Flask, render_template, Response
import cv2
import torch
import numpy as np

app = Flask(__name__)

# YOLOv5モデルのロード
model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True)

# カメラの初期化
camera = cv2.VideoCapture(0)

def generate_frames():
    while True:
        success, frame = camera.read()
        if not success:
            break
        else:
            # YOLOv5による推論
            results = model(frame)
            annotated_frame = np.squeeze(results.render())

            # フレームをJPEG形式にエンコード
            ret, buffer = cv2.imencode('.jpg', annotated_frame)
            frame = buffer.tobytes()

            yield (b'--frame\r\n'
                   b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/video_feed')
def video_feed():
    return Response(generate_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
```

`templates` フォルダを作成し、その中に `index.html` を配置します。

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>YOLOv5 Real-Time Object Detection</title>
</head>
<body>
    <h1>Real-Time Object Detection</h1>
    <img src="{{ url_for('video_feed') }}" width="800">
</body>
</html>
```

Flaskアプリケーションを実行します。

```bash
python app.py
```

ブラウザで `http://localhost:5000` にアクセスすると、YOLOv5のリアルタイム推論結果が表示されます。

## トラブルシューティング
### TemplateNotFoundエラー
Flaskアプリケーションで `jinja2.exceptions.TemplateNotFound: index.html` エラーが出た場合、`index.html` が `templates` フォルダ内に正しく保存されているか確認してください。

### cv2.imshow()が動作しない
Docker環境でGUI表示が必要な場合、X11転送を使用するか、ホスト環境で直接実行する方法を検討してください。

### CPU環境での速度低下
GPUがない場合、推論速度が遅くなる可能性があります。リアルタイム処理が求められる場合は、GPU搭載マシンでの実行が望ましいでしょう。

## まとめ
この記事では、Ubuntu上のDocker環境でYOLOv5をCPUで実行し、Flaskを使ってリアルタイムに表示する方法を詳しく解説しました。Docker環境での制約を理解し、発生しやすいエラーとその対策も含めて説明しました。この手順を参考に、トラブルを解決しながらYOLOv5をDockerで実行してみてください。