目录
yolov相关介绍
预训练是检测动物世界的动物的,1060显卡单帧需要300毫秒
yolov相关介绍
论文地址: https://arxiv.org/pdf/2208.09686.pdf
代码地址: https://github.com/YuHengsss/YOLOV
Model | size | mAP@50val | Speed 2080Ti(batch size=1) (ms) | weights |
---|---|---|---|---|
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 | |
YOLOV-l | 576 | 83.6 | 16.4 | |
YOLOV-x | 576 | 85.5 | 22.7 | |
YOLOV-x + post | 576 | 87.5 | - |
视频目标检测(VID)具有挑战性,因为目标外观的高度变化以及某些帧中的各种劣化。积极的一面是,与静止图像相比,在视频的某一帧中进行检测可以得到其他帧的支持。因此,如何跨不同帧聚合特征是VID问题的关键。
大多数现有的聚合算法都是为两阶段检测器定制的。但是,由于两阶段的性质,此类检测器通常在计算上很耗时。今天分享的研究者提出了一种简单而有效的策略来解决上述问题,该策略花费了边际开销,并显著提高了准确性。具体来说,与传统的两阶段流水线不同,研究者主张将区域级候选放在一阶段检测之后,以避免处理大量低质量候选。此外,构建了一个新的模块来评估目标框架与其参考框架之间的关系,并指导聚合。
进行了广泛的实验和消融研究以验证新提出设计的有效性,并揭示其在有效性和效率方面优于其他最先进的VID方法。基于YOLOX的模型可以实现可观的性能(例如,在单个2080Ti GPU上的ImageNet VID数据集上以超过30 FPS的速度达到87.5% AP50),使其对大规模或实时应用程序具有吸引力。
视频目标检测可以看作是静止图像目标检测的高级版本。直观地说,可以通过将帧一一输入静止图像目标检测器来处理视频序列。但是,通过这种方式,跨帧的时间信息将被浪费,这可能是消除/减少单个图像中发生的歧义的关键。
如上图所示,视频帧中经常出现运动模糊、相机散焦和遮挡等退化,显着增加了检测的难度。例如,仅通过查看上图中的最后一帧,人类很难甚至不可能分辨出物体在哪里和是什么。另一方面,视频序列可以提供比单个静止图像更丰富的信息。换言之,同一序列中的其他帧可能支持对某一帧的预测。因此,如何有效地聚合来自不同帧的时间消息对于准确性至关重要。从上图可以看出,研究者提出的方法给出了正确的答案。
03
新框架
考虑到视频的特性(各种退化与丰富的时间信息),而不是单独处理帧,如何从其他帧中为目标帧(关键帧)寻求支持信息对于提高视频检测的准确性起着关键作用。最近的尝试是在准确性上的显着提高证实了时间聚合对问题的重要性。然而,大多数现有方法都是基于两阶段的技术。
如前所述,与一级基础相比,它们的主要缺点是推理速度相对较慢。为了减轻这种限制,研究者将区域/特征选择放在单级检测器的预测头之后。
研究者选择YOLOX作为基础来展示研究者的主要主张。提出的框架如上图所示。
让我们回顾一下传统的两阶段管道:
1)首先“选择”大量候选区域作为提议;
2)确定每个提议是否是一个目标以及它属于哪个类。计算瓶颈主要来自于处理大量的低置信区域候选。
从上图可以看出,提出的框架也包含两个阶段。不同的是,它的第一阶段是预测(丢弃大量低置信度的区域),而第二阶段可以被视为区域级细化(通过聚合利用其他帧)。
通过这一原则,新的设计可以同时受益于一级检测器的效率和从时间聚合中获得的准确性。值得强调的是,如此微小的设计差异会导致性能上的巨大差异。所提出的策略可以推广到许多基础检测器,例如YOLOX、FCOS和PPYOLOE。
此外,考虑到softmax的特性,可能一小部分参考特征持有大部分权重。换句话说,它经常忽略低权重的特征,这限制了可能后续使用的参考特征的多样性。
为了避免这种风险,研究者引入了平均池化参考特征(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)