update v1.4.1

This commit is contained in:
zeyinzi.jzyz
2025-04-02 19:27:43 +08:00
parent 467652bd69
commit 6c8af8d7a8
31 changed files with 2134 additions and 58 deletions
+4
View File
@@ -25,6 +25,8 @@ if TYPE_CHECKING:
from scepter.modules.annotator.segmentation import ESAMAnnotator
from scepter.modules.annotator.sketch import SketchAnnotator
from scepter.modules.annotator.lama import LamaAnnotator
from scepter.modules.annotator.mask_aug import MaskAugAnnotator, MaskDrawAnnotator, MaskLayoutAnnotator
from scepter.modules.annotator.raft import RAFTAnnotator, RAFTVisAnnotator
else:
_import_structure = {
'base_annotator': ['GeneralAnnotator'],
@@ -48,6 +50,8 @@ else:
'segmentation': ['ESAMAnnotator'],
'sketch': ['SketchAnnotator'],
'lama': ['LamaAnnotator'],
'mask_aug': ['MaskAugAnnotator', 'MaskDrawAnnotator', 'MaskLayoutAnnotator'],
'raft': ['RAFTAnnotator', 'RAFTVisAnnotator'],
}
import sys
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
+127
View File
@@ -0,0 +1,127 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
import cv2
import numpy as np
import onnxruntime
def nms(boxes, scores, nms_thr):
"""Single class NMS implemented in Numpy."""
x1 = boxes[:, 0]
y1 = boxes[:, 1]
x2 = boxes[:, 2]
y2 = boxes[:, 3]
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
order = scores.argsort()[::-1]
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
xx1 = np.maximum(x1[i], x1[order[1:]])
yy1 = np.maximum(y1[i], y1[order[1:]])
xx2 = np.minimum(x2[i], x2[order[1:]])
yy2 = np.minimum(y2[i], y2[order[1:]])
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
ovr = inter / (areas[i] + areas[order[1:]] - inter)
inds = np.where(ovr <= nms_thr)[0]
order = order[inds + 1]
return keep
def multiclass_nms(boxes, scores, nms_thr, score_thr):
"""Multiclass NMS implemented in Numpy. Class-aware version."""
final_dets = []
num_classes = scores.shape[1]
for cls_ind in range(num_classes):
cls_scores = scores[:, cls_ind]
valid_score_mask = cls_scores > score_thr
if valid_score_mask.sum() == 0:
continue
else:
valid_scores = cls_scores[valid_score_mask]
valid_boxes = boxes[valid_score_mask]
keep = nms(valid_boxes, valid_scores, nms_thr)
if len(keep) > 0:
cls_inds = np.ones((len(keep), 1)) * cls_ind
dets = np.concatenate(
[valid_boxes[keep], valid_scores[keep, None], cls_inds], 1
)
final_dets.append(dets)
if len(final_dets) == 0:
return None
return np.concatenate(final_dets, 0)
def demo_postprocess(outputs, img_size, p6=False):
grids = []
expanded_strides = []
strides = [8, 16, 32] if not p6 else [8, 16, 32, 64]
hsizes = [img_size[0] // stride for stride in strides]
wsizes = [img_size[1] // stride for stride in strides]
for hsize, wsize, stride in zip(hsizes, wsizes, strides):
xv, yv = np.meshgrid(np.arange(wsize), np.arange(hsize))
grid = np.stack((xv, yv), 2).reshape(1, -1, 2)
grids.append(grid)
shape = grid.shape[:2]
expanded_strides.append(np.full((*shape, 1), stride))
grids = np.concatenate(grids, 1)
expanded_strides = np.concatenate(expanded_strides, 1)
outputs[..., :2] = (outputs[..., :2] + grids) * expanded_strides
outputs[..., 2:4] = np.exp(outputs[..., 2:4]) * expanded_strides
return outputs
def preprocess(img, input_size, swap=(2, 0, 1)):
if len(img.shape) == 3:
padded_img = np.ones((input_size[0], input_size[1], 3), dtype=np.uint8) * 114
else:
padded_img = np.ones(input_size, dtype=np.uint8) * 114
r = min(input_size[0] / img.shape[0], input_size[1] / img.shape[1])
resized_img = cv2.resize(
img,
(int(img.shape[1] * r), int(img.shape[0] * r)),
interpolation=cv2.INTER_LINEAR,
).astype(np.uint8)
padded_img[: int(img.shape[0] * r), : int(img.shape[1] * r)] = resized_img
padded_img = padded_img.transpose(swap)
padded_img = np.ascontiguousarray(padded_img, dtype=np.float32)
return padded_img, r
def inference_detector(session, oriImg):
input_shape = (640,640)
img, ratio = preprocess(oriImg, input_shape)
ort_inputs = {session.get_inputs()[0].name: img[None, :, :, :]}
output = session.run(None, ort_inputs)
predictions = demo_postprocess(output[0], input_shape)[0]
boxes = predictions[:, :4]
scores = predictions[:, 4:5] * predictions[:, 5:]
boxes_xyxy = np.ones_like(boxes)
boxes_xyxy[:, 0] = boxes[:, 0] - boxes[:, 2]/2.
boxes_xyxy[:, 1] = boxes[:, 1] - boxes[:, 3]/2.
boxes_xyxy[:, 2] = boxes[:, 0] + boxes[:, 2]/2.
boxes_xyxy[:, 3] = boxes[:, 1] + boxes[:, 3]/2.
boxes_xyxy /= ratio
dets = multiclass_nms(boxes_xyxy, scores, nms_thr=0.45, score_thr=0.1)
if dets is not None:
final_boxes, final_scores, final_cls_inds = dets[:, :4], dets[:, 4], dets[:, 5]
isscore = final_scores>0.3
iscat = final_cls_inds == 0
isbbox = [ i and j for (i, j) in zip(isscore, iscat)]
final_boxes = final_boxes[isbbox]
else:
final_boxes = np.array([])
return final_boxes
@@ -0,0 +1,362 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
from typing import List, Tuple
import cv2
import numpy as np
import onnxruntime as ort
def preprocess(
img: np.ndarray, out_bbox, input_size: Tuple[int, int] = (192, 256)
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
"""Do preprocessing for RTMPose model inference.
Args:
img (np.ndarray): Input image in shape.
input_size (tuple): Input image size in shape (w, h).
Returns:
tuple:
- resized_img (np.ndarray): Preprocessed image.
- center (np.ndarray): Center of image.
- scale (np.ndarray): Scale of image.
"""
# get shape of image
img_shape = img.shape[:2]
out_img, out_center, out_scale = [], [], []
if len(out_bbox) == 0:
out_bbox = [[0, 0, img_shape[1], img_shape[0]]]
for i in range(len(out_bbox)):
x0 = out_bbox[i][0]
y0 = out_bbox[i][1]
x1 = out_bbox[i][2]
y1 = out_bbox[i][3]
bbox = np.array([x0, y0, x1, y1])
# get center and scale
center, scale = bbox_xyxy2cs(bbox, padding=1.25)
# do affine transformation
resized_img, scale = top_down_affine(input_size, scale, center, img)
# normalize image
mean = np.array([123.675, 116.28, 103.53])
std = np.array([58.395, 57.12, 57.375])
resized_img = (resized_img - mean) / std
out_img.append(resized_img)
out_center.append(center)
out_scale.append(scale)
return out_img, out_center, out_scale
def inference(sess: ort.InferenceSession, img: np.ndarray) -> np.ndarray:
"""Inference RTMPose model.
Args:
sess (ort.InferenceSession): ONNXRuntime session.
img (np.ndarray): Input image in shape.
Returns:
outputs (np.ndarray): Output of RTMPose model.
"""
all_out = []
# build input
for i in range(len(img)):
input = [img[i].transpose(2, 0, 1)]
# build output
sess_input = {sess.get_inputs()[0].name: input}
sess_output = []
for out in sess.get_outputs():
sess_output.append(out.name)
# run model
outputs = sess.run(sess_output, sess_input)
all_out.append(outputs)
return all_out
def postprocess(outputs: List[np.ndarray],
model_input_size: Tuple[int, int],
center: Tuple[int, int],
scale: Tuple[int, int],
simcc_split_ratio: float = 2.0
) -> Tuple[np.ndarray, np.ndarray]:
"""Postprocess for RTMPose model output.
Args:
outputs (np.ndarray): Output of RTMPose model.
model_input_size (tuple): RTMPose model Input image size.
center (tuple): Center of bbox in shape (x, y).
scale (tuple): Scale of bbox in shape (w, h).
simcc_split_ratio (float): Split ratio of simcc.
Returns:
tuple:
- keypoints (np.ndarray): Rescaled keypoints.
- scores (np.ndarray): Model predict scores.
"""
all_key = []
all_score = []
for i in range(len(outputs)):
# use simcc to decode
simcc_x, simcc_y = outputs[i]
keypoints, scores = decode(simcc_x, simcc_y, simcc_split_ratio)
# rescale keypoints
keypoints = keypoints / model_input_size * scale[i] + center[i] - scale[i] / 2
all_key.append(keypoints[0])
all_score.append(scores[0])
return np.array(all_key), np.array(all_score)
def bbox_xyxy2cs(bbox: np.ndarray,
padding: float = 1.) -> Tuple[np.ndarray, np.ndarray]:
"""Transform the bbox format from (x,y,w,h) into (center, scale)
Args:
bbox (ndarray): Bounding box(es) in shape (4,) or (n, 4), formatted
as (left, top, right, bottom)
padding (float): BBox padding factor that will be multilied to scale.
Default: 1.0
Returns:
tuple: A tuple containing center and scale.
- np.ndarray[float32]: Center (x, y) of the bbox in shape (2,) or
(n, 2)
- np.ndarray[float32]: Scale (w, h) of the bbox in shape (2,) or
(n, 2)
"""
# convert single bbox from (4, ) to (1, 4)
dim = bbox.ndim
if dim == 1:
bbox = bbox[None, :]
# get bbox center and scale
x1, y1, x2, y2 = np.hsplit(bbox, [1, 2, 3])
center = np.hstack([x1 + x2, y1 + y2]) * 0.5
scale = np.hstack([x2 - x1, y2 - y1]) * padding
if dim == 1:
center = center[0]
scale = scale[0]
return center, scale
def _fix_aspect_ratio(bbox_scale: np.ndarray,
aspect_ratio: float) -> np.ndarray:
"""Extend the scale to match the given aspect ratio.
Args:
scale (np.ndarray): The image scale (w, h) in shape (2, )
aspect_ratio (float): The ratio of ``w/h``
Returns:
np.ndarray: The reshaped image scale in (2, )
"""
w, h = np.hsplit(bbox_scale, [1])
bbox_scale = np.where(w > h * aspect_ratio,
np.hstack([w, w / aspect_ratio]),
np.hstack([h * aspect_ratio, h]))
return bbox_scale
def _rotate_point(pt: np.ndarray, angle_rad: float) -> np.ndarray:
"""Rotate a point by an angle.
Args:
pt (np.ndarray): 2D point coordinates (x, y) in shape (2, )
angle_rad (float): rotation angle in radian
Returns:
np.ndarray: Rotated point in shape (2, )
"""
sn, cs = np.sin(angle_rad), np.cos(angle_rad)
rot_mat = np.array([[cs, -sn], [sn, cs]])
return rot_mat @ pt
def _get_3rd_point(a: np.ndarray, b: np.ndarray) -> np.ndarray:
"""To calculate the affine matrix, three pairs of points are required. This
function is used to get the 3rd point, given 2D points a & b.
The 3rd point is defined by rotating vector `a - b` by 90 degrees
anticlockwise, using b as the rotation center.
Args:
a (np.ndarray): The 1st point (x,y) in shape (2, )
b (np.ndarray): The 2nd point (x,y) in shape (2, )
Returns:
np.ndarray: The 3rd point.
"""
direction = a - b
c = b + np.r_[-direction[1], direction[0]]
return c
def get_warp_matrix(center: np.ndarray,
scale: np.ndarray,
rot: float,
output_size: Tuple[int, int],
shift: Tuple[float, float] = (0., 0.),
inv: bool = False) -> np.ndarray:
"""Calculate the affine transformation matrix that can warp the bbox area
in the input image to the output size.
Args:
center (np.ndarray[2, ]): Center of the bounding box (x, y).
scale (np.ndarray[2, ]): Scale of the bounding box
wrt [width, height].
rot (float): Rotation angle (degree).
output_size (np.ndarray[2, ] | list(2,)): Size of the
destination heatmaps.
shift (0-100%): Shift translation ratio wrt the width/height.
Default (0., 0.).
inv (bool): Option to inverse the affine transform direction.
(inv=False: src->dst or inv=True: dst->src)
Returns:
np.ndarray: A 2x3 transformation matrix
"""
shift = np.array(shift)
src_w = scale[0]
dst_w = output_size[0]
dst_h = output_size[1]
# compute transformation matrix
rot_rad = np.deg2rad(rot)
src_dir = _rotate_point(np.array([0., src_w * -0.5]), rot_rad)
dst_dir = np.array([0., dst_w * -0.5])
# get four corners of the src rectangle in the original image
src = np.zeros((3, 2), dtype=np.float32)
src[0, :] = center + scale * shift
src[1, :] = center + src_dir + scale * shift
src[2, :] = _get_3rd_point(src[0, :], src[1, :])
# get four corners of the dst rectangle in the input image
dst = np.zeros((3, 2), dtype=np.float32)
dst[0, :] = [dst_w * 0.5, dst_h * 0.5]
dst[1, :] = np.array([dst_w * 0.5, dst_h * 0.5]) + dst_dir
dst[2, :] = _get_3rd_point(dst[0, :], dst[1, :])
if inv:
warp_mat = cv2.getAffineTransform(np.float32(dst), np.float32(src))
else:
warp_mat = cv2.getAffineTransform(np.float32(src), np.float32(dst))
return warp_mat
def top_down_affine(input_size: dict, bbox_scale: dict, bbox_center: dict,
img: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""Get the bbox image as the model input by affine transform.
Args:
input_size (dict): The input size of the model.
bbox_scale (dict): The bbox scale of the img.
bbox_center (dict): The bbox center of the img.
img (np.ndarray): The original image.
Returns:
tuple: A tuple containing center and scale.
- np.ndarray[float32]: img after affine transform.
- np.ndarray[float32]: bbox scale after affine transform.
"""
w, h = input_size
warp_size = (int(w), int(h))
# reshape bbox to fixed aspect ratio
bbox_scale = _fix_aspect_ratio(bbox_scale, aspect_ratio=w / h)
# get the affine matrix
center = bbox_center
scale = bbox_scale
rot = 0
warp_mat = get_warp_matrix(center, scale, rot, output_size=(w, h))
# do affine transform
img = cv2.warpAffine(img, warp_mat, warp_size, flags=cv2.INTER_LINEAR)
return img, bbox_scale
def get_simcc_maximum(simcc_x: np.ndarray,
simcc_y: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""Get maximum response location and value from simcc representations.
Note:
instance number: N
num_keypoints: K
heatmap height: H
heatmap width: W
Args:
simcc_x (np.ndarray): x-axis SimCC in shape (K, Wx) or (N, K, Wx)
simcc_y (np.ndarray): y-axis SimCC in shape (K, Wy) or (N, K, Wy)
Returns:
tuple:
- locs (np.ndarray): locations of maximum heatmap responses in shape
(K, 2) or (N, K, 2)
- vals (np.ndarray): values of maximum heatmap responses in shape
(K,) or (N, K)
"""
N, K, Wx = simcc_x.shape
simcc_x = simcc_x.reshape(N * K, -1)
simcc_y = simcc_y.reshape(N * K, -1)
# get maximum value locations
x_locs = np.argmax(simcc_x, axis=1)
y_locs = np.argmax(simcc_y, axis=1)
locs = np.stack((x_locs, y_locs), axis=-1).astype(np.float32)
max_val_x = np.amax(simcc_x, axis=1)
max_val_y = np.amax(simcc_y, axis=1)
# get maximum value across x and y axis
mask = max_val_x > max_val_y
max_val_x[mask] = max_val_y[mask]
vals = max_val_x
locs[vals <= 0.] = -1
# reshape
locs = locs.reshape(N, K, 2)
vals = vals.reshape(N, K)
return locs, vals
def decode(simcc_x: np.ndarray, simcc_y: np.ndarray,
simcc_split_ratio) -> Tuple[np.ndarray, np.ndarray]:
"""Modulate simcc distribution with Gaussian.
Args:
simcc_x (np.ndarray[K, Wx]): model predicted simcc in x.
simcc_y (np.ndarray[K, Wy]): model predicted simcc in y.
simcc_split_ratio (int): The split ratio of simcc.
Returns:
tuple: A tuple containing center and scale.
- np.ndarray[float32]: keypoints in shape (K, 2) or (n, K, 2)
- np.ndarray[float32]: scores in shape (K,) or (n, K)
"""
keypoints, scores = get_simcc_maximum(simcc_x, simcc_y)
keypoints /= simcc_split_ratio
return keypoints, scores
def inference_pose(session, out_bbox, oriImg):
h, w = session.get_inputs()[0].shape[2:]
model_input_size = (w, h)
resized_img, center, scale = preprocess(oriImg, out_bbox, model_input_size)
outputs = inference(session, resized_img)
keypoints, scores = postprocess(outputs, model_input_size, center, scale)
return keypoints, scores
+299
View File
@@ -0,0 +1,299 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
import math
import numpy as np
import matplotlib
import cv2
eps = 0.01
def smart_resize(x, s):
Ht, Wt = s
if x.ndim == 2:
Ho, Wo = x.shape
Co = 1
else:
Ho, Wo, Co = x.shape
if Co == 3 or Co == 1:
k = float(Ht + Wt) / float(Ho + Wo)
return cv2.resize(x, (int(Wt), int(Ht)), interpolation=cv2.INTER_AREA if k < 1 else cv2.INTER_LANCZOS4)
else:
return np.stack([smart_resize(x[:, :, i], s) for i in range(Co)], axis=2)
def smart_resize_k(x, fx, fy):
if x.ndim == 2:
Ho, Wo = x.shape
Co = 1
else:
Ho, Wo, Co = x.shape
Ht, Wt = Ho * fy, Wo * fx
if Co == 3 or Co == 1:
k = float(Ht + Wt) / float(Ho + Wo)
return cv2.resize(x, (int(Wt), int(Ht)), interpolation=cv2.INTER_AREA if k < 1 else cv2.INTER_LANCZOS4)
else:
return np.stack([smart_resize_k(x[:, :, i], fx, fy) for i in range(Co)], axis=2)
def padRightDownCorner(img, stride, padValue):
h = img.shape[0]
w = img.shape[1]
pad = 4 * [None]
pad[0] = 0 # up
pad[1] = 0 # left
pad[2] = 0 if (h % stride == 0) else stride - (h % stride) # down
pad[3] = 0 if (w % stride == 0) else stride - (w % stride) # right
img_padded = img
pad_up = np.tile(img_padded[0:1, :, :]*0 + padValue, (pad[0], 1, 1))
img_padded = np.concatenate((pad_up, img_padded), axis=0)
pad_left = np.tile(img_padded[:, 0:1, :]*0 + padValue, (1, pad[1], 1))
img_padded = np.concatenate((pad_left, img_padded), axis=1)
pad_down = np.tile(img_padded[-2:-1, :, :]*0 + padValue, (pad[2], 1, 1))
img_padded = np.concatenate((img_padded, pad_down), axis=0)
pad_right = np.tile(img_padded[:, -2:-1, :]*0 + padValue, (1, pad[3], 1))
img_padded = np.concatenate((img_padded, pad_right), axis=1)
return img_padded, pad
def transfer(model, model_weights):
transfered_model_weights = {}
for weights_name in model.state_dict().keys():
transfered_model_weights[weights_name] = model_weights['.'.join(weights_name.split('.')[1:])]
return transfered_model_weights
def draw_bodypose(canvas, candidate, subset):
H, W, C = canvas.shape
candidate = np.array(candidate)
subset = np.array(subset)
stickwidth = 4
limbSeq = [[2, 3], [2, 6], [3, 4], [4, 5], [6, 7], [7, 8], [2, 9], [9, 10], \
[10, 11], [2, 12], [12, 13], [13, 14], [2, 1], [1, 15], [15, 17], \
[1, 16], [16, 18], [3, 17], [6, 18]]
colors = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0], [85, 255, 0], [0, 255, 0], \
[0, 255, 85], [0, 255, 170], [0, 255, 255], [0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], \
[170, 0, 255], [255, 0, 255], [255, 0, 170], [255, 0, 85]]
for i in range(17):
for n in range(len(subset)):
index = subset[n][np.array(limbSeq[i]) - 1]
if -1 in index:
continue
Y = candidate[index.astype(int), 0] * float(W)
X = candidate[index.astype(int), 1] * float(H)
mX = np.mean(X)
mY = np.mean(Y)
length = ((X[0] - X[1]) ** 2 + (Y[0] - Y[1]) ** 2) ** 0.5
angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1]))
polygon = cv2.ellipse2Poly((int(mY), int(mX)), (int(length / 2), stickwidth), int(angle), 0, 360, 1)
cv2.fillConvexPoly(canvas, polygon, colors[i])
canvas = (canvas * 0.6).astype(np.uint8)
for i in range(18):
for n in range(len(subset)):
index = int(subset[n][i])
if index == -1:
continue
x, y = candidate[index][0:2]
x = int(x * W)
y = int(y * H)
cv2.circle(canvas, (int(x), int(y)), 4, colors[i], thickness=-1)
return canvas
def draw_handpose(canvas, all_hand_peaks):
H, W, C = canvas.shape
edges = [[0, 1], [1, 2], [2, 3], [3, 4], [0, 5], [5, 6], [6, 7], [7, 8], [0, 9], [9, 10], \
[10, 11], [11, 12], [0, 13], [13, 14], [14, 15], [15, 16], [0, 17], [17, 18], [18, 19], [19, 20]]
for peaks in all_hand_peaks:
peaks = np.array(peaks)
for ie, e in enumerate(edges):
x1, y1 = peaks[e[0]]
x2, y2 = peaks[e[1]]
x1 = int(x1 * W)
y1 = int(y1 * H)
x2 = int(x2 * W)
y2 = int(y2 * H)
if x1 > eps and y1 > eps and x2 > eps and y2 > eps:
cv2.line(canvas, (x1, y1), (x2, y2), matplotlib.colors.hsv_to_rgb([ie / float(len(edges)), 1.0, 1.0]) * 255, thickness=2)
for i, keyponit in enumerate(peaks):
x, y = keyponit
x = int(x * W)
y = int(y * H)
if x > eps and y > eps:
cv2.circle(canvas, (x, y), 4, (0, 0, 255), thickness=-1)
return canvas
def draw_facepose(canvas, all_lmks):
H, W, C = canvas.shape
for lmks in all_lmks:
lmks = np.array(lmks)
for lmk in lmks:
x, y = lmk
x = int(x * W)
y = int(y * H)
if x > eps and y > eps:
cv2.circle(canvas, (x, y), 3, (255, 255, 255), thickness=-1)
return canvas
# detect hand according to body pose keypoints
# please refer to https://github.com/CMU-Perceptual-Computing-Lab/openpose/blob/master/src/openpose/hand/handDetector.cpp
def handDetect(candidate, subset, oriImg):
# right hand: wrist 4, elbow 3, shoulder 2
# left hand: wrist 7, elbow 6, shoulder 5
ratioWristElbow = 0.33
detect_result = []
image_height, image_width = oriImg.shape[0:2]
for person in subset.astype(int):
# if any of three not detected
has_left = np.sum(person[[5, 6, 7]] == -1) == 0
has_right = np.sum(person[[2, 3, 4]] == -1) == 0
if not (has_left or has_right):
continue
hands = []
#left hand
if has_left:
left_shoulder_index, left_elbow_index, left_wrist_index = person[[5, 6, 7]]
x1, y1 = candidate[left_shoulder_index][:2]
x2, y2 = candidate[left_elbow_index][:2]
x3, y3 = candidate[left_wrist_index][:2]
hands.append([x1, y1, x2, y2, x3, y3, True])
# right hand
if has_right:
right_shoulder_index, right_elbow_index, right_wrist_index = person[[2, 3, 4]]
x1, y1 = candidate[right_shoulder_index][:2]
x2, y2 = candidate[right_elbow_index][:2]
x3, y3 = candidate[right_wrist_index][:2]
hands.append([x1, y1, x2, y2, x3, y3, False])
for x1, y1, x2, y2, x3, y3, is_left in hands:
# pos_hand = pos_wrist + ratio * (pos_wrist - pos_elbox) = (1 + ratio) * pos_wrist - ratio * pos_elbox
# handRectangle.x = posePtr[wrist*3] + ratioWristElbow * (posePtr[wrist*3] - posePtr[elbow*3]);
# handRectangle.y = posePtr[wrist*3+1] + ratioWristElbow * (posePtr[wrist*3+1] - posePtr[elbow*3+1]);
# const auto distanceWristElbow = getDistance(poseKeypoints, person, wrist, elbow);
# const auto distanceElbowShoulder = getDistance(poseKeypoints, person, elbow, shoulder);
# handRectangle.width = 1.5f * fastMax(distanceWristElbow, 0.9f * distanceElbowShoulder);
x = x3 + ratioWristElbow * (x3 - x2)
y = y3 + ratioWristElbow * (y3 - y2)
distanceWristElbow = math.sqrt((x3 - x2) ** 2 + (y3 - y2) ** 2)
distanceElbowShoulder = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
width = 1.5 * max(distanceWristElbow, 0.9 * distanceElbowShoulder)
# x-y refers to the center --> offset to topLeft point
# handRectangle.x -= handRectangle.width / 2.f;
# handRectangle.y -= handRectangle.height / 2.f;
x -= width / 2
y -= width / 2 # width = height
# overflow the image
if x < 0: x = 0
if y < 0: y = 0
width1 = width
width2 = width
if x + width > image_width: width1 = image_width - x
if y + width > image_height: width2 = image_height - y
width = min(width1, width2)
# the max hand box value is 20 pixels
if width >= 20:
detect_result.append([int(x), int(y), int(width), is_left])
'''
return value: [[x, y, w, True if left hand else False]].
width=height since the network require squared input.
x, y is the coordinate of top left
'''
return detect_result
# Written by Lvmin
def faceDetect(candidate, subset, oriImg):
# left right eye ear 14 15 16 17
detect_result = []
image_height, image_width = oriImg.shape[0:2]
for person in subset.astype(int):
has_head = person[0] > -1
if not has_head:
continue
has_left_eye = person[14] > -1
has_right_eye = person[15] > -1
has_left_ear = person[16] > -1
has_right_ear = person[17] > -1
if not (has_left_eye or has_right_eye or has_left_ear or has_right_ear):
continue
head, left_eye, right_eye, left_ear, right_ear = person[[0, 14, 15, 16, 17]]
width = 0.0
x0, y0 = candidate[head][:2]
if has_left_eye:
x1, y1 = candidate[left_eye][:2]
d = max(abs(x0 - x1), abs(y0 - y1))
width = max(width, d * 3.0)
if has_right_eye:
x1, y1 = candidate[right_eye][:2]
d = max(abs(x0 - x1), abs(y0 - y1))
width = max(width, d * 3.0)
if has_left_ear:
x1, y1 = candidate[left_ear][:2]
d = max(abs(x0 - x1), abs(y0 - y1))
width = max(width, d * 1.5)
if has_right_ear:
x1, y1 = candidate[right_ear][:2]
d = max(abs(x0 - x1), abs(y0 - y1))
width = max(width, d * 1.5)
x, y = x0, y0
x -= width
y -= width
if x < 0:
x = 0
if y < 0:
y = 0
width1 = width * 2
width2 = width * 2
if x + width > image_width:
width1 = image_width - x
if y + width > image_height:
width2 = image_height - y
width = min(width1, width2)
if width >= 20:
detect_result.append([int(x), int(y), int(width)])
return detect_result
# get max index of 2d array
def npmax(array):
arrayindex = array.argmax(1)
arrayvalue = array.max(1)
i = arrayvalue.argmax()
j = arrayindex[i]
return i, j
@@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
import cv2
import numpy as np
import onnxruntime as ort
from .onnxdet import inference_detector
from .onnxpose import inference_pose
def HWC3(x):
assert x.dtype == np.uint8
if x.ndim == 2:
x = x[:, :, None]
assert x.ndim == 3
H, W, C = x.shape
assert C == 1 or C == 3 or C == 4
if C == 3:
return x
if C == 1:
return np.concatenate([x, x, x], axis=2)
if C == 4:
color = x[:, :, 0:3].astype(np.float32)
alpha = x[:, :, 3:4].astype(np.float32) / 255.0
y = color * alpha + 255.0 * (1.0 - alpha)
y = y.clip(0, 255).astype(np.uint8)
return y
def resize_image(input_image, resolution):
H, W, C = input_image.shape
H = float(H)
W = float(W)
k = float(resolution) / min(H, W)
H *= k
W *= k
H = int(np.round(H / 64.0)) * 64
W = int(np.round(W / 64.0)) * 64
img = cv2.resize(input_image, (W, H), interpolation=cv2.INTER_LANCZOS4 if k > 1 else cv2.INTER_AREA)
return img
class Wholebody:
def __init__(self, onnx_det, onnx_pose, device = 'cuda:0'):
providers = ['CPUExecutionProvider'
] if device == 'cpu' else ['CUDAExecutionProvider']
# onnx_det = 'annotator/ckpts/yolox_l.onnx'
# onnx_pose = 'annotator/ckpts/dw-ll_ucoco_384.onnx'
self.session_det = ort.InferenceSession(path_or_bytes=onnx_det, providers=providers)
self.session_pose = ort.InferenceSession(path_or_bytes=onnx_pose, providers=providers)
def __call__(self, ori_img):
det_result = inference_detector(self.session_det, ori_img)
keypoints, scores = inference_pose(self.session_pose, det_result, ori_img)
keypoints_info = np.concatenate(
(keypoints, scores[..., None]), axis=-1)
# compute neck joint
neck = np.mean(keypoints_info[:, [5, 6]], axis=1)
# neck score when visualizing pred
neck[:, 2:4] = np.logical_and(
keypoints_info[:, 5, 2:4] > 0.3,
keypoints_info[:, 6, 2:4] > 0.3).astype(int)
new_keypoints_info = np.insert(
keypoints_info, 17, neck, axis=1)
mmpose_idx = [
17, 6, 8, 10, 7, 9, 12, 14, 16, 13, 15, 2, 1, 4, 3
]
openpose_idx = [
1, 2, 3, 4, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17
]
new_keypoints_info[:, openpose_idx] = \
new_keypoints_info[:, mmpose_idx]
keypoints_info = new_keypoints_info
keypoints, scores = keypoints_info[
..., :2], keypoints_info[..., 2]
return keypoints, scores, det_result
+203
View File
@@ -0,0 +1,203 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
# Openpose
# Original from CMU https://github.com/CMU-Perceptual-Computing-Lab/openpose
# 2nd Edited by https://github.com/Hzzone/pytorch-openpose
# 3rd Edited by ControlNet
# 4th Edited by ControlNet (added face and correct hands)
# ``` requirements for cuda 12.1:
# onnxruntime==1.19
# onnxruntime-gpu==1.19
# ```
import os
import numpy as np
import torch
from PIL import Image
import cv2
from scepter.modules.annotator.base_annotator import BaseAnnotator
from scepter.modules.annotator.dwpose import util
from scepter.modules.annotator.dwpose.wholebody import (HWC3, Wholebody,
resize_image)
from scepter.modules.annotator.registry import ANNOTATORS
from scepter.modules.utils.distribute import we
from scepter.modules.utils.file_system import FS
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
def draw_pose(pose, H, W, use_hand=False, use_body=False, use_face=False):
bodies = pose['bodies']
faces = pose['faces']
hands = pose['hands']
candidate = bodies['candidate']
subset = bodies['subset']
canvas = np.zeros(shape=(H, W, 3), dtype=np.uint8)
if use_body:
canvas = util.draw_bodypose(canvas, candidate, subset)
if use_hand:
canvas = util.draw_handpose(canvas, hands)
if use_face:
canvas = util.draw_facepose(canvas, faces)
return canvas
@ANNOTATORS.register_class()
class DWposeAnnotator(BaseAnnotator):
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
with FS.get_from(cfg['DETECTION_MODEL'],
wait_finish=True) as onnx_det, FS.get_from(
cfg['POSE_MODEL'], wait_finish=True) as onnx_pose:
self.pose_estimation = Wholebody(onnx_det,
onnx_pose,
device=f'cuda:{we.device_id}')
self.resize_size = cfg.get('RESIZE_SIZE', 1024)
self.use_body = cfg.get('USE_BODY', True)
self.use_face = cfg.get('USE_FACE', True)
self.use_hand = cfg.get('USE_HAND', True)
@torch.no_grad()
@torch.inference_mode
def forward(self, image):
if isinstance(image, Image.Image):
image = np.array(image)
elif isinstance(image, torch.Tensor):
image = image.detach().cpu().numpy()
elif isinstance(image, np.ndarray):
image = image.copy()
else:
raise f'Unsurpport datatype{type(image)}, only surpport np.ndarray, torch.Tensor, Pillow Image.'
input_image = HWC3(image[..., ::-1])
return self.process(resize_image(input_image, self.resize_size),
image.shape[:2])
def process(self, ori_img, ori_shape):
ori_h, ori_w = ori_shape
ori_img = ori_img.copy()
H, W, C = ori_img.shape
with torch.no_grad():
candidate, subset, det_result = self.pose_estimation(ori_img)
nums, keys, locs = candidate.shape
candidate[..., 0] /= float(W)
candidate[..., 1] /= float(H)
body = candidate[:, :18].copy()
body = body.reshape(nums * 18, locs)
score = subset[:, :18]
for i in range(len(score)):
for j in range(len(score[i])):
if score[i][j] > 0.3:
score[i][j] = int(18 * i + j)
else:
score[i][j] = -1
un_visible = subset < 0.3
candidate[un_visible] = -1
foot = candidate[:, 18:24]
faces = candidate[:, 24:92]
hands = candidate[:, 92:113]
hands = np.vstack([hands, candidate[:, 113:]])
bodies = dict(candidate=body, subset=score)
pose = dict(bodies=bodies, hands=hands, faces=faces)
ret_data = {}
if self.use_body:
detected_map_body = draw_pose(pose, H, W, use_body=True)
detected_map_body = cv2.resize(
detected_map_body[..., ::-1], (ori_w, ori_h),
interpolation=cv2.INTER_LANCZOS4
if ori_h * ori_w > H * W else cv2.INTER_AREA)
ret_data['detected_map_body'] = detected_map_body
if self.use_face:
detected_map_face = draw_pose(pose, H, W, use_face=True)
detected_map_face = cv2.resize(
detected_map_face[..., ::-1], (ori_w, ori_h),
interpolation=cv2.INTER_LANCZOS4
if ori_h * ori_w > H * W else cv2.INTER_AREA)
ret_data['detected_map_face'] = detected_map_face
if self.use_body and self.use_face:
detected_map_bodyface = draw_pose(pose,
H,
W,
use_body=True,
use_face=True)
detected_map_bodyface = cv2.resize(
detected_map_bodyface[..., ::-1], (ori_w, ori_h),
interpolation=cv2.INTER_LANCZOS4
if ori_h * ori_w > H * W else cv2.INTER_AREA)
ret_data['detected_map_bodyface'] = detected_map_bodyface
if self.use_hand and self.use_body and self.use_face:
detected_map_handbodyface = draw_pose(pose,
H,
W,
use_hand=True,
use_body=True,
use_face=True)
detected_map_handbodyface = cv2.resize(
detected_map_handbodyface[..., ::-1], (ori_w, ori_h),
interpolation=cv2.INTER_LANCZOS4
if ori_h * ori_w > H * W else cv2.INTER_AREA)
ret_data[
'detected_map_handbodyface'] = detected_map_handbodyface
# convert_size
if det_result.shape[0] > 0:
w_ratio, h_ratio = ori_w / W, ori_h / H
det_result[..., ::2] *= h_ratio
det_result[..., 1::2] *= w_ratio
det_result = det_result.astype(np.int32)
# for det_tup in det_result:
# cv2.rectangle(detected_map, det_tup[2:].tolist(), det_tup[:2].tolist(), color=(255, 0, 0), thickness=3)
return ret_data, det_result
@ANNOTATORS.register_class()
class DWposeBodyAnnotator(DWposeAnnotator):
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
self.use_body, self.use_face, self.use_hand = True, False, False
@torch.no_grad()
@torch.inference_mode
def forward(self, image):
ret_data, det_result = super().forward(image)
return ret_data['detected_map_body']
@ANNOTATORS.register_class()
class DWposeFaceAnnotator(DWposeAnnotator):
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
self.use_body, self.use_face, self.use_hand = False, True, False
@torch.no_grad()
@torch.inference_mode
def forward(self, image):
ret_data, det_result = super().forward(image)
return ret_data['detected_map_face']
@ANNOTATORS.register_class()
class DWposeBodyFaceAnnotator(DWposeAnnotator):
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
self.use_body, self.use_face, self.use_hand = True, True, False
@torch.no_grad()
@torch.inference_mode
def forward(self, image):
ret_data, det_result = super().forward(image)
return ret_data['detected_map_bodyface']
+63
View File
@@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
import os
from abc import ABCMeta
import numpy as np
import torch
from PIL import Image
from scepter.modules.annotator.base_annotator import BaseAnnotator
from scepter.modules.annotator.registry import ANNOTATORS
from scepter.modules.utils.distribute import we
from scepter.modules.utils.file_system import FS
@ANNOTATORS.register_class()
class FaceAnnotator(BaseAnnotator, metaclass=ABCMeta):
para_dict = {}
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
from insightface.app import FaceAnalysis
local_path = FS.map_to_local(cfg.PRETRAINED_MODEL)[0]
local_model_path = os.path.join(local_path, 'models', cfg.MODEL_NAME)
FS.get_dir_to_local_dir(cfg.PRETRAINED_MODEL, local_model_path)
self.model = FaceAnalysis(name=cfg.MODEL_NAME, root=local_path, providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
self.model.prepare(ctx_id=we.device_id, det_size=(640, 640))
def forward(self, image=None):
if isinstance(image, Image.Image):
image = np.array(image)
elif isinstance(image, torch.Tensor):
image = image.detach().cpu().numpy()
elif isinstance(image, np.ndarray):
image = image.copy()
else:
raise f'Unsurpport datatype{type(image)}, only surpport np.ndarray, torch.Tensor, Pillow Image.'
# [dict_keys(['bbox', 'kps', 'det_score', 'landmark_3d_68', 'pose', 'landmark_2d_106', 'gender', 'age', 'embedding'])]
faces = self.model.get(image)
return faces
@ANNOTATORS.register_class()
class FaceMaskAnnotator(FaceAnnotator):
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
self.multi_face = cfg.get('MULTI_FACE', True)
def forward(self, image=None):
faces = super().forward(image)
if len(faces) > 0:
if not self.multi_face:
faces = faces[:1]
mask = np.zeros_like(image[:, :, 0])
for face in faces:
x_min, y_min, x_max, y_max = face['bbox'].tolist()
mask[int(y_min): int(y_max) + 1, int(x_min): int(x_max) + 1] = 255
return mask
else:
return np.zeros_like(image[:, :, 0])
@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
import random
import numpy as np
from scepter.modules.annotator.base_annotator import BaseAnnotator
from scepter.modules.annotator.registry import ANNOTATORS
from scepter.modules.utils.config import Config
@ANNOTATORS.register_class()
class FrameReferenceAnnotator(BaseAnnotator):
para_dict = {}
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
# first / last / firstlast / random
self.ref_cfg = cfg.get('REF_CFG', [{"mode": "first", "proba": 0.1},
{"mode": "last", "proba": 0.1},
{"mode": "firstlast", "proba": 0.1},
{"mode": "random", "proba": 0.1}])
self.ref_num = cfg.get('REF_NUM', 1)
self.ref_cfg = Config.get_dict(self.ref_cfg) if isinstance(
self.ref_cfg, Config) else self.ref_cfg
self.ref_color = cfg.get('REF_COLOR', 127.5)
def forward(self, frames, ref_cfg=None, ref_num=None):
ref_cfg = ref_cfg if ref_cfg is not None else self.ref_cfg
ref_cfg = [ref_cfg] if not isinstance(ref_cfg, list) else ref_cfg
probas = [item['proba'] if 'proba' in item else 1.0 / len(ref_cfg) for item in ref_cfg]
sel_ref_cfg = random.choices(ref_cfg, weights=probas, k=1)[0]
mode = sel_ref_cfg['mode'] if 'mode' in sel_ref_cfg else 'original'
ref_num = int(ref_num) if ref_num is not None else self.ref_num
frame_num = len(frames)
frame_num_range = list(range(frame_num))
if mode == "first":
sel_idx = frame_num_range[:ref_num]
elif mode == "last":
sel_idx = frame_num_range[-ref_num:]
elif mode == "firstlast":
sel_idx = frame_num_range[:ref_num] + frame_num_range[-ref_num:]
elif mode == "random":
sel_idx = random.sample(frame_num_range, ref_num)
else:
raise NotImplementedError
out_frames, out_masks = [], []
for i in range(frame_num):
if i in sel_idx:
out_frame = frames[i]
out_mask = np.zeros_like(frames[i][:, :, 0])
else:
out_frame = np.ones_like(frames[i]) * self.ref_color
out_mask = np.ones_like(frames[i][:, :, 0]) * 255
out_frames.append(out_frame)
out_masks.append(out_mask)
return out_frames, out_masks
+450
View File
@@ -0,0 +1,450 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
import random
from abc import ABCMeta
from functools import partial
import numpy as np
import torch
from PIL import Image, ImageDraw
import cv2
from scepter.modules.annotator.base_annotator import BaseAnnotator
from scepter.modules.annotator.registry import ANNOTATORS
from scepter.modules.utils.config import Config
from scepter.modules.utils.file_system import FS
from scipy import ndimage
from scipy.spatial import ConvexHull
from skimage.draw import polygon
@ANNOTATORS.register_class()
class MaskDrawAnnotator(BaseAnnotator, metaclass=ABCMeta):
para_dict = {}
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
self.task_type = cfg.get('TASK_TYPE', 'input_box')
def forward(self, mask=None, image=None, input_box=None, task_type=None):
task_type = task_type if task_type is not None else self.task_type
if mask is not None:
if isinstance(mask, Image.Image):
mask = np.array(mask)
elif isinstance(mask, torch.Tensor):
mask = mask.detach().cpu().numpy()
elif isinstance(mask, np.ndarray):
mask = mask.copy()
else:
raise f'Unsurpport datatype{type(mask)}, only surpport np.ndarray, torch.Tensor, Pillow Image.'
if image is not None:
if isinstance(image, Image.Image):
image = np.array(image)
elif isinstance(image, torch.Tensor):
image = image.detach().cpu().numpy()
elif isinstance(image, np.ndarray):
image = image.copy()
else:
raise f'Unsurpport datatype{type(image)}, only surpport np.ndarray, torch.Tensor, Pillow Image.'
mask_shape = mask.shape
if task_type == 'mask_point':
scribble = mask.transpose(1, 0)
labeled_array, num_features = ndimage.label(scribble >= 255)
centers = ndimage.center_of_mass(scribble, labeled_array,
range(1, num_features + 1))
centers = np.array(centers)
out_mask = np.zeros(mask_shape, dtype=np.uint8)
hull = ConvexHull(centers)
hull_vertices = centers[hull.vertices]
rr, cc = polygon(hull_vertices[:, 1], hull_vertices[:, 0],
mask_shape)
out_mask[rr, cc] = 255
elif task_type == 'mask_box':
scribble = mask.transpose(1, 0)
labeled_array, num_features = ndimage.label(scribble >= 255)
centers = ndimage.center_of_mass(scribble, labeled_array,
range(1, num_features + 1))
centers = np.array(centers)
# (x1, y1, x2, y2)
x_min = centers[:, 0].min()
x_max = centers[:, 0].max()
y_min = centers[:, 1].min()
y_max = centers[:, 1].max()
out_mask = np.zeros(mask_shape, dtype=np.uint8)
out_mask[int(y_min):int(y_max) + 1,
int(x_min):int(x_max) + 1] = 255
if image is not None:
out_image = image[int(y_min):int(y_max) + 1,
int(x_min):int(x_max) + 1]
elif task_type == 'input_box':
if isinstance(input_box, list):
input_box = np.array(input_box)
x_min, y_min, x_max, y_max = input_box
out_mask = np.zeros(mask_shape, dtype=np.uint8)
out_mask[int(y_min):int(y_max) + 1,
int(x_min):int(x_max) + 1] = 255
if image is not None:
out_image = image[int(y_min):int(y_max) + 1,
int(x_min):int(x_max) + 1]
elif task_type == 'mask':
out_mask = mask
else:
raise NotImplementedError
if image is not None:
return out_image, out_mask
else:
return out_mask
@ANNOTATORS.register_class()
class MaskAugAnnotator(BaseAnnotator, metaclass=ABCMeta):
para_dict = {}
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
# original / original_expand / hull / hull_expand / bbox / bbox_expand
self.mask_cfg = cfg.get('MASK_CFG', [{
'mode': 'original',
'proba': 0.1
}, {
'mode': 'original_expand',
'proba': 0.1
}, {
'mode': 'hull',
'proba': 0.1
}, {
'mode': 'hull_expand',
'proba': 0.1,
'kwargs': {
'expand_rate': 0.2
}
}, {
'mode': 'bbox',
'proba': 0.1
}, {
'mode': 'bbox_expand',
'proba': 0.1,
'kwargs': {
'min_expand_rate': 0.2,
'max_expand_rate': 0.5
}
}])
self.mask_cfg = Config.get_dict(self.mask_cfg) if isinstance(
self.mask_cfg, Config) else self.mask_cfg
def forward(self, mask, mask_cfg=None):
mask_cfg = mask_cfg if mask_cfg is not None else self.mask_cfg
if not isinstance(mask, list):
is_batch = False
masks = [mask]
else:
is_batch = True
masks = mask
mask_func = self.get_mask_func(mask_cfg)
# print(mask_func)
aug_masks = []
for submask in masks:
mask = self.get_mask(submask)
valid, large, h, w, bbox = self.get_mask_info(mask)
# print(valid, large, h, w, bbox)
if valid:
mask = mask_func(mask, bbox, h, w)
else:
mask = mask.astype(np.uint8)
aug_masks.append(mask)
return aug_masks if is_batch else aug_masks[0]
def get_mask(self, mask):
if isinstance(mask, Image.Image):
mask = np.array(mask)
elif isinstance(mask, torch.Tensor):
mask = mask.detach().cpu().numpy()
elif isinstance(mask, np.ndarray):
mask = mask.copy()
else:
raise f'Unsurpport datatype{type(mask)}, only surpport np.ndarray, torch.Tensor, Pillow Image.'
return mask
def get_mask_info(self, mask):
h, w = mask.shape
locs = mask.nonzero()
valid = True
if len(locs) < 1 or locs[0].shape[0] < 1 or locs[1].shape[0] < 1:
valid = False
return valid, False, h, w, [0, 0, 0, 0]
left, right = np.min(locs[1]), np.max(locs[1])
top, bottom = np.min(locs[0]), np.max(locs[0])
bbox = [left, top, right, bottom]
large = False
if (right - left + 1) * (bottom - top + 1) > 0.9 * h * w:
large = True
return valid, large, h, w, bbox
def get_expand_params(self, mask_kwargs):
if 'expand_rate' in mask_kwargs:
expand_rate = mask_kwargs['expand_rate']
elif 'min_expand_rate' in mask_kwargs and 'max_expand_rate' in mask_kwargs:
expand_rate = random.uniform(mask_kwargs['min_expand_rate'],
mask_kwargs['max_expand_rate'])
else:
expand_rate = 0.3
if 'expand_iters' in mask_kwargs:
expand_iters = mask_kwargs['expand_iters']
else:
expand_iters = random.randint(1, 10)
if 'expand_lrtp' in mask_kwargs:
expand_lrtp = mask_kwargs['expand_lrtp']
else:
expand_lrtp = [
random.random(),
random.random(),
random.random(),
random.random()
]
return expand_rate, expand_iters, expand_lrtp
def get_mask_func(self, mask_cfg):
if not isinstance(mask_cfg, list):
mask_cfg = [mask_cfg]
probas = [
item['proba'] if 'proba' in item else 1.0 / len(mask_cfg)
for item in mask_cfg
]
sel_mask_cfg = random.choices(mask_cfg, weights=probas, k=1)[0]
mode = sel_mask_cfg['mode'] if 'mode' in sel_mask_cfg else 'original'
mask_kwargs = sel_mask_cfg[
'kwargs'] if 'kwargs' in sel_mask_cfg else {}
if mode == 'random':
mode = random.choice([
'original', 'original_expand', 'hull', 'hull_expand', 'bbox',
'bbox_expand'
])
if mode == 'original':
mask_func = partial(self.generate_mask)
elif mode == 'original_expand':
expand_rate, expand_iters, expand_lrtp = self.get_expand_params(
mask_kwargs)
mask_func = partial(self.generate_mask,
expand_rate=expand_rate,
expand_iters=expand_iters,
expand_lrtp=expand_lrtp)
elif mode == 'hull':
clockwise = random.choice([
True, False
]) if 'clockwise' not in mask_kwargs else mask_kwargs['clockwise']
mask_func = partial(self.generate_hull_mask, clockwise=clockwise)
elif mode == 'hull_expand':
expand_rate, expand_iters, expand_lrtp = self.get_expand_params(
mask_kwargs)
clockwise = random.choice([
True, False
]) if 'clockwise' not in mask_kwargs else mask_kwargs['clockwise']
mask_func = partial(self.generate_hull_mask,
clockwise=clockwise,
expand_rate=expand_rate,
expand_iters=expand_iters,
expand_lrtp=expand_lrtp)
elif mode == 'bbox':
mask_func = partial(self.generate_bbox_mask)
elif mode == 'bbox_expand':
expand_rate, expand_iters, expand_lrtp = self.get_expand_params(
mask_kwargs)
mask_func = partial(self.generate_bbox_mask,
expand_rate=expand_rate,
expand_iters=expand_iters,
expand_lrtp=expand_lrtp)
else:
raise NotImplementedError
return mask_func
def generate_mask(self,
mask,
bbox,
h,
w,
expand_rate=None,
expand_iters=None,
expand_lrtp=None):
bin_mask = mask.astype(np.uint8)
if expand_rate:
bin_mask = self.rand_expand_mask(bin_mask, bbox, h, w, expand_rate,
expand_iters, expand_lrtp)
return bin_mask
@staticmethod
def rand_expand_mask(mask,
bbox,
h,
w,
expand_rate=None,
expand_iters=None,
expand_lrtp=None):
expand_rate = 0.3 if expand_rate is None else expand_rate
expand_iters = random.randint(
1, 10) if expand_iters is None else expand_iters
expand_lrtp = [
random.random(),
random.random(),
random.random(),
random.random()
] if expand_lrtp is None else expand_lrtp
# print('iters', expand_iters, 'expand_rate', expand_rate, 'expand_lrtp', expand_lrtp)
# mask = np.squeeze(mask)
left, top, right, bottom = bbox
# mask expansion
box_w = (right - left + 1) * expand_rate
box_h = (bottom - top + 1) * expand_rate
left_, right_ = int(
expand_lrtp[0] * min(box_w, left / 2) / expand_iters), int(
expand_lrtp[1] * min(box_w, (w - right) / 2) / expand_iters)
top_, bottom_ = int(
expand_lrtp[2] * min(box_h, top / 2) / expand_iters), int(
expand_lrtp[3] * min(box_h, (h - bottom) / 2) / expand_iters)
kernel_size = max(left_, right_, top_, bottom_)
if kernel_size > 0:
kernel = np.zeros((kernel_size * 2, kernel_size * 2),
dtype=np.uint8)
new_left, new_right = kernel_size - right_, kernel_size + left_
new_top, new_bottom = kernel_size - bottom_, kernel_size + top_
kernel[new_top:new_bottom + 1, new_left:new_right + 1] = 1
mask = mask.astype(np.uint8)
mask = cv2.dilate(mask, kernel,
iterations=expand_iters).astype(np.uint8)
# mask = new_mask - (mask / 2).astype(np.uint8)
# mask = np.expand_dims(mask, axis=-1)
return mask
@staticmethod
def _convexhull(image, clockwise):
# print('clockwise', clockwise)
contours, hierarchy = cv2.findContours(image, 2, 1)
cnt = np.concatenate(contours) # merge all regions
hull = cv2.convexHull(cnt, clockwise=clockwise)
hull = np.squeeze(hull, axis=1).astype(np.float32).tolist()
hull = [tuple(x) for x in hull]
return hull # b, 1, 2
def generate_hull_mask(self,
mask,
bbox,
h,
w,
clockwise=None,
expand_rate=None,
expand_iters=None,
expand_lrtp=None):
clockwise = random.choice([True, False
]) if clockwise is None else clockwise
hull = self._convexhull(mask, clockwise)
mask_img = Image.new('L', (w, h), 0)
pt_list = hull
mask_img_draw = ImageDraw.Draw(mask_img)
mask_img_draw.polygon(pt_list, fill=255)
bin_mask = np.array(mask_img).astype(np.uint8)
if expand_rate:
bin_mask = self.rand_expand_mask(bin_mask, bbox, h, w, expand_rate,
expand_iters, expand_lrtp)
return bin_mask
def generate_bbox_mask(self,
mask,
bbox,
h,
w,
expand_rate=None,
expand_iters=None,
expand_lrtp=None):
left, top, right, bottom = bbox
bin_mask = np.zeros((h, w), dtype=np.uint8)
bin_mask[top:bottom + 1, left:right + 1] = 255
if expand_rate:
bin_mask = self.rand_expand_mask(bin_mask, bbox, h, w, expand_rate,
expand_iters, expand_lrtp)
return bin_mask
@ANNOTATORS.register_class()
class MaskLayoutAnnotator(BaseAnnotator, metaclass=ABCMeta):
para_dict = {}
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
ram_tag_color = cfg.get('RAM_TAG_COLOR', None)
default_color = cfg.get('DEFAULT_COLOR', [0, 0, 0])
self.use_aug = cfg.get('USE_AUG', False)
self.color_dict = {'default': tuple(default_color)}
if ram_tag_color is not None:
with FS.get_object(ram_tag_color) as object:
lines = object.decode('utf-8').strip().split('\n')
lines = [id_name_color.split('#;#') for id_name_color in lines]
self.color_dict.update({
id_name_color[1]: tuple(eval(id_name_color[2]))
for id_name_color in lines
})
if self.use_aug:
mask_aug_dict = {'NAME': 'MaskAugAnnotator'}
mask_aug_cfg = Config(cfg_dict=mask_aug_dict, load=False)
self.mask_aug_anno = ANNOTATORS.build(mask_aug_cfg)
def find_contours(self, mask):
# @mask: gray cv2 image
# contours, hier = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
contours, hier = cv2.findContours(mask, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
return contours
def draw_contours(self, canvas, contour, color):
canvas = np.ascontiguousarray(canvas, dtype=np.uint8)
canvas = cv2.drawContours(canvas, contour, -1, color, thickness=3)
return canvas
def get_mask(self, mask):
if isinstance(mask, Image.Image):
mask = np.array(mask)
elif isinstance(mask, torch.Tensor):
mask = mask.detach().cpu().numpy()
elif isinstance(mask, np.ndarray):
mask = mask.copy()
else:
raise f'Unsurpport datatype{type(mask)}, only surpport np.ndarray, torch.Tensor, Pillow Image.'
return mask
def forward(self, mask=None, color=None, label=None, mask_cfg=None):
if not isinstance(mask, list):
is_batch = False
mask = [mask]
else:
is_batch = True
if label is not None and label in self.color_dict:
color = self.color_dict[label]
elif color is not None:
color = color
else:
color = self.color_dict['default']
ret_data = []
for sub_mask in mask:
sub_mask = self.get_mask(sub_mask)
if self.use_aug:
sub_mask = self.mask_aug_anno(sub_mask, mask_cfg)
canvas = np.ones((sub_mask.shape[0], sub_mask.shape[1], 3)) * 255
contour = self.find_contours(sub_mask)
frame = self.draw_contours(canvas, contour, color)
ret_data.append(frame)
if is_batch:
return ret_data
else:
return ret_data[0]
+8 -2
View File
@@ -98,9 +98,15 @@ class OutpaintingAnnotator(BaseAnnotator, metaclass=ABCMeta):
draw.rectangle(
(left + (self.mask_blur * 2 if left > 0 else 0), up +
(self.mask_blur * 2 if up > 0 else 0), mask.width - right -
(self.mask_blur * 2 if right > 0 else 0), mask.height - down -
(self.mask_blur * 2 if down > 0 else 0)),
(self.mask_blur * 2 if right > 0 else 0) - 1, mask.height - down -
(self.mask_blur * 2 if down > 0 else 0) - 1),
fill='black')
# draw.rectangle(
# (left + (self.mask_blur * 2 if left > 0 else 0), up +
# (self.mask_blur * 2 if up > 0 else 0), left + src_width -
# (self.mask_blur * 2 if right > 0 else 0), up + src_height -
# (self.mask_blur * 2 if down > 0 else 0)),
# fill='black')
else:
bbox = self.get_box(np.array(mask))
if bbox is None:
+62
View File
@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
import torch
import random
import numpy as np
import argparse
from scepter.modules.annotator.base_annotator import BaseAnnotator
from scepter.modules.annotator.registry import ANNOTATORS
from scepter.modules.utils.config import Config
from scepter.modules.utils.distribute import we
from scepter.modules.utils.file_system import FS
try:
from raft import RAFT
from raft.utils.utils import InputPadder
from raft.utils import flow_viz
except:
import warnings
warnings.warn("ignore raft import, please pip install raft.")
@ANNOTATORS.register_class()
class RAFTAnnotator(BaseAnnotator):
para_dict = {}
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
params = {
"small": False,
"mixed_precision": False,
"alternate_corr": False
}
params = argparse.Namespace(**params)
model = RAFT(params)
if cfg.PRETRAINED_MODEL is not None:
with FS.get_from(cfg.PRETRAINED_MODEL,
wait_finish=True) as local_path:
model.load_state_dict({k.replace('module.', ''): v for k, v in torch.load(local_path, map_location="cpu", weights_only=True).items()})
self.model = model.to(we.device_id).eval()
def forward(self, frames):
# frames / RGB
frames = [torch.from_numpy(frame.astype(np.uint8)).permute(2, 0, 1).float()[None].to(we.device_id) for frame in frames]
flow_up_list, flow_up_vis_list = [], []
with torch.no_grad():
for i, (image1, image2) in enumerate(zip(frames[:-1], frames[1:])):
padder = InputPadder(image1.shape)
image1, image2 = padder.pad(image1, image2)
flow_low, flow_up = self.model(image1, image2, iters=20, test_mode=True)
flow_up = flow_up[0].permute(1, 2, 0).cpu().numpy()
flow_up_vis = flow_viz.flow_to_image(flow_up)
flow_up_list.append(flow_up)
flow_up_vis_list.append(flow_up_vis)
return flow_up_list, flow_up_vis_list # RGB
@ANNOTATORS.register_class()
class RAFTVisAnnotator(RAFTAnnotator):
def forward(self, frames):
flow_up_list, flow_up_vis_list = super().forward(frames)
return flow_up_vis_list[:1] + flow_up_vis_list
@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
import random
from abc import ABCMeta
import cv2
import numpy as np
import torch
from PIL import Image
from scepter.modules.annotator.base_annotator import BaseAnnotator
from scepter.modules.annotator.registry import ANNOTATORS
from scepter.modules.utils.config import Config, dict_to_yaml
@ANNOTATORS.register_class()
class RegionCanvasAnnotator(BaseAnnotator, metaclass=ABCMeta):
para_dict = {}
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
self.scale_range = cfg.get('SCALE_RANGE', [0.75, 1.0])
self.canvas_value = cfg.get('CANVAS_VALUE', 255)
self.use_resize = cfg.get('USE_RESIZE', True)
self.use_canvas = cfg.get('USE_CANVAS', True)
self.use_aug = cfg.get('USE_AUG', False)
if self.use_aug:
mask_aug_dict = {'NAME': 'MaskAugAnnotator'}
mask_aug_cfg = Config(cfg_dict=mask_aug_dict, load=False)
self.mask_aug_anno = ANNOTATORS.build(mask_aug_cfg)
def forward(self,
image,
mask,
mask_cfg=None):
if isinstance(image, Image.Image):
image = np.array(image)
elif isinstance(image, torch.Tensor):
image = image.detach().cpu().numpy()
elif isinstance(image, np.ndarray):
image = image
else:
raise f'Unsurpport datatype{type(image)}, only surpport np.ndarray, torch.Tensor, Pillow Image.'
mask = np.array(mask).astype(np.uint8)
image_h, image_w = image.shape[:2]
if self.use_aug:
mask = self.mask_aug_anno(mask, mask_cfg)
# get region with white bg
image[np.array(mask) == 0] = self.canvas_value
x, y, w, h = cv2.boundingRect(mask)
region_crop = image[y:y + h, x:x + w]
if self.use_resize:
# resize region
scale_min, scale_max = self.scale_range
scale_factor = random.uniform(scale_min, scale_max)
new_w, new_h = int(image_w * scale_factor), int(image_h * scale_factor)
obj_scale_factor = min(new_w/w, new_h/h)
new_w = int(w * obj_scale_factor)
new_h = int(h * obj_scale_factor)
region_crop_resized = cv2.resize(region_crop, (new_w, new_h), interpolation=cv2.INTER_AREA)
else:
region_crop_resized = region_crop
if self.use_canvas:
# plot region into canvas
new_canvas = np.ones_like(image) * self.canvas_value
max_x = max(0, image_w - new_w)
max_y = max(0, image_h - new_h)
new_x = random.randint(0, max_x)
new_y = random.randint(0, max_y)
new_canvas[new_y:new_y + new_h, new_x:new_x + new_w] = region_crop_resized
else:
new_canvas = region_crop_resized
return new_canvas
@staticmethod
def get_config_template():
return dict_to_yaml('ANNOTATORS',
__class__.__name__,
RegionCanvasAnnotator.para_dict,
set_name=True)
@ANNOTATORS.register_class()
class RegionCanvasCropAnnotator(RegionCanvasAnnotator):
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
self.use_resize, self.use_canvas = False, False
@@ -0,0 +1,153 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
from abc import ABCMeta
import numpy as np
import torch
from PIL import Image
from scipy import ndimage
try:
from sklearn.cluster import KMeans
except:
import warnings
warnings.warn("ignore sklearn import, please pip install scikit-learn.")
from scepter.modules.annotator.base_annotator import BaseAnnotator
from scepter.modules.annotator.registry import ANNOTATORS
from scepter.modules.utils.config import dict_to_yaml
from scepter.modules.utils.file_system import FS
import pycocotools.mask as mask_utils
def single_mask_to_rle(mask):
rle = mask_utils.encode(np.array(mask[:, :, None], order="F", dtype="uint8"))[0]
rle["counts"] = rle["counts"].decode("utf-8")
return rle
def single_rle_to_mask(rle):
mask = np.array(mask_utils.decode(rle)).astype(np.uint8)
return mask
def single_mask_to_xyxy(mask):
bbox = np.zeros((4), dtype=int)
rows, cols = np.where(np.array(mask))
if len(rows) > 0 and len(cols) > 0:
x_min, x_max = np.min(cols), np.max(cols)
y_min, y_max = np.min(rows), np.max(rows)
bbox[:] = [x_min, y_min, x_max, y_max]
return bbox.tolist()
@ANNOTATORS.register_class()
class SAM2DrawVideoAnnotator(BaseAnnotator, metaclass=ABCMeta):
para_dict = {}
def __init__(self, cfg, logger=None):
super().__init__(cfg, logger=logger)
self.task_type = cfg.get('TASK_TYPE', 'input_box')
from sam2.build_sam import build_sam2_video_predictor
config_path = FS.get_from(cfg.CONFIG_PATH, local_path=cfg.CONFIG_LOCAL_PATH, wait_finish=True)
pretrained_model = FS.get_from(cfg.PRETRAINED_MODEL, wait_finish=True)
self.video_predictor = build_sam2_video_predictor(config_path, pretrained_model, fill_hole_area=0)
def forward(self,
video,
input_box=None,
mask=None,
task_type=None):
task_type = task_type if task_type is not None else self.task_type
if mask is not None:
if isinstance(mask, Image.Image):
mask = np.array(mask)
elif isinstance(mask, torch.Tensor):
mask = mask.detach().cpu().numpy()
elif isinstance(mask, np.ndarray):
mask = mask.copy()
else:
raise f'Unsurpport datatype{type(mask)}, only surpport np.ndarray, torch.Tensor, Pillow Image.'
if task_type == 'mask_point':
if len(mask.shape) == 3:
scribble = mask.transpose(2, 1, 0)[0]
else:
scribble = mask.transpose(1, 0) # (H, W) -> (W, H)
labeled_array, num_features = ndimage.label(scribble >= 255)
centers = ndimage.center_of_mass(scribble, labeled_array,
range(1, num_features + 1))
point_coords = np.array(centers)
point_labels = np.array([1] * len(centers))
sample = {
'points': point_coords,
'labels': point_labels
}
elif task_type == 'mask_box':
if len(mask.shape) == 3:
scribble = mask.transpose(2, 1, 0)[0]
else:
scribble = mask.transpose(1, 0) # (H, W) -> (W, H)
labeled_array, num_features = ndimage.label(scribble >= 255)
centers = ndimage.center_of_mass(scribble, labeled_array,
range(1, num_features + 1))
centers = np.array(centers)
# (x1, y1, x2, y2)
x_min = centers[:, 0].min()
x_max = centers[:, 0].max()
y_min = centers[:, 1].min()
y_max = centers[:, 1].max()
bbox = np.array([x_min, y_min, x_max, y_max])
sample = {'box': bbox}
elif task_type == 'input_box':
if isinstance(input_box, list):
input_box = np.array(input_box)
sample = {'box': input_box}
elif task_type == 'mask':
sample = {'mask': mask}
else:
raise NotImplementedError
ann_frame_idx = 0
object_id = 0
with (torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16)):
inference_state = self.video_predictor.init_state(video_path=video)
if task_type in ['mask_point', 'mask_box', 'input_box']:
_, out_obj_ids, out_mask_logits = self.video_predictor.add_new_points_or_box(
inference_state=inference_state,
frame_idx=ann_frame_idx,
obj_id=object_id,
**sample
)
elif task_type in ['mask']:
_, out_obj_ids, out_mask_logits = self.video_predictor.add_new_mask(
inference_state=inference_state,
frame_idx=ann_frame_idx,
obj_id=object_id,
**sample
)
else:
raise NotImplementedError
video_segments = {} # video_segments contains the per-frame segmentation results
for out_frame_idx, out_obj_ids, out_mask_logits in self.video_predictor.propagate_in_video(inference_state):
frame_segments = {}
for i, out_obj_id in enumerate(out_obj_ids):
mask = (out_mask_logits[i] > 0.0).cpu().numpy().squeeze(0)
frame_segments[out_obj_id] = {
"mask": single_mask_to_rle(mask),
"mask_area": int(mask.sum()),
"mask_box": single_mask_to_xyxy(mask),
}
video_segments[out_frame_idx] = frame_segments
ret_data = {
"annotations": video_segments
}
return ret_data
@staticmethod
def get_config_template():
return dict_to_yaml('ANNOTATORS',
__class__.__name__,
SAM2DrawVideoAnnotator.para_dict,
set_name=True)
+2 -1
View File
@@ -304,9 +304,10 @@ class DataObject(object):
delimiter = sampler_config.get('DELIMITER', ',')
path_prefix = sampler_config.get('PATH_PREFIX', '')
prompt_prefix = sampler_config.get('PROMPT_PREFIX', '')
oss_prefix = sampler_config.get('OSS_PREFIX', '')
return MultiLevelBatchSampler(batch_size, index_file, image_size,
fields, delimiter, path_prefix,
prompt_prefix, rank, seed)
prompt_prefix, oss_prefix, rank, seed)
def build_dataset_config(cfg, registry, logger=None, *args, **kwargs):
+4 -2
View File
@@ -79,6 +79,7 @@ class MultiLevelBatchSamplerMultiSource(BaseSampler):
self.num_fields = len(self.fields)
self.delimiter = cfg.get('DELIMITER', ',')
self.path_prefix = cfg.get('PATH_PREFIX', '')
oss_prefix = cfg.get('OSS_PREFIX', '')
common_prob = cfg.get('PROB', 1)
sub_data_weights = cfg.get('SUB_DATA_WEIGHTS', None)
sub_data_weights = {} if sub_data_weights is None else sub_data_weights.get_dict(
@@ -137,7 +138,7 @@ class MultiLevelBatchSamplerMultiSource(BaseSampler):
f"{p * common_prob} and samples'num: {sub_data['total']} in this cluster."
)
self.rng = np.random.default_rng(self.seed + we.rank)
self.oss_prefix = '/'.join(index_file.split('/')[:3])
self.oss_prefix = '/'.join(index_file.split('/')[:3]) if (oss_prefix is None or oss_prefix == '') and index_file.startswith('oss') else oss_prefix
self.index_dir = os.path.dirname(index_file)
def __iter__(self):
@@ -434,6 +435,7 @@ class MultiLevelBatchSampler(BaseSampler):
delimiter=',',
path_prefix='',
prompt_prefix='',
oss_prefix='',
rank=0,
seed=8888):
self.batch_size = batch_size
@@ -457,7 +459,7 @@ class MultiLevelBatchSampler(BaseSampler):
'index_level': 1,
'num_fields': self.num_fields
}
self.oss_prefix = '/'.join(index_file.split('/')[:3])
self.oss_prefix = '/'.join(index_file.split('/')[:3]) if (oss_prefix is None or oss_prefix == '') and index_file.startswith('oss') else oss_prefix
self.index_dir = os.path.dirname(index_file)
def __iter__(self):
+12 -1
View File
@@ -3,8 +3,10 @@
import copy
import torch.nn as nn
from scepter.modules.utils.config import dict_to_yaml
from scepter.modules.utils.distribute import gather_data, we
from scepter.modules.utils.model import get_parameter_dtype
from scepter.modules.utils.probe import (ProbeData, merge_gathered_probe,
register_data)
@@ -43,13 +45,15 @@ class BaseModel(nn.Module):
self._dist_data[key][k] += v
else:
self._dist_data[key][k] = v
def collect_probe(self):
probe_data_dict = self._probe_data
for k, v in self._modules.items():
if isinstance(getattr(self, k), BaseModel):
for kk, vv in getattr(self, k).collect_probe().items():
probe_data_dict[f'{k}/{kk}'] = vv
probe_data_dict[f'{k}/{kk}'] = vv
return probe_data_dict
def probe_data(self):
gather_probe_data = gather_data(self._probe_data)
_dist_data_list = gather_data([self._dist_data])
@@ -97,6 +101,13 @@ class BaseModel(nn.Module):
self._probe_data = {}
return ret_data
@property
def model_dtype(self):
"""
`torch.dtype`: The dtype of the module (assuming that all the module parameters have the same dtype).
"""
return get_parameter_dtype(self)
def clear_probe(self):
self._probe_data.clear()
+16 -4
View File
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
import torch
from scepter.modules.utils.config import Config
from scepter.modules.utils.registry import Registry, build_from_config
@@ -15,17 +17,25 @@ def build_model(cfg, registry, logger=None, *args, **kwargs):
raise TypeError(f'Config must be type dict, got {type(cfg)}')
if cfg.have('PRETRAINED_MODEL'):
pretrain_cfg = cfg.PRETRAINED_MODEL
if pretrain_cfg is not None and not isinstance(pretrain_cfg, (str, list)):
if pretrain_cfg is not None and not isinstance(pretrain_cfg,
(str, list)):
raise TypeError('Pretrain parameter must be a string or list')
else:
pretrain_cfg = None
device = cfg.get("DEVICE", None)
if cfg.get('MODEL_DTYPE', None):
default_dtype = getattr(torch, cfg.MODEL_DTYPE)
ori_default_dtype = torch.get_default_dtype()
torch.set_default_dtype(default_dtype)
device = cfg.get('DEVICE', None)
model = build_from_config(cfg, registry, logger=logger, *args, **kwargs)
if cfg.get('MODEL_DTYPE', None):
torch.set_default_dtype(ori_default_dtype)
if pretrain_cfg is not None:
if hasattr(model, 'load_pretrained_model'):
model.load_pretrained_model(pretrain_cfg)
return model
def build_diffusion(cfg, registry, logger=None, *args, **kwargs):
""" After build model, load pretrained model if exists key `pretrain`.
@@ -37,11 +47,13 @@ def build_diffusion(cfg, registry, logger=None, *args, **kwargs):
raise TypeError(f'Config must be type dict, got {type(cfg)}')
return build_from_config(cfg, registry, logger=logger, *args, **kwargs)
def build_scheduler(cfg, registry, logger=None, *args, **kwargs):
if not isinstance(cfg, Config):
raise TypeError(f'Config must be type dict, got {type(cfg)}')
return build_from_config(cfg, registry, logger=logger, *args, **kwargs)
def build_diffusion_sampler(cfg, registry, logger=None, *args, **kwargs):
if not isinstance(cfg, Config):
raise TypeError(f'Config must be type dict, got {type(cfg)}')
@@ -60,7 +72,7 @@ LOSSES = Registry('LOSSES', build_func=build_model)
TUNERS = Registry('TUNERS', build_func=build_model)
# reigister cls for diffusion.
DIFFUSIONS = Registry('DIFFUSIONS', build_func=build_diffusion)
NOISE_SCHEDULERS = Registry('NOISE_SCHEDULERS', build_func=build_diffusion)
DIFFUSION_SAMPLERS = Registry('DIFFUSION_SAMPLERS', build_func=build_diffusion_sampler)
DIFFUSION_SAMPLERS = Registry('DIFFUSION_SAMPLERS',
build_func=build_diffusion_sampler)
+4 -2
View File
@@ -111,9 +111,11 @@ def pack_imagelist_into_tensor(image_list):
image_tensor.append(img.view(c, h * w).transpose(1, 0)) # h*w, c
shapes.append((h, w))
image_tensor = pad_sequence(image_tensor, batch_first=True).permute(0, 2, 1) # b, c, l
image_tensor = pad_sequence(image_tensor,
batch_first=True).permute(0, 2, 1) # b, c, l
return image_tensor, shapes
def limit_batch_data(batch_data_list, log_num):
if log_num and log_num > 0:
batch_data_list_limited = []
@@ -123,4 +125,4 @@ def limit_batch_data(batch_data_list, log_num):
batch_data_list_limited.append(sub_data)
return batch_data_list_limited
else:
return batch_data_list
return batch_data_list
+2 -1
View File
@@ -196,6 +196,7 @@ class LatentDiffusionSolver(BaseSolver):
self.logger.info('Use fsdp as the backend of ddp.')
else:
self.logger.info('Use default backend.')
self.find_unused_parameters = cfg.get('FIND_UNUSED_PARAMETERS', False)
self.use_scaler = cfg.get('USE_SCALER', True)
self.enable_gradscaler = cfg.get('ENABLE_GRADSCALER', False)
self.use_orig_params = cfg.get('USE_ORIG_PARAMS', False)
@@ -408,7 +409,7 @@ class LatentDiffusionSolver(BaseSolver):
self.model,
device_ids=[torch.cuda.current_device()],
output_device=torch.cuda.current_device(),
find_unused_parameters=False)
find_unused_parameters=self.find_unused_parameters)
self.optimizer = OPTIMIZERS.build(
self.cfg.OPTIMIZER,
logger=self.logger,
+2
View File
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
from __future__ import annotations
import ast
import logging
import os
+64 -11
View File
@@ -12,7 +12,6 @@ import yaml
from scepter.modules.utils.logger import StdMsg
_SECURE_KEYWORDS = [
'ENDPOINT', 'BUCKET', 'OSS_AK', 'OSS_SK', 'OSS', 'TOKEN', 'APPKEY'
'SECRET', 'ACCESS_ID', 'ACCESS_KEY', 'PASSWORD', 'TEMP_DIR'
@@ -21,7 +20,11 @@ _SECURE_KEYWORDS = [
_SECURE_VALUEWORDS = ['oss://', 'oss-'] # -> "#####"
def dict_to_yaml(module_name, name, json_config, set_name=False, exclude_keys=[]):
def dict_to_yaml(module_name,
name,
json_config,
set_name=False,
exclude_keys=[]):
'''
{ "ENV" :
{ "description" : "",
@@ -227,6 +230,23 @@ yaml.SafeLoader.add_constructor('$', env_var_constructor)
yaml.SafeLoader.add_implicit_resolver('$', pattern, None)
def check_surppor_type(v):
if isinstance(v, str) or isinstance(v, numbers.Number):
return True
elif isinstance(v, dict):
for k, v in v.items():
if not check_surppor_type(v):
return False
return True
elif isinstance(v, list):
for v in v:
if not check_surppor_type(v):
return False
return True
else:
return False
def _parse_args(parser):
if parser is None:
parser = argparse.ArgumentParser(
@@ -353,13 +373,11 @@ class Config(object):
if file_name.endswith('.json'):
self.cfg_dict = self._load_json(file_name)
self.logger.info(
f'Loading config from [{file_name}] as json file.'
)
f'Loading config from [{file_name}] as json file.')
elif file_name.endswith('.yaml'):
self.cfg_dict = self._load_yaml(file_name)
self.logger.info(
f'Loading config from [{file_name}] as yaml file.'
)
f'Loading config from [{file_name}] as yaml file.')
else:
self.logger.info(
f'No config file found! Because we do not find json or yaml in --cfg {file_name}'
@@ -394,8 +412,43 @@ class Config(object):
elem = float(elem)
return key, elem
def recur_raw(key, elem):
if type(elem) is dict:
new_elem = {}
for k, v in elem.items():
k, v = recur_raw(k, v)
new_elem[k] = v
return key, new_elem
elif type(elem) is list:
new_elem = []
for idx, ele in enumerate(elem):
if type(ele) is str and ele[1:3] == 'e-':
ele = float(ele)
new_elem.append(ele)
elif type(ele) is str:
new_elem.append(ele)
elif type(ele) is dict:
new_ele = {}
for k, v in ele.items():
k, v = recur_raw(k, v)
new_ele[k] = v
new_elem.append(new_ele)
elif type(ele) is list:
new_ele = []
for ele_ in ele:
new_ele.append(recur_raw('', ele_)[1])
new_elem.append(new_ele)
else:
new_elem.append(ele)
return key, new_elem
else:
if type(elem) is str and elem[1:3] == 'e-':
elem = float(elem)
return key, elem
dic = dict(recur(k, v) for k, v in cfg_dict.items())
self.__dict__.update(dic)
self.cfg_dict = dict(recur_raw(k, v) for k, v in cfg_dict.items())
def _load_json(self, cfg_file):
'''
@@ -586,13 +639,12 @@ class Config(object):
def __setattr__(self, key, value):
super().__setattr__(key, value)
if hasattr(self, 'cfg_dict') and key in self.cfg_dict:
if isinstance(value, Config):
value = value.cfg_dict
self.cfg_dict[key] = value
if check_surppor_type(value) and key not in ['cfg_dict', 'logger']:
if hasattr(self, 'cfg_dict'):
self.cfg_dict[key] = value
self._update_dict(self.cfg_dict)
def __setitem__(self, key, value):
self.__dict__[key] = value
self.__setattr__(key, value)
def __iter__(self):
@@ -602,6 +654,7 @@ class Config(object):
new_dict = {name: value}
self.__dict__.update(new_dict)
self.__setattr__(name, value)
self.cfg_dict.update(new_dict)
def get_dict(self):
return self.cfg_dict
+42
View File
@@ -3,9 +3,11 @@
import os
import re
from collections import OrderedDict
from typing import List, Tuple, Union
import torch
import torch.nn as nn
from torch import Tensor
from torch.utils.model_zoo import load_url as load_state_dict_from_url
@@ -147,3 +149,43 @@ def init_weights(module):
module.weight.data.fill_(1.0)
if isinstance(module, nn.Linear) and module.bias is not None:
module.bias.data.zero_()
# copy from transformers.modeling_utils
def get_parameter_dtype(parameter: Union[nn.Module, 'ModuleUtilsMixin']):
"""
Returns the first found floating dtype in parameters if there is one, otherwise returns the last dtype it found.
"""
last_dtype = None
for t in parameter.parameters():
last_dtype = t.dtype
if t.is_floating_point():
return t.dtype
if last_dtype is not None:
# if no floating dtype was found return whatever the first dtype is
return last_dtype
# For nn.DataParallel compatibility in PyTorch > 1.5
def find_tensor_attributes(module: nn.Module) -> List[Tuple[str, Tensor]]:
tuples = [(k, v) for k, v in module.__dict__.items()
if torch.is_tensor(v)]
return tuples
gen = parameter._named_members(get_members_fn=find_tensor_attributes)
last_tuple = None
for tuple in gen:
last_tuple = tuple
if tuple[1].is_floating_point():
return tuple[1].dtype
if last_tuple is not None:
# fallback to the last dtype
return last_tuple[1].dtype
# fallback to buffer dtype
for t in parameter.buffers():
last_dtype = t.dtype
if t.is_floating_point():
return t.dtype
return last_dtype