Added crop factor
This commit is contained in:
PowerHouseMan
2024-08-04 15:20:22 +09:00
parent bc14ee274a
commit 9ab8a41afb
5 changed files with 474 additions and 311 deletions
+105 -42
View File
@@ -8,12 +8,14 @@ import folder_paths
import comfy.utils
import time
import copy
import math
import dill
import torch.nn.functional as torchfn
import yaml
import random
from torchvision import transforms
from ultralytics import YOLO
from scipy.spatial.transform import Rotation as R
#import math
#import torch.nn.functional as torchfn
#from scipy.spatial.transform import Rotation as R
current_file_path = os.path.abspath(__file__)
current_directory = os.path.dirname(current_file_path)
@@ -29,7 +31,7 @@ from .LivePortrait.modules.motion_extractor import MotionExtractor
from .LivePortrait.modules.appearance_feature_extractor import AppearanceFeatureExtractor
from .LivePortrait.modules.stitching_retargeting_network import StitchingRetargetingNetwork
from collections import OrderedDict
import yaml
def tensor2pil(image):
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
@@ -42,8 +44,8 @@ def rgb_crop_batch(rgbs, region):
return rgbs[:, region[1]:region[3], region[0]:region[2]]
def get_rgb_size(rgb):
return rgb.shape[1], rgb.shape[0]
def create_transform_matrix(x, y, scale=1):
return np.float32([[scale, 0, x], [0, scale, y]])
def create_transform_matrix(x, y, s_x, s_y):
return np.float32([[s_x, 0, x], [0, s_y, y]])
def get_model_dir(m):
try:
@@ -221,9 +223,9 @@ class LP_Engine:
return self.detect_model
def detect_face(self, image_rgb):
def detect_face(self, image_rgb, crop_factor):
crop_factor = 1.7
#crop_factor = 1.7
bbox_drop_size = 10
detect_model = self.get_detect_model()
@@ -246,26 +248,69 @@ class LP_Engine:
kernel_x = x1 + bbox_w / 2
kernel_y = y1 + bbox_h / 2
new_x1, new_x2, crop_w = calc_crop_limit(kernel_x, w, crop_w)
# new_x1, new_x2, crop_w = calc_crop_limit(kernel_x, w, crop_w)
# if crop_w < crop_h:
# crop_h = crop_w
# new_y1, new_y2, crop_h = calc_crop_limit(kernel_y, h, crop_h)
# if crop_h < crop_w:
# crop_w = crop_h
# new_x1, new_x2, crop_w = calc_crop_limit(kernel_x, w, crop_w)
if crop_w < crop_h:
crop_h = crop_w
new_y1, new_y2, crop_h = calc_crop_limit(kernel_y, h, crop_h)
if crop_h < crop_w:
crop_w = crop_h
new_x1, new_x2, crop_w = calc_crop_limit(kernel_x, w, crop_w)
new_x1 = kernel_x - crop_w / 2
new_x2 = kernel_x + crop_w / 2
new_y1 = kernel_y - crop_h / 2
new_y2 = kernel_y + crop_h / 2
# square = [int(new_x1), int(new_y1), int(new_x2), int(new_y2)]
# mask =
return [int(new_x1), int(new_y1), int(new_x2), int(new_y2)]
print("Failed to detect face!!")
return [0, 0, w, h]
def crop_face(self, rgb_img):
region = self.detect_face(rgb_img)
face_image = rgb_crop(rgb_img, region)
return face_image, region
# def crop_face(self, rgb_img, crop_factor):
# square = self.detect_face(rgb_img, crop_factor)
# region = copy.deepcopy(square)
#
# w, h = get_rgb_size(rgb_img)
# is_different_size = False
# if region[0] < 0: region[0] = 0
# if region[1] < 0: region[1] = 0
# if w < region[2]: region[2] = w
# if h < region[3]: region[3] = h
# for i in range(4):
# if region[i] != square[i]:
# is_different_size = True
# break
# face_image = rgb_crop(rgb_img, region)
# if is_different_size:
# crop_trans_m = create_transform_matrix(max(-square[0], 0), max(-square[1], 0), 1)
# log(f"crop_trans_m:{crop_trans_m}, face_image.shape:{face_image.shape}, square:{square}")
# face_image = cv2.warpAffine(face_image, crop_trans_m, (square[2] - square[0], square[3] - square[1]), cv2.INTER_LINEAR)
# log(f"face_image.shape:{face_image.shape}")
#
# return face_image, region
def calc_face_region(self, square, dsize):
region = copy.deepcopy(square)
is_changed = False
if region[0] < 0: region[0] = 0
if region[1] < 0: region[1] = 0
if dsize[0] < region[2]: region[2] = dsize[0]
if dsize[1] < region[3]: region[3] = dsize[1]
for i in range(4):
if region[i] != square[i]:
is_changed = True
break
return region, is_changed
def expand_img(self, rgb_img, square):
#new_img = rgb_crop(rgb_img, face_region)
crop_trans_m = create_transform_matrix(max(-square[0], 0), max(-square[1], 0), 1, 1)
new_img = cv2.warpAffine(rgb_img, crop_trans_m, (square[2] - square[0], square[3] - square[1]),
cv2.INTER_LINEAR)
return new_img
def get_pipeline(self):
if self.pipeline == None:
@@ -293,27 +338,41 @@ class LP_Engine:
x = x.cuda()
return x
def GetMask(self):
def GetMaskImg(self):
if self.mask_img is None:
path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "./LivePortrait/utils/resources/mask_template.png")
self.mask_img = cv2.imread(path, cv2.IMREAD_COLOR)
return self.mask_img
def prepare_source(self, source_image, is_video = False):
def crop_face(self, img_rgb, crop_factor):
crop_region = self.detect_face(img_rgb, crop_factor)
face_region, is_changed = self.calc_face_region(crop_region, get_rgb_size(img_rgb))
face_img = rgb_crop(img_rgb, face_region)
if is_changed: face_img = self.expand_img(face_img, crop_region)
return face_img
def prepare_source(self, source_image, crop_factor, is_video = False):
print("Prepare source...")
engine = self.get_pipeline()
source_image_np = (source_image * 255).byte().numpy()
img_rgb = source_image_np[0]
face_img, crop_region = self.crop_face(img_rgb)
crop_region = self.detect_face(img_rgb, crop_factor)
face_region, is_changed = self.calc_face_region(crop_region, get_rgb_size(img_rgb))
scale = face_img.shape[0] / 512.
crop_trans_m = create_transform_matrix(crop_region[0], crop_region[1], scale)
mask_ori = cv2.warpAffine(self.GetMask(), crop_trans_m, get_rgb_size(img_rgb), cv2.INTER_LINEAR)
s_x = (face_region[2] - face_region[0]) / 512.
s_y = (face_region[3] - face_region[1]) / 512.
crop_trans_m = create_transform_matrix(crop_region[0], crop_region[1], s_x, s_y)
mask_ori = cv2.warpAffine(self.GetMaskImg(), crop_trans_m, get_rgb_size(img_rgb), cv2.INTER_LINEAR)
mask_ori = mask_ori.astype(np.float32) / 255.
if is_changed:
s = (crop_region[2] - crop_region[0]) / 512.
crop_trans_m = create_transform_matrix(crop_region[0], crop_region[1], s, s)
psi_list = []
for img_rgb in source_image_np:
face_img = rgb_crop(img_rgb, crop_region)
face_img = rgb_crop(img_rgb, face_region)
if is_changed: face_img = self.expand_img(face_img, crop_region)
i_s = self.prepare_src_image(face_img)
x_s_info = engine.get_kp_info(i_s)
f_s_user = engine.extract_feature_3d(i_s)
@@ -584,10 +643,11 @@ class Command:
self.change = change
self.keep = keep
class AdvancedLivePortrait:
def __init__(s):
s.src_images = None
s.driving_images = None
s.pbar = comfy.utils.ProgressBar(1)
def __init__(self):
self.src_images = None
self.driving_images = None
self.pbar = comfy.utils.ProgressBar(1)
self.crop_factor = None
@classmethod
def INPUT_TYPES(s):
@@ -596,6 +656,7 @@ class AdvancedLivePortrait:
"required": {
"retargeting_eyes": ("FLOAT", {"default": 0, "min": 0, "max": 1, "step": 0.01}),
"retargeting_mouth": ("FLOAT", {"default": 0, "min": 0, "max": 1, "step": 0.01}),
"crop_factor": ("FLOAT", {"default": 2, "min": 1.5, "max": 3, "step": 0.1}),
"turn_on": ("BOOLEAN", {"default": True}),
"command": ("STRING", {"multiline": True, "default": ""}),
},
@@ -650,7 +711,7 @@ class AdvancedLivePortrait:
return cmd_list, total_length
def run(self, retargeting_eyes, retargeting_mouth, turn_on, command,
def run(self, retargeting_eyes, retargeting_mouth, turn_on, command, crop_factor,
src_images=None, driving_images=None, motion_link=None):
if turn_on == False: return (None,None)
src_length = 1
@@ -662,12 +723,13 @@ class AdvancedLivePortrait:
if src_images != None:
src_length = len(src_images)
if id(src_images) != id(self.src_images):
if id(src_images) != id(self.src_images) or self.crop_factor != crop_factor:
self.crop_factor = crop_factor
self.src_images = src_images
if 1 < src_length:
self.psi_list = g_engine.prepare_source(src_images, True)
self.psi_list = g_engine.prepare_source(src_images, crop_factor, True)
else:
self.psi_list = [g_engine.prepare_source(src_images)]
self.psi_list = [g_engine.prepare_source(src_images, crop_factor)]
cmd_list, cmd_length = self.parsing_command(command, motion_link)
@@ -727,11 +789,9 @@ class AdvancedLivePortrait:
if d_0_es is None:
d_0_es = ExpressionSet(erst = (d_i_info['exp'], d_i_r, d_i_info['scale'], d_i_info['t']))
#d_i_es = d_0_es
retargeting(s_es.e, d_0_es.e, retargeting_eyes, (11, 13, 15, 16))
retargeting(s_es.e, d_0_es.e, retargeting_mouth, (14, 17, 19, 20))
#r_new = (r_d_i @ d_0_es.r.permute(0, 2, 1)) @ r_new
new_es.e += d_i_info['exp'] - d_0_es.e
new_es.r += d_i_r - d_0_es.r
new_es.t += d_i_info['t'] - d_0_es.t
@@ -760,6 +820,7 @@ class ExpressionEditor:
def __init__(self):
self.sample_image = None
self.src_image = None
self.crop_factor = None
@classmethod
def INPUT_TYPES(s):
@@ -784,6 +845,7 @@ class ExpressionEditor:
"src_ratio": ("FLOAT", {"default": 1, "min": 0, "max": 1, "step": 0.01, "display": display}),
"sample_ratio": ("FLOAT", {"default": 1, "min": 0, "max": 1, "step": 0.01, "display": display}),
"crop_factor": ("FLOAT", {"default": 2, "min": 1.5, "max": 3, "step": 0.1, "display": display}),
},
"optional": {"src_image": ("IMAGE",), "motion_link": ("EDITOR_LINK",),
@@ -804,7 +866,7 @@ class ExpressionEditor:
# OUTPUT_IS_LIST = (False,)
def run(self, rotate_pitch, rotate_yaw, rotate_roll, blink, eyebrow, wink, pupil_x, pupil_y, aaa, eee, woo, smile,
src_ratio, sample_ratio, src_image=None, sample_image=None, motion_link=None, add_exp=None):
src_ratio, sample_ratio, crop_factor, src_image=None, sample_image=None, motion_link=None, add_exp=None):
rotate_yaw = -rotate_yaw
new_editor_link = None
@@ -812,8 +874,9 @@ class ExpressionEditor:
self.psi = motion_link[0]
new_editor_link = motion_link.copy()
elif src_image != None:
if id(src_image) != id(self.src_image):
self.psi = g_engine.prepare_source(src_image)
if id(src_image) != id(self.src_image) or self.crop_factor != crop_factor:
self.crop_factor = crop_factor
self.psi = g_engine.prepare_source(src_image, crop_factor)
self.src_image = src_image
new_editor_link = []
new_editor_link.append(self.psi)
@@ -835,7 +898,7 @@ class ExpressionEditor:
if id(self.sample_image) != id(sample_image):
self.sample_image = sample_image
d_image_np = (sample_image * 255).byte().numpy()
d_face, _ = g_engine.crop_face(d_image_np[0])
d_face = g_engine.crop_face(d_image_np[0], 1.7)
i_d = pipeline.prepare_source(d_face)
self.d_info = pipeline.get_kp_info(i_d)
self.d_info['exp'][0, 5, 0] = 0
+129 -124
View File
@@ -202,58 +202,6 @@
}
}
},
{
"id": 23,
"type": "AdvancedLivePortrait",
"pos": [
1896.174414062501,
71.12568228759817
],
"size": {
"0": 235.1999969482422,
"1": 408
},
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "src_images",
"type": "IMAGE",
"link": 27
},
{
"name": "motion_link",
"type": "EDITOR_LINK",
"link": null
},
{
"name": "driving_images",
"type": "IMAGE",
"link": 28
}
],
"outputs": [
{
"name": "images",
"type": "IMAGE",
"links": [
29
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "AdvancedLivePortrait"
},
"widgets_values": [
0,
0,
true,
""
]
},
{
"id": 21,
"type": "ExpressionEditor",
@@ -263,7 +211,7 @@
],
"size": [
210,
642
666
],
"flags": {},
"order": 10,
@@ -328,7 +276,8 @@
0,
0,
1,
1
1,
2
]
},
{
@@ -453,58 +402,6 @@
}
}
},
{
"id": 28,
"type": "AdvancedLivePortrait",
"pos": [
1824.4893678710996,
860.0075893554671
],
"size": {
"0": 235.1999969482422,
"1": 408.0000305175781
},
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "src_images",
"type": "IMAGE",
"link": 32
},
{
"name": "motion_link",
"type": "EDITOR_LINK",
"link": null
},
{
"name": "driving_images",
"type": "IMAGE",
"link": 31
}
],
"outputs": [
{
"name": "images",
"type": "IMAGE",
"links": [
33
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "AdvancedLivePortrait"
},
"widgets_values": [
0,
0,
true,
""
]
},
{
"id": 30,
"type": "ExpressionEditor",
@@ -512,10 +409,10 @@
1287.4893678710926,
850.0075893554672
],
"size": {
"0": 210,
"1": 642
},
"size": [
210,
665.9999771118164
],
"flags": {},
"order": 9,
"mode": 0,
@@ -581,7 +478,8 @@
0,
1.3,
1,
1
1,
2
]
},
{
@@ -645,9 +543,9 @@
"hidden": false,
"paused": false,
"params": {
"filename": "AdvancedLivePortrait_00002.mp4",
"filename": "AdvancedLivePortrait_00007.mp4",
"subfolder": "",
"type": "temp",
"type": "output",
"format": "video/h264-mp4",
"frame_rate": 15
}
@@ -715,9 +613,9 @@
"hidden": false,
"paused": false,
"params": {
"filename": "AdvancedLivePortrait_00001.mp4",
"filename": "AdvancedLivePortrait_00008.mp4",
"subfolder": "",
"type": "temp",
"type": "output",
"format": "video/h264-mp4",
"frame_rate": 15
}
@@ -731,10 +629,10 @@
703.4307320312498,
25.292402087402362
],
"size": [
210.9286651611328,
314
],
"size": {
"0": 210.9286651611328,
"1": 314
},
"flags": {},
"order": 6,
"mode": 0,
@@ -763,6 +661,112 @@
"image"
]
},
{
"id": 28,
"type": "AdvancedLivePortrait",
"pos": [
1824.4893678710996,
860.0075893554671
],
"size": [
235.1999969482422,
628.0000305175781
],
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "src_images",
"type": "IMAGE",
"link": 32
},
{
"name": "motion_link",
"type": "EDITOR_LINK",
"link": null
},
{
"name": "driving_images",
"type": "IMAGE",
"link": 31
}
],
"outputs": [
{
"name": "images",
"type": "IMAGE",
"links": [
33
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "AdvancedLivePortrait"
},
"widgets_values": [
0,
0,
2,
"",
""
]
},
{
"id": 23,
"type": "AdvancedLivePortrait",
"pos": [
1896.174414062501,
71.12568228759817
],
"size": [
235.1999969482422,
628
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "src_images",
"type": "IMAGE",
"link": 27
},
{
"name": "motion_link",
"type": "EDITOR_LINK",
"link": null
},
{
"name": "driving_images",
"type": "IMAGE",
"link": 28
}
],
"outputs": [
{
"name": "images",
"type": "IMAGE",
"links": [
29
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "AdvancedLivePortrait"
},
"widgets_values": [
0,
0,
2,
"",
""
]
},
{
"id": 14,
"type": "ExpressionEditor",
@@ -772,7 +776,7 @@
],
"size": [
210,
642
666
],
"flags": {},
"order": 7,
@@ -837,7 +841,8 @@
0,
0,
1,
1
1,
2
]
}
],
@@ -972,10 +977,10 @@
"config": {},
"extra": {
"ds": {
"scale": 0.6209213230591555,
"scale": 0.751314800901578,
"offset": [
550.93444395092,
459.81407391929736
408.91997303598856,
268.57718967671127
]
}
},
Binary file not shown.

