LoginSignup
12
11

More than 1 year has passed since last update.

Darknet YOLOv4 を Google Open Images Datasetのデータを使って学習させる with Elixir

Last updated at Posted at 2021-07-10

はじめに

この記事はcvlibで使うように独自データセットでyolov4を学習した手順をまとめたものです
ファイル周りの処理やファイルリストの作成にElixirを使用しています

環境

OS ubuntu 20.04.2 LTS
CPU intel core i7-9700k
GPU Geforce RTX 3060
メモリ 32GB
CUDA 11030
cuDNN 8.2.1
OpenCV 4.2.0

datasetの準備

データはGoogle Open Images Datasetから
pythonのopenimagesを使用してダウンロードします
darknet形式のannotationファイルを出力してくれるのでOIDv4_Toolkitより楽です

インストールはpipで行いダウンロード先を作っておきます

pip install openimages
mkdir ~/openimages

ダウンロードはコマンドかスクリプトで行います
コマンドはdirを指定してlabelsに取得したいクラス
クラス名はサークルcsvに一覧があるのでそこから選んでください
クラス名の注意点としてv4とv5で以下のように微妙に違ってダウンロード時にエラーになるので気をつけましょう

Bat -> Bat (Animal)
Lady bug -> Ladybug
Jugger -> Jaguar (Animal)
oi_download_dataset --csv_dir ~/openimages --base_dir ~/openimages --labels Dog Cat --format darknet
from openimages.download import download_dataset
labels = ["Woodpecker","Blue jay","Ostrich","Penguin","Raven","Chicken","Eagle","Owl","Duck","Canary","Goose","Swan","Falcon","Parrot","Sparrow","Turkey","Tick","Centipede","Starfish","Isopod","Squid","Jellyfish","Shrimp","Bee","Beetle","Ladybug","Ant","Caterpillar","Butterfly","Dragonfly","Scorpion","Worm","Spider","Snail","Bat (Animal)","Brown bear","Panda","Polar bear","Teddy bear","Cat","Fox","Jaguar (Animal)","Lynx","Red panda","Tiger","Lion","Dog","Leopard","Cheetah","Otter","Raccoon","Camel","Cattle","Giraffe","Rhinoceros","Goat","Horse","Hamster","Kangaroo","Koala","Mouse","Pig","Rabbit","Squirrel","Sheep","Zebra","Monkey","Hippopotamus","Deer","Elephant","Porcupine","Hedgehog","Bull","Antelope","Mule","Dolphin","Whale","Sea lion","Harbor seal","Skunk","Alpaca","Armadillo","Dinosaur","Lizard","Snake","Turtle","Tortoise","Sea turtle","Crocodile","Frog","Goldfish","Shark","Rays and skates","Seahorse","Shellfish","Oyster","Lobster","Shrimp","Crab","Person"]
labels.sort()
download_dataset("openimages",labels, annotation_format="darknet")

darknet学習用に準備

darknetの学習には以下が必要になりますのでそちらも準備していきましょう

  • クラス名一覧
  • 画像とアノテーションデータ
  • train,testの画像リスト

クラス名一覧

openimagesでダウンロードしたときに darknet_obj_names.txtというファイルが作成されていますのでそちらをそのまま使用します

画像とアノテーションデータ

darknetの学習用データセットは1つのフォルダに画像ファイルと同名アノテーションデータのファイルを以下のようにいれます

    └── datasets
       ├── image1.jpg
       ├── image1.txt
       ...
       ├── imagex.jpg
       └── imagex.txt

画像ファイルとアノテーションデータはopenimagesでダウンロードされていますがクラスごとに別れていますのでそれをまとめていきましょう

アノテーションデータの中身はこんな感じで先頭はIDでそれ以降が対象が描画されている位置になります
IDはopenimageのIDではなく、darknet_obj_names.txtの行番号になります
これは犬だけをダウンロードしたときなので dog => 0を示しています

0 0.77625 0.597092 0.21999999999999997 0.20356400000000005

ダウンロード先のopenimagesに移動して iexを起動させて以下を実行します
mkdirでまとめる用のdatasetフォルダを作成しておきましょう

File.ls! 
|> Enum.each(fn d -> 
  System.cmd("/bin/sh",["-c","mv #{d}/images/* ./dataset/"]) 
  System.cmd("/bin/sh",["-c","mv #{d}/darknet/* ./dataset/"]) 
end)

File.ls!でクラスのフォルダ一覧を取得し
Enum.eachでフォルダ以下の画像とアノテーションデータを移動させるコマンドをbashで移動させます
cpでもいいですが容量がでかいのでmvにしています、ストレージ容量と相談でお好きな方を
System.cmd("mv",opt)だと*(wildcard)が使えないのでsh -cで実行しています

Train, Test 画像のリスト

こちらも, train-annotations-bbox.csvとtest-annotations-bbox.csvがありますが
全open imagesのファイルのリストなので不要なものを除いていきます

中身はこんな感じ

