Spaces:
No application file
No application file
File size: 7,141 Bytes
6755a2d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
import math
from heapq import nsmallest
import logging
import numpy as np
import cv2
from moviepy.editor import (
VideoFileClip,
VideoClip,
concatenate_videoclips,
vfx,
TextClip,
CompositeVideoClip,
)
from ..utils.vision_util import (
cal_crop_coord,
round_up_coord_to_even,
cal_small_bbox_coord_of_big_bbox,
)
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
class VideoClipOperator(object):
def __init__(self, *args, **kwds) -> None:
pass
def __call__(self, *args, **kwds):
pass
def get_subclip_from_clipseq_by_time():
pass
def get_mvpclip_from_clip_by_time(
clips, final_duration: float, method: str = None, delta=0
):
"""根据视频长度,对齐到指定长度
Args:
clips (VideoClipSeq): 媒体文件片段序列
final_duration (float): 目标长度
method (int, optional): how to chang video length. Defaults to `None`.
speed: chang length by sample
cut: change length by cut middle length
None: change length accorrding difference of clip duration and final_duration. Defaults to None.
Returns:
VideoClip: 读取、对齐后moviepy VideoClip
"""
n_clips = len(clips)
video_clips = []
for i, clip in enumerate(clips):
start_delta = 0
end_delta = 0
# TODO: 为了解决夹帧问题,视视觉片段长音乐片段一些,便于只取中间部分。
## 适用于多个视频源的片段
## 适用于同一个视频源的 多个连续片段
if n_clips > 1:
if i == 0:
start_delta = delta
if i == n_clips - 1:
end_delta = delta
else:
start_delta = delta
end_delta = delta
video_clip = clip.get_mvp_clip(start_delta=start_delta, end_delta=end_delta)
video_clips.append(video_clip)
video_clips = concatenate_videoclips(clips=video_clips, method="compose")
video_clips = get_sub_mvpclip_by_time(
clip=video_clips, final_duration=final_duration, method=method
)
return video_clips
def get_sub_mvpclip_by_time(
clip, final_duration: float, method: str = "speed", center_ratio: float = 0.5
):
duration = clip.duration
center = duration * center_ratio
center = min(max(center, final_duration / 2), duration - final_duration / 2)
if method == "speed":
clip = clip.fx(vfx.speedx, final_duration=final_duration)
elif method == "cut" or method is None:
if duration >= final_duration:
t_start = center - final_duration / 2
t_end = center + final_duration / 2
clip = clip.subclip(t_start, t_end)
logger.debug(
"[cut_clip_time]: change length by cut: t_start={:.3f}, t_end={:.3f}, duration={:.3f}, final_duration={:.3f}".format(
t_start, t_end, duration, final_duration
)
)
clip = clip.fx(vfx.speedx, final_duration=final_duration)
else:
raise NotImplementedError(
"var_video_clip_length do not support mode={}".format(clip)
)
return clip
def crop_by_ratio(
clip, target_width_height_ratio, restricted_bbox=None, need_round2even=False
):
"""将原视频中的有效部分剪辑成目标宽高比,有效部分用坐标表示,一般来说是非黑边、非水印位置
Args:
clip (VideoClip): moviepy中的视频片段
target_width_height_ratio (float): 目标宽高比,常见的有2.35, 1.777, 0.75, 1, 0.5625
restricted_bbox ((float, float, float, float), optional): (x1, y1, x2, y2). Defaults to None.
Returns:
VideoClip: 剪辑好的moviepy视频片段
"""
width = clip.w
height = clip.h
target_coord = cal_crop_coord(
width=width,
height=height,
target_width_height_ratio=target_width_height_ratio,
restricted_bbox=restricted_bbox,
)
if need_round2even:
target_coord = round_up_coord_to_even(*target_coord)
clip = clip.crop(*target_coord)
return clip
def crop_by_perception(
clip,
target_width_height_ratio: float,
perception: dict,
need_round2even: bool = True,
):
"""将原视频中的有效部分剪辑成目标宽高比,有效部分用坐标表示,一般来说是非黑边、非水印位置
Args:
clip (VideoClip): moviepy中的视频片段
target_width_height_ratio (float): 目标宽高比,常见的有2.35, 1.777, 0.75, 1, 0.5625
Returns:
VideoClip: 剪辑好的moviepy视频片段
"""
return crop_by_face_clip(
clip, target_width_height_ratio, perception, need_round2even
)
def crop_by_face_clip(
clip,
target_width_height_ratio: float,
perception,
need_round2even: bool = True,
topk: int = 1,
):
w = clip.w
h = clip.h
target_w = target_width_height_ratio * h
perception_objs = []
if len(perception) > 0:
for i, frame_perception in enumerate(perception.clips):
if frame_perception.objs is not None:
for obj in frame_perception.objs:
perception_objs.append({"bbox": obj.bbox, "trackid": obj.trackid})
# 如果没有目标人物,则依然使用中间crop方式
if len(perception) == 0 or len(perception_objs) == 0:
return crop_by_ratio(
clip, target_width_height_ratio, need_round2even=need_round2even
)
topk_rolid = nsmallest(topk, [obj["trackid"] for obj in perception_objs])
topk_clip = [obj for obj in perception_objs if obj["trackid"] in topk_rolid]
# TODO: topk_clip 具有时间的先后顺序,先暂定取中间的obj的框作为参考
target_idx = int(len(topk_clip) // 2)
x1, y1, x2, y2 = topk_clip[target_idx]["bbox"]
# TODO:当前适用于 target_w 大于 obj_width对应的人体宽度,当不符合条件时存在crop部分人体部分情况,此时应该提前过滤。
obj_width = x2 - x1
obj_height = y2 - y1
obj_center_width = (x1 + x2) / 2
obj_center_height = (y1 + y2) / 2
target_coord = cal_small_bbox_coord_of_big_bbox(
bigbbox_width=w,
bigbbox_height=h,
smallbbox_width=target_w,
smallbbox_height=obj_height,
center_width=obj_center_width,
center_height=obj_center_height,
need_round2even=need_round2even,
)
clip = clip.mv.crop(*target_coord)
return clip
def crop_target_bbox(clip, target_coord, need_round2even=False):
if need_round2even:
target_coord = round_up_coord_to_even(*target_coord)
clip = clip.crop(*target_coord)
return clip
def crop_edge_2_even(clip):
w, h = clip.w, clip.h
# logger.debug("crop_target_bbox-round_up_coord_to_even, before {} {} {} {}".format(0, 0, w, h))
target_coord = round_up_coord_to_even(0, 0, w, h)
# logger.debug("crop_target_bbox-round_up_coord_to_even, after {} {} {} {}".format(target_coord[0], target_coord[1], target_coord[2], target_coord[3]))
clip = clip.crop(*target_coord)
return clip
|