はじめに
自動で動画切り抜きする方法を、クラウドを使わずにローカル環境で試してみた。
検知能力もそこそこ優秀で比較的処理が軽いと言われるYOLOv5
と OpenCV を使って作ってみたので、メモとして残しておく。
実施環境
■ PCスペック
CPU:Intel(R) Core(TM) i5-3470 CPU @ 3.20GHz 3.20 GHz
メモリ:8.00 GB
■ OS
Windows 10 Pro
■ 各種SW&パッケージのバージョン(今回インストールするもの含む)
python 3.9.5
pip 21.3.1
opencv-python 4.5.4.60
YOLOv5
メモ内容
①環境準備の流れと、②Pythonコードをメモとして残す。
① 環境準備
色々なパッケージが必要となりローカル環境が散らかりそうだったため、自分は仮想環境上で環境を構築している。(手順は下記を参照)
パッケージのインストールの前にYoLov5
のインストールに必要なソースをダウンロードする
※clone先は仮想環境(venv)で作成した場所を指定
git clone https://github.com/ultralytics/yolov5
必要パッケージのインストール
仮想環境をActivate
にした状態で以下を実行。
pip install --upgrade pip
# パッケージのインストールコマンド
pip install
pip install datetime
pip install numpy pandas
pip install opencv-python
# 物体検出のための学習済みモデル利用に使うパッケージ一覧
pip install -r ./yolov5/requirements.txt
Pythonコマンドでimport cv2
や import torch
が実行できれば、一旦インストール完了と考えてOK
(opencv) C:\Users\*******\*******>python ## opencvという名前の仮想環境を作っているため(opencv)となっている。
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> import torch
YOLOv5
の動作確認
ターミナルから動かす場合は以下のコマンドを実行する。
cd yolov5
python detect.py --source ./data/images/ --weights yolov5s.pt --conf 0.4
上記コマンドの引数の説明。
source:画像のフォルダ、または画像のパスを指定。
※./data/images
にサンプル画像が入っている。
conf:指定した値以下の確率値は表示しない。
weights:物体検出時に利用する事前学習済みモデルの重みファイルを指定。
※モデルが存在しない初回時はyolov5s.pt
がダウンロードされる。。
実行後にフレームとラベル付きの画像ファイルがruns/detect/exp
(デフォルト)に保存される。
※1).以下の様なオプションを付けることで、画像は保存せず、結果をテキストで出力すること可能。
・--save-txt
推論結果(検出座標と予測クラス)をtxtファイルに出力する
・--save-conf
推論結果(クラスの確率)をtxtファイルに出力する
・--nosave
画像を保存しない
実行対象を動画ファイルにした場合、全てのフレームに対して画像検出処理がされる
※2).YOLOv5で検知できる物体の種類
person
bicycle
car
motorcycle
airplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
couch
potted plant
bed
dining table
toilet
tv
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrus
物体種類の指定の時は、クラス番号で指定する必要がある。詳しくは ./data/coco.yaml
を見れば分かる。
② 実現するためのPython サンプルコードを紹介
今回は 『動画の中で鳥が映っている部分のみを自動で切り抜いてくれるプログラム』 を作ってみる。
サンプル動画は、森の中で一匹の鳥が見え隠れするような動画 を選定。
※以下の動画サンプルサイトから良さそうなものをダウンロードしました。
https://pixabay.com/ja/videos/
https://www.pexels.com/ja-jp/videos/
https://www.motionelements.com/ja/free/stock-footage
以下の STEP で機能を作って実現していく。
[STEP1] 動画のフレーム数を減らして短くする処理。
[STEP2] 各フレームに対して物体検出を行い、鳥が映っているフレームのみ繋ぎ合わせる。
[STEP1] 動画のフレーム数を減らして短くする。
動画ファイルでそのまま物体検出を行うと、全てのフレームに対して検知処理が走ってしまい、必要以上の計算コストかかってしまう。
実際には 数秒間隔くらいで検出を行えれば十分なので、元の動画から数秒間隔のフレームだけで動画を繋ぎ合わす加工を行う。
# パッケージインポート
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import cv2
###################################################################
# 指定した "秒" 間隔のみのフレームに絞り、動画ファイルを生成。
###################################################################
### 引数の説明
# path_in:元動画ファイルのパス
# path_out:出力動画ファイルのパス
# interval:抽出するフレームの間隔(秒)
def frame_out(file_in, file_out, interval):
#
movie= cv2.VideoCapture(file_in)
# 元画像の画像サイズを取得
w = int(movie.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(movie.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(w, ' × ', h)
# 元画像のfps 1秒あたりのフレーム数を取得
fps = int(movie.get(cv2.CAP_PROP_FPS))
# 再生時間と、抽出するフレームの間隔から、フレーム数を取得。
end_frame = int(movie.get(cv2.CAP_PROP_FRAME_COUNT)/(movie.get(cv2.CAP_PROP_FPS) * interval))
print('フレーム数:', end_frame)
# 出力動画ファイルの生成
fourcc = cv2.VideoWriter_fourcc('m','p','4', 'v')
video = cv2.VideoWriter(file_out, fourcc, 1, (w, h))
for sec in range(end_frame):
# 取得するフレーム番号を指定
pos_frame = int(fps * (sec + 1) * interval)
movie.set(cv2.CAP_PROP_POS_FRAMES, pos_frame)
# フレームを読みだす。
ret, frame = movie.read()
if not ret:
break
video.write(frame)
# 出力動画ファイルの解放
video.release()
# 上記関数の実行処理(サンプル)
source_file = './サンプル動画_org.mp4'
output_file = './処理後動画.mp4'
interval = 1
frame_out(source_file, output_file, interval)
[STEP2] 各フレームに対して物体検出を行い、鳥が映っているフレームのみ繋ぎ合わせる。
上記の[STEP1] を少し改良し、動画ファイルから抽出したフレームに対して物体検出を行い、"鳥" が映っているか判定する。
映っていた場合、生成動画に繋ぎ合わせる。
# パッケージインポート
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import torch # モデルを呼び出すために追加
import cv2
###################################################################
# 指定した "秒" 間隔のみのフレームに絞り、動画ファイルを生成。
###################################################################
### 引数の説明
# path_in:元動画ファイルのパス
# path_out:出力動画ファイルのパス
# interval:抽出するフレームの間隔(秒)
# mdl:物体検知に使う学習済みモデル
# label:検知したい物体
# conf_border:検知したい物体の確率閾値
def frame_extract_withlabel(file_in, file_out, interval, mdl, label, conf_border):
# 物体検出に使用するモデルの読み込み & 設定
model = torch.hub.load('ultralytics/yolov5', mdl)
model.classes = label
model.conf = conf_border
# 動画の読み込み
movie= cv2.VideoCapture(file_in)
# 元画像の画像サイズを取得
w = int(movie.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(movie.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(w, ' × ', h)
# 元画像のfps 1秒あたりのフレーム数を取得
fps = int(movie.get(cv2.CAP_PROP_FPS))
# 再生時間と、抽出するフレームの間隔から、フレーム数を取得。
end_frame = int(movie.get(cv2.CAP_PROP_FRAME_COUNT)/(movie.get(cv2.CAP_PROP_FPS) * interval))
print('フレーム数:', end_frame)
# 出力動画ファイルの生成
fourcc = cv2.VideoWriter_fourcc('m','p','4', 'v')
video = cv2.VideoWriter(file_out, fourcc, 1, (w, h))
for sec in range(end_frame):
# 取得するフレーム番号を指定
pos_frame = int(fps * (sec + 1) * interval)
movie.set(cv2.CAP_PROP_POS_FRAMES, pos_frame)
# フレームを読みだす。
ret, frame = movie.read()
if not ret:
break
### 読み出されたフレーム毎に物体検知を行う
results = model(frame)
if len(results.pandas().xywh[0]) >0 :
# 1~4カラムまでの物体位置を表す表現の仕方が異なる
#display(results.pandas().xyxy[0]) # 検知した物体の square の4点の座標
display(results.pandas().xywh[0]) #検知した物体の 中心X・Y座標 & square の高さと幅
video.write(frame)
# 出力動画ファイルの解放
video.release()
# 上記関数の実行処理
source_file = './サンプル動画_自然_鳥.mp4'
output_file = './処理後動画.mp4'
interval = 0.5
mdl_name = 'yolov5x'
target_label = 14 # 鳥は "14" のため
frame_extract_withlabel(source_file, output_file, interval, mdl_name, target_label, 0.3)
上記pythonコードをを実行すれば、鳥だけが映っている動画を自動生成できる。