After

Width:  |  Height:  |  Size: 886 KiB

+240 -145
View File
@@ -1,139 +1,20 @@
{
"last_node_id": 10,
"last_node_id": 14,
"last_link_id": 8,
"nodes": [
{
"id": 3,
"type": "LoadImage",
"pos": [
465,
299
],
"size": {
"0": 315,
"1": 314
},
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
3
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3,
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"source_image.png",
"image"
]
},
{
"id": 6,
"type": "ExpressionEditor",
"pos": [
844,
301
],
"size": {
"0": 315,
"1": 642
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "src_image",
"type": "IMAGE",
"link": 3
},
{
"name": "motion_link",
"type": "EDITOR_LINK",
"link": null
},
{
"name": "sample_image",
"type": "IMAGE",
"link": null
},
{
"name": "add_exp",
"type": "EXP_DATA",
"link": null
}
],
"outputs": [
{
"name": "image",
"type": "IMAGE",
"links": null,
"shape": 3
},
{
"name": "motion_link",
"type": "EDITOR_LINK",
"links": [
5
],
"shape": 3,
"slot_index": 1
},
{
"name": "save_exp",
"type": "EXP_DATA",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "ExpressionEditor"
},
"widgets_values": [
0,
0,
0,
0,
0,
0,
-15,
0,
0,
0,
0,
0,
1,
1
]
},
{
"id": 8,
"type": "ExpressionEditor",
"pos": [
1205,
299
1300,
288
],
"size": [
210,
666.122442592595
],
"size": {
"0": 315,
"1": 642
},
"flags": {},
"order": 3,
"order": 7,
"mode": 0,
"inputs": [
{
@@ -197,22 +78,103 @@
0,
0,
1,
1
1,
1.7000000000000002
]
},
{
"id": 6,
"type": "ExpressionEditor",
"pos": [
1049,
286
],
"size": [
210,
669.4464425925951
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "src_image",
"type": "IMAGE",
"link": 3
},
{
"name": "motion_link",
"type": "EDITOR_LINK",
"link": null
},
{
"name": "sample_image",
"type": "IMAGE",
"link": null
},
{
"name": "add_exp",
"type": "EXP_DATA",
"link": null
}
],
"outputs": [
{
"name": "image",
"type": "IMAGE",
"links": null,
"shape": 3
},
{
"name": "motion_link",
"type": "EDITOR_LINK",
"links": [
5
],
"shape": 3,
"slot_index": 1
},
{
"name": "save_exp",
"type": "EXP_DATA",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "ExpressionEditor"
},
"widgets_values": [
0,
0,
0,
0,
0,
0,
-15,
0,
0,
0,
0,
0,
1,
1,
1.7000000000000002
]
},
{
"id": 9,
"type": "VHS_LoadVideo",
"pos": [
1551,
399
1548,
378
],
"size": [
235.1999969482422,
491.1999969482422
],
"flags": {},
"order": 1,
"order": 0,
"mode": 0,
"inputs": [
{
@@ -284,19 +246,128 @@
}
},
{
"id": 7,
"type": "AdvancedLivePortrait",
"id": 3,
"type": "LoadImage",
"pos": [
1823,
301
670,
283
],
"size": {
"0": 289.72344970703125,
"1": 513.248779296875
"0": 315,
"1": 314
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
3
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3,
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"source_image.png",
"image"
]
},
{
"id": 14,
"type": "Note",
"pos": [
667,
633
],
"size": {
"0": 210,
"1": 58
},
"flags": {},
"order": 2,
"mode": 0,
"properties": {
"text": ""
},
"widgets_values": [
"orginal src : motion0\n"
],
"color": "#432",
"bgcolor": "#653"
},
{
"id": 12,
"type": "Note",
"pos": [
1014,
973
],
"size": [
210,
58
],
"flags": {},
"order": 3,
"mode": 0,
"properties": {
"text": ""
},
"widgets_values": [
"motion1\n"
],
"color": "#432",
"bgcolor": "#653"
},
{
"id": 13,
"type": "Note",
"pos": [
1301,
974
],
"size": {
"0": 210,
"1": 58
},
"flags": {},
"order": 4,
"mode": 0,
"properties": {
"text": ""
},
"widgets_values": [
"motion2\n"
],
"color": "#432",
"bgcolor": "#653"
},
{
"id": 7,
"type": "AdvancedLivePortrait",
"pos": [
1824,
340
],
"size": [
235.1999969482422,
523.0364425925951
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "src_images",
@@ -331,23 +402,47 @@
"widgets_values": [
0,
0,
true,
1.7000000000000002,
"1 = 1:10\n2 = 5:10\n0 = 2:50\n1 = 2:0",
"1 = 1:10\n2 = 5:10\n0 = 2:50\n1 = 2:0"
]
},
{
"id": 11,
"type": "Note",
"pos": [
1823,
892
],
"size": [
539.0599993763458,
184.96444259259488
],
"flags": {},
"order": 5,
"mode": 0,
"properties": {
"text": ""
},
"widgets_values": [
"Command Description\n1 = 1:10\n2 = 5:10\n0 = 2:50\n1 = 2:0\n\n[Motion index] = [Changing frame length] : [Length of frames waiting for next motion]\n\nfor example, second line -> 2 = 5:10\nChange over 5 frames with motion 2, and wait for the next motion for 10 frames\n\n"
],
"color": "#432",
"bgcolor": "#653"
},
{
"id": 10,
"type": "VHS_VideoCombine",
"pos": [
2150,
307
2122,
244
],
"size": [
261.79998779296875,
565.7999877929688
],
"flags": {},
"order": 5,
"order": 9,
"mode": 0,
"inputs": [
{
@@ -396,7 +491,7 @@
"hidden": false,
"paused": false,
"params": {
"filename": "AnimateDiff_00001.mp4",
"filename": "AnimateDiff_00003.mp4",
"subfolder": "",
"type": "temp",
"format": "video/h264-mp4",
@@ -452,10 +547,10 @@
"config": {},
"extra": {
"ds": {
"scale": 0.7513148009015777,
"scale": 1,
"offset": [
38.677019273408085,
465.2026083711572
-410.5455448308912,
-80.52844259259504
]
}
},
Binary file not shown.

After

Width:  |  Height:  |  Size: 875 KiB