YOLOv3 – 学習済みモデルで画像から人や車を検出する方法

目次

概要

YOLOv3 の MSCOCO の学習済みモデルで画像から人や車を検出する方法について紹介します。

環境

  • Ubuntu 18.04
  • Windows 10

準備

YOLOv3 の Pytorch 実装である nekobean/pytorch_yolov3 を使用します。

まず、レポジトリをクローンします。

git clone https://github.com/nekobean/pytorch_yolov3.git
cd pytorch_yolov3

依存ライブラリをインストールします。

pip install -r requirements.txt

古いバージョンの Pytorch がインストールされている場合は、次のコマンドで最新バージョンに更新してください。

pip install -U torch torchvision torchaudio

重みをダウンロードします。

./weights/download_weights.sh

ダウンロードが完了すると、weights ディレクトリ以下に2つのファイルがあるはずです。

weights/
|-- download_weights.sh
|-- yolov3-tiny.weights
`-- yolov3.weights
  • yolov3.weights: MSCOCO で学習した YOLOv3 の重み
  • yolov3-tiny.weights: MSCOCO で学習した YOLOv3-tiny の重み

Windows の場合、bash が使えないので、手動で yolov3.weights 及び yolov3-tiny.weights をダウンロードして、weights/ ディレクトリに置いてください。

推論する

MSCOCO の学習済みモデル

公式サイトで配布されている yolov3.weights 及び yolov3-tiny.weights は、次の80クラスを含むおよそ10万枚の画像で構成されるデータセットで学習した重みです。 以下の物体を検出したい場合は、自分でデータセットを作成して学習を行わなくても、学習済みモデルを利用するとすぐに検出を行うことができます。

クラス ID クラス名 クラス ID クラス名 クラス ID クラス名 クラス ID クラス名
0 20 ゾウ 40 ワイングラス 60 ダイニングテーブル
1 自転車 21 クマ 41 カップ 61 トイレ
2 22 シマウマ 42 フォーク 62 テレビ
3 バイク 23 キリン 43 ナイフ 63 ノートパソコン
4 飛行機 24 リュックサック 44 スプーン 64 マウス
5 バス 25 45 ボウル 65 リモコン
6 電車 26 ハンドバッグ 46 バナナ 66 キーボード
7 トラック 27 ネクタイ 47 リンゴ 67 携帯電話
8 28 スーツケース 48 サンドウィッチ 68 電子レンジ
9 信号機 29 フリスビー 49 オレンジ 69 オーブン
10 消火栓 30 スキー 50 ブロッコリー 70 トースター
11 ストップサイン 31 スノーボード 51 キャロット 71 シンク
12 パーキングメーター 32 スポーツボール 52 ホットドッグ 72 冷蔵庫
13 ベンチ 33 カイト 53 ピザ 73
14 34 野球バット 54 ドーナツ 74 時計
15 35 野球グラブ 55 ケーキ 75 花瓶
16 36 スケートボード 56 椅子 76 ハサミ
17 37 サーフボード 57 ソファー 77 テディベア
18 38 テニスラケット 58 鉢植え 78 ヘアードライヤー
19 39 ボトル 59 ベッド 79 歯ブラシ

YOLOv3 と YOLOv3-tiny について

YOLOv3-tiny は YOLOv3 よりパラメータ数を減らした軽量版のモデルです。 検出精度は YOLOv3 に劣りますが、高速に動作します。計算リソースが潤沢でなく、YOLOv3 を動かすのが難しい場合は YOLOv3-tiny を試してみてください。

画像から検出する

--input に画像ファイルのパス、または画像があるディレクトリを指定します。--output には出力ディレクトリを指定します。

python detect_image.py \
    --input <画像ファイルまたは画像があるディレクトリ> \
    --output <出力ディレクトリ> \
    --weights weights/yolov3.weights \
    --config config/yolov3_coco.yaml

試しに data/herd_of_horses.png に対して、検出を行ってみます。

python detect_image.py --input data/herd_of_horses.png --output output --weights weights/yolov3.weights --config config/yolov3_coco.yaml

実行が完了すると、output/herd_of_horses.png に結果が出力されます。

検出結果

YOLOv3-tiny を使う

YOLOv3-tiny を使用する場合、--weightsweights/yolov3-tiny.weights--configconfig/yolov3tiny_coco.yaml を指定してください。

python detect_image.py --input data/herd_of_horses.png --output output --weights weights/yolov3-tiny.weights --config config/yolov3tiny_coco.yaml

YOLOv3-tiny は高速ですが、検出精度は YOLOv3 と比較すると、下がります。

画像から人を検出する

YOLOv3 を利用して人検出を行うサンプルコードです。クラス「人」は MSCOCO の学習済みモデルに含まれているため、YOLOv3 の検出結果からクラス名が person となっている矩形だけ抽出すればよいです。

people.png

yolov3_pathgit clone した pytorch_yolov3 ディレクトリのパスを指定してください。

In [1]:
import sys
from pathlib import Path

import cv2
from IPython.display import Image, display

# git clone した pytorch_yolov3 ディレクトリのパスを指定してください。
yolov3_path = Path("../pytorch_yolov3")
sys.path.append(str(yolov3_path))
from yolov3.detector import Detector

config_path = yolov3_path / "config/yolov3_coco.yaml"
weights_path = yolov3_path / "weights/yolov3.weights"


def imshow(img):
    """ndarray 配列をインラインで Notebook 上に表示する。"""
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))


# 検出器を作成する。
detector = Detector(config_path, weights_path)

# 画像を読み込む。
img = cv2.imread("people.png")

# 検出する。
detections = detector.detect_from_imgs(img)

# 人の検出結果のみ抽出する。
people = list(filter(lambda x: x["class_name"] == "person", detections[0]))

# 検出結果を画像に描画する。
for bbox in people:
    cv2.rectangle(
        img,
        (int(bbox["x1"]), int(bbox["y1"])),
        (int(bbox["x2"]), int(bbox["y2"])),
        color=(0, 255, 0),
        thickness=2,
    )

imshow(img)
Darknet format weights file loaded. ../pytorch_yolov3/weights/yolov3.weights

画像に写っている人物にモザイクをかける

物体検出を活用することで人物にモザイクをかける作業を自動化できます。

[blogcard url=”https://pystyle.info/opencv-mosaic-processing”]

In [2]:
import sys
from pathlib import Path

import cv2
from IPython.display import Image, display

# git clone した pytorch_yolov3 ディレクトリのパスを指定してください
yolov3_path = Path("../pytorch_yolov3")

sys.path.append(str(yolov3_path))
from yolov3.detector import Detector

config_path = yolov3_path / "config/yolov3_coco.yaml"
weights_path = yolov3_path / "weights/yolov3.weights"


def imshow(img):
    """ndarray 配列をインラインで Notebook 上に表示する。
    """
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))


def mosaic(img, k=15):
    h, w = img.shape[:2]  # 画像の大きさ

    dst = cv2.blur(img, ksize=(k, k))

    return dst


# 検出器を作成する。
detector = Detector(config_path, weights_path)

# 画像を読み込む。
img = cv2.imread("people.png")

# 検出する。
detections = detector.detect_from_imgs(img)

# 人の検出結果のみ抽出する。
people = list(filter(lambda x: x["class_name"] == "person", detections[0]))

# 検出結果を画像に描画する。
for bbox in people:
    roi = img[int(bbox["y1"]):int(bbox["y2"]), int(bbox["x1"]):int(bbox["x2"])]
    roi[:] = mosaic(roi)

imshow(img)
Darknet format weights file loaded. ../pytorch_yolov3/weights/yolov3.weights

画像から自動車を検出する

自動車も、「自転車」「車」「バイク」「バス」「トラック」が MSCOCO の学習済みモデルに含まれているので、検出結果からこれらのクラスでフィルタすれば、同様に検出できます。

cars.png

In [3]:
import sys
from pathlib import Path

import cv2
from IPython.display import Image, display

# git clone した pytorch_yolov3 ディレクトリのパスを指定してください
yolov3_path = Path("../pytorch_yolov3")

sys.path.append(str(yolov3_path))
from yolov3.detector import Detector

config_path = yolov3_path / "config/yolov3_coco.yaml"
weights_path = yolov3_path / "weights/yolov3.weights"


def imshow(img):
    """ndarray 配列をインラインで Notebook 上に表示する。
    """
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))


# 検出器を作成する。
detector = Detector(config_path, weights_path)

# 画像を読み込む。
img = cv2.imread("cars.png")

# 検出する。
detections = detector.detect_from_imgs(img)

# 人の検出結果のみ抽出する。
target = ["bicycle", "car", "motorcycle", "bus", "truck"]
cars = list(filter(lambda x: x["class_name"] in target, detections[0]))

# 検出結果を画像に描画する。
for bbox in cars:
    cv2.rectangle(
        img,
        (int(bbox["x1"]), int(bbox["y1"])),
        (int(bbox["x2"]), int(bbox["y2"])),
        color=(0, 255, 0),
        thickness=2,
    )

imshow(img)
Darknet format weights file loaded. ../pytorch_yolov3/weights/yolov3.weights

コメント

コメント一覧 (0件)

  • 管理者様の下記のサイトを参考にして物体検出を行っており、大変感謝しております。
    ・学習済みモデルで画像から人や車を検出する方法
    ・自作データセットで学習する方法について
    Windows環境下でOSはWindows10、Pythonのバージョンは3.6.13で問題なく作動しています(GPU非搭載なので自作データの学習には時間を要していますが)。

    先日、LINUXのGPU搭載PCを購入した友人の依頼で、pytorch_yolov3と依存ライブラリーをインストールして「学習済みモデルで・・・・・」の方を実行したところ、下記のエラーが出力されました。
    AttributeError: ‘Detector’ object has no attribute ‘detect’

    友人のPC(LINUX)の環境は下記です。
    OS:Ubuntu 18.04
    GPU:NVIDIA GeForce RTX 3090
    Pythonバージョン:3.9.7

    LINUXとGPUともにあまり詳しくないのですが、もし対処方法がありましたらご助言いただくと幸いです。
    いつも恐縮ですが、よろしくお願いいたします。

    • 参考にしていただきありがとうございます。
      エラーが発生したのは、本記事のどちらのコードになりますでしょうか?
      セットアップ及び実行された手順に関する情報があればなにかわかることがあるかもしれません。

      先程、以下の環境で確認してみましたが、動作しました。

      環境
      Ubuntu 20.04
      Python 3.9.10

      セットアップ手順
      git clone https://github.com/nekobean/pytorch_yolov3.git
      cd pytorch_yolov3
      pip install -r requirements.txt
      ./weights/download_weights.sh

      ※「4.6 画像から人を検出する」以降のコードは Notebook で実行することを想定しています。その場合は、`yolov3_path = Path(“.”)` に clone した pytorch_yolov3 のディレクトリを指定します。相対パスの場合は Notebook から見た相対パス

      • 早速のご助言、ありがとうございます。
        「4.6 画像から人を検出する」のコードを利用させていただいていますが、私の方は Spyder で実行しています。
        セットアップ手順はご指摘の手順で行っております。
        友人に確認したところ CUDA と cuDNN はセットアップしていないとのことです。
        GPUは初めてで初歩的な質問で恐縮ですが、まずはこの両者をインストールしてから、ということでしょうか。
        よろしくお願いします

        • > まずはこの両者をインストールしてから、ということでしょうか。

          CUDA など必要なものがインストールされておらず GPU が利用できない環境では、CPU で動くようになっているので、エラーの原因には関係ないと思います。
          ちなみに GPU を利用するには Nvidia のドライバ、CUDA 及び cuDNN のセットアップが必要です。インストールするバージョンは GPU によって異なるので、以下の記事でバージョンについて紹介しています。具体的なインストール方法については Qiita の記事など参考にするといいと思います。
          https://pystyle.info/pytorch-relationship-between-gpu-and-driver-cuda-and-cudnn-versions/

          もし実行を PyCharm など IDE を使って行っている場合は、それが原因の可能性もあるため、動作確認が取れている Jupyter Notebook (または Jupyter Lab) やコマンドラインで `python [pythonファイル名]` で .py ファイルを実行するやり方でエラーがでないかお試しください。

  • コメント失礼します。
    社会人3年目で画像認識について勉強中で、本記事とても分かりやすくまとめていただいており参考にさせていただきました。
    本記事を参考にしながら、対象ディレクトリ内にある複数画像を取得し、モザイク処理をした後に出力するようにしました。
    20枚ほどの画像を格納し、モザイク処理を行いましたが半分ほどは処理が完了しましたが、途中で下記エラーとなりました。(完了したものを削除し、未完了なものだけで再度実施すると全て処理できましたが、一気に20枚ほどができない状況です。)
    何か考えれあれる原因等ありましたらご教示いただけないでしょうか。。
    MacOSでAnacondaにて仮想環境構築し(python3.16.13)、本記事の手順で実施しました。

    ——————-【エラー内容】———————————————-
    error Traceback (most recent call last)
    in
    66 for bbox in mosaic_subject:
    67 roi = img[int(bbox[“y1”]):int(bbox[“y2”]), int(bbox[“x1”]):int(bbox[“x2”])]
    —> 68 roi[:] = mosaic(roi)
    69
    70 cv2.imwrite(os.path.join(mosaic_folder_path, file_name), img)

    in mosaic(img, k)
    27 h, w = img.shape[:2] # 画像の大きさ
    28
    —> 29 dst = cv2.blur(img, ksize=(k, k))
    30
    31 return dst

    error: OpenCV(4.7.0) /private/var/folders/td/gwxc2_bx4t1_bglm3m2hwwk80000gn/T/pip-install-wz27dlfp/opencv-contrib-python_f24da42e492840dcbd9e18321e71a3c9/opencv/modules/imgproc/src/box_filter.dispatch.cpp:446: error: (-215:Assertion failed) !_src.empty() in function ‘boxFilter’
    ——————-【エラー内容】———————————————-

    ———————————–【追加箇所】——————————–
    # 読み込む画像の格納先を指定
    input_folder_path = “/Users/〜/pytorch_yolov3/data/test”

    # フォルダ内の全ての画像ファイルを取得し.から始まる隠しファイルは削除
    file_list = os.listdir(input_folder_path)
    file_list = [f for f in file_list if not f.startswith(‘.’)]

    # モザイク加工したファイルの格納先を準備
    mosaic_folder = “face-mosaic”
    mosaic_folder_path = os.path.join(input_folder_path, mosaic_folder)
    os.makedirs(mosaic_folder_path)

    for file_name in file_list:

    file_path = os.path.join(input_folder_path, file_name)
    img = cv2.imread(file_path)

    # 検出する。
    detections = detector.detect_from_imgs(img)

    # 人と車とバイクの検出結果のみ抽出する。
    mosaic_subject = list(filter(lambda x: x[“class_name”] in [ ‘person’,’car’,’motorcycle’], detections[0]))

    # 検出結果を画像に描画する。
    for bbox in mosaic_subject:
    roi = img[int(bbox[“y1”]):int(bbox[“y2”]), int(bbox[“x1”]):int(bbox[“x2”])]
    roi[:] = mosaic(roi)

    cv2.imwrite(os.path.join(mosaic_folder_path, file_name), img)
    ———————————–【追加箇所】——————————–

    • コメントありがとうございます。
      おそらくですが、読み込もうとしているパスに画像ファイル以外が含まれているのではないでしょか。
      img = cv2.imread(file_path)
      は読み込みに失敗した場合、None を返すため、この行の下に
      assert img is not None, f”{file_path} は読み込みに失敗”
      という1行を追加して確認してみてください

      • ご回答いただきありがとうございます。
        いただいたアドバイスを元に調べてみると画像外に見切れた対象物を検出した際に座標が画像外の座標を取得していることが原因でした。
        矩形の四隅の座標が画像外になる場合は画像端の座標を取得するように修正することで解消されました。
        改めてありがとうございました。

コメントする

目次