目次
事前トレーニングは、動物界の動物を検出することです。1060 グラフィックス カード上の単一フレームには 300 ミリ秒かかります。
ヨロフ関連の紹介
論文アドレス: https://arxiv.org/pdf/2208.09686.pdf
コードアドレス: https://github.com/YuHengsss/YOLOV
モデル | サイズ | mAP@50val | 速度 2080Ti(バッチ サイズ=1) (ミリ秒) |
重み |
---|---|---|---|---|
YOLOX-s | 576 | 69.5 | 9.4 | グーグル |
YOLOX-l | 576 | 76.1 | 14.8 | グーグル |
YOLOX-x | 576 | 77.8 | 20.4 | グーグル |
YOLOV-s | 576 | 77.3 | 11.3 | グーグル |
ヨロフ-l | 576 | 83.6 | 16.4 | グーグル |
YOLOV-x | 576 | 85.5 | 22.7 | グーグル |
YOLOV-x + 投稿 | 576 | 87.5 | - |
ビデオオブジェクト検出 (VID) は、オブジェクトの外観のばらつきが大きく、特定のフレームではさまざまな劣化が発生するため、困難です。良い面としては、静止画と比較して、ビデオの 1 つのフレームでの検出を他のフレームでサポートできることです。したがって、異なるフレームにわたる特徴をどのように集約するかが、VID 問題の鍵となります。
既存の集約アルゴリズムのほとんどは、2 段階の検出器に合わせて調整されています。ただし、2 段階の性質により、このような検出器は計算に時間がかかることがよくあります。研究者らは本日、上記の問題を解決するためのシンプルで効果的な戦略を提案しました。これにより、最小限のオーバーヘッドが消費され、精度が大幅に向上します。具体的には、従来の 2 段階のパイプラインとは異なり、研究者らは、大量の低品質の候補の処理を避けるために、1 段階の検出後に領域レベルの候補を配置することを推奨しています。さらに、ターゲット フレームとその参照フレームの間の関係を評価し、集約をガイドするための新しいモジュールが構築されています。
新しく提案された設計の有効性を検証するために、広範な実験およびアブレーション研究が実施され、有効性と効率の点で他の最先端の VID 方法よりも優れていることが明らかになりました。 YOLOX ベースのモデルは、かなりのパフォーマンス (たとえば、単一の 2080Ti GPU 上の ImageNet VID データセットで 30 FPS 以上で 87.5% AP50) を達成できるため、大規模なアプリケーションやリアルタイム アプリケーションにとって魅力的です。
ビデオ オブジェクト検出は、静止画像オブジェクト検出の高度なバージョンとみなすことができます。直観的に、ビデオ シーケンスは、フレームを 1 つずつ静止画像オブジェクト検出器にフィードすることで処理できます。ただし、この方法では、フレーム全体の時間情報が無駄になるため、単一の画像内で発生する曖昧さを除去/軽減する鍵となる可能性があります。
上の図に示されているように、ビデオ フレームではモーション ブラー、カメラの焦点ぼけ、オクルージョンなどの劣化が発生することが多く、検出が大幅に困難になります。たとえば、上の画像の最後のフレームを見るだけで、人間がそのオブジェクトがどこにあるのか、何であるのかを判断することは困難または不可能です。一方、ビデオ シーケンスは、単一の静止画像よりも豊富な情報を提供できます。言い換えれば、特定のフレームの予測は、同じシーケンス内の他のフレームによってサポートされる可能性があります。したがって、さまざまなフレームからの時間メッセージを効率的に集約する方法が精度にとって重要です。上の図からわかるように、研究者が提案した方法は正しい答えを与えます。
03
新しい枠組み
映像の特性(さまざまな劣化や豊富な時間情報)を考慮し、フレームを個別に処理するのではなく、対象フレーム(キーフレーム)の裏付けとなる情報を他のフレームからいかに探すかが映像検出の精度向上に重要となります。最近の試みに比べて精度が大幅に向上したことから、この問題に対する時間的集計の重要性が確認されました。ただし、既存の手法のほとんどは 2 段階の手法に基づいています。
前述したように、主な欠点は、第 1 レベルの基底と比較して推論速度が比較的遅いことです。この制限を軽減するために、研究者は領域/特徴の選択を単一ステージ検出器の予測ヘッドの後に配置します。
研究者は、研究者の主な主張を実証する根拠として YOLOX を選択しました。提案されたフレームワークは上の図に示されています。
従来の 2 段階のパイプラインを確認してみましょう。
1) まず、多数の候補領域を提案として「選択」します。
2) 各提案がターゲットであるかどうか、およびそれがどのクラスに属するかを決定します。計算上のボトルネックは主に、多数の低信頼領域候補を処理することに起因します。
上の図からわかるように、提案されたフレームワークにも 2 つの段階が含まれています。違いは、第 1 段階が予測 (多数の低信頼領域を破棄する) であるのに対し、第 2 段階は領域レベルの洗練 (集約を通じて他のフレームを利用する) とみなすことができることです。
この原理により、新しい設計は、第 1 段階の検出器の効率と時間的集約から得られる精度の両方の恩恵を受けることができます。このような小さな設計の違いが、パフォーマンスの大きな違いにつながる可能性があることを強調する価値があります。提案された戦略は、YOLOX、FCOS、PPYOLOE などの多くの基本的な検出器に一般化できます。
さらに、ソフトマックスの特性を考慮すると、少数の参照特徴が重みの大部分を保持する可能性があります。言い換えれば、重み付けの低い特徴が無視されることが多く、その後使用される参照特徴の多様性が制限されます。
このリスクを回避するために、研究者は平均プーリング参照機能 (A.P.) を導入しました。具体的には、閾値τよりも高い類似性スコアを持つすべての参照が選択され、平均プーリングがこれらに適用されます。なお、この作品における類似度はN(Vc)N(Vc)Tで計算されます。演算子 N(・) はレイヤーの正規化を表し、値が特定の範囲内に収まるようにすることで、スケールの違いの影響を排除します。こうすることで、関連する機能からのより多くの情報を維持できます。次に、平均プールされたフィーチャと主要なフィーチャは、最終的な分類のために線形投影レイヤーに転送されます。プロセスは上の図に示されています。
N(Qc)N(Kc)T または N(Qr)N(Kr)T を類似性として実行できるかどうか疑問に思われるかもしれません。実際、これは別のオプションです。ただし、実際には、Q と K の違いにより、トレーニング中に選択した値ほど安定しません。
事前トレーニングは、動物界の動物を検出することです。1060 グラフィックス カード上の単一フレームには 300 ミリ秒かかります。
ビデオ予測コード:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Copyright (c) Megvii, Inc. and its affiliates.
import argparse
import os
import time
from loguru import logger
import cv2
import torch
from yolox.data.data_augment import ValTransform
from yolox.data.datasets import COCO_CLASSES
from yolox.data.datasets.vid_classes import VID_classes
# from yolox.data.datasets.vid_classes import OVIS_classes as VID_classes
from yolox.exp import get_exp
from yolox.utils import fuse_model, get_model_info, postprocess, vis
import random
IMAGE_EXT = [".jpg", ".jpeg", ".webp", ".bmp", ".png"]
def make_parser():
parser = argparse.ArgumentParser("YOLOX Demo!")
# parser.add_argument(
# "demo", default="video", help="demo type, eg. image, video and webcam"
# )
parser.add_argument("-expn", "--experiment_name", type=str, default=None)
parser.add_argument("-n", "--name", type=str, default=None, help="model name")
parser.add_argument("--path", default=r"C:\Users\Administrator\Videos\f7.mp4", help="path to images or video")
parser.add_argument("--camid", type=int, default=0, help="webcam demo camera id")
# exp file
parser.add_argument("-f", "--exp_file", default='../exps/yolov/yolov_s.py', type=str, help="pls input your expriment description file", )
parser.add_argument("-c", "--ckpt", default='../yolov_s.pth', type=str, help="ckpt for eval")
# parser.add_argument("-c", "--ckpt", default='../yoloxs_vid.pth', type=str, help="ckpt for eval")
parser.add_argument("--device", default="gpu", type=str, help="device to run our model, can either be cpu or gpu", )
parser.add_argument("--dataset", default='vid', type=str, help="Decide pred classes")
parser.add_argument("--conf", default=0.05, type=float, help="test conf")
parser.add_argument("--nms", default=0.5, type=float, help="test nms threshold")
parser.add_argument("--tsize", default=576, type=int, help="test img size")
parser.add_argument("--fp16", dest="fp16", default=True, action="store_true", help="Adopting mix precision evaluating.", )
parser.add_argument("--legacy", dest="legacy", default=False, action="store_true", help="To be compatible with older versions", )
parser.add_argument("--fuse", dest="fuse", default=False, action="store_true", help="Fuse conv and bn for testing.", )
parser.add_argument("--trt", dest="trt", default=False, action="store_true", help="Using TensorRT model for testing.", )
parser.add_argument('--output_dir', default='out', help='path where to save, empty for no saving')
parser.add_argument('--gframe', default=32, help='global frame num')
parser.add_argument('--save_result', default=True)
return parser
def get_image_list(path):
image_names = []
for maindir, subdir, file_name_list in os.walk(path):
for filename in file_name_list:
apath = os.path.join(maindir, filename)
ext = os.path.splitext(apath)[1]
if ext in IMAGE_EXT:
image_names.append(apath)
return image_names
class Predictor(object):
def __init__(self, model, exp, cls_names=COCO_CLASSES, trt_file=None, decoder=None, device="cpu", legacy=False, ):
self.model = model
self.cls_names = cls_names
self.decoder = decoder
self.num_classes = exp.num_classes
self.confthre = exp.test_conf
self.nmsthre = exp.nmsthre
self.test_size = exp.test_size
self.device = device
self.preproc = ValTransform(legacy=legacy)
self.model = model.half()
def inference(self, img, img_path=None):
tensor_type = torch.cuda.HalfTensor
if self.device == "gpu":
img = img.cuda()
img = img.type(tensor_type)
with torch.no_grad():
t0 = time.time()
outputs, outputs_ori = self.model(img, nms_thresh=self.nmsthre)
logger.info("Infer time: {:.4f}s".format(time.time() - t0))
return outputs
def visual(self, output, img, ratio, cls_conf=0.0):
if output is None:
return img
bboxes = output[:, 0:4]
# preprocessing: resize
bboxes /= ratio
cls = output[:, 6]
scores = output[:, 4] * output[:, 5]
vis_res = vis(img, bboxes, scores, cls, cls_conf, self.cls_names)
return vis_res
def image_demo(predictor, vis_folder, path, current_time, save_result):
if os.path.isdir(path):
files = get_image_list(path)
else:
files = [path]
files.sort()
for image_name in files:
outputs, img_info = predictor.inference(image_name, [image_name])
result_image = predictor.visual(outputs[0], img_info, predictor.confthre)
if save_result:
save_folder = os.path.join(vis_folder, time.strftime("%Y_%m_%d_%H_%M_%S", current_time))
os.makedirs(save_folder, exist_ok=True)
save_file_name = os.path.join(save_folder, os.path.basename(image_name))
logger.info("Saving detection result in {}".format(save_file_name))
cv2.imwrite(save_file_name, result_image)
ch = cv2.waitKey(0)
if ch == 27 or ch == ord("q") or ch == ord("Q"):
break
def imageflow_demo(predictor, vis_folder, current_time, args):
gframe = args.gframe
cap = cv2.VideoCapture(args.path)
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) # float
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) # float
fps = cap.get(cv2.CAP_PROP_FPS)
save_folder = os.path.join(vis_folder, time.strftime("%Y_%m_%d_%H_%M_%S", current_time))
os.makedirs(save_folder, exist_ok=True)
ratio = min(predictor.test_size[0] / height, predictor.test_size[1] / width)
save_path = os.path.join(save_folder, args.path.split("/")[-1])
save_path='out.mp4'
logger.info(f"video save_path is {save_path}")
vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*"mp4v"), fps, (int(width), int(height)))
frames = []
outputs = []
ori_frames = []
while True:
ret_val, frame = cap.read()
if ret_val:
ori_frames.append(frame)
frame, _ = predictor.preproc(frame, None, exp.test_size)
frames.append(torch.tensor(frame))
else:
break
res = []
frame_len = len(frames)
index_list = list(range(frame_len))
random.seed(41)
random.shuffle(index_list)
random.seed(41)
random.shuffle(frames)
split_num = int(frame_len / (gframe)) #
for i in range(split_num):
res.append(frames[i * gframe:(i + 1) * gframe])
res.append(frames[(i + 1) * gframe:])
for ele in res:
if ele == []: continue
ele = torch.stack(ele)
t0 = time.time()
outputs.extend(predictor.inference(ele))
outputs = [j for _, j in sorted(zip(index_list, outputs))]
for output, img in zip(outputs, ori_frames[:len(outputs)]):
result_frame = predictor.visual(output, img, ratio, cls_conf=args.conf)
if args.save_result:
cv2.imshow("sdf",result_frame)
cv2.waitKey(0)
vid_writer.write(result_frame)
def main(exp, args):
if not args.experiment_name:
args.experiment_name = exp.exp_name
file_name = os.path.join(exp.output_dir, args.experiment_name)
os.makedirs(file_name, exist_ok=True)
vis_folder = None
if args.save_result:
vis_folder = os.path.join(file_name, "vis_res")
os.makedirs(vis_folder, exist_ok=True)
if args.trt:
args.device = "gpu"
logger.info("Args: {}".format(args))
if args.conf is not None:
exp.test_conf = args.conf
if args.nms is not None:
exp.nmsthre = args.nms
if args.tsize is not None:
exp.test_size = (args.tsize, args.tsize)
model = exp.get_model()
logger.info("Model Summary: {}".format(get_model_info(model, exp.test_size)))
if args.device == "gpu":
model.cuda()
model.eval()
if not args.trt:
if args.ckpt is None:
ckpt_file = os.path.join(file_name, "best_ckpt.pth")
else:
ckpt_file = args.ckpt
logger.info("loading checkpoint")
ckpt = torch.load(ckpt_file, map_location="cpu")
# load the model state dict
model.load_state_dict(ckpt["model"])
logger.info("loaded checkpoint done.")
if args.fuse:
logger.info("\tFusing model...")
model = fuse_model(model)
if args.trt:
assert not args.fuse, "TensorRT model is not support model fusing!"
trt_file = os.path.join(file_name, "model_trt.pth")
assert os.path.exists(trt_file), "TensorRT model is not found!\n Run python3 tools/trt.py first!"
model.head.decode_in_inference = False
decoder = model.head.decode_outputs
logger.info("Using TensorRT to inference")
else:
trt_file = None
decoder = None
if args.dataset == 'vid':
predictor = Predictor(model, exp, VID_classes, trt_file, decoder, args.device, args.legacy)
else:
predictor = Predictor(model, exp, COCO_CLASSES, trt_file, decoder, args.device, args.legacy)
current_time = time.localtime()
imageflow_demo(predictor, vis_folder, current_time, args)
if __name__ == "__main__":
args = make_parser().parse_args()
exp = get_exp(args.exp_file, args.name)
main(exp, args)