Files
chaojie-ComfyUI_StreamingT2V/thirdparty/modelscope/preprocessors/cv/action_detection_mapper.py
T
2024-04-08 06:24:30 +08:00

187 lines
7.4 KiB
Python

# These codes are copied from modelscope revision c58451baead80d83281f063d12fb377fad415257
# Copyright (c) Alibaba, Inc. and its affiliates.
import copy
import random
import decord
import numpy as np
import torch
from detectron2.data.transforms import (ExtentTransform, RandomBrightness,
RandomFlip, ResizeShortestEdge)
from detectron2.structures import Boxes, Instances
from scipy.interpolate import interp1d
def inp_boxes(boxes: dict, start, end):
idxs = sorted([int(i) for i in boxes.keys()])
bbox = [boxes[str(i)] for i in idxs]
new_bboxes = []
for i in range(4):
f = interp1d(idxs, [b[i] for b in bbox])
new_b = f(list(range(start, end + 1)))
new_bboxes.append(new_b)
new_bboxes = np.stack(new_bboxes, axis=1)
return new_bboxes
def assign_label(start, end, data_dict):
"""
根据视频起始位置,以及标注的label,给这小段视频安排bbox检测标签
方法,取交集,交集占到样本的一半或者标签的一半,即将该label赋给样本
:param start: 起始帧号(含)
:param end: 结束帧号(含)
:param labels: 标注的label, 字符串形式
:return:[[行为,x1,y1,x2,y2],]
"""
if 'actions' not in data_dict:
return []
scale = data_dict['scale']
gt_labels = []
for action in data_dict['actions']:
low = max(int(action['start']), start)
high = min(int(action['end']), end)
inter = 0 if low > high else high - low
if inter > (end - start) * 0.7 or inter > (action['end']
- action['start']) * 0.7:
boxes = inp_boxes(action['boxes'], low, high)
box = boxes.mean(axis=0) / scale
label = [action['label']] + box.tolist()
gt_labels.append(label)
return gt_labels
class VideoDetMapper:
def __init__(self,
classes_id_map,
used_seconds=2,
input_frames=4,
is_train=True,
tile=False):
self.classes_id = classes_id_map
self.is_train = is_train
self.used_seconds = used_seconds
self.input_frames = input_frames
self.tile = tile
self.trans = [RandomBrightness(0.5, 1.5)]
self.tfm_gens = [
ResizeShortestEdge((480, 512, 544, 576, 608, 640, 672, 704, 736,
768) if is_train else 512,
1280 if is_train else 896, 'choice')
]
if is_train:
self.tfm_gens.append(RandomFlip())
def __call__(self, data_dict):
data_dict = copy.deepcopy(data_dict)
try:
data_dict = self._call(data_dict)
except Exception as e:
print(data_dict['path:FILE'], e)
data_dict = None
return data_dict
def _call(self, data_dict):
video_name = data_dict['path:FILE']
if data_dict['actions'] is not None:
data_dict['actions'] = eval(data_dict['actions'])
else:
data_dict['actions'] = []
v = decord.VideoReader(video_name, ctx=decord.cpu(0))
num_frames = len(v)
used_frames = max(int((1 + random.random()) * v.get_avg_fps()), 1)
if self.is_train:
start_idx = random.randint(0, max(0, num_frames - used_frames))
else:
start_idx = max(0, num_frames - used_frames) // 2
idxs = np.linspace(start_idx, min(start_idx + used_frames, num_frames) - 1, self.input_frames) \
.round().astype('int32').tolist()
imgs = v.get_batch(idxs).asnumpy()
del v
labels = assign_label(idxs[0], idxs[-1] + 1, data_dict)
bboxes = np.array([label[-4:] for label in labels])
if self.is_train:
if self.tile:
imgs, labels, bboxes = self.random_tile(
video_name, imgs, labels, bboxes, pos_choices=[1, 1, 2, 4])
else:
imgs, labels, bboxes = self.random_tile(
video_name, imgs, labels, bboxes, pos_choices=[1])
for g in self.trans:
tfm = g.get_transform(imgs)
imgs = tfm.apply_image(imgs)
imgs, bboxes = self.random_extent(imgs, bboxes)
for trans in self.tfm_gens:
tfm = trans.get_transform(imgs[0])
imgs = np.stack([tfm.apply_image(img) for img in imgs])
bboxes = tfm.apply_box(bboxes)
_, h, w, c = imgs.shape
data_dict['height'] = h
data_dict['width'] = w
gt_boxes = Boxes(torch.from_numpy(bboxes)) # XYXY_ABS
gt_classes = [self.classes_id[label[0]]
for label in labels] # N is background
instances = Instances((data_dict['height'], data_dict['width']))
instances.set('gt_boxes', gt_boxes)
instances.set('gt_classes',
torch.as_tensor(gt_classes, dtype=torch.int64))
data_dict['instances'] = instances
data_dict['frames'] = torch.as_tensor(
np.ascontiguousarray(imgs.transpose([3, 0, 1, 2])))
return data_dict
def random_tile(self, name, imgs, labels, bboxes,
pos_choices=(1, 1, 2, 4)):
_, h, w, c = imgs.shape
bboxes = bboxes.tolist()
if len(labels) == 0: # 负样本 1/2, 1, 2, 4
ratio = random.choice([0, 1, 2, 4])
if ratio == 0: # 随机取部分区域
h0, w0 = random.randint(0, h // 2), random.randint(0, w // 2)
imgs = imgs[:, h0:h0 + h // 2, w0:w0 + h // 2]
elif ratio == 2:
imgs = np.tile(imgs,
(1, 1, 2,
1)) if h > w else np.tile(imgs, (1, 2, 1, 1))
elif ratio == 4:
imgs = np.tile(imgs, (1, 2, 2, 1))
else: # 正样本 1, 2, 4
ratio = random.choice(pos_choices)
if ratio == 2:
labels = labels * 2
if h >= w: # 左右拼接
imgs = np.tile(imgs, (1, 1, 2, 1))
bbox2 = [[x1 + w, y1, x2 + w, y2]
for x1, y1, x2, y2 in bboxes]
else: # 上下拼接
imgs = np.tile(imgs, (1, 2, 1, 1))
bbox2 = [[x1, y1 + h, x2, y2 + h]
for x1, y1, x2, y2 in bboxes]
bboxes = bboxes + bbox2
elif ratio == 4:
labels = labels * 4
imgs = np.tile(imgs, (1, 2, 2, 1))
bbox2 = [[x1 + w, y1, x2 + w, y2] for x1, y1, x2, y2 in bboxes] + \
[[x1, y1 + h, x2, y2 + h] for x1, y1, x2, y2 in bboxes] + \
[[x1 + w, y1 + h, x2 + w, y2 + h] for x1, y1, x2, y2 in bboxes]
bboxes = bboxes + bbox2
bboxes = np.array(bboxes)
return imgs.copy(), labels, bboxes
def random_extent(self, imgs, bboxes):
t, h, w, c = imgs.shape
r_h, r_w = int(h * 0.1), int(w * 0.1)
x0, y0 = random.randint(-r_w, r_w), random.randint(-r_h, r_h)
x1, y1 = random.randint(w - r_w,
w + r_w), random.randint(h - r_h, h + r_h)
tfm = ExtentTransform((x0, y0, x1, y1), output_size=(y1 - y0, x1 - x0))
imgs = np.stack([tfm.apply_image(img) for img in imgs])
bboxes = tfm.apply_box(bboxes)
return imgs, bboxes