ImageID,Source,LabelName,Confidence,XMin,XMax,YMin,YMax,IsOccluded,IsTruncated,IsGroupOf,IsDepiction,IsInside
000002b66c9c498e,xclick,/m/01g317,1,0.012500,0.195312,0.148438,0.587500,0,1,0,0,0
000002b66c9c498e,xclick,/m/01g317,1,0.025000,0.276563,0.714063,0.948438,0,1,0,0,0
000002b66c9c498e,xclick,/m/01g317,1,0.151562,0.310937,0.198437,0.590625,1,0,0,0,0
class = File.read!("path/to/train-annotations-bbox.csv")
  |> String.split("\n") # 一塊の文字列で来るので改行コードで分割
  |> Enum.map(fn s -> String.split(s, ",") end) # ListになったのでEnum.mapでコンマで分割
  |> Enum.drop(1) # header部分が邪魔なので先頭をdrop
  |> Enum.filter(fn s -> File.exists?("dataset/#{Enum.at(s, 0)}.jpg") end)
  |> Enum.map(fn s -> Enum.at(s, 0) end)

File.open("train.txt",[:write], fn file ->  
  Enum.each(class, fn str -> IO.write(file, "/home/user_name/openimages/dataset/#{str}.jpg\n") end)                
end)

前半はFile.read!だと結果が文字列で返ってくるのでsplitでcsvの形にして
imageIDと同じ画像が存在するか?を条件にフィルタリングして、imageIDだけのListを作成しています
後半はimageIDのListの内容を絶対パスで書き込んでいます

darknetの仕様なので相対パスではなく絶対パスにしてください

darknetの準備

データセット準備が整ったのでdarknetの準備をしていきましょう
こちらを参考にインストール

git clone https://github.com/AlexeyAB/darknet.git
cd darknet
vim Makefile

変更箇所
GPUがリストになかったけどアーキテクチャ的に同じだろうから ARCHは86
nvccのパスがエラーになったのでフルパスでセット

Makefile.
GPU=1
CUDNN=1
CUDNN_HALF=0
OPENCV=1

ARCH= -gencode arch=compute_86,code=[sm_86,compute_86]

NVCC=/usr/local/cuda/bin/nvcc

変更したらmakeでコンパイル

.dataファイルの準備

train,testファイルの一覧や学習途中のweigthファイルの保存先を設定します
ファイル名はお好みで今回はdog.dataとしています
エラーになるのでそれぞれ絶対パスで設定してください

class= 1 # 今回はdogだけなので1
train = /home/user_name/openimages/train.txt
valid = /home/user_name/openimages/test.txt
names = /home/user_name/openimages/darknet_obj_names.txt
backup = /home/user_name/darknet/backup/

configの編集

custom-objectのときはこれ使ってとここにあるのでyolov4-custom.cfgを使用します
git cloneしたdarknetのcfg以下あるのでコピーしておく

cp darknet/cfg/yolov4-custom.cfg ~/openimages/

こちらを参考に編集

subdivisions=64
max_batches = 46000
4000以上で、クラス数×2000
steps=36800,41400
max_batchesの80%,90%
classes=1
クラス数を指定します
下の方のyoloが3つあるのでそこを編集
filters=18
(クラス数+5)×3
yoloの1つ前のconv層にあるので編集

学習

以下のコマンドで学習を開始します
重みファイルのyolov4.conv.137はこちらからダウンロード

darknet/darknet detector train ~/openimages/dog.data ~/openimages/yolov4-custom.cfg ~/openimages/yolov4.conv.137

あとは気長にお待ち下さい

cvlibで使う

学習中でも学習済みパラメーターがbackupに保存されてると思うのでそちらを使用して
wights, .cfg, darknet_obj_names.txtを渡せば物体検知を行って検知した箇所を囲ってくれます
https://github.com/arunponnusamy/cvlib/blob/master/examples/yolo_custom_weights_inference.py

yolo_custom_weights_inference.py
import cvlib as cv
from cvlib.object_detection import YOLO
import sys
import cv2

weights = sys.argv[1]
config = sys.argv[2]
labels = sys.argv[3]

# read input image
image = cv2.imread(sys.argv[4])

yolo = YOLO(weights, config, labels)

# apply object detection
bbox, label, conf = yolo.detect_objects(image)

print(bbox, label, conf)

# draw bounding box over detected objects
yolo.draw_bbox(image, bbox, label, conf, write_conf=True)

# display output
# press any key to close window           
cv2.imshow("object_detection", image)
cv2.waitKey()

# save output
cv2.imwrite("yolo_object_detection.jpg", image)

# release resources
cv2.destroyAllWindows()
python yolo_custom_weights_inference.py yolov4-custom_2000.weights yolov4-custom.cfg darknet_obj_names.txt dog.jpeg

スクリーンショット 2021-07-11 1.35.44.png

無事犬だけを検知するyolov4が完成しました!
本記事は以上になりますありがとうございました

参考ページ
https://github.com/arunponnusamy/cvlib
https://qiita.com/Ihmon/items/a331d63a7a5d3fa51918
https://github.com/AlexeyAB/darknet#how-to-train-to-detect-your-custom-objects
https://pjreddie.com/darknet/yolo/
https://qiita.com/kitata9/items/936c97f0ac33dae13ab5
https://pypi.org/project/openimages/
https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v3_optimal/yolov4.conv.137
https://storage.googleapis.com/openimages/2018_04/bbox_labels_600_hierarchy_visualizer/circle.html
https://github.com/EscVM/OIDv4_ToolKit
https://stackoverflow.com/questions/49952153/elixir-shell-command-with-wildcard-failing-when-using-system-cmd

12
11
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11