Instructions to use deepsafe/deepsafe-services with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use deepsafe/deepsafe-services with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("deepsafe/deepsafe-services", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
chore: remove non-weight files (batch 7)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- video/pwtf-dvd/model_code/inference/test_tools/__init__.py +0 -0
- video/pwtf-dvd/model_code/inference/test_tools/common.py +0 -122
- video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__init__.py +0 -56
- video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/__init__.cpython-310.pyc +0 -0
- video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/__init__.cpython-39.pyc +0 -0
- video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/alignment.cpython-310.pyc +0 -0
- video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/alignment.cpython-39.pyc +0 -0
- video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/detector.cpython-310.pyc +0 -0
- video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/detector.cpython-39.pyc +0 -0
- video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/utils.cpython-310.pyc +0 -0
- video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/utils.cpython-39.pyc +0 -0
- video/pwtf-dvd/model_code/inference/test_tools/ct/detection/alignment.py +0 -608
- video/pwtf-dvd/model_code/inference/test_tools/ct/detection/detector.py +0 -46
- video/pwtf-dvd/model_code/inference/test_tools/ct/detection/utils.py +0 -146
- video/pwtf-dvd/model_code/inference/test_tools/ct/face_alignment/__init__.py +0 -1
- video/pwtf-dvd/model_code/inference/test_tools/ct/face_alignment/basenet.py +0 -107
- video/pwtf-dvd/model_code/inference/test_tools/ct/face_alignment/predictor.py +0 -143
- video/pwtf-dvd/model_code/inference/test_tools/ct/face_alignment/utils.py +0 -17
- video/pwtf-dvd/model_code/inference/test_tools/ct/operations.py +0 -79
- video/pwtf-dvd/model_code/inference/test_tools/ct/tracking/__init__.py +0 -0
- video/pwtf-dvd/model_code/inference/test_tools/ct/tracking/sort.py +0 -285
- video/pwtf-dvd/model_code/inference/test_tools/ct/tracking/tracker.py +0 -27
- video/pwtf-dvd/model_code/inference/test_tools/ct/utils.py +0 -5
- video/pwtf-dvd/model_code/inference/test_tools/faster_crop_align_xray.py +0 -73
- video/pwtf-dvd/model_code/inference/test_tools/supply_writer.py +0 -49
- video/pwtf-dvd/model_code/inference/test_tools/utils.py +0 -115
- video/pwtf-dvd/model_code/inference/test_tools/warp_for_xray.py +0 -574
- video/pwtf-dvd/model_code/inference/utils/__init__.py +0 -7
- video/pwtf-dvd/model_code/inference/utils/common.py +0 -80
- video/pwtf-dvd/model_code/inference/utils/logger.py +0 -182
- video/pwtf-dvd/model_code/inference/utils/model_loader.py +0 -117
- video/pwtf-dvd/model_code/inference/utils/plugin_loader.py +0 -69
- video/pwtf-dvd/model_code/inference/utils/torch_save.py +0 -9
- video/pwtf-dvd/model_code/preprocessing/preprocess.py +0 -258
- video/pwtf-dvd/model_code/preprocessing/test_tools/__init__.py +0 -0
- video/pwtf-dvd/model_code/preprocessing/test_tools/common.py +0 -122
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/detection/__init__.py +0 -56
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/detection/alignment.py +0 -608
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/detection/detector.py +0 -46
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/detection/utils.py +0 -147
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/face_alignment/__init__.py +0 -1
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/face_alignment/basenet.py +0 -107
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/face_alignment/predictor.py +0 -143
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/face_alignment/utils.py +0 -17
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/operations.py +0 -79
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/tracking/__init__.py +0 -0
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/tracking/sort.py +0 -285
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/tracking/tracker.py +0 -27
- video/pwtf-dvd/model_code/preprocessing/test_tools/ct/utils.py +0 -5
- video/pwtf-dvd/model_code/preprocessing/test_tools/faster_crop_align_xray.py +0 -73
video/pwtf-dvd/model_code/inference/test_tools/__init__.py
DELETED
|
File without changes
|
video/pwtf-dvd/model_code/inference/test_tools/common.py
DELETED
|
@@ -1,122 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
|
| 3 |
-
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
|
| 4 |
-
|
| 5 |
-
from .ct.detection.utils import grab_all_frames, get_valid_faces, sample_chunks
|
| 6 |
-
from .ct.operations import multiple_tracking
|
| 7 |
-
import numpy as np
|
| 8 |
-
from .ct.face_alignment import LandmarkPredictor
|
| 9 |
-
from .ct.detection import FaceDetector
|
| 10 |
-
import cv2
|
| 11 |
-
from .utils import flatten,partition
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
detector = FaceDetector(0)
|
| 15 |
-
predictor = LandmarkPredictor(0)
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def get_five(ldm68):
|
| 19 |
-
groups = [range(36, 42), range(42, 48), [30], [48], [54]]
|
| 20 |
-
points = []
|
| 21 |
-
for group in groups:
|
| 22 |
-
points.append(ldm68[group].mean(0))
|
| 23 |
-
return np.array(points)
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
def get_bbox(mask):
|
| 27 |
-
try:
|
| 28 |
-
y, x = np.nonzero(mask[..., 0])
|
| 29 |
-
return x.min() - 1, y.min() - 1, x.max() + 1, y.max() + 1
|
| 30 |
-
except:
|
| 31 |
-
return None
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
def get_bigger_box(image, box, scale=0.5):
|
| 35 |
-
height, width = image.shape[:2]
|
| 36 |
-
box = np.rint(box).astype(np.int)
|
| 37 |
-
new_box = box.reshape(2, 2)
|
| 38 |
-
size = new_box[1] - new_box[0]
|
| 39 |
-
diff = scale * size
|
| 40 |
-
diff = diff[None, :] * np.array([-1, 1])[:, None]
|
| 41 |
-
new_box = new_box + diff
|
| 42 |
-
new_box[:, 0] = np.clip(new_box[:, 0], 0, width - 1)
|
| 43 |
-
new_box[:, 1] = np.clip(new_box[:, 1], 0, height - 1)
|
| 44 |
-
new_box = np.rint(new_box).astype(np.int)
|
| 45 |
-
return new_box.reshape(-1)
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
def process_bigger_clips(clips, dete_res, clip_size, step, scale=0.5):
|
| 49 |
-
assert len(clips) % clip_size == 0
|
| 50 |
-
detect_results = sample_chunks(dete_res, clip_size, step)
|
| 51 |
-
clips = sample_chunks(clips, clip_size, step)
|
| 52 |
-
new_clips = []
|
| 53 |
-
for i, (frame_clip, record_clip) in enumerate(zip(clips, detect_results)):
|
| 54 |
-
tracks = multiple_tracking(record_clip)
|
| 55 |
-
for j, track in enumerate(tracks):
|
| 56 |
-
new_images = []
|
| 57 |
-
for (box, ldm, _), frame in zip(track, frame_clip):
|
| 58 |
-
big_box = get_bigger_box(frame, box, scale)
|
| 59 |
-
x1, y1, x2, y2 = big_box
|
| 60 |
-
top_left = big_box[:2][None, :]
|
| 61 |
-
new_ldm5 = ldm - top_left
|
| 62 |
-
box = np.rint(box).astype(np.int)
|
| 63 |
-
new_box = (box.reshape(2, 2) - top_left).reshape(-1)
|
| 64 |
-
feed = LandmarkPredictor.prepare_feed(frame, box)
|
| 65 |
-
ldm68 = predictor(feed) - top_left
|
| 66 |
-
new_images.append(
|
| 67 |
-
(frame[y1:y2, x1:x2], big_box, new_box, new_ldm5, ldm68)
|
| 68 |
-
)
|
| 69 |
-
new_clips.append(new_images)
|
| 70 |
-
return new_clips
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
def post(detected_faces):
|
| 74 |
-
return [[face[:4], None, face[-1]] for face in detected_faces]
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
def check(detect_res):
|
| 78 |
-
return min([len(faces) for faces in detect_res]) != 0
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
def detect_all(file, sfd_only=False, return_frames=False, max_size=None):
|
| 82 |
-
frames = grab_all_frames(file, max_size=max_size, cvt=True)
|
| 83 |
-
if not sfd_only:
|
| 84 |
-
detect_res = flatten(
|
| 85 |
-
[detector.detect(item) for item in partition(frames, 50)]
|
| 86 |
-
)
|
| 87 |
-
detect_res = get_valid_faces(detect_res, thres=0.5)
|
| 88 |
-
else:
|
| 89 |
-
raise NotImplementedError
|
| 90 |
-
|
| 91 |
-
all_68 = get_lm68(frames, detect_res)
|
| 92 |
-
if not return_frames:
|
| 93 |
-
return detect_res, all_68
|
| 94 |
-
else:
|
| 95 |
-
return detect_res, all_68, frames
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
def get_lm68(frames, detect_res):
|
| 99 |
-
assert len(frames) == len(detect_res)
|
| 100 |
-
frame_count = len(frames)
|
| 101 |
-
all_68 = []
|
| 102 |
-
for i in range(frame_count):
|
| 103 |
-
frame = frames[i]
|
| 104 |
-
faces = detect_res[i]
|
| 105 |
-
if len(faces) == 0:
|
| 106 |
-
res_68 = []
|
| 107 |
-
else:
|
| 108 |
-
feeds = []
|
| 109 |
-
for face in faces:
|
| 110 |
-
assert len(face) == 3
|
| 111 |
-
box = face[0]
|
| 112 |
-
feed = LandmarkPredictor.prepare_feed(frame, box)
|
| 113 |
-
feeds.append(feed)
|
| 114 |
-
res_68 = predictor(feeds)
|
| 115 |
-
assert len(res_68) == len(faces)
|
| 116 |
-
for face, l_68 in zip(faces, res_68):
|
| 117 |
-
if face[1] is None:
|
| 118 |
-
face[1] = get_five(l_68)
|
| 119 |
-
all_68.append(res_68)
|
| 120 |
-
|
| 121 |
-
assert len(all_68) == len(detect_res)
|
| 122 |
-
return all_68
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__init__.py
DELETED
|
@@ -1,56 +0,0 @@
|
|
| 1 |
-
import cv2
|
| 2 |
-
from .detector import RetinaFace
|
| 3 |
-
from .utils import *
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
def assert_bounded(val, low, up):
|
| 7 |
-
return val >= low and val < up
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def check_valid(face, w, h):
|
| 11 |
-
box = face[0]
|
| 12 |
-
if box[0] > box[2]:
|
| 13 |
-
return False
|
| 14 |
-
if box[1] > box[3]:
|
| 15 |
-
return False
|
| 16 |
-
for idx, bound in zip([0, 1, 2, 3], [w, h, w, h]):
|
| 17 |
-
if not assert_bounded(box[idx], 0, bound):
|
| 18 |
-
return False
|
| 19 |
-
pts = face[1]
|
| 20 |
-
for p in pts:
|
| 21 |
-
for idx, bound in zip([0, 1], [w, h]):
|
| 22 |
-
if not assert_bounded(p[idx], 0, bound):
|
| 23 |
-
return False
|
| 24 |
-
return True
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
def post_detect(detect_results, scale, w, h):
|
| 28 |
-
new_results = []
|
| 29 |
-
for frame_faces in detect_results:
|
| 30 |
-
new_frame_faces = []
|
| 31 |
-
for box, ldm, score in frame_faces:
|
| 32 |
-
box = box * scale
|
| 33 |
-
ldm = ldm * scale
|
| 34 |
-
face = (box, ldm, score)
|
| 35 |
-
if check_valid(face, w=w, h=h):
|
| 36 |
-
new_frame_faces.append(face)
|
| 37 |
-
new_results.append(new_frame_faces)
|
| 38 |
-
return new_results
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
class FaceDetector(RetinaFace):
|
| 42 |
-
def scale_detect(self, images):
|
| 43 |
-
max_res = 1920
|
| 44 |
-
h, w = images[0].shape[:2]
|
| 45 |
-
if max(h, w) > max_res:
|
| 46 |
-
init_scale = max(h, w) / max_res
|
| 47 |
-
else:
|
| 48 |
-
init_scale = 1
|
| 49 |
-
resize_scale = 2 * init_scale
|
| 50 |
-
resize_w = int(w / resize_scale)
|
| 51 |
-
resize_h = int(h / resize_scale)
|
| 52 |
-
detect_input = [cv2.resize(frame, (resize_w, resize_h)) for frame in images]
|
| 53 |
-
detect_results = post_detect(
|
| 54 |
-
self.detect(detect_input), scale=resize_scale, w=w, h=h,
|
| 55 |
-
)
|
| 56 |
-
return detect_results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/__init__.cpython-310.pyc
DELETED
|
Binary file (1.89 kB)
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/__init__.cpython-39.pyc
DELETED
|
Binary file (1.88 kB)
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/alignment.cpython-310.pyc
DELETED
|
Binary file (16.6 kB)
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/alignment.cpython-39.pyc
DELETED
|
Binary file (17 kB)
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/detector.cpython-310.pyc
DELETED
|
Binary file (1.67 kB)
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/detector.cpython-39.pyc
DELETED
|
Binary file (1.66 kB)
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/utils.cpython-310.pyc
DELETED
|
Binary file (4.34 kB)
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/detection/__pycache__/utils.cpython-39.pyc
DELETED
|
Binary file (4.34 kB)
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/detection/alignment.py
DELETED
|
@@ -1,608 +0,0 @@
|
|
| 1 |
-
from itertools import product as product
|
| 2 |
-
from math import ceil
|
| 3 |
-
|
| 4 |
-
import numpy as np
|
| 5 |
-
import torch
|
| 6 |
-
import torch.backends.cudnn as cudnn
|
| 7 |
-
import torch.nn as nn
|
| 8 |
-
import torch.nn.functional as F
|
| 9 |
-
import torchvision.models._utils as _utils
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
def conv_bn(inp, oup, stride=1, leaky=0):
|
| 13 |
-
return nn.Sequential(
|
| 14 |
-
nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
|
| 15 |
-
nn.BatchNorm2d(oup),
|
| 16 |
-
nn.LeakyReLU(negative_slope=leaky, inplace=True),
|
| 17 |
-
)
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
def conv_bn_no_relu(inp, oup, stride):
|
| 21 |
-
return nn.Sequential(
|
| 22 |
-
nn.Conv2d(inp, oup, 3, stride, 1, bias=False), nn.BatchNorm2d(oup),
|
| 23 |
-
)
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
def conv_bn1X1(inp, oup, stride, leaky=0):
|
| 27 |
-
return nn.Sequential(
|
| 28 |
-
nn.Conv2d(inp, oup, 1, stride, padding=0, bias=False),
|
| 29 |
-
nn.BatchNorm2d(oup),
|
| 30 |
-
nn.LeakyReLU(negative_slope=leaky, inplace=True),
|
| 31 |
-
)
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
def conv_dw(inp, oup, stride, leaky=0.1):
|
| 35 |
-
return nn.Sequential(
|
| 36 |
-
nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),
|
| 37 |
-
nn.BatchNorm2d(inp),
|
| 38 |
-
nn.LeakyReLU(negative_slope=leaky, inplace=True),
|
| 39 |
-
nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
|
| 40 |
-
nn.BatchNorm2d(oup),
|
| 41 |
-
nn.LeakyReLU(negative_slope=leaky, inplace=True),
|
| 42 |
-
)
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
class SSH(nn.Module):
|
| 46 |
-
def __init__(self, in_channel, out_channel):
|
| 47 |
-
super(SSH, self).__init__()
|
| 48 |
-
assert out_channel % 4 == 0
|
| 49 |
-
leaky = 0
|
| 50 |
-
if out_channel <= 64:
|
| 51 |
-
leaky = 0.1
|
| 52 |
-
self.conv3X3 = conv_bn_no_relu(in_channel, out_channel // 2, stride=1)
|
| 53 |
-
|
| 54 |
-
self.conv5X5_1 = conv_bn(in_channel, out_channel // 4, stride=1, leaky=leaky)
|
| 55 |
-
self.conv5X5_2 = conv_bn_no_relu(out_channel // 4, out_channel // 4, stride=1)
|
| 56 |
-
|
| 57 |
-
self.conv7X7_2 = conv_bn(
|
| 58 |
-
out_channel // 4, out_channel // 4, stride=1, leaky=leaky
|
| 59 |
-
)
|
| 60 |
-
self.conv7x7_3 = conv_bn_no_relu(out_channel // 4, out_channel // 4, stride=1)
|
| 61 |
-
|
| 62 |
-
def forward(self, input):
|
| 63 |
-
conv3X3 = self.conv3X3(input)
|
| 64 |
-
|
| 65 |
-
conv5X5_1 = self.conv5X5_1(input)
|
| 66 |
-
conv5X5 = self.conv5X5_2(conv5X5_1)
|
| 67 |
-
|
| 68 |
-
conv7X7_2 = self.conv7X7_2(conv5X5_1)
|
| 69 |
-
conv7X7 = self.conv7x7_3(conv7X7_2)
|
| 70 |
-
|
| 71 |
-
out = torch.cat([conv3X3, conv5X5, conv7X7], dim=1)
|
| 72 |
-
out = F.relu(out)
|
| 73 |
-
return out
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
class FPN(nn.Module):
|
| 77 |
-
def __init__(self, in_channels_list, out_channels):
|
| 78 |
-
super(FPN, self).__init__()
|
| 79 |
-
leaky = 0
|
| 80 |
-
if out_channels <= 64:
|
| 81 |
-
leaky = 0.1
|
| 82 |
-
self.output1 = conv_bn1X1(
|
| 83 |
-
in_channels_list[0], out_channels, stride=1, leaky=leaky
|
| 84 |
-
)
|
| 85 |
-
self.output2 = conv_bn1X1(
|
| 86 |
-
in_channels_list[1], out_channels, stride=1, leaky=leaky
|
| 87 |
-
)
|
| 88 |
-
self.output3 = conv_bn1X1(
|
| 89 |
-
in_channels_list[2], out_channels, stride=1, leaky=leaky
|
| 90 |
-
)
|
| 91 |
-
|
| 92 |
-
self.merge1 = conv_bn(out_channels, out_channels, leaky=leaky)
|
| 93 |
-
self.merge2 = conv_bn(out_channels, out_channels, leaky=leaky)
|
| 94 |
-
|
| 95 |
-
def forward(self, input):
|
| 96 |
-
# names = list(input.keys())
|
| 97 |
-
input = list(input.values())
|
| 98 |
-
|
| 99 |
-
output1 = self.output1(input[0])
|
| 100 |
-
output2 = self.output2(input[1])
|
| 101 |
-
output3 = self.output3(input[2])
|
| 102 |
-
|
| 103 |
-
up3 = F.interpolate(
|
| 104 |
-
output3, size=[output2.size(2), output2.size(3)], mode="nearest"
|
| 105 |
-
)
|
| 106 |
-
output2 = output2 + up3
|
| 107 |
-
output2 = self.merge2(output2)
|
| 108 |
-
|
| 109 |
-
up2 = F.interpolate(
|
| 110 |
-
output2, size=[output1.size(2), output1.size(3)], mode="nearest"
|
| 111 |
-
)
|
| 112 |
-
output1 = output1 + up2
|
| 113 |
-
output1 = self.merge1(output1)
|
| 114 |
-
|
| 115 |
-
out = [output1, output2, output3]
|
| 116 |
-
return out
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
class MobileNetV1(nn.Module):
|
| 120 |
-
def __init__(self):
|
| 121 |
-
super(MobileNetV1, self).__init__()
|
| 122 |
-
self.stage1 = nn.Sequential(
|
| 123 |
-
conv_bn(3, 8, 2, leaky=0.1), # 3
|
| 124 |
-
conv_dw(8, 16, 1), # 7
|
| 125 |
-
conv_dw(16, 32, 2), # 11
|
| 126 |
-
conv_dw(32, 32, 1), # 19
|
| 127 |
-
conv_dw(32, 64, 2), # 27
|
| 128 |
-
conv_dw(64, 64, 1), # 43
|
| 129 |
-
)
|
| 130 |
-
self.stage2 = nn.Sequential(
|
| 131 |
-
conv_dw(64, 128, 2), # 43 + 16 = 59
|
| 132 |
-
conv_dw(128, 128, 1), # 59 + 32 = 91
|
| 133 |
-
conv_dw(128, 128, 1), # 91 + 32 = 123
|
| 134 |
-
conv_dw(128, 128, 1), # 123 + 32 = 155
|
| 135 |
-
conv_dw(128, 128, 1), # 155 + 32 = 187
|
| 136 |
-
conv_dw(128, 128, 1), # 187 + 32 = 219
|
| 137 |
-
)
|
| 138 |
-
self.stage3 = nn.Sequential(
|
| 139 |
-
conv_dw(128, 256, 2), # 219 +3 2 = 241
|
| 140 |
-
conv_dw(256, 256, 1), # 241 + 64 = 301
|
| 141 |
-
)
|
| 142 |
-
self.avg = nn.AdaptiveAvgPool2d((1, 1))
|
| 143 |
-
self.fc = nn.Linear(256, 1000)
|
| 144 |
-
|
| 145 |
-
def forward(self, x):
|
| 146 |
-
x = self.stage1(x)
|
| 147 |
-
x = self.stage2(x)
|
| 148 |
-
x = self.stage3(x)
|
| 149 |
-
x = self.avg(x)
|
| 150 |
-
# x = self.model(x)
|
| 151 |
-
x = x.view(-1, 256)
|
| 152 |
-
x = self.fc(x)
|
| 153 |
-
return x
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
class ClassHead(nn.Module):
|
| 157 |
-
def __init__(self, inchannels=512, num_anchors=3):
|
| 158 |
-
super(ClassHead, self).__init__()
|
| 159 |
-
self.num_anchors = num_anchors
|
| 160 |
-
self.conv1x1 = nn.Conv2d(
|
| 161 |
-
inchannels, self.num_anchors * 2, kernel_size=(1, 1), stride=1, padding=0
|
| 162 |
-
)
|
| 163 |
-
|
| 164 |
-
def forward(self, x):
|
| 165 |
-
out = self.conv1x1(x)
|
| 166 |
-
out = out.permute(0, 2, 3, 1).contiguous()
|
| 167 |
-
|
| 168 |
-
return out.view(out.shape[0], -1, 2)
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
class BboxHead(nn.Module):
|
| 172 |
-
def __init__(self, inchannels=512, num_anchors=3):
|
| 173 |
-
super(BboxHead, self).__init__()
|
| 174 |
-
self.conv1x1 = nn.Conv2d(
|
| 175 |
-
inchannels, num_anchors * 4, kernel_size=(1, 1), stride=1, padding=0
|
| 176 |
-
)
|
| 177 |
-
|
| 178 |
-
def forward(self, x):
|
| 179 |
-
out = self.conv1x1(x)
|
| 180 |
-
out = out.permute(0, 2, 3, 1).contiguous()
|
| 181 |
-
|
| 182 |
-
return out.view(out.shape[0], -1, 4)
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
class LandmarkHead(nn.Module):
|
| 186 |
-
def __init__(self, inchannels=512, num_anchors=3):
|
| 187 |
-
super(LandmarkHead, self).__init__()
|
| 188 |
-
self.conv1x1 = nn.Conv2d(
|
| 189 |
-
inchannels, num_anchors * 10, kernel_size=(1, 1), stride=1, padding=0
|
| 190 |
-
)
|
| 191 |
-
|
| 192 |
-
def forward(self, x):
|
| 193 |
-
out = self.conv1x1(x)
|
| 194 |
-
out = out.permute(0, 2, 3, 1).contiguous()
|
| 195 |
-
|
| 196 |
-
return out.view(out.shape[0], -1, 10)
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
class RetinaFace(nn.Module):
|
| 200 |
-
def __init__(self, cfg=None, phase="train"):
|
| 201 |
-
"""
|
| 202 |
-
:param cfg: Network related settings.
|
| 203 |
-
:param phase: train or test.
|
| 204 |
-
"""
|
| 205 |
-
super(RetinaFace, self).__init__()
|
| 206 |
-
self.phase = phase
|
| 207 |
-
backbone = None
|
| 208 |
-
if cfg["name"] == "mobilenet0.25":
|
| 209 |
-
backbone = MobileNetV1()
|
| 210 |
-
elif cfg["name"] == "Resnet50":
|
| 211 |
-
import torchvision.models as models
|
| 212 |
-
|
| 213 |
-
backbone = models.resnet50(pretrained=cfg["pretrain"])
|
| 214 |
-
|
| 215 |
-
self.body = _utils.IntermediateLayerGetter(backbone, cfg["return_layers"])
|
| 216 |
-
in_channels_stage2 = cfg["in_channel"]
|
| 217 |
-
in_channels_list = [
|
| 218 |
-
in_channels_stage2 * 2,
|
| 219 |
-
in_channels_stage2 * 4,
|
| 220 |
-
in_channels_stage2 * 8,
|
| 221 |
-
]
|
| 222 |
-
out_channels = cfg["out_channel"]
|
| 223 |
-
self.fpn = FPN(in_channels_list, out_channels)
|
| 224 |
-
self.ssh1 = SSH(out_channels, out_channels)
|
| 225 |
-
self.ssh2 = SSH(out_channels, out_channels)
|
| 226 |
-
self.ssh3 = SSH(out_channels, out_channels)
|
| 227 |
-
|
| 228 |
-
self.ClassHead = self._make_class_head(fpn_num=3, inchannels=cfg["out_channel"])
|
| 229 |
-
self.BboxHead = self._make_bbox_head(fpn_num=3, inchannels=cfg["out_channel"])
|
| 230 |
-
self.LandmarkHead = self._make_landmark_head(
|
| 231 |
-
fpn_num=3, inchannels=cfg["out_channel"]
|
| 232 |
-
)
|
| 233 |
-
|
| 234 |
-
def _make_class_head(self, fpn_num=3, inchannels=64, anchor_num=2):
|
| 235 |
-
classhead = nn.ModuleList()
|
| 236 |
-
for i in range(fpn_num):
|
| 237 |
-
classhead.append(ClassHead(inchannels, anchor_num))
|
| 238 |
-
return classhead
|
| 239 |
-
|
| 240 |
-
def _make_bbox_head(self, fpn_num=3, inchannels=64, anchor_num=2):
|
| 241 |
-
bboxhead = nn.ModuleList()
|
| 242 |
-
for i in range(fpn_num):
|
| 243 |
-
bboxhead.append(BboxHead(inchannels, anchor_num))
|
| 244 |
-
return bboxhead
|
| 245 |
-
|
| 246 |
-
def _make_landmark_head(self, fpn_num=3, inchannels=64, anchor_num=2):
|
| 247 |
-
landmarkhead = nn.ModuleList()
|
| 248 |
-
for i in range(fpn_num):
|
| 249 |
-
landmarkhead.append(LandmarkHead(inchannels, anchor_num))
|
| 250 |
-
return landmarkhead
|
| 251 |
-
|
| 252 |
-
def forward(self, inputs):
|
| 253 |
-
out = self.body(inputs)
|
| 254 |
-
|
| 255 |
-
# FPN
|
| 256 |
-
fpn = self.fpn(out)
|
| 257 |
-
|
| 258 |
-
# SSH
|
| 259 |
-
feature1 = self.ssh1(fpn[0])
|
| 260 |
-
feature2 = self.ssh2(fpn[1])
|
| 261 |
-
feature3 = self.ssh3(fpn[2])
|
| 262 |
-
features = [feature1, feature2, feature3]
|
| 263 |
-
|
| 264 |
-
bbox_regressions = torch.cat(
|
| 265 |
-
[self.BboxHead[i](feature) for i, feature in enumerate(features)], dim=1
|
| 266 |
-
)
|
| 267 |
-
classifications = torch.cat(
|
| 268 |
-
[self.ClassHead[i](feature) for i, feature in enumerate(features)], dim=1
|
| 269 |
-
)
|
| 270 |
-
ldm_regressions = torch.cat(
|
| 271 |
-
[self.LandmarkHead[i](feature) for i, feature in enumerate(features)], dim=1
|
| 272 |
-
)
|
| 273 |
-
|
| 274 |
-
if self.phase == "train":
|
| 275 |
-
output = (bbox_regressions, classifications, ldm_regressions)
|
| 276 |
-
else:
|
| 277 |
-
output = (
|
| 278 |
-
bbox_regressions,
|
| 279 |
-
F.softmax(classifications, dim=-1),
|
| 280 |
-
ldm_regressions,
|
| 281 |
-
)
|
| 282 |
-
return output
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
# Adapted from https://github.com/Hakuyume/chainer-ssd
|
| 286 |
-
def decode(loc, priors, variances):
|
| 287 |
-
boxes = torch.cat(
|
| 288 |
-
(
|
| 289 |
-
priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:],
|
| 290 |
-
priors[:, 2:] * torch.exp(loc[:, 2:] * variances[1]),
|
| 291 |
-
),
|
| 292 |
-
1,
|
| 293 |
-
)
|
| 294 |
-
boxes[:, :2] -= boxes[:, 2:] / 2
|
| 295 |
-
boxes[:, 2:] += boxes[:, :2]
|
| 296 |
-
return boxes
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
def decode_landm(pre, priors, variances):
|
| 300 |
-
landms = torch.cat(
|
| 301 |
-
(
|
| 302 |
-
priors[:, :2] + pre[:, :2] * variances[0] * priors[:, 2:],
|
| 303 |
-
priors[:, :2] + pre[:, 2:4] * variances[0] * priors[:, 2:],
|
| 304 |
-
priors[:, :2] + pre[:, 4:6] * variances[0] * priors[:, 2:],
|
| 305 |
-
priors[:, :2] + pre[:, 6:8] * variances[0] * priors[:, 2:],
|
| 306 |
-
priors[:, :2] + pre[:, 8:10] * variances[0] * priors[:, 2:],
|
| 307 |
-
),
|
| 308 |
-
dim=1,
|
| 309 |
-
)
|
| 310 |
-
return landms
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
def py_cpu_nms(dets, thresh):
|
| 314 |
-
"""Pure Python NMS baseline."""
|
| 315 |
-
x1 = dets[:, 0]
|
| 316 |
-
y1 = dets[:, 1]
|
| 317 |
-
x2 = dets[:, 2]
|
| 318 |
-
y2 = dets[:, 3]
|
| 319 |
-
scores = dets[:, 4]
|
| 320 |
-
|
| 321 |
-
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
|
| 322 |
-
order = scores.argsort()[::-1]
|
| 323 |
-
|
| 324 |
-
keep = []
|
| 325 |
-
while order.size > 0:
|
| 326 |
-
i = order[0]
|
| 327 |
-
keep.append(i)
|
| 328 |
-
xx1 = np.maximum(x1[i], x1[order[1:]])
|
| 329 |
-
yy1 = np.maximum(y1[i], y1[order[1:]])
|
| 330 |
-
xx2 = np.minimum(x2[i], x2[order[1:]])
|
| 331 |
-
yy2 = np.minimum(y2[i], y2[order[1:]])
|
| 332 |
-
|
| 333 |
-
w = np.maximum(0.0, xx2 - xx1 + 1)
|
| 334 |
-
h = np.maximum(0.0, yy2 - yy1 + 1)
|
| 335 |
-
inter = w * h
|
| 336 |
-
ovr = inter / (areas[i] + areas[order[1:]] - inter)
|
| 337 |
-
|
| 338 |
-
inds = np.where(ovr <= thresh)[0]
|
| 339 |
-
order = order[inds + 1]
|
| 340 |
-
|
| 341 |
-
return keep
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
class PriorBox(object):
|
| 345 |
-
def __init__(self, cfg, image_size=None, phase="train"):
|
| 346 |
-
super(PriorBox, self).__init__()
|
| 347 |
-
self.min_sizes = cfg["min_sizes"]
|
| 348 |
-
self.steps = cfg["steps"]
|
| 349 |
-
self.clip = cfg["clip"]
|
| 350 |
-
self.image_size = image_size
|
| 351 |
-
self.feature_maps = [
|
| 352 |
-
[ceil(self.image_size[0] / step), ceil(self.image_size[1] / step)]
|
| 353 |
-
for step in self.steps
|
| 354 |
-
]
|
| 355 |
-
self.name = "s"
|
| 356 |
-
|
| 357 |
-
def forward(self):
|
| 358 |
-
anchors = []
|
| 359 |
-
for k, f in enumerate(self.feature_maps):
|
| 360 |
-
min_sizes = self.min_sizes[k]
|
| 361 |
-
for i, j in product(range(f[0]), range(f[1])):
|
| 362 |
-
for min_size in min_sizes:
|
| 363 |
-
s_kx = min_size / self.image_size[1]
|
| 364 |
-
s_ky = min_size / self.image_size[0]
|
| 365 |
-
dense_cx = [
|
| 366 |
-
x * self.steps[k] / self.image_size[1] for x in [j + 0.5]
|
| 367 |
-
]
|
| 368 |
-
dense_cy = [
|
| 369 |
-
y * self.steps[k] / self.image_size[0] for y in [i + 0.5]
|
| 370 |
-
]
|
| 371 |
-
for cy, cx in product(dense_cy, dense_cx):
|
| 372 |
-
anchors += [cx, cy, s_kx, s_ky]
|
| 373 |
-
|
| 374 |
-
# back to torch land
|
| 375 |
-
output = torch.Tensor(anchors).view(-1, 4)
|
| 376 |
-
if self.clip:
|
| 377 |
-
output.clamp_(max=1, min=0)
|
| 378 |
-
return output
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
cfg_mnet = {
|
| 382 |
-
"name": "mobilenet0.25",
|
| 383 |
-
"min_sizes": [[16, 32], [64, 128], [256, 512]],
|
| 384 |
-
"steps": [8, 16, 32],
|
| 385 |
-
"variance": [0.1, 0.2],
|
| 386 |
-
"clip": False,
|
| 387 |
-
"loc_weight": 2.0,
|
| 388 |
-
"gpu_train": True,
|
| 389 |
-
"batch_size": 32,
|
| 390 |
-
"ngpu": 1,
|
| 391 |
-
"epoch": 250,
|
| 392 |
-
"decay1": 190,
|
| 393 |
-
"decay2": 220,
|
| 394 |
-
"image_size": 640,
|
| 395 |
-
"pretrain": True,
|
| 396 |
-
"return_layers": {"stage1": 1, "stage2": 2, "stage3": 3},
|
| 397 |
-
"in_channel": 32,
|
| 398 |
-
"out_channel": 64,
|
| 399 |
-
}
|
| 400 |
-
|
| 401 |
-
cfg_re50 = {
|
| 402 |
-
"name": "Resnet50",
|
| 403 |
-
"min_sizes": [[16, 32], [64, 128], [256, 512]],
|
| 404 |
-
"steps": [8, 16, 32],
|
| 405 |
-
"variance": [0.1, 0.2],
|
| 406 |
-
"clip": False,
|
| 407 |
-
"loc_weight": 2.0,
|
| 408 |
-
"gpu_train": True,
|
| 409 |
-
"batch_size": 24,
|
| 410 |
-
"ngpu": 4,
|
| 411 |
-
"epoch": 100,
|
| 412 |
-
"decay1": 70,
|
| 413 |
-
"decay2": 90,
|
| 414 |
-
"image_size": 840,
|
| 415 |
-
"pretrain": False,
|
| 416 |
-
"return_layers": {"layer2": 1, "layer3": 2, "layer4": 3},
|
| 417 |
-
"in_channel": 256,
|
| 418 |
-
"out_channel": 256,
|
| 419 |
-
}
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
def check_keys(model, pretrained_state_dict):
|
| 423 |
-
ckpt_keys = set(pretrained_state_dict.keys())
|
| 424 |
-
model_keys = set(model.state_dict().keys())
|
| 425 |
-
used_pretrained_keys = model_keys & ckpt_keys
|
| 426 |
-
assert len(used_pretrained_keys) > 0, "load NONE from pretrained checkpoint"
|
| 427 |
-
return True
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
def remove_prefix(state_dict, prefix):
|
| 431 |
-
""" Old style model is stored with all names of parameters sharing common prefix 'module.' """
|
| 432 |
-
f = lambda x: x.split(prefix, 1)[-1] if x.startswith(prefix) else x
|
| 433 |
-
return {f(key): value for key, value in state_dict.items()}
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
def load_model(model, pretrained_path, load_to_cpu):
|
| 437 |
-
if load_to_cpu:
|
| 438 |
-
if pretrained_path is None:
|
| 439 |
-
url = "https://github.com/yinglinzheng/face_weights/releases/download/v1/mobilenet0.25_Final.pth"
|
| 440 |
-
pretrained_dict = torch.utils.model_zoo.load_url(url)
|
| 441 |
-
else:
|
| 442 |
-
pretrained_dict = torch.load(
|
| 443 |
-
pretrained_path, map_location=lambda storage, loc: storage
|
| 444 |
-
)
|
| 445 |
-
else:
|
| 446 |
-
device = torch.cuda.current_device()
|
| 447 |
-
pretrained_dict = torch.load(
|
| 448 |
-
pretrained_path, map_location=lambda storage, loc: storage.cuda(device)
|
| 449 |
-
)
|
| 450 |
-
if "state_dict" in pretrained_dict.keys():
|
| 451 |
-
pretrained_dict = remove_prefix(pretrained_dict["state_dict"], "module.")
|
| 452 |
-
else:
|
| 453 |
-
pretrained_dict = remove_prefix(pretrained_dict, "module.")
|
| 454 |
-
check_keys(model, pretrained_dict)
|
| 455 |
-
model.load_state_dict(pretrained_dict, strict=False)
|
| 456 |
-
return model
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
def load_net(model_path, device, network="mobilenet"):
|
| 460 |
-
if network == "mobilenet":
|
| 461 |
-
cfg = cfg_mnet
|
| 462 |
-
elif network == "resnet50":
|
| 463 |
-
cfg = cfg_re50
|
| 464 |
-
# net and model
|
| 465 |
-
net = RetinaFace(cfg=cfg, phase="test")
|
| 466 |
-
net = load_model(net, model_path, True)
|
| 467 |
-
net.eval()
|
| 468 |
-
cudnn.benchmark = True
|
| 469 |
-
net = net.to(device)
|
| 470 |
-
return net
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
def parse_det(det):
|
| 474 |
-
landmarks = det[5:].reshape(5, 2)
|
| 475 |
-
box = det[:4]
|
| 476 |
-
score = det[4]
|
| 477 |
-
return box, landmarks, score
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
def post_process(
|
| 481 |
-
loc,
|
| 482 |
-
conf,
|
| 483 |
-
landms,
|
| 484 |
-
prior_data,
|
| 485 |
-
cfg,
|
| 486 |
-
scale,
|
| 487 |
-
scale1,
|
| 488 |
-
resize,
|
| 489 |
-
confidence_threshold,
|
| 490 |
-
top_k,
|
| 491 |
-
nms_threshold,
|
| 492 |
-
keep_top_k,
|
| 493 |
-
):
|
| 494 |
-
boxes = decode(loc, prior_data, cfg["variance"])
|
| 495 |
-
boxes = boxes * scale / resize
|
| 496 |
-
boxes = boxes.cpu().numpy()
|
| 497 |
-
scores = conf.cpu().numpy()[:, 1]
|
| 498 |
-
landms_copy = decode_landm(landms, prior_data, cfg["variance"])
|
| 499 |
-
|
| 500 |
-
landms_copy = landms_copy * scale1 / resize
|
| 501 |
-
landms_copy = landms_copy.cpu().numpy()
|
| 502 |
-
|
| 503 |
-
# ignore low scores
|
| 504 |
-
inds = np.where(scores > confidence_threshold)[0]
|
| 505 |
-
boxes = boxes[inds]
|
| 506 |
-
landms_copy = landms_copy[inds]
|
| 507 |
-
scores = scores[inds]
|
| 508 |
-
|
| 509 |
-
# keep top-K before NMS
|
| 510 |
-
order = scores.argsort()[::-1][:top_k]
|
| 511 |
-
boxes = boxes[order]
|
| 512 |
-
landms_copy = landms_copy[order]
|
| 513 |
-
scores = scores[order]
|
| 514 |
-
|
| 515 |
-
# do NMS
|
| 516 |
-
dets = np.hstack((boxes, scores[:, np.newaxis])).astype(np.float32, copy=False)
|
| 517 |
-
keep = py_cpu_nms(dets, nms_threshold)
|
| 518 |
-
# keep = nms(dets, args.nms_threshold,force_cpu=args.cpu)
|
| 519 |
-
dets = dets[keep, :]
|
| 520 |
-
landms_copy = landms_copy[keep]
|
| 521 |
-
|
| 522 |
-
# keep top-K faster NMS
|
| 523 |
-
dets = dets[:keep_top_k, :]
|
| 524 |
-
landms_copy = landms_copy[:keep_top_k, :]
|
| 525 |
-
|
| 526 |
-
dets = np.concatenate((dets, landms_copy), axis=1)
|
| 527 |
-
# show image
|
| 528 |
-
dets = sorted(dets, key=lambda x: x[4], reverse=True)
|
| 529 |
-
dets = [parse_det(x) for x in dets]
|
| 530 |
-
|
| 531 |
-
return dets
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
def batch_detect(net, images, device, is_tensor=False, normalized=False):
|
| 535 |
-
with torch.no_grad():
|
| 536 |
-
confidence_threshold = 0.02
|
| 537 |
-
cfg = cfg_mnet
|
| 538 |
-
top_k = 5000
|
| 539 |
-
nms_threshold = 0.4
|
| 540 |
-
keep_top_k = 750
|
| 541 |
-
resize = 1
|
| 542 |
-
if not is_tensor:
|
| 543 |
-
try:
|
| 544 |
-
img = np.float32(images)
|
| 545 |
-
except ValueError:
|
| 546 |
-
raise NotImplementedError("Input images must of same size")
|
| 547 |
-
img = torch.from_numpy(img)
|
| 548 |
-
else:
|
| 549 |
-
img = images.float()
|
| 550 |
-
img = img.to(device)
|
| 551 |
-
mean = (
|
| 552 |
-
torch.as_tensor([104, 117, 123], dtype=img.dtype, device=img.device)
|
| 553 |
-
.unsqueeze(0)
|
| 554 |
-
.unsqueeze(0)
|
| 555 |
-
.unsqueeze(0)
|
| 556 |
-
)
|
| 557 |
-
img -= mean
|
| 558 |
-
img = img.permute(0, 3, 1, 2)
|
| 559 |
-
(batch_size, _, im_height, im_width,) = img.shape
|
| 560 |
-
scale = torch.as_tensor(
|
| 561 |
-
[im_width, im_height, im_width, im_height],
|
| 562 |
-
dtype=img.dtype,
|
| 563 |
-
device=img.device,
|
| 564 |
-
)
|
| 565 |
-
scale = scale.to(device)
|
| 566 |
-
|
| 567 |
-
loc, conf, landms = net(img) # forward pass
|
| 568 |
-
|
| 569 |
-
priorbox = PriorBox(cfg, image_size=(im_height, im_width))
|
| 570 |
-
priors = priorbox.forward()
|
| 571 |
-
prior_data = priors.to(device)
|
| 572 |
-
scale1 = torch.as_tensor(
|
| 573 |
-
[
|
| 574 |
-
img.shape[3],
|
| 575 |
-
img.shape[2],
|
| 576 |
-
img.shape[3],
|
| 577 |
-
img.shape[2],
|
| 578 |
-
img.shape[3],
|
| 579 |
-
img.shape[2],
|
| 580 |
-
img.shape[3],
|
| 581 |
-
img.shape[2],
|
| 582 |
-
img.shape[3],
|
| 583 |
-
img.shape[2],
|
| 584 |
-
],
|
| 585 |
-
dtype=img.dtype,
|
| 586 |
-
device=img.device,
|
| 587 |
-
)
|
| 588 |
-
scale1 = scale1.to(device)
|
| 589 |
-
|
| 590 |
-
all_dets = [
|
| 591 |
-
post_process(
|
| 592 |
-
loc_i,
|
| 593 |
-
conf_i,
|
| 594 |
-
landms_i,
|
| 595 |
-
prior_data,
|
| 596 |
-
cfg,
|
| 597 |
-
scale,
|
| 598 |
-
scale1,
|
| 599 |
-
resize,
|
| 600 |
-
confidence_threshold,
|
| 601 |
-
top_k,
|
| 602 |
-
nms_threshold,
|
| 603 |
-
keep_top_k,
|
| 604 |
-
)
|
| 605 |
-
for loc_i, conf_i, landms_i in zip(loc, conf, landms)
|
| 606 |
-
]
|
| 607 |
-
|
| 608 |
-
return all_dets
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/detection/detector.py
DELETED
|
@@ -1,46 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
|
| 3 |
-
import numpy as np
|
| 4 |
-
import torch
|
| 5 |
-
|
| 6 |
-
from .alignment import load_net, batch_detect
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
def get_project_dir():
|
| 10 |
-
current_path = os.path.abspath(os.path.join(__file__, "../"))
|
| 11 |
-
return current_path
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
def relative(path):
|
| 15 |
-
path = os.path.join(get_project_dir(), path)
|
| 16 |
-
return os.path.abspath(path)
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
class RetinaFace:
|
| 20 |
-
def __init__(
|
| 21 |
-
self, gpu_id=-1, model_path=None, network="mobilenet",
|
| 22 |
-
):
|
| 23 |
-
self.gpu_id = gpu_id
|
| 24 |
-
self.device = (
|
| 25 |
-
torch.device("cpu") if gpu_id == -1 else torch.device("cuda", gpu_id)
|
| 26 |
-
)
|
| 27 |
-
self.model = load_net(model_path, self.device, network)
|
| 28 |
-
|
| 29 |
-
def detect(self, images):
|
| 30 |
-
if isinstance(images, np.ndarray):
|
| 31 |
-
if len(images.shape) == 3:
|
| 32 |
-
return batch_detect(self.model, [images], self.device)[0]
|
| 33 |
-
elif len(images.shape) == 4:
|
| 34 |
-
return batch_detect(self.model, images, self.device)
|
| 35 |
-
elif isinstance(images, list):
|
| 36 |
-
return batch_detect(self.model, np.array(images), self.device)
|
| 37 |
-
elif isinstance(images, torch.Tensor):
|
| 38 |
-
if len(images.shape) == 3:
|
| 39 |
-
return batch_detect(self.model, images.unsqueeze(0), self.device)[0]
|
| 40 |
-
elif len(images.shape) == 4:
|
| 41 |
-
return batch_detect(self.model, images, self.device)
|
| 42 |
-
else:
|
| 43 |
-
raise NotImplementedError()
|
| 44 |
-
|
| 45 |
-
def __call__(self, images):
|
| 46 |
-
return self.detect(images)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/detection/utils.py
DELETED
|
@@ -1,146 +0,0 @@
|
|
| 1 |
-
import cv2
|
| 2 |
-
from test_tools.utils import flatten
|
| 3 |
-
import numpy as np
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
def chunks(l, n, step=None):
|
| 7 |
-
if step is None:
|
| 8 |
-
step = n
|
| 9 |
-
return [l[i : i + n] for i in range(0, len(l), step)]
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
def sample_chunks(l, n, step=None):
|
| 13 |
-
return [l[i : i + n] for i in range(0, len(l), step) if i + n <= len(l)]
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
def grab_all_frames(path, max_size, cvt=False):
|
| 17 |
-
capture = cv2.VideoCapture(path)
|
| 18 |
-
ret = True
|
| 19 |
-
frames = []
|
| 20 |
-
while ret:
|
| 21 |
-
ret, frame = capture.read()
|
| 22 |
-
if ret:
|
| 23 |
-
if cvt:
|
| 24 |
-
frame = frame[..., ::-1]
|
| 25 |
-
frames.append(frame)
|
| 26 |
-
if len(frames) == max_size:
|
| 27 |
-
break
|
| 28 |
-
capture.release()
|
| 29 |
-
return frames
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
def get_clips_uniform(path, count, clip_size):
|
| 33 |
-
capture = cv2.VideoCapture(path)
|
| 34 |
-
n_frames = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
|
| 35 |
-
max_clip_available = n_frames + 1 - clip_size
|
| 36 |
-
if count > max_clip_available:
|
| 37 |
-
count = max_clip_available
|
| 38 |
-
final_start = max_clip_available - 1
|
| 39 |
-
start_indices = np.linspace(0, final_start, count, endpoint=True, dtype=np.int)
|
| 40 |
-
all_clip_idx = [list(range(start, start + clip_size)) for start in start_indices]
|
| 41 |
-
valid = set(flatten(all_clip_idx))
|
| 42 |
-
max_idx = max(valid)
|
| 43 |
-
|
| 44 |
-
frames = {}
|
| 45 |
-
for idx in range(max_idx + 1):
|
| 46 |
-
# Get the next frame, but don't decode if we're not using it.
|
| 47 |
-
ret = capture.grab()
|
| 48 |
-
if not ret:
|
| 49 |
-
continue
|
| 50 |
-
|
| 51 |
-
if idx in valid:
|
| 52 |
-
ret, frame = capture.retrieve()
|
| 53 |
-
if not ret or frame is None:
|
| 54 |
-
continue
|
| 55 |
-
else:
|
| 56 |
-
# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 57 |
-
frames[idx] = frame
|
| 58 |
-
|
| 59 |
-
capture.release()
|
| 60 |
-
clips = []
|
| 61 |
-
for clip_idx in all_clip_idx:
|
| 62 |
-
clip = []
|
| 63 |
-
flag = True
|
| 64 |
-
for idx in clip_idx:
|
| 65 |
-
if idx not in frames:
|
| 66 |
-
flag = False
|
| 67 |
-
break
|
| 68 |
-
clip.append(frames[idx])
|
| 69 |
-
if flag:
|
| 70 |
-
clips.append(clip)
|
| 71 |
-
return clips
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
def get_valid_faces(detect_results, max_count=10, thres=0.5, at_least=False):
|
| 75 |
-
new_results = []
|
| 76 |
-
for i, faces in enumerate(detect_results):
|
| 77 |
-
if len(faces) > max_count:
|
| 78 |
-
faces = faces[:max_count]
|
| 79 |
-
l = []
|
| 80 |
-
for j, face in enumerate(faces):
|
| 81 |
-
if face[-1] < thres and not (j == 0 and at_least):
|
| 82 |
-
continue
|
| 83 |
-
box, lm, score = face
|
| 84 |
-
box = box.astype(np.float32)
|
| 85 |
-
lm = lm.astype(np.float32)
|
| 86 |
-
l.append((box, lm, score))
|
| 87 |
-
new_results.append(l)
|
| 88 |
-
return new_results
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
def scale_box(box, scale_h, scale_w, h, w):
|
| 92 |
-
x1, y1, x2, y2 = box.astype(np.int32)
|
| 93 |
-
center_x = (x1 + x2) // 2
|
| 94 |
-
center_y = (y1 + y2) // 2
|
| 95 |
-
box_h = int((y2 - y1) * scale_h)
|
| 96 |
-
box_w = int((x2 - x1) * scale_w)
|
| 97 |
-
new_x1 = center_x - box_w // 2
|
| 98 |
-
new_x2 = new_x1 + box_w
|
| 99 |
-
new_y1 = center_y - box_h // 2
|
| 100 |
-
new_y2 = new_y1 + box_h
|
| 101 |
-
new_x1 = max(new_x1, 0)
|
| 102 |
-
new_y1 = max(new_y1, 0)
|
| 103 |
-
new_y2 = min(new_y2, h)
|
| 104 |
-
new_x2 = min(new_x2, w)
|
| 105 |
-
return new_x1, new_y1, new_x2, new_y2
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
def get_bbox(detect_res):
|
| 109 |
-
tmp_detect_res = get_valid_faces(detect_res, max_count=4, thres=0.5)
|
| 110 |
-
all_face_bboxs = []
|
| 111 |
-
for faces in tmp_detect_res:
|
| 112 |
-
all_face_bboxs.extend([face[0] for face in faces])
|
| 113 |
-
all_face_bboxs = np.array(all_face_bboxs).astype(np.int)
|
| 114 |
-
x1 = all_face_bboxs[:, 0].min()
|
| 115 |
-
x2 = all_face_bboxs[:, 2].max()
|
| 116 |
-
y1 = all_face_bboxs[:, 1].min()
|
| 117 |
-
y2 = all_face_bboxs[:, 3].max()
|
| 118 |
-
|
| 119 |
-
return x1, y1, x2, y2
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
def delta_detect_res(detect_res, x1, y1):
|
| 123 |
-
diff = np.array([[x1, y1]])
|
| 124 |
-
new_detect_res = []
|
| 125 |
-
for faces in detect_res:
|
| 126 |
-
f = []
|
| 127 |
-
for face in faces:
|
| 128 |
-
box, lm, score = face
|
| 129 |
-
box = box.astype(np.float)
|
| 130 |
-
box[[0, 2]] -= x1
|
| 131 |
-
box[[1, 3]] -= y1
|
| 132 |
-
lm = lm.astype(np.float) - diff
|
| 133 |
-
f.append((box, lm, score))
|
| 134 |
-
new_detect_res.append(f)
|
| 135 |
-
return new_detect_res
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
def pre_crop(clips, detect_res):
|
| 139 |
-
box = np.array(get_bbox(detect_res))
|
| 140 |
-
w = box[2] - box[0]
|
| 141 |
-
h = box[3] - box[1]
|
| 142 |
-
x1, y1, x2, y2 = scale_box(
|
| 143 |
-
box, 1.5, 1.2 if w > 2 * h else 1.5, clips[0].shape[0], clips[0].shape[1]
|
| 144 |
-
)
|
| 145 |
-
clips = np.array(clips)
|
| 146 |
-
return clips[:, y1:y2, x1:x2], delta_detect_res(detect_res, x1, y1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/face_alignment/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
from .predictor import LandmarkPredictor
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/face_alignment/basenet.py
DELETED
|
@@ -1,107 +0,0 @@
|
|
| 1 |
-
# Backbone networks used for face landmark detection
|
| 2 |
-
# Cunjian Chen (cunjian@msu.edu)
|
| 3 |
-
|
| 4 |
-
import torch.nn as nn
|
| 5 |
-
import torchvision.models as models
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
class ConvBlock(nn.Module):
|
| 9 |
-
def __init__(self, inp, oup, k, s, p, dw=False, linear=False):
|
| 10 |
-
super(ConvBlock, self).__init__()
|
| 11 |
-
self.linear = linear
|
| 12 |
-
if dw:
|
| 13 |
-
self.conv = nn.Conv2d(inp, oup, k, s, p, groups=inp, bias=False)
|
| 14 |
-
else:
|
| 15 |
-
self.conv = nn.Conv2d(inp, oup, k, s, p, bias=False)
|
| 16 |
-
self.bn = nn.BatchNorm2d(oup)
|
| 17 |
-
if not linear:
|
| 18 |
-
self.prelu = nn.PReLU(oup)
|
| 19 |
-
|
| 20 |
-
def forward(self, x):
|
| 21 |
-
x = self.conv(x)
|
| 22 |
-
x = self.bn(x)
|
| 23 |
-
if self.linear:
|
| 24 |
-
return x
|
| 25 |
-
else:
|
| 26 |
-
return self.prelu(x)
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
# SE module
|
| 30 |
-
# https://github.com/wujiyang/Face_Pytorch/blob/master/backbone/cbam.py
|
| 31 |
-
class SEModule(nn.Module):
|
| 32 |
-
"""Squeeze and Excitation Module"""
|
| 33 |
-
|
| 34 |
-
def __init__(self, channels, reduction):
|
| 35 |
-
super(SEModule, self).__init__()
|
| 36 |
-
self.avg_pool = nn.AdaptiveAvgPool2d(1)
|
| 37 |
-
self.fc1 = nn.Conv2d(
|
| 38 |
-
channels, channels // reduction, kernel_size=1, padding=0, bias=False
|
| 39 |
-
)
|
| 40 |
-
self.relu = nn.ReLU(inplace=True)
|
| 41 |
-
self.fc2 = nn.Conv2d(
|
| 42 |
-
channels // reduction, channels, kernel_size=1, padding=0, bias=False
|
| 43 |
-
)
|
| 44 |
-
self.sigmoid = nn.Sigmoid()
|
| 45 |
-
|
| 46 |
-
def forward(self, x):
|
| 47 |
-
input = x
|
| 48 |
-
x = self.avg_pool(x)
|
| 49 |
-
x = self.fc1(x)
|
| 50 |
-
x = self.relu(x)
|
| 51 |
-
x = self.fc2(x)
|
| 52 |
-
x = self.sigmoid(x)
|
| 53 |
-
|
| 54 |
-
return input * x
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
# USE global depthwise convolution layer. Compatible with MobileNetV2 (224×224), MobileNetV2_ExternalData (224×224)
|
| 58 |
-
class MobileNet_GDConv(nn.Module):
|
| 59 |
-
def __init__(self, num_classes):
|
| 60 |
-
super(MobileNet_GDConv, self).__init__()
|
| 61 |
-
self.pretrain_net = models.mobilenet_v2(pretrained=False)
|
| 62 |
-
self.base_net = nn.Sequential(*list(self.pretrain_net.children())[:-1])
|
| 63 |
-
self.linear7 = ConvBlock(1280, 1280, (7, 7), 1, 0, dw=True, linear=True)
|
| 64 |
-
self.linear1 = ConvBlock(1280, num_classes, 1, 1, 0, linear=True)
|
| 65 |
-
|
| 66 |
-
def forward(self, x):
|
| 67 |
-
x = self.base_net(x)
|
| 68 |
-
x = self.linear7(x)
|
| 69 |
-
x = self.linear1(x)
|
| 70 |
-
x = x.view(x.size(0), -1)
|
| 71 |
-
return x
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
# USE global depthwise convolution layer. Compatible with MobileNetV2 (56×56)
|
| 75 |
-
class MobileNet_GDConv_56(nn.Module):
|
| 76 |
-
def __init__(self, num_classes):
|
| 77 |
-
super(MobileNet_GDConv_56, self).__init__()
|
| 78 |
-
self.pretrain_net = models.mobilenet_v2(pretrained=False)
|
| 79 |
-
self.base_net = nn.Sequential(*list(self.pretrain_net.children())[:-1])
|
| 80 |
-
self.linear7 = ConvBlock(1280, 1280, (2, 2), 1, 0, dw=True, linear=True)
|
| 81 |
-
self.linear1 = ConvBlock(1280, num_classes, 1, 1, 0, linear=True)
|
| 82 |
-
|
| 83 |
-
def forward(self, x):
|
| 84 |
-
x = self.base_net(x)
|
| 85 |
-
x = self.linear7(x)
|
| 86 |
-
x = self.linear1(x)
|
| 87 |
-
x = x.view(x.size(0), -1)
|
| 88 |
-
return x
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
# MobileNetV2 with SE; Compatible with MobileNetV2_SE (224×224) and MobileNetV2_SE_RE (224×224)
|
| 92 |
-
class MobileNet_GDConv_SE(nn.Module):
|
| 93 |
-
def __init__(self, num_classes):
|
| 94 |
-
super(MobileNet_GDConv_SE, self).__init__()
|
| 95 |
-
self.pretrain_net = models.mobilenet_v2(pretrained=True)
|
| 96 |
-
self.base_net = nn.Sequential(*list(self.pretrain_net.children())[:-1])
|
| 97 |
-
self.linear7 = ConvBlock(1280, 1280, (7, 7), 1, 0, dw=True, linear=True)
|
| 98 |
-
self.linear1 = ConvBlock(1280, num_classes, 1, 1, 0, linear=True)
|
| 99 |
-
self.attention = SEModule(1280, 8)
|
| 100 |
-
|
| 101 |
-
def forward(self, x):
|
| 102 |
-
x = self.base_net(x)
|
| 103 |
-
x = self.attention(x)
|
| 104 |
-
x = self.linear7(x)
|
| 105 |
-
x = self.linear1(x)
|
| 106 |
-
x = x.view(x.size(0), -1)
|
| 107 |
-
return x
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/face_alignment/predictor.py
DELETED
|
@@ -1,143 +0,0 @@
|
|
| 1 |
-
# Face alignment demo
|
| 2 |
-
# Uses MTCNN as face detector
|
| 3 |
-
# Cunjian Chen (ccunjian@gmail.com)
|
| 4 |
-
import torch
|
| 5 |
-
import cv2
|
| 6 |
-
import numpy as np
|
| 7 |
-
from torch.utils.data import DataLoader
|
| 8 |
-
from .basenet import MobileNet_GDConv
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
def get_device(gpu_id):
|
| 12 |
-
if gpu_id > -1:
|
| 13 |
-
return torch.device(f"cuda:{str(gpu_id)}")
|
| 14 |
-
else:
|
| 15 |
-
return torch.device("cpu")
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def load_model(file):
|
| 19 |
-
model = MobileNet_GDConv(136)
|
| 20 |
-
if file is not None:
|
| 21 |
-
model.load_state_dict(torch.load(file, map_location="cpu"))
|
| 22 |
-
else:
|
| 23 |
-
url = "https://github.com/yinglinzheng/face_weights/releases/download/v1/mobilenet_224_model_best_gdconv_external.pth"
|
| 24 |
-
model.load_state_dict(torch.utils.model_zoo.load_url(url))
|
| 25 |
-
return model
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
# landmark of (5L, 2L) from [0,1] to real range
|
| 29 |
-
def reproject(bbox, landmark):
|
| 30 |
-
landmark_ = landmark.clone()
|
| 31 |
-
x1, y1, x2, y2 = bbox
|
| 32 |
-
w = x2 - x1
|
| 33 |
-
h = y2 - y1
|
| 34 |
-
landmark_[:, 0] *= w
|
| 35 |
-
landmark_[:, 0] += x1
|
| 36 |
-
landmark_[:, 1] *= h
|
| 37 |
-
landmark_[:, 1] += y1
|
| 38 |
-
return landmark_
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
def prepare_feed(img, face):
|
| 42 |
-
height, width, _ = img.shape
|
| 43 |
-
mean = np.asarray([0.485, 0.456, 0.406])
|
| 44 |
-
std = np.asarray([0.229, 0.224, 0.225])
|
| 45 |
-
out_size = 224
|
| 46 |
-
x1, y1, x2, y2 = face[:4]
|
| 47 |
-
|
| 48 |
-
w = x2 - x1 + 1
|
| 49 |
-
h = y2 - y1 + 1
|
| 50 |
-
size = int(min([w, h]) * 1.2)
|
| 51 |
-
cx = x1 + w // 2
|
| 52 |
-
cy = y1 + h // 2
|
| 53 |
-
x1 = cx - size // 2
|
| 54 |
-
x2 = x1 + size
|
| 55 |
-
y1 = cy - size // 2
|
| 56 |
-
y2 = y1 + size
|
| 57 |
-
|
| 58 |
-
dx = max(0, -x1)
|
| 59 |
-
dy = max(0, -y1)
|
| 60 |
-
x1 = max(0, x1)
|
| 61 |
-
y1 = max(0, y1)
|
| 62 |
-
|
| 63 |
-
edx = max(0, x2 - width)
|
| 64 |
-
edy = max(0, y2 - height)
|
| 65 |
-
x2 = min(width, x2)
|
| 66 |
-
y2 = min(height, y2)
|
| 67 |
-
new_bbox = torch.Tensor([x1, y1, x2, y2]).int()
|
| 68 |
-
x1, y1, x2, y2 = new_bbox
|
| 69 |
-
cropped = img[y1:y2, x1:x2]
|
| 70 |
-
if dx > 0 or dy > 0 or edx > 0 or edy > 0:
|
| 71 |
-
cropped = cv2.copyMakeBorder(
|
| 72 |
-
cropped, int(dy), int(edy), int(dx), int(edx), cv2.BORDER_CONSTANT, 0
|
| 73 |
-
)
|
| 74 |
-
cropped_face = cv2.resize(cropped, (out_size, out_size))
|
| 75 |
-
|
| 76 |
-
if cropped_face.shape[0] <= 0 or cropped_face.shape[1] <= 0:
|
| 77 |
-
return None
|
| 78 |
-
test_face = cropped_face.copy()
|
| 79 |
-
test_face = test_face / 255.0
|
| 80 |
-
test_face = (test_face - mean) / std
|
| 81 |
-
test_face = test_face.transpose((2, 0, 1))
|
| 82 |
-
data = torch.from_numpy(test_face).float()
|
| 83 |
-
return dict(data=data, bbox=new_bbox)
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
@torch.no_grad()
|
| 87 |
-
def single_predict(model, feed, device):
|
| 88 |
-
landmark = model(feed["data"].unsqueeze(0).to(device)).cpu()
|
| 89 |
-
landmark = landmark.reshape(-1, 2)
|
| 90 |
-
landmark = reproject(feed["bbox"], landmark)
|
| 91 |
-
return landmark.numpy()
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
@torch.no_grad()
|
| 95 |
-
def batch_predict(model, feeds, device):
|
| 96 |
-
if not isinstance(feeds, list):
|
| 97 |
-
feeds = [feeds]
|
| 98 |
-
# loader = DataLoader(FeedDataset(feeds), batch_size=50, shuffle=False)
|
| 99 |
-
data = []
|
| 100 |
-
for feed in feeds:
|
| 101 |
-
data.append(feed["data"].unsqueeze(0))
|
| 102 |
-
data = torch.cat(data, 0).to(device)
|
| 103 |
-
results = []
|
| 104 |
-
|
| 105 |
-
landmarks = model(data).cpu()
|
| 106 |
-
for landmark, feed in zip(landmarks, feeds):
|
| 107 |
-
landmark = landmark.reshape(-1, 2)
|
| 108 |
-
landmark = reproject(feed["bbox"], landmark)
|
| 109 |
-
results.append(landmark.numpy())
|
| 110 |
-
return results
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
@torch.no_grad()
|
| 114 |
-
def batch_predict2(model, feeds, device, batch_size=None):
|
| 115 |
-
if not isinstance(feeds, list):
|
| 116 |
-
feeds = [feeds]
|
| 117 |
-
if batch_size is None:
|
| 118 |
-
batch_size = len(feeds)
|
| 119 |
-
loader = DataLoader(feeds, batch_size=len(feeds), shuffle=False)
|
| 120 |
-
results = []
|
| 121 |
-
for feed in loader:
|
| 122 |
-
landmarks = model(feed["data"].to(device)).cpu()
|
| 123 |
-
for landmark, bbox in zip(landmarks, feed["bbox"]):
|
| 124 |
-
landmark = landmark.reshape(-1, 2)
|
| 125 |
-
landmark = reproject(bbox, landmark)
|
| 126 |
-
results.append(landmark.numpy())
|
| 127 |
-
return results
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
class LandmarkPredictor:
|
| 131 |
-
def __init__(self, gpu_id=0, file=None):
|
| 132 |
-
self.device = get_device(gpu_id)
|
| 133 |
-
self.model = load_model(file).to(self.device).eval()
|
| 134 |
-
|
| 135 |
-
def __call__(self, feeds):
|
| 136 |
-
results = batch_predict2(self.model, feeds, self.device)
|
| 137 |
-
if not isinstance(feeds, list):
|
| 138 |
-
results = results[0]
|
| 139 |
-
return results
|
| 140 |
-
|
| 141 |
-
@staticmethod
|
| 142 |
-
def prepare_feed(img, face):
|
| 143 |
-
return prepare_feed(img, face)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/face_alignment/utils.py
DELETED
|
@@ -1,17 +0,0 @@
|
|
| 1 |
-
import cv2
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
def drawLandmark_multiple(img, bbox, landmark):
|
| 5 |
-
"""
|
| 6 |
-
Input:
|
| 7 |
-
- img: gray or RGB
|
| 8 |
-
- bbox: type of BBox
|
| 9 |
-
- landmark: reproject landmark of (5L, 2L)
|
| 10 |
-
Output:
|
| 11 |
-
- img marked with landmark and bbox
|
| 12 |
-
"""
|
| 13 |
-
x1, y1, x2, y2 = bbox
|
| 14 |
-
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
|
| 15 |
-
for x, y in landmark:
|
| 16 |
-
cv2.circle(img, (int(x), int(y)), 2, (0, 255, 0), -1)
|
| 17 |
-
return img
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/operations.py
DELETED
|
@@ -1,79 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
|
| 3 |
-
import os
|
| 4 |
-
import cv2
|
| 5 |
-
import numpy as np
|
| 6 |
-
from .tracking.sort import iou
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
def face_iou(f1, f2):
|
| 10 |
-
return iou(f1[0], f2[0])
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
def simple_tracking(batch_landmarks, index=0, thres=0.5):
|
| 14 |
-
track = []
|
| 15 |
-
|
| 16 |
-
for i, faces in enumerate(batch_landmarks):
|
| 17 |
-
if i == 0:
|
| 18 |
-
if len(faces) <= index or faces[index][-1] < 0.8:
|
| 19 |
-
return None
|
| 20 |
-
if index != 0:
|
| 21 |
-
for idx in range(index):
|
| 22 |
-
if face_iou(faces[idx], faces[index]) > thres:
|
| 23 |
-
return None
|
| 24 |
-
track.append(faces[index])
|
| 25 |
-
else:
|
| 26 |
-
last = track[i - 1]
|
| 27 |
-
if len(faces) == 0:
|
| 28 |
-
return None
|
| 29 |
-
sorted_faces = sorted(faces, key=lambda x: face_iou(x, last), reverse=True)
|
| 30 |
-
if face_iou(sorted_faces[0], last) < thres:
|
| 31 |
-
return None
|
| 32 |
-
track.append(sorted_faces[0])
|
| 33 |
-
return track
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
def multiple_tracking(batch_landmarks):
|
| 37 |
-
tracks = []
|
| 38 |
-
for i in range(len(batch_landmarks[0])):
|
| 39 |
-
track = simple_tracking(batch_landmarks, index=i)
|
| 40 |
-
if track is None:
|
| 41 |
-
continue
|
| 42 |
-
tracks.append(track)
|
| 43 |
-
return tracks
|
| 44 |
-
|
| 45 |
-
def find_longest(detect_res):
|
| 46 |
-
fc = len(detect_res)
|
| 47 |
-
tuples = []
|
| 48 |
-
start = 0
|
| 49 |
-
end = 0
|
| 50 |
-
previous_count = -1
|
| 51 |
-
all_tracks = []
|
| 52 |
-
# start 取得到,end 取不到
|
| 53 |
-
while start < (fc - 1):
|
| 54 |
-
for end in range(start + 2, fc + 1):
|
| 55 |
-
tracks = multiple_tracking(detect_res[start:end])
|
| 56 |
-
if (len(tracks) != previous_count and previous_count != -1) or len(
|
| 57 |
-
tracks
|
| 58 |
-
) == 0:
|
| 59 |
-
break
|
| 60 |
-
previous_count = len(tracks)
|
| 61 |
-
if end - start > 2:
|
| 62 |
-
if end != fc:
|
| 63 |
-
un_reach_end = end - 1
|
| 64 |
-
else:
|
| 65 |
-
un_reach_end = end
|
| 66 |
-
sub_tracks = multiple_tracking(detect_res[start:un_reach_end])
|
| 67 |
-
if end == fc and len(sub_tracks) == 0:
|
| 68 |
-
un_reach_end = end - 1
|
| 69 |
-
sub_tracks = multiple_tracking(detect_res[start:un_reach_end])
|
| 70 |
-
if len(sub_tracks) > 0:
|
| 71 |
-
tpl = (start, un_reach_end)
|
| 72 |
-
tuples.append(tpl)
|
| 73 |
-
all_tracks.append(sub_tracks[0])
|
| 74 |
-
else:
|
| 75 |
-
raise NotImplementedError
|
| 76 |
-
previous_count = -1
|
| 77 |
-
end = un_reach_end
|
| 78 |
-
start = end
|
| 79 |
-
return tuples, all_tracks
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/tracking/__init__.py
DELETED
|
File without changes
|
video/pwtf-dvd/model_code/inference/test_tools/ct/tracking/sort.py
DELETED
|
@@ -1,285 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
SORT: A Simple, Online and Realtime Tracker
|
| 3 |
-
Copyright (C) 2016 Alex Bewley alex@dynamicdetection.com
|
| 4 |
-
|
| 5 |
-
This program is free software: you can redistribute it and/or modify
|
| 6 |
-
it under the terms of the GNU General Public License as published by
|
| 7 |
-
the Free Software Foundation, either version 3 of the License, or
|
| 8 |
-
(at your option) any later version.
|
| 9 |
-
|
| 10 |
-
This program is distributed in the hope that it will be useful,
|
| 11 |
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 12 |
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 13 |
-
GNU General Public License for more details.
|
| 14 |
-
|
| 15 |
-
You should have received a copy of the GNU General Public License
|
| 16 |
-
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
| 17 |
-
"""
|
| 18 |
-
from __future__ import print_function
|
| 19 |
-
import os.path
|
| 20 |
-
import numpy as np
|
| 21 |
-
import matplotlib.pyplot as plt
|
| 22 |
-
import matplotlib.patches as patches
|
| 23 |
-
from scipy.optimize import linear_sum_assignment
|
| 24 |
-
import glob
|
| 25 |
-
import time
|
| 26 |
-
import argparse
|
| 27 |
-
from filterpy.kalman import KalmanFilter
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
def iou(bb_test, bb_gt):
|
| 31 |
-
"""
|
| 32 |
-
Computes IUO between two bboxes in the form [x1,y1,x2,y2]
|
| 33 |
-
"""
|
| 34 |
-
xx1 = np.maximum(bb_test[0], bb_gt[0])
|
| 35 |
-
yy1 = np.maximum(bb_test[1], bb_gt[1])
|
| 36 |
-
xx2 = np.minimum(bb_test[2], bb_gt[2])
|
| 37 |
-
yy2 = np.minimum(bb_test[3], bb_gt[3])
|
| 38 |
-
w = np.maximum(0.0, xx2 - xx1)
|
| 39 |
-
h = np.maximum(0.0, yy2 - yy1)
|
| 40 |
-
wh = w * h
|
| 41 |
-
o = wh / (
|
| 42 |
-
(bb_test[2] - bb_test[0]) * (bb_test[3] - bb_test[1])
|
| 43 |
-
+ (bb_gt[2] - bb_gt[0]) * (bb_gt[3] - bb_gt[1])
|
| 44 |
-
- wh
|
| 45 |
-
)
|
| 46 |
-
return o
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
def convert_bbox_to_z(bbox):
|
| 50 |
-
"""
|
| 51 |
-
Takes a bounding box in the form [x1,y1,x2,y2] and returns z in the form
|
| 52 |
-
[x,y,s,r] where x,y is the centre of the box and s is the scale/area and r is
|
| 53 |
-
the aspect ratio
|
| 54 |
-
"""
|
| 55 |
-
w = bbox[2] - bbox[0]
|
| 56 |
-
h = bbox[3] - bbox[1]
|
| 57 |
-
x = bbox[0] + w / 2.0
|
| 58 |
-
y = bbox[1] + h / 2.0
|
| 59 |
-
s = w * h # scale is just area
|
| 60 |
-
r = w / float(h)
|
| 61 |
-
return np.array([x, y, s, r]).reshape((4, 1))
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
def convert_x_to_bbox(x, score=None):
|
| 65 |
-
"""
|
| 66 |
-
Takes a bounding box in the centre form [x,y,s,r] and returns it in the form
|
| 67 |
-
[x1,y1,x2,y2] where x1,y1 is the top left and x2,y2 is the bottom right
|
| 68 |
-
"""
|
| 69 |
-
w = np.sqrt(x[2] * x[3])
|
| 70 |
-
h = x[2] / w
|
| 71 |
-
if score == None:
|
| 72 |
-
return np.array(
|
| 73 |
-
[x[0] - w / 2.0, x[1] - h / 2.0, x[0] + w / 2.0, x[1] + h / 2.0]
|
| 74 |
-
).reshape((1, 4))
|
| 75 |
-
else:
|
| 76 |
-
return np.array(
|
| 77 |
-
[x[0] - w / 2.0, x[1] - h / 2.0, x[0] + w / 2.0, x[1] + h / 2.0, score]
|
| 78 |
-
).reshape((1, 5))
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
class KalmanBoxTracker(object):
|
| 82 |
-
"""
|
| 83 |
-
This class represents the internel state of individual tracked objects observed as bbox.
|
| 84 |
-
"""
|
| 85 |
-
|
| 86 |
-
count = 0
|
| 87 |
-
|
| 88 |
-
def __init__(self, bbox):
|
| 89 |
-
"""
|
| 90 |
-
Initialises a tracker using initial bounding box.
|
| 91 |
-
"""
|
| 92 |
-
# define constant velocity model
|
| 93 |
-
self.kf = KalmanFilter(dim_x=7, dim_z=4)
|
| 94 |
-
self.kf.F = np.array(
|
| 95 |
-
[
|
| 96 |
-
[1, 0, 0, 0, 1, 0, 0],
|
| 97 |
-
[0, 1, 0, 0, 0, 1, 0],
|
| 98 |
-
[0, 0, 1, 0, 0, 0, 1],
|
| 99 |
-
[0, 0, 0, 1, 0, 0, 0],
|
| 100 |
-
[0, 0, 0, 0, 1, 0, 0],
|
| 101 |
-
[0, 0, 0, 0, 0, 1, 0],
|
| 102 |
-
[0, 0, 0, 0, 0, 0, 1],
|
| 103 |
-
]
|
| 104 |
-
)
|
| 105 |
-
self.kf.H = np.array(
|
| 106 |
-
[
|
| 107 |
-
[1, 0, 0, 0, 0, 0, 0],
|
| 108 |
-
[0, 1, 0, 0, 0, 0, 0],
|
| 109 |
-
[0, 0, 1, 0, 0, 0, 0],
|
| 110 |
-
[0, 0, 0, 1, 0, 0, 0],
|
| 111 |
-
]
|
| 112 |
-
)
|
| 113 |
-
|
| 114 |
-
self.kf.R[2:, 2:] *= 10.0
|
| 115 |
-
self.kf.P[
|
| 116 |
-
4:, 4:
|
| 117 |
-
] *= 1000.0 # give high uncertainty to the unobservable initial velocities
|
| 118 |
-
self.kf.P *= 10.0
|
| 119 |
-
self.kf.Q[-1, -1] *= 0.01
|
| 120 |
-
self.kf.Q[4:, 4:] *= 0.01
|
| 121 |
-
|
| 122 |
-
self.kf.x[:4] = convert_bbox_to_z(bbox)
|
| 123 |
-
self.time_since_update = 0
|
| 124 |
-
self.id = KalmanBoxTracker.count
|
| 125 |
-
KalmanBoxTracker.count += 1
|
| 126 |
-
self.history = []
|
| 127 |
-
self.hits = 0
|
| 128 |
-
self.hit_streak = 0
|
| 129 |
-
self.age = 0
|
| 130 |
-
|
| 131 |
-
def update(self, bbox):
|
| 132 |
-
"""
|
| 133 |
-
Updates the state vector with observed bbox.
|
| 134 |
-
"""
|
| 135 |
-
self.time_since_update = 0
|
| 136 |
-
self.history = []
|
| 137 |
-
self.hits += 1
|
| 138 |
-
self.hit_streak += 1
|
| 139 |
-
self.kf.update(convert_bbox_to_z(bbox))
|
| 140 |
-
|
| 141 |
-
def predict(self):
|
| 142 |
-
"""
|
| 143 |
-
Advances the state vector and returns the predicted bounding box estimate.
|
| 144 |
-
"""
|
| 145 |
-
if (self.kf.x[6] + self.kf.x[2]) <= 0:
|
| 146 |
-
self.kf.x[6] *= 0.0
|
| 147 |
-
self.kf.predict()
|
| 148 |
-
self.age += 1
|
| 149 |
-
if self.time_since_update > 0:
|
| 150 |
-
self.hit_streak = 0
|
| 151 |
-
self.time_since_update += 1
|
| 152 |
-
self.history.append(convert_x_to_bbox(self.kf.x))
|
| 153 |
-
return self.history[-1]
|
| 154 |
-
|
| 155 |
-
def get_state(self):
|
| 156 |
-
"""
|
| 157 |
-
Returns the current bounding box estimate.
|
| 158 |
-
"""
|
| 159 |
-
return convert_x_to_bbox(self.kf.x)
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
def associate_detections_to_trackers(detections, trackers, iou_threshold=0.3):
|
| 163 |
-
"""
|
| 164 |
-
Assigns detections to tracked object (both represented as bounding boxes)
|
| 165 |
-
|
| 166 |
-
Returns 3 lists of matches, unmatched_detections and unmatched_trackers
|
| 167 |
-
"""
|
| 168 |
-
if len(trackers) == 0:
|
| 169 |
-
return (
|
| 170 |
-
np.empty((0, 2), dtype=int),
|
| 171 |
-
np.arange(len(detections)),
|
| 172 |
-
np.empty((0, 5), dtype=int),
|
| 173 |
-
)
|
| 174 |
-
iou_matrix = np.zeros((len(detections), len(trackers)), dtype=np.float32)
|
| 175 |
-
|
| 176 |
-
for d, det in enumerate(detections):
|
| 177 |
-
for t, trk in enumerate(trackers):
|
| 178 |
-
iou_matrix[d, t] = iou(det, trk)
|
| 179 |
-
|
| 180 |
-
matched_indices = linear_sum_assignment(-iou_matrix)
|
| 181 |
-
matched_indices = np.array(list(zip(*matched_indices)), dtype=np.int)
|
| 182 |
-
matched_indices.shape = (-1, 2)
|
| 183 |
-
# print(matched_indices)
|
| 184 |
-
# print(type(matched_indices))
|
| 185 |
-
|
| 186 |
-
unmatched_detections = []
|
| 187 |
-
for d, det in enumerate(detections):
|
| 188 |
-
if d not in matched_indices[:, 0]:
|
| 189 |
-
unmatched_detections.append(d)
|
| 190 |
-
unmatched_trackers = []
|
| 191 |
-
for t, trk in enumerate(trackers):
|
| 192 |
-
if t not in matched_indices[:, 1]:
|
| 193 |
-
unmatched_trackers.append(t)
|
| 194 |
-
|
| 195 |
-
# filter out matched with low IOU
|
| 196 |
-
matches = []
|
| 197 |
-
for m in matched_indices:
|
| 198 |
-
if iou_matrix[m[0], m[1]] < iou_threshold:
|
| 199 |
-
unmatched_detections.append(m[0])
|
| 200 |
-
unmatched_trackers.append(m[1])
|
| 201 |
-
else:
|
| 202 |
-
matches.append(m.reshape(1, 2))
|
| 203 |
-
if len(matches) == 0:
|
| 204 |
-
matches = np.empty((0, 2), dtype=int)
|
| 205 |
-
else:
|
| 206 |
-
matches = np.concatenate(matches, axis=0)
|
| 207 |
-
|
| 208 |
-
return matches, np.array(unmatched_detections), np.array(unmatched_trackers)
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
class Sort(object):
|
| 212 |
-
def __init__(self, max_age=1, min_hits=3):
|
| 213 |
-
"""
|
| 214 |
-
Sets key parameters for SORT
|
| 215 |
-
"""
|
| 216 |
-
self.max_age = max_age
|
| 217 |
-
self.min_hits = min_hits
|
| 218 |
-
self.trackers = []
|
| 219 |
-
self.frame_count = 0
|
| 220 |
-
|
| 221 |
-
def update(self, dets):
|
| 222 |
-
"""
|
| 223 |
-
Params:
|
| 224 |
-
dets - a numpy array of detections in the format [[x1,y1,x2,y2,score],[x1,y1,x2,y2,score],...]
|
| 225 |
-
Requires: this method must be called once for each frame even with empty detections.
|
| 226 |
-
Returns the a similar array, where the last column is the object ID.
|
| 227 |
-
|
| 228 |
-
NOTE: The number of objects returned may differ from the number of detections provided.
|
| 229 |
-
"""
|
| 230 |
-
self.frame_count += 1
|
| 231 |
-
# get predicted locations from existing trackers.
|
| 232 |
-
trks = np.zeros((len(self.trackers), 5))
|
| 233 |
-
to_del = []
|
| 234 |
-
ret = []
|
| 235 |
-
for t, trk in enumerate(trks):
|
| 236 |
-
pos = self.trackers[t].predict()[0]
|
| 237 |
-
trk[:] = [pos[0], pos[1], pos[2], pos[3], 0]
|
| 238 |
-
if np.any(np.isnan(pos)):
|
| 239 |
-
to_del.append(t)
|
| 240 |
-
trks = np.ma.compress_rows(np.ma.masked_invalid(trks))
|
| 241 |
-
for t in reversed(to_del):
|
| 242 |
-
self.trackers.pop(t)
|
| 243 |
-
matched, unmatched_dets, unmatched_trks = associate_detections_to_trackers(
|
| 244 |
-
dets, trks
|
| 245 |
-
)
|
| 246 |
-
|
| 247 |
-
# update matched trackers with assigned detections
|
| 248 |
-
for t, trk in enumerate(self.trackers):
|
| 249 |
-
if t not in unmatched_trks:
|
| 250 |
-
d = matched[np.where(matched[:, 1] == t)[0], 0]
|
| 251 |
-
trk.update(dets[d, :][0])
|
| 252 |
-
|
| 253 |
-
# create and initialise new trackers for unmatched detections
|
| 254 |
-
for i in unmatched_dets:
|
| 255 |
-
trk = KalmanBoxTracker(dets[i, :])
|
| 256 |
-
self.trackers.append(trk)
|
| 257 |
-
i = len(self.trackers)
|
| 258 |
-
for trk in reversed(self.trackers):
|
| 259 |
-
d = trk.get_state()[0]
|
| 260 |
-
if (trk.time_since_update < 1) and (
|
| 261 |
-
trk.hit_streak >= self.min_hits or self.frame_count <= self.min_hits
|
| 262 |
-
):
|
| 263 |
-
ret.append(
|
| 264 |
-
np.concatenate((d, [trk.id + 1])).reshape(1, -1)
|
| 265 |
-
) # +1 as MOT benchmark requires positive
|
| 266 |
-
i -= 1
|
| 267 |
-
# remove dead tracklet
|
| 268 |
-
if trk.time_since_update > self.max_age:
|
| 269 |
-
self.trackers.pop(i)
|
| 270 |
-
if len(ret) > 0:
|
| 271 |
-
return np.concatenate(ret)
|
| 272 |
-
return np.empty((0, 5))
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
def parse_args():
|
| 276 |
-
"""Parse input arguments."""
|
| 277 |
-
parser = argparse.ArgumentParser(description="SORT demo")
|
| 278 |
-
parser.add_argument(
|
| 279 |
-
"--display",
|
| 280 |
-
dest="display",
|
| 281 |
-
help="Display online tracker output (slow) [False]",
|
| 282 |
-
action="store_true",
|
| 283 |
-
)
|
| 284 |
-
args = parser.parse_args()
|
| 285 |
-
return args
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/tracking/tracker.py
DELETED
|
@@ -1,27 +0,0 @@
|
|
| 1 |
-
from .sort import Sort
|
| 2 |
-
import numpy as np
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
def get_detections(faces):
|
| 6 |
-
detections = []
|
| 7 |
-
for face in faces:
|
| 8 |
-
x1, y1, x2, y2 = face[0]
|
| 9 |
-
detections.append((x1, y1, x2, y2, face[-1]))
|
| 10 |
-
return np.array(detections)
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
def get_tracks(detect_results):
|
| 14 |
-
tracks = {}
|
| 15 |
-
mot_tracker = Sort()
|
| 16 |
-
for faces in detect_results:
|
| 17 |
-
detections = get_detections(faces)
|
| 18 |
-
track_bbs_ids = mot_tracker.update(detections)
|
| 19 |
-
for track in track_bbs_ids: # 单独框出每一张人脸
|
| 20 |
-
id = int(track[-1])
|
| 21 |
-
box = track[:4]
|
| 22 |
-
if id in tracks:
|
| 23 |
-
tracks[id].append(box)
|
| 24 |
-
else:
|
| 25 |
-
tracks[id] = [box]
|
| 26 |
-
|
| 27 |
-
return [track for id, track in tracks.items() if len(track) == len(detect_results)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/ct/utils.py
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
import cv2
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
def write_img(file, img):
|
| 5 |
-
cv2.imwrite(file, img, [cv2.IMWRITE_PNG_COMPRESSION, 0])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/faster_crop_align_xray.py
DELETED
|
@@ -1,73 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
import cv2
|
| 3 |
-
from .warp_for_xray import (
|
| 4 |
-
estimiate_batch_transform,
|
| 5 |
-
transform_landmarks,
|
| 6 |
-
std_points_256,
|
| 7 |
-
)
|
| 8 |
-
import numpy as np
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
class FasterCropAlignXRay:
|
| 12 |
-
"""
|
| 13 |
-
修正到统一坐标系,统一图像大小到标准尺寸
|
| 14 |
-
"""
|
| 15 |
-
|
| 16 |
-
def __init__(self, size=256):
|
| 17 |
-
self.image_size = size
|
| 18 |
-
self.std_points = std_points_256 * size / 256.0
|
| 19 |
-
|
| 20 |
-
def __call__(self, landmarks, images=None, jitter=False):
|
| 21 |
-
landmarks = [landmark[:4] for landmark in landmarks]
|
| 22 |
-
ori_boxes = np.array([ori_box for _, _, _, ori_box in landmarks])
|
| 23 |
-
five_landmarks = np.array([ldm5 for _, ldm5, _, _ in landmarks])
|
| 24 |
-
landmarks68 = np.array([ldm68 for _, _, ldm68, _ in landmarks])
|
| 25 |
-
# assert landmarks68.min() > 0
|
| 26 |
-
|
| 27 |
-
left_top = ori_boxes[:, :2].min(0)
|
| 28 |
-
|
| 29 |
-
right_bottom = ori_boxes[:, 2:].max(0)
|
| 30 |
-
|
| 31 |
-
size = right_bottom - left_top
|
| 32 |
-
|
| 33 |
-
w, h = size
|
| 34 |
-
|
| 35 |
-
diff = ori_boxes[:, :2] - left_top[None, ...]
|
| 36 |
-
|
| 37 |
-
new_five_landmarks = five_landmarks + diff[:, None, :]
|
| 38 |
-
new_landmarks68 = landmarks68 + diff[:, None, :]
|
| 39 |
-
|
| 40 |
-
landmark_for_estimiate = new_five_landmarks.copy()
|
| 41 |
-
if jitter:
|
| 42 |
-
landmark_for_estimiate += np.random.uniform(
|
| 43 |
-
-4, 4, landmark_for_estimiate.shape
|
| 44 |
-
)
|
| 45 |
-
|
| 46 |
-
tfm, trans = estimiate_batch_transform(
|
| 47 |
-
landmark_for_estimiate, tgt_pts=self.std_points
|
| 48 |
-
)
|
| 49 |
-
|
| 50 |
-
transformed_landmarks68 = np.array(
|
| 51 |
-
[transform_landmarks(ldm68, trans) for ldm68 in new_landmarks68]
|
| 52 |
-
)
|
| 53 |
-
|
| 54 |
-
if images is not None:
|
| 55 |
-
transformed_images = [
|
| 56 |
-
self.process_sinlge(tfm, image, d, h, w)
|
| 57 |
-
for image, d in zip(images, diff)
|
| 58 |
-
] # 拼接 func 的参数
|
| 59 |
-
transformed_images = np.stack(transformed_images)
|
| 60 |
-
return transformed_landmarks68, transformed_images
|
| 61 |
-
else:
|
| 62 |
-
return transformed_landmarks68
|
| 63 |
-
|
| 64 |
-
def process_sinlge(self, tfm, image, d, h, w):
|
| 65 |
-
assert isinstance(image, np.ndarray)
|
| 66 |
-
new_image = np.zeros((h, w, 3), dtype=np.uint8)
|
| 67 |
-
x, y = d
|
| 68 |
-
ih, iw, _ = image.shape
|
| 69 |
-
new_image[y : y + ih, x : x + iw] = image
|
| 70 |
-
transformed_image = cv2.warpAffine(
|
| 71 |
-
new_image, tfm, (self.image_size, self.image_size)
|
| 72 |
-
)
|
| 73 |
-
return transformed_image
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/supply_writer.py
DELETED
|
@@ -1,49 +0,0 @@
|
|
| 1 |
-
import cv2
|
| 2 |
-
|
| 3 |
-
class SupplyWriter:
|
| 4 |
-
def __init__(self, intput_video, output_video, opt_thres, rgb_input=True):
|
| 5 |
-
reader = cv2.VideoCapture(intput_video)
|
| 6 |
-
fourcc = cv2.VideoWriter_fourcc(*"XVID")
|
| 7 |
-
fps = reader.get(cv2.CAP_PROP_FPS)
|
| 8 |
-
width = int(reader.get(3))
|
| 9 |
-
height = int(reader.get(4))
|
| 10 |
-
reader.release()
|
| 11 |
-
self.padding = 40
|
| 12 |
-
|
| 13 |
-
self.writer = cv2.VideoWriter(output_video, fourcc, fps, (height, width)[::-1])
|
| 14 |
-
self.rgb_input = rgb_input
|
| 15 |
-
self.opt_thres = opt_thres
|
| 16 |
-
|
| 17 |
-
def run(self, images, scores, boxes):
|
| 18 |
-
# Text variables
|
| 19 |
-
font_face = cv2.FONT_HERSHEY_SIMPLEX
|
| 20 |
-
thickness = 5
|
| 21 |
-
font_scale = 3
|
| 22 |
-
|
| 23 |
-
for image, score, box in zip(images, scores, boxes):
|
| 24 |
-
if self.rgb_input:
|
| 25 |
-
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
|
| 26 |
-
if box is not None:
|
| 27 |
-
label = "fake" if score > self.opt_thres else "real"
|
| 28 |
-
x1, y1, x2, y2 = box
|
| 29 |
-
x = int(x1)
|
| 30 |
-
y = int(y1)
|
| 31 |
-
w = int(x2 - x1)
|
| 32 |
-
h = int(y2 - y1)
|
| 33 |
-
color = (
|
| 34 |
-
(255, 255, 0) if label == "real" else (0, 255, 255)
|
| 35 |
-
) # BGR 255 0
|
| 36 |
-
cv2.putText(
|
| 37 |
-
image,
|
| 38 |
-
label,
|
| 39 |
-
(x, y + h + 68),
|
| 40 |
-
font_face,
|
| 41 |
-
font_scale,
|
| 42 |
-
color,
|
| 43 |
-
thickness,
|
| 44 |
-
2,
|
| 45 |
-
)
|
| 46 |
-
# draw box over face
|
| 47 |
-
cv2.rectangle(image, (x, y), (x + w, y + h), color, 10)
|
| 48 |
-
self.writer.write(image)
|
| 49 |
-
self.writer.release()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/utils.py
DELETED
|
@@ -1,115 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
import cv2
|
| 3 |
-
import os
|
| 4 |
-
import platform
|
| 5 |
-
import json
|
| 6 |
-
import errno
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def weak_check(detect_res):
|
| 11 |
-
return sum([len(faces) for faces in detect_res]) > len(detect_res) * 0.75
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
def get_crop_box(shape, box, scale=0.5):
|
| 15 |
-
height, width = shape
|
| 16 |
-
box = np.rint(box).astype(np.int32)
|
| 17 |
-
new_box = box.reshape(2, 2)
|
| 18 |
-
size = new_box[1] - new_box[0]
|
| 19 |
-
diff = scale * size
|
| 20 |
-
diff = diff[None, :] * np.array([-1, 1])[:, None]
|
| 21 |
-
new_box = new_box + diff
|
| 22 |
-
new_box[:, 0] = np.clip(new_box[:, 0], 0, width - 1)
|
| 23 |
-
new_box[:, 1] = np.clip(new_box[:, 1], 0, height - 1)
|
| 24 |
-
new_box = np.rint(new_box).astype(np.int32)
|
| 25 |
-
return new_box.reshape(-1)
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
def get_fps(input_file):
|
| 29 |
-
reader = cv2.VideoCapture(input_file)
|
| 30 |
-
fps = reader.get(cv2.CAP_PROP_FPS)
|
| 31 |
-
reader.release()
|
| 32 |
-
return fps
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
def mkdir_p(dirname):
|
| 37 |
-
"""Like "mkdir -p", make a dir recursively, but do nothing if the dir exists
|
| 38 |
-
这个是线程安全的, from Lingzhi Li
|
| 39 |
-
Args:
|
| 40 |
-
dirname(str):
|
| 41 |
-
"""
|
| 42 |
-
assert dirname is not None
|
| 43 |
-
if dirname == "" or os.path.isdir(dirname):
|
| 44 |
-
return
|
| 45 |
-
try:
|
| 46 |
-
os.makedirs(dirname)
|
| 47 |
-
except OSError as e:
|
| 48 |
-
if e.errno != errno.EEXIST:
|
| 49 |
-
raise e
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
def mkdir(*args):
|
| 53 |
-
for folder in args:
|
| 54 |
-
if not os.path.isdir(folder):
|
| 55 |
-
mkdir_p(folder)
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
def make_join(*args):
|
| 59 |
-
folder = os.path.join(*args)
|
| 60 |
-
mkdir(folder)
|
| 61 |
-
return folder
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
def list_dir(folder, condition=None, key=lambda x: x, reverse=False, co_join=[]):
|
| 65 |
-
files = os.listdir(folder)
|
| 66 |
-
if condition is not None:
|
| 67 |
-
files = filter(condition, files)
|
| 68 |
-
co_join = [folder] + co_join
|
| 69 |
-
if key is not None:
|
| 70 |
-
files = sorted(files, key=key, reverse=reverse)
|
| 71 |
-
files = [(file, *[os.path.join(fold, file) for fold in co_join]) for file in files]
|
| 72 |
-
return files
|
| 73 |
-
|
| 74 |
-
def get_jointer(file):
|
| 75 |
-
def jointer(folder):
|
| 76 |
-
return os.path.join(folder, file)
|
| 77 |
-
|
| 78 |
-
return jointer
|
| 79 |
-
|
| 80 |
-
def flatten(l):
|
| 81 |
-
return [item for sublist in l for item in sublist]
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
def is_win():
|
| 85 |
-
return platform.system() == "Windows"
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
def get_postfix(post_fix):
|
| 89 |
-
return lambda x: x.endswith(post_fix)
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
def partition(images, size):
|
| 93 |
-
"""
|
| 94 |
-
Returns a new list with elements
|
| 95 |
-
of which is a list of certain size.
|
| 96 |
-
|
| 97 |
-
>>> partition([1, 2, 3, 4], 3)
|
| 98 |
-
[[1, 2, 3], [4]]
|
| 99 |
-
"""
|
| 100 |
-
return [
|
| 101 |
-
images[i : i + size] if i + size <= len(images) else images[i:]
|
| 102 |
-
for i in range(0, len(images), size)
|
| 103 |
-
]
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
def load_json(file):
|
| 107 |
-
with open(file, "r") as f:
|
| 108 |
-
res = json.load(f)
|
| 109 |
-
return res
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
def save_json(file, obj):
|
| 113 |
-
with open(file, "w", encoding="utf-8") as f:
|
| 114 |
-
json.dump(obj, f, indent=4, ensure_ascii=False)
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/test_tools/warp_for_xray.py
DELETED
|
@@ -1,574 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
import cv2
|
| 3 |
-
|
| 4 |
-
# -*- coding: utf-8 -*-
|
| 5 |
-
"""
|
| 6 |
-
Created on Tue Jul 11 06:54:28 2017
|
| 7 |
-
|
| 8 |
-
@author: zhaoyafei
|
| 9 |
-
"""
|
| 10 |
-
|
| 11 |
-
import numpy as np
|
| 12 |
-
from numpy.linalg import inv, norm, lstsq
|
| 13 |
-
from numpy.linalg import matrix_rank as rank
|
| 14 |
-
|
| 15 |
-
"""
|
| 16 |
-
Introduction:
|
| 17 |
-
----------
|
| 18 |
-
numpy implemetation form matlab function CP2TFORM(...)
|
| 19 |
-
with 'transformtype':
|
| 20 |
-
1) 'nonreflective similarity'
|
| 21 |
-
2) 'similarity'
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
MATLAB code:
|
| 25 |
-
----------
|
| 26 |
-
%--------------------------------------
|
| 27 |
-
% Function findNonreflectiveSimilarity
|
| 28 |
-
%
|
| 29 |
-
function [trans, output] = findNonreflectiveSimilarity(uv,xy,options)
|
| 30 |
-
%
|
| 31 |
-
% For a nonreflective similarity:
|
| 32 |
-
%
|
| 33 |
-
% let sc = s*cos(theta)
|
| 34 |
-
% let ss = s*sin(theta)
|
| 35 |
-
%
|
| 36 |
-
% [ sc -ss
|
| 37 |
-
% [u v] = [x y 1] * ss sc
|
| 38 |
-
% tx ty]
|
| 39 |
-
%
|
| 40 |
-
% There are 4 unknowns: sc,ss,tx,ty.
|
| 41 |
-
%
|
| 42 |
-
% Another way to write this is:
|
| 43 |
-
%
|
| 44 |
-
% u = [x y 1 0] * [sc
|
| 45 |
-
% ss
|
| 46 |
-
% tx
|
| 47 |
-
% ty]
|
| 48 |
-
%
|
| 49 |
-
% v = [y -x 0 1] * [sc
|
| 50 |
-
% ss
|
| 51 |
-
% tx
|
| 52 |
-
% ty]
|
| 53 |
-
%
|
| 54 |
-
% With 2 or more correspondence points we can combine the u equations and
|
| 55 |
-
% the v equations for one linear system to solve for sc,ss,tx,ty.
|
| 56 |
-
%
|
| 57 |
-
% [ u1 ] = [ x1 y1 1 0 ] * [sc]
|
| 58 |
-
% [ u2 ] [ x2 y2 1 0 ] [ss]
|
| 59 |
-
% [ ... ] [ ... ] [tx]
|
| 60 |
-
% [ un ] [ xn yn 1 0 ] [ty]
|
| 61 |
-
% [ v1 ] [ y1 -x1 0 1 ]
|
| 62 |
-
% [ v2 ] [ y2 -x2 0 1 ]
|
| 63 |
-
% [ ... ] [ ... ]
|
| 64 |
-
% [ vn ] [ yn -xn 0 1 ]
|
| 65 |
-
%
|
| 66 |
-
% Or rewriting the above matrix equation:
|
| 67 |
-
% U = X * r, where r = [sc ss tx ty]'
|
| 68 |
-
% so r = X\ U.
|
| 69 |
-
%
|
| 70 |
-
|
| 71 |
-
K = options.K;
|
| 72 |
-
M = size(xy,1);
|
| 73 |
-
x = xy(:,1);
|
| 74 |
-
y = xy(:,2);
|
| 75 |
-
X = [x y ones(M,1) zeros(M,1);
|
| 76 |
-
y -x zeros(M,1) ones(M,1) ];
|
| 77 |
-
|
| 78 |
-
u = uv(:,1);
|
| 79 |
-
v = uv(:,2);
|
| 80 |
-
U = [u; v];
|
| 81 |
-
|
| 82 |
-
% We know that X * r = U
|
| 83 |
-
if rank(X) >= 2*K
|
| 84 |
-
r = X \ U;
|
| 85 |
-
else
|
| 86 |
-
error(message('images:cp2tform:twoUniquePointsReq'))
|
| 87 |
-
end
|
| 88 |
-
|
| 89 |
-
sc = r(1);
|
| 90 |
-
ss = r(2);
|
| 91 |
-
tx = r(3);
|
| 92 |
-
ty = r(4);
|
| 93 |
-
|
| 94 |
-
Tinv = [sc -ss 0;
|
| 95 |
-
ss sc 0;
|
| 96 |
-
tx ty 1];
|
| 97 |
-
|
| 98 |
-
T = inv(Tinv);
|
| 99 |
-
T(:,3) = [0 0 1]';
|
| 100 |
-
|
| 101 |
-
trans = maketform('affine', T);
|
| 102 |
-
output = [];
|
| 103 |
-
|
| 104 |
-
%-------------------------
|
| 105 |
-
% Function findSimilarity
|
| 106 |
-
%
|
| 107 |
-
function [trans, output] = findSimilarity(uv,xy,options)
|
| 108 |
-
%
|
| 109 |
-
% The similarities are a superset of the nonreflective similarities as they may
|
| 110 |
-
% also include reflection.
|
| 111 |
-
%
|
| 112 |
-
% let sc = s*cos(theta)
|
| 113 |
-
% let ss = s*sin(theta)
|
| 114 |
-
%
|
| 115 |
-
% [ sc -ss
|
| 116 |
-
% [u v] = [x y 1] * ss sc
|
| 117 |
-
% tx ty]
|
| 118 |
-
%
|
| 119 |
-
% OR
|
| 120 |
-
%
|
| 121 |
-
% [ sc ss
|
| 122 |
-
% [u v] = [x y 1] * ss -sc
|
| 123 |
-
% tx ty]
|
| 124 |
-
%
|
| 125 |
-
% Algorithm:
|
| 126 |
-
% 1) Solve for trans1, a nonreflective similarity.
|
| 127 |
-
% 2) Reflect the xy data across the Y-axis,
|
| 128 |
-
% and solve for trans2r, also a nonreflective similarity.
|
| 129 |
-
% 3) Transform trans2r to trans2, undoing the reflection done in step 2.
|
| 130 |
-
% 4) Use TFORMFWD to transform uv using both trans1 and trans2,
|
| 131 |
-
% and compare the results, Returnsing the transformation corresponding
|
| 132 |
-
% to the smaller L2 norm.
|
| 133 |
-
|
| 134 |
-
% Need to reset options.K to prepare for calls to findNonreflectiveSimilarity.
|
| 135 |
-
% This is safe because we already checked that there are enough point pairs.
|
| 136 |
-
options.K = 2;
|
| 137 |
-
|
| 138 |
-
% Solve for trans1
|
| 139 |
-
[trans1, output] = findNonreflectiveSimilarity(uv,xy,options);
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
% Solve for trans2
|
| 143 |
-
|
| 144 |
-
% manually reflect the xy data across the Y-axis
|
| 145 |
-
xyR = xy;
|
| 146 |
-
xyR(:,1) = -1*xyR(:,1);
|
| 147 |
-
|
| 148 |
-
trans2r = findNonreflectiveSimilarity(uv,xyR,options);
|
| 149 |
-
|
| 150 |
-
% manually reflect the tform to undo the reflection done on xyR
|
| 151 |
-
TreflectY = [-1 0 0;
|
| 152 |
-
0 1 0;
|
| 153 |
-
0 0 1];
|
| 154 |
-
trans2 = maketform('affine', trans2r.tdata.T * TreflectY);
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
% Figure out if trans1 or trans2 is better
|
| 158 |
-
xy1 = tformfwd(trans1,uv);
|
| 159 |
-
norm1 = norm(xy1-xy);
|
| 160 |
-
|
| 161 |
-
xy2 = tformfwd(trans2,uv);
|
| 162 |
-
norm2 = norm(xy2-xy);
|
| 163 |
-
|
| 164 |
-
if norm1 <= norm2
|
| 165 |
-
trans = trans1;
|
| 166 |
-
else
|
| 167 |
-
trans = trans2;
|
| 168 |
-
end
|
| 169 |
-
"""
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
class MatlabCp2tormException(Exception):
|
| 173 |
-
def __str__(self):
|
| 174 |
-
return "In File {}:{}".format(__file__, super.__str__(self))
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
def tformfwd(trans, uv):
|
| 178 |
-
"""
|
| 179 |
-
Function:
|
| 180 |
-
----------
|
| 181 |
-
apply affine transform 'trans' to uv
|
| 182 |
-
|
| 183 |
-
Parameters:
|
| 184 |
-
----------
|
| 185 |
-
@trans: 3x3 np.array
|
| 186 |
-
transform matrix
|
| 187 |
-
@uv: Kx2 np.array
|
| 188 |
-
each row is a pair of coordinates (x, y)
|
| 189 |
-
|
| 190 |
-
Returns:
|
| 191 |
-
----------
|
| 192 |
-
@xy: Kx2 np.array
|
| 193 |
-
each row is a pair of transformed coordinates (x, y)
|
| 194 |
-
"""
|
| 195 |
-
uv = np.hstack((uv, np.ones((uv.shape[0], 1))))
|
| 196 |
-
xy = np.dot(uv, trans)
|
| 197 |
-
xy = xy[:, 0:-1]
|
| 198 |
-
return xy
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
def tforminv(trans, uv):
|
| 202 |
-
"""
|
| 203 |
-
Function:
|
| 204 |
-
----------
|
| 205 |
-
apply the inverse of affine transform 'trans' to uv
|
| 206 |
-
|
| 207 |
-
Parameters:
|
| 208 |
-
----------
|
| 209 |
-
@trans: 3x3 np.array
|
| 210 |
-
transform matrix
|
| 211 |
-
@uv: Kx2 np.array
|
| 212 |
-
each row is a pair of coordinates (x, y)
|
| 213 |
-
|
| 214 |
-
Returns:
|
| 215 |
-
----------
|
| 216 |
-
@xy: Kx2 np.array
|
| 217 |
-
each row is a pair of inverse-transformed coordinates (x, y)
|
| 218 |
-
"""
|
| 219 |
-
Tinv = inv(trans)
|
| 220 |
-
xy = tformfwd(Tinv, uv)
|
| 221 |
-
return xy
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
def findNonreflectiveSimilarity(uv, xy, options=None):
|
| 225 |
-
"""
|
| 226 |
-
Function:
|
| 227 |
-
----------
|
| 228 |
-
Find Non-reflective Similarity Transform Matrix 'trans':
|
| 229 |
-
u = uv[:, 0]
|
| 230 |
-
v = uv[:, 1]
|
| 231 |
-
x = xy[:, 0]
|
| 232 |
-
y = xy[:, 1]
|
| 233 |
-
[x, y, 1] = [u, v, 1] * trans
|
| 234 |
-
|
| 235 |
-
Parameters:
|
| 236 |
-
----------
|
| 237 |
-
@uv: Kx2 np.array
|
| 238 |
-
source points each row is a pair of coordinates (x, y)
|
| 239 |
-
@xy: Kx2 np.array
|
| 240 |
-
each row is a pair of inverse-transformed
|
| 241 |
-
@option: not used, keep it as None
|
| 242 |
-
|
| 243 |
-
Returns:
|
| 244 |
-
@trans: 3x3 np.array
|
| 245 |
-
transform matrix from uv to xy
|
| 246 |
-
@trans_inv: 3x3 np.array
|
| 247 |
-
inverse of trans, transform matrix from xy to uv
|
| 248 |
-
|
| 249 |
-
Matlab:
|
| 250 |
-
----------
|
| 251 |
-
% For a nonreflective similarity:
|
| 252 |
-
%
|
| 253 |
-
% let sc = s*cos(theta)
|
| 254 |
-
% let ss = s*sin(theta)
|
| 255 |
-
%
|
| 256 |
-
% [ sc -ss
|
| 257 |
-
% [u v] = [x y 1] * ss sc
|
| 258 |
-
% tx ty]
|
| 259 |
-
%
|
| 260 |
-
% There are 4 unknowns: sc,ss,tx,ty.
|
| 261 |
-
%
|
| 262 |
-
% Another way to write this is:
|
| 263 |
-
%
|
| 264 |
-
% u = [x y 1 0] * [sc
|
| 265 |
-
% ss
|
| 266 |
-
% tx
|
| 267 |
-
% ty]
|
| 268 |
-
%
|
| 269 |
-
% v = [y -x 0 1] * [sc
|
| 270 |
-
% ss
|
| 271 |
-
% tx
|
| 272 |
-
% ty]
|
| 273 |
-
%
|
| 274 |
-
% With 2 or more correspondence points we can combine the u equations and
|
| 275 |
-
% the v equations for one linear system to solve for sc,ss,tx,ty.
|
| 276 |
-
%
|
| 277 |
-
% [ u1 ] = [ x1 y1 1 0 ] * [sc]
|
| 278 |
-
% [ u2 ] [ x2 y2 1 0 ] [ss]
|
| 279 |
-
% [ ... ] [ ... ] [tx]
|
| 280 |
-
% [ un ] [ xn yn 1 0 ] [ty]
|
| 281 |
-
% [ v1 ] [ y1 -x1 0 1 ]
|
| 282 |
-
% [ v2 ] [ y2 -x2 0 1 ]
|
| 283 |
-
% [ ... ] [ ... ]
|
| 284 |
-
% [ vn ] [ yn -xn 0 1 ]
|
| 285 |
-
%
|
| 286 |
-
% Or rewriting the above matrix equation:
|
| 287 |
-
% U = X * r, where r = [sc ss tx ty]'
|
| 288 |
-
% so r = X\ U.
|
| 289 |
-
%
|
| 290 |
-
"""
|
| 291 |
-
options = {"K": 2}
|
| 292 |
-
|
| 293 |
-
K = options["K"]
|
| 294 |
-
M = xy.shape[0]
|
| 295 |
-
x = xy[:, 0].reshape((-1, 1)) # use reshape to keep a column vector
|
| 296 |
-
y = xy[:, 1].reshape((-1, 1)) # use reshape to keep a column vector
|
| 297 |
-
# print '--->x, y:\n', x, y
|
| 298 |
-
|
| 299 |
-
tmp1 = np.hstack((x, y, np.ones((M, 1)), np.zeros((M, 1))))
|
| 300 |
-
tmp2 = np.hstack((y, -x, np.zeros((M, 1)), np.ones((M, 1))))
|
| 301 |
-
X = np.vstack((tmp1, tmp2))
|
| 302 |
-
# print '--->X.shape: ', X.shape
|
| 303 |
-
# print 'X:\n', X
|
| 304 |
-
|
| 305 |
-
u = uv[:, 0].reshape((-1, 1)) # use reshape to keep a column vector
|
| 306 |
-
v = uv[:, 1].reshape((-1, 1)) # use reshape to keep a column vector
|
| 307 |
-
U = np.vstack((u, v))
|
| 308 |
-
# print '--->U.shape: ', U.shape
|
| 309 |
-
# print 'U:\n', U
|
| 310 |
-
|
| 311 |
-
# We know that X * r = U
|
| 312 |
-
if rank(X) >= 2 * K:
|
| 313 |
-
r, _, _, _ = lstsq(X, U, rcond=-1)
|
| 314 |
-
r = np.squeeze(r)
|
| 315 |
-
else:
|
| 316 |
-
raise Exception("cp2tform:twoUniquePointsReq")
|
| 317 |
-
|
| 318 |
-
# print '--->r:\n', r
|
| 319 |
-
|
| 320 |
-
sc = r[0]
|
| 321 |
-
ss = r[1]
|
| 322 |
-
tx = r[2]
|
| 323 |
-
ty = r[3]
|
| 324 |
-
|
| 325 |
-
Tinv = np.array([[sc, -ss, 0], [ss, sc, 0], [tx, ty, 1]])
|
| 326 |
-
|
| 327 |
-
# print '--->Tinv:\n', Tinv
|
| 328 |
-
|
| 329 |
-
T = inv(Tinv)
|
| 330 |
-
# print '--->T:\n', T
|
| 331 |
-
|
| 332 |
-
T[:, 2] = np.array([0, 0, 1])
|
| 333 |
-
|
| 334 |
-
return T, Tinv
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
def findSimilarity(uv, xy, options=None):
|
| 338 |
-
"""
|
| 339 |
-
Function:
|
| 340 |
-
----------
|
| 341 |
-
Find Reflective Similarity Transform Matrix 'trans':
|
| 342 |
-
u = uv[:, 0]
|
| 343 |
-
v = uv[:, 1]
|
| 344 |
-
x = xy[:, 0]
|
| 345 |
-
y = xy[:, 1]
|
| 346 |
-
[x, y, 1] = [u, v, 1] * trans
|
| 347 |
-
|
| 348 |
-
Parameters:
|
| 349 |
-
----------
|
| 350 |
-
@uv: Kx2 np.array
|
| 351 |
-
source points each row is a pair of coordinates (x, y)
|
| 352 |
-
@xy: Kx2 np.array
|
| 353 |
-
each row is a pair of inverse-transformed
|
| 354 |
-
@option: not used, keep it as None
|
| 355 |
-
|
| 356 |
-
Returns:
|
| 357 |
-
----------
|
| 358 |
-
@trans: 3x3 np.array
|
| 359 |
-
transform matrix from uv to xy
|
| 360 |
-
@trans_inv: 3x3 np.array
|
| 361 |
-
inverse of trans, transform matrix from xy to uv
|
| 362 |
-
|
| 363 |
-
Matlab:
|
| 364 |
-
----------
|
| 365 |
-
% The similarities are a superset of the nonreflective similarities as they may
|
| 366 |
-
% also include reflection.
|
| 367 |
-
%
|
| 368 |
-
% let sc = s*cos(theta)
|
| 369 |
-
% let ss = s*sin(theta)
|
| 370 |
-
%
|
| 371 |
-
% [ sc -ss
|
| 372 |
-
% [u v] = [x y 1] * ss sc
|
| 373 |
-
% tx ty]
|
| 374 |
-
%
|
| 375 |
-
% OR
|
| 376 |
-
%
|
| 377 |
-
% [ sc ss
|
| 378 |
-
% [u v] = [x y 1] * ss -sc
|
| 379 |
-
% tx ty]
|
| 380 |
-
%
|
| 381 |
-
% Algorithm:
|
| 382 |
-
% 1) Solve for trans1, a nonreflective similarity.
|
| 383 |
-
% 2) Reflect the xy data across the Y-axis,
|
| 384 |
-
% and solve for trans2r, also a nonreflective similarity.
|
| 385 |
-
% 3) Transform trans2r to trans2, undoing the reflection done in step 2.
|
| 386 |
-
% 4) Use TFORMFWD to transform uv using both trans1 and trans2,
|
| 387 |
-
% and compare the results, Returnsing the transformation corresponding
|
| 388 |
-
% to the smaller L2 norm.
|
| 389 |
-
|
| 390 |
-
% Need to reset options.K to prepare for calls to findNonreflectiveSimilarity.
|
| 391 |
-
% This is safe because we already checked that there are enough point pairs.
|
| 392 |
-
"""
|
| 393 |
-
options = {"K": 2}
|
| 394 |
-
|
| 395 |
-
# uv = np.array(uv)
|
| 396 |
-
# xy = np.array(xy)
|
| 397 |
-
|
| 398 |
-
# Solve for trans1
|
| 399 |
-
trans1, trans1_inv = findNonreflectiveSimilarity(uv, xy, options)
|
| 400 |
-
|
| 401 |
-
# Solve for trans2
|
| 402 |
-
|
| 403 |
-
# manually reflect the xy data across the Y-axis
|
| 404 |
-
xyR = xy
|
| 405 |
-
xyR[:, 0] = -1 * xyR[:, 0]
|
| 406 |
-
|
| 407 |
-
trans2r, trans2r_inv = findNonreflectiveSimilarity(uv, xyR, options)
|
| 408 |
-
|
| 409 |
-
# manually reflect the tform to undo the reflection done on xyR
|
| 410 |
-
TreflectY = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
| 411 |
-
|
| 412 |
-
trans2 = np.dot(trans2r, TreflectY)
|
| 413 |
-
|
| 414 |
-
# Figure out if trans1 or trans2 is better
|
| 415 |
-
xy1 = tformfwd(trans1, uv)
|
| 416 |
-
norm1 = norm(xy1 - xy)
|
| 417 |
-
|
| 418 |
-
xy2 = tformfwd(trans2, uv)
|
| 419 |
-
norm2 = norm(xy2 - xy)
|
| 420 |
-
|
| 421 |
-
if norm1 <= norm2:
|
| 422 |
-
return trans1, trans1_inv
|
| 423 |
-
else:
|
| 424 |
-
trans2_inv = inv(trans2)
|
| 425 |
-
return trans2, trans2_inv
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
def get_similarity_transform(src_pts, dst_pts, reflective=True):
|
| 429 |
-
"""
|
| 430 |
-
Function:
|
| 431 |
-
----------
|
| 432 |
-
Find Similarity Transform Matrix 'trans':
|
| 433 |
-
u = src_pts[:, 0]
|
| 434 |
-
v = src_pts[:, 1]
|
| 435 |
-
x = dst_pts[:, 0]
|
| 436 |
-
y = dst_pts[:, 1]
|
| 437 |
-
[x, y, 1] = [u, v, 1] * trans
|
| 438 |
-
|
| 439 |
-
Parameters:
|
| 440 |
-
----------
|
| 441 |
-
@src_pts: Kx2 np.array
|
| 442 |
-
source points, each row is a pair of coordinates (x, y)
|
| 443 |
-
@dst_pts: Kx2 np.array
|
| 444 |
-
destination points, each row is a pair of transformed
|
| 445 |
-
coordinates (x, y)
|
| 446 |
-
@reflective: True or False
|
| 447 |
-
if True:
|
| 448 |
-
use reflective similarity transform
|
| 449 |
-
else:
|
| 450 |
-
use non-reflective similarity transform
|
| 451 |
-
|
| 452 |
-
Returns:
|
| 453 |
-
----------
|
| 454 |
-
@trans: 3x3 np.array
|
| 455 |
-
transform matrix from uv to xy
|
| 456 |
-
trans_inv: 3x3 np.array
|
| 457 |
-
inverse of trans, transform matrix from xy to uv
|
| 458 |
-
"""
|
| 459 |
-
|
| 460 |
-
if reflective:
|
| 461 |
-
trans, trans_inv = findSimilarity(src_pts, dst_pts)
|
| 462 |
-
else:
|
| 463 |
-
trans, trans_inv = findNonreflectiveSimilarity(src_pts, dst_pts)
|
| 464 |
-
|
| 465 |
-
return trans, trans_inv
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
def cvt_tform_mat_for_cv2(trans):
|
| 469 |
-
"""
|
| 470 |
-
Function:
|
| 471 |
-
----------
|
| 472 |
-
Convert Transform Matrix 'trans' into 'cv2_trans' which could be
|
| 473 |
-
directly used by cv2.warpAffine():
|
| 474 |
-
u = src_pts[:, 0]
|
| 475 |
-
v = src_pts[:, 1]
|
| 476 |
-
x = dst_pts[:, 0]
|
| 477 |
-
y = dst_pts[:, 1]
|
| 478 |
-
[x, y].T = cv_trans * [u, v, 1].T
|
| 479 |
-
|
| 480 |
-
Parameters:
|
| 481 |
-
----------
|
| 482 |
-
@trans: 3x3 np.array
|
| 483 |
-
transform matrix from uv to xy
|
| 484 |
-
|
| 485 |
-
Returns:
|
| 486 |
-
----------
|
| 487 |
-
@cv2_trans: 2x3 np.array
|
| 488 |
-
transform matrix from src_pts to dst_pts, could be directly used
|
| 489 |
-
for cv2.warpAffine()
|
| 490 |
-
"""
|
| 491 |
-
cv2_trans = trans[:, 0:2].T
|
| 492 |
-
|
| 493 |
-
return cv2_trans
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
def get_similarity_transform_for_cv2(src_pts, dst_pts, reflective=True):
|
| 497 |
-
"""
|
| 498 |
-
Function:
|
| 499 |
-
----------
|
| 500 |
-
Find Similarity Transform Matrix 'cv2_trans' which could be
|
| 501 |
-
directly used by cv2.warpAffine():
|
| 502 |
-
u = src_pts[:, 0]
|
| 503 |
-
v = src_pts[:, 1]
|
| 504 |
-
x = dst_pts[:, 0]
|
| 505 |
-
y = dst_pts[:, 1]
|
| 506 |
-
[x, y].T = cv_trans * [u, v, 1].T
|
| 507 |
-
|
| 508 |
-
Parameters:
|
| 509 |
-
----------
|
| 510 |
-
@src_pts: Kx2 np.array
|
| 511 |
-
source points, each row is a pair of coordinates (x, y)
|
| 512 |
-
@dst_pts: Kx2 np.array
|
| 513 |
-
destination points, each row is a pair of transformed
|
| 514 |
-
coordinates (x, y)
|
| 515 |
-
reflective: True or False
|
| 516 |
-
if True:
|
| 517 |
-
use reflective similarity transform
|
| 518 |
-
else:
|
| 519 |
-
use non-reflective similarity transform
|
| 520 |
-
|
| 521 |
-
Returns:
|
| 522 |
-
----------
|
| 523 |
-
@cv2_trans: 2x3 np.array
|
| 524 |
-
transform matrix from src_pts to dst_pts, could be directly used
|
| 525 |
-
for cv2.warpAffine()
|
| 526 |
-
"""
|
| 527 |
-
trans, trans_inv = get_similarity_transform(src_pts, dst_pts, reflective)
|
| 528 |
-
cv2_trans = cvt_tform_mat_for_cv2(trans)
|
| 529 |
-
return cv2_trans, trans
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
std_points_317 = np.array(
|
| 533 |
-
[
|
| 534 |
-
[85.82991, 115.7792],
|
| 535 |
-
[169.0532, 114.3381],
|
| 536 |
-
[127.574, 167.0006],
|
| 537 |
-
[90.6964, 204.7014],
|
| 538 |
-
[167.3069, 203.3733],
|
| 539 |
-
]
|
| 540 |
-
)
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
padding = 30
|
| 544 |
-
|
| 545 |
-
std_points_317 = std_points_317 + padding
|
| 546 |
-
|
| 547 |
-
std_points_256=std_points_317.copy()
|
| 548 |
-
std_points_256[..., 0] -= 30
|
| 549 |
-
std_points_256[..., 1] -= 60
|
| 550 |
-
|
| 551 |
-
def warp_as_face_x_ray(img, src_pts, tgt_pts=std_points_317):
|
| 552 |
-
tfm, trans = get_similarity_transform_for_cv2(src_pts.copy(), tgt_pts.copy())
|
| 553 |
-
return cv2.warpAffine(img, tfm, (317, 317)), trans
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
def estimiate_batch_transform(all_src_pts, tgt_pts=std_points_317):
|
| 557 |
-
tgt_pts = np.repeat(tgt_pts[None, ...], len(all_src_pts), 0).reshape(-1, 2)
|
| 558 |
-
src_pts = np.array(all_src_pts).reshape(-1, 2)
|
| 559 |
-
tfm, trans = get_similarity_transform_for_cv2(src_pts, tgt_pts)
|
| 560 |
-
return tfm, trans
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
def batch_warp_as_face_x_ray(images, all_src_pts, tgt_pts=std_points_317):
|
| 564 |
-
tfm, trans = estimiate_batch_transform(all_src_pts, tgt_pts)
|
| 565 |
-
return [cv2.warpAffine(img, tfm, (317, 317)) for img in images], trans
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
def transform_landmarks(landmarks, trans):
|
| 569 |
-
transformed = np.hstack((landmarks, np.ones((landmarks.shape[0], 1))))
|
| 570 |
-
transformed = np.dot(transformed, trans)
|
| 571 |
-
return transformed[:, :2]
|
| 572 |
-
|
| 573 |
-
def compute_reverse_trans(trans):
|
| 574 |
-
return np.linalg.inv(trans)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/utils/__init__.py
DELETED
|
@@ -1,7 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
__all__ = [] # do not use ' from utils import *'
|
| 3 |
-
|
| 4 |
-
from .common import *
|
| 5 |
-
from .plugin_loader import PluginLoader
|
| 6 |
-
#from .plugin_loaderv2 import PluginLoader as PluginLoaderV2
|
| 7 |
-
from .model_loader import add_loader
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/utils/common.py
DELETED
|
@@ -1,80 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/python
|
| 2 |
-
# -*- coding: UTF-8 -*-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
import os
|
| 6 |
-
import torch
|
| 7 |
-
from torch.autograd import Variable
|
| 8 |
-
import errno
|
| 9 |
-
import torch.distributed as dist
|
| 10 |
-
import math
|
| 11 |
-
from functools import reduce
|
| 12 |
-
def make_folder(path, version):
|
| 13 |
-
if not os.path.exists(os.path.join(path, version)):
|
| 14 |
-
print(os.path.join(path, version))
|
| 15 |
-
os.makedirs(os.path.join(path, version))
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def tensor2var(x, grad=False):
|
| 19 |
-
if torch.cuda.is_available():
|
| 20 |
-
x = x.cuda()
|
| 21 |
-
return Variable(x, requires_grad=grad)
|
| 22 |
-
|
| 23 |
-
def var2tensor(x):
|
| 24 |
-
return x.data.cpu()
|
| 25 |
-
|
| 26 |
-
def var2numpy(x):
|
| 27 |
-
return x.data.cpu().numpy()
|
| 28 |
-
|
| 29 |
-
def denorm(x):
|
| 30 |
-
out = (x + 1) / 2
|
| 31 |
-
return out.clamp_(0, 1)
|
| 32 |
-
|
| 33 |
-
def mkdir_p(dirname):
|
| 34 |
-
""" Like "mkdir -p", make a dir recursively, but do nothing if the dir exists
|
| 35 |
-
Args:
|
| 36 |
-
dirname(str):
|
| 37 |
-
"""
|
| 38 |
-
assert dirname is not None
|
| 39 |
-
if dirname == '' or os.path.isdir(dirname):
|
| 40 |
-
return
|
| 41 |
-
try:
|
| 42 |
-
os.makedirs(dirname)
|
| 43 |
-
except OSError as e:
|
| 44 |
-
if e.errno != errno.EEXIST:
|
| 45 |
-
raise e
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
def skipShardSplit(aList, drop_last=False, num_replicas=None, rank=None):
|
| 49 |
-
if not isinstance(aList, list) and not isinstance(aList, tuple):
|
| 50 |
-
aList = List
|
| 51 |
-
|
| 52 |
-
if num_replicas is None:
|
| 53 |
-
num_replicas = dist.get_world_size() if dist.is_initialized() else 1
|
| 54 |
-
if rank is None:
|
| 55 |
-
rank = dist.get_rank() if dist.is_initialized() else 0
|
| 56 |
-
|
| 57 |
-
num_replicas = num_replicas
|
| 58 |
-
rank = rank
|
| 59 |
-
drop_last = drop_last
|
| 60 |
-
|
| 61 |
-
if drop_last:
|
| 62 |
-
aList = aList[0: (len(aList) // num_replicas) * num_replicas]
|
| 63 |
-
|
| 64 |
-
# subsample
|
| 65 |
-
aList = aList[rank::num_replicas]
|
| 66 |
-
|
| 67 |
-
return aList
|
| 68 |
-
|
| 69 |
-
def mixb2a(a,b):
|
| 70 |
-
if len(b) > len(a):
|
| 71 |
-
a,b = b,a
|
| 72 |
-
if len(b) == 0:
|
| 73 |
-
return a
|
| 74 |
-
chunk_num = (len(b))
|
| 75 |
-
a_chunk = splitIntoChunk(a, chunk_num)
|
| 76 |
-
b_chunk = list(map(lambda x:[x],b))
|
| 77 |
-
return reduce(lambda x, y: x+y, [_a+_b for _a,_b in zip(a_chunk, b_chunk)])
|
| 78 |
-
|
| 79 |
-
def splitIntoChunk(aList, chunk_num):
|
| 80 |
-
return [aList[math.ceil(k * (len(aList) / chunk_num)):math.ceil((k + 1) * (len(aList) / chunk_num)):] for k in range(chunk_num)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/utils/logger.py
DELETED
|
@@ -1,182 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/python
|
| 2 |
-
# -*- coding: UTF-8 -*-
|
| 3 |
-
# Modified by: algohunt
|
| 4 |
-
# Microsoft Research & Peking University
|
| 5 |
-
# lilingzhi@pku.edu.cn
|
| 6 |
-
# Copyright (c) 2019
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
# -*- coding: utf-8 -*-
|
| 10 |
-
|
| 11 |
-
"""
|
| 12 |
-
Borrow from tensorpack credit goes to yuxin wu
|
| 13 |
-
The logger module itself has the common logging functions of Python's
|
| 14 |
-
:class:`logging.Logger`. For example:
|
| 15 |
-
|
| 16 |
-
.. code-block:: python
|
| 17 |
-
|
| 18 |
-
from utils import logger
|
| 19 |
-
logger.set_logger_dir('train_log/test')
|
| 20 |
-
logger.info("Test")
|
| 21 |
-
logger.error("Error happened!")
|
| 22 |
-
"""
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
import logging
|
| 26 |
-
import os
|
| 27 |
-
import os.path
|
| 28 |
-
import shutil
|
| 29 |
-
import sys
|
| 30 |
-
from datetime import datetime, timedelta
|
| 31 |
-
from six.moves import input
|
| 32 |
-
from termcolor import colored
|
| 33 |
-
import time
|
| 34 |
-
|
| 35 |
-
__all__ = ['set_logger_dir', 'auto_set_dir', 'get_logger_dir']
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
class _MyFormatter(logging.Formatter):
|
| 39 |
-
def format(self, record):
|
| 40 |
-
date = colored('[%(asctime)s @%(filename)s:%(lineno)d]', 'green')
|
| 41 |
-
msg = '%(message)s'
|
| 42 |
-
if record.levelno == logging.WARNING:
|
| 43 |
-
fmt = date + ' ' + colored('WRN', 'red', attrs=['blink']) + ' ' + msg
|
| 44 |
-
elif record.levelno == logging.ERROR or record.levelno == logging.CRITICAL:
|
| 45 |
-
fmt = date + ' ' + colored('ERR', 'red', attrs=['blink', 'underline']) + ' ' + msg
|
| 46 |
-
elif record.levelno == logging.DEBUG:
|
| 47 |
-
fmt = date + ' ' + colored('DBG', 'yellow', attrs=['blink']) + ' ' + msg
|
| 48 |
-
else:
|
| 49 |
-
fmt = date + ' ' + msg
|
| 50 |
-
if hasattr(self, '_style'):
|
| 51 |
-
# Python3 compatibility
|
| 52 |
-
self._style._fmt = fmt
|
| 53 |
-
self._fmt = fmt
|
| 54 |
-
return super(_MyFormatter, self).format(record)
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
def _getlogger():
|
| 58 |
-
logger = logging.getLogger('tensorpack')
|
| 59 |
-
logger.propagate = False
|
| 60 |
-
logger.setLevel(logging.INFO)
|
| 61 |
-
handler = logging.StreamHandler(sys.stdout)
|
| 62 |
-
handler.setFormatter(_MyFormatter(datefmt='%m%d %H:%M:%S'))
|
| 63 |
-
logger.addHandler(handler)
|
| 64 |
-
return logger
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
_logger = _getlogger()
|
| 68 |
-
_LOGGING_METHOD = ['info', 'warning', 'error', 'critical', 'exception', 'debug', 'setLevel']
|
| 69 |
-
# export logger functions
|
| 70 |
-
for func in _LOGGING_METHOD:
|
| 71 |
-
locals()[func] = getattr(_logger, func)
|
| 72 |
-
__all__.append(func)
|
| 73 |
-
# 'warn' is deprecated in logging module
|
| 74 |
-
warn = _logger.warning
|
| 75 |
-
__all__.append('warn')
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
def _get_time_str():
|
| 79 |
-
utc_time = datetime.utcfromtimestamp(time.time())
|
| 80 |
-
beijing_time = utc_time- timedelta(hours=8)
|
| 81 |
-
return beijing_time.strftime('%m%d-%H%M%S')
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
# globals: logger file and directory:
|
| 85 |
-
LOG_DIR = None
|
| 86 |
-
_FILE_HANDLER = None
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
def _set_file(path):
|
| 90 |
-
global _FILE_HANDLER
|
| 91 |
-
if os.path.isfile(path):
|
| 92 |
-
backup_name = path + '.' + _get_time_str()
|
| 93 |
-
shutil.move(path, backup_name)
|
| 94 |
-
_logger.info("Existing log file '{}' backuped to '{}'".format(path, backup_name)) # noqa: F821
|
| 95 |
-
hdl = logging.FileHandler(
|
| 96 |
-
filename=path, encoding='utf-8', mode='w')
|
| 97 |
-
hdl.setFormatter(_MyFormatter(datefmt='%m%d %H:%M:%S'))
|
| 98 |
-
|
| 99 |
-
_FILE_HANDLER = hdl
|
| 100 |
-
_logger.addHandler(hdl)
|
| 101 |
-
_logger.info("Argv: " + ' '.join(sys.argv))
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
def set_logger_dir(dirname, action=None):
|
| 105 |
-
"""
|
| 106 |
-
Set the directory for global logging.
|
| 107 |
-
|
| 108 |
-
Args:
|
| 109 |
-
dirname(str): log directory
|
| 110 |
-
action(str): an action of ["k","d","q"] to be performed
|
| 111 |
-
when the directory exists. Will ask user by default.
|
| 112 |
-
|
| 113 |
-
"d": delete the directory. Note that the deletion may fail when
|
| 114 |
-
the directory is used by tensorboard.
|
| 115 |
-
|
| 116 |
-
"k": keep the directory. This is useful when you resume from a
|
| 117 |
-
previous training and want the directory to look as if the
|
| 118 |
-
training was not interrupted.
|
| 119 |
-
Note that this option does not load old models or any other
|
| 120 |
-
old states for you. It simply does nothing.
|
| 121 |
-
|
| 122 |
-
"""
|
| 123 |
-
global LOG_DIR, _FILE_HANDLER
|
| 124 |
-
if _FILE_HANDLER:
|
| 125 |
-
# unload and close the old file handler, so that we may safely delete the logger directory
|
| 126 |
-
_logger.removeHandler(_FILE_HANDLER)
|
| 127 |
-
del _FILE_HANDLER
|
| 128 |
-
|
| 129 |
-
def dir_nonempty(dirname):
|
| 130 |
-
# If directory exists and nonempty (ignore hidden files), prompt for action
|
| 131 |
-
return os.path.isdir(dirname) and len([x for x in os.listdir(dirname) if x[0] != '.'])
|
| 132 |
-
|
| 133 |
-
if dir_nonempty(dirname):
|
| 134 |
-
if not action:
|
| 135 |
-
_logger.warn("""\
|
| 136 |
-
Log directory {} exists! Use 'd' to delete it. """.format(dirname))
|
| 137 |
-
_logger.warn("""\
|
| 138 |
-
If you're resuming from a previous run, you can choose to keep it.
|
| 139 |
-
Press any other key to exit. """)
|
| 140 |
-
while not action:
|
| 141 |
-
action = input("Select Action: k (keep) / d (delete) / q (quit):").lower().strip()
|
| 142 |
-
act = action
|
| 143 |
-
if act == 'b':
|
| 144 |
-
backup_name = dirname + _get_time_str()
|
| 145 |
-
shutil.move(dirname, backup_name)
|
| 146 |
-
info("Directory '{}' backuped to '{}'".format(dirname, backup_name)) # noqa: F821
|
| 147 |
-
elif act == 'd':
|
| 148 |
-
shutil.rmtree(dirname, ignore_errors=True)
|
| 149 |
-
if dir_nonempty(dirname):
|
| 150 |
-
shutil.rmtree(dirname, ignore_errors=False)
|
| 151 |
-
elif act == 'n':
|
| 152 |
-
dirname = dirname + _get_time_str()
|
| 153 |
-
info("Use a new log directory {}".format(dirname)) # noqa: F821
|
| 154 |
-
elif act == 'k':
|
| 155 |
-
pass
|
| 156 |
-
else:
|
| 157 |
-
raise OSError("Directory {} exits!".format(dirname))
|
| 158 |
-
LOG_DIR = dirname
|
| 159 |
-
from . import mkdir_p
|
| 160 |
-
mkdir_p(dirname)
|
| 161 |
-
_set_file(os.path.join(dirname, 'log.log'))
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
def auto_set_dir(action=None, name=None):
|
| 165 |
-
"""
|
| 166 |
-
Use :func:`logger.set_logger_dir` to set log directory to
|
| 167 |
-
"./train_log/{scriptname}:{name}". "scriptname" is the name of the main python file currently running"""
|
| 168 |
-
mod = sys.modules['__main__']
|
| 169 |
-
basename = os.path.basename(mod.__file__)
|
| 170 |
-
auto_dirname = os.path.join('train_log', basename[:basename.rfind('.')])
|
| 171 |
-
if name:
|
| 172 |
-
auto_dirname += '_%s' % name if os.name == 'nt' else ':%s' % name
|
| 173 |
-
set_logger_dir(auto_dirname, action=action)
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
def get_logger_dir():
|
| 177 |
-
"""
|
| 178 |
-
Returns:
|
| 179 |
-
The logger directory, or None if not set.
|
| 180 |
-
The directory is used for general logging, tensorboard events, checkpoints, etc.
|
| 181 |
-
"""
|
| 182 |
-
return LOG_DIR
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/utils/model_loader.py
DELETED
|
@@ -1,117 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/python
|
| 2 |
-
# -*- coding: UTF-8 -*-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
import types
|
| 6 |
-
from utils import logger
|
| 7 |
-
from config import config as cfg
|
| 8 |
-
import os
|
| 9 |
-
import sys
|
| 10 |
-
import glob
|
| 11 |
-
import torch
|
| 12 |
-
import traceback
|
| 13 |
-
import types
|
| 14 |
-
import torch.distributed as dist
|
| 15 |
-
import copy
|
| 16 |
-
from .torch_save import torch_save
|
| 17 |
-
|
| 18 |
-
def add_loader(target, name,max_to_keep=2):
|
| 19 |
-
|
| 20 |
-
def get_rank(self):
|
| 21 |
-
return dist.get_rank() if dist.is_initialized() else 0
|
| 22 |
-
|
| 23 |
-
def save_models(self, epoch):
|
| 24 |
-
""" Backup and save the models """
|
| 25 |
-
if self.get_rank() == 0:
|
| 26 |
-
logger.debug("Backing up and saving models")
|
| 27 |
-
if not os.path.exists(self.model_dir):
|
| 28 |
-
os.mkdir(self.model_dir)
|
| 29 |
-
|
| 30 |
-
torch_save(self.state_dict(), self.get_checkpoint_path(epoch))
|
| 31 |
-
if os.path.exists(self.get_checkpoint_path(epoch - self.max_to_keep)):
|
| 32 |
-
os.remove(self.get_checkpoint_path(epoch - self.max_to_keep))
|
| 33 |
-
logger.info("{} models saved".format(self.name))
|
| 34 |
-
|
| 35 |
-
def load(self, fullpath=None, epoch=-1):
|
| 36 |
-
""" Force Loading a model, or load the latest model"""
|
| 37 |
-
if fullpath is None:
|
| 38 |
-
fullpath, loaded_epoch = self.find_last(epoch)
|
| 39 |
-
else:
|
| 40 |
-
loaded_epoch = epoch
|
| 41 |
-
|
| 42 |
-
if fullpath is None:
|
| 43 |
-
logger.info("No existing {} model found".format(self.name))
|
| 44 |
-
return False, -1
|
| 45 |
-
logger.debug("Loading model: '%s'", fullpath)
|
| 46 |
-
try:
|
| 47 |
-
saved_state_dict = torch.load(fullpath, map_location='cpu')
|
| 48 |
-
self.load_state_dict(saved_state_dict)
|
| 49 |
-
logger.info(" consume training from {}".format(fullpath))
|
| 50 |
-
except ValueError as err:
|
| 51 |
-
logger.warning("Failed loading existing training data for {}. Generating new models".format(self.name))
|
| 52 |
-
logger.debug("Exception: %s", str(err))
|
| 53 |
-
return False, -1
|
| 54 |
-
except OSError as err:
|
| 55 |
-
logger.warning("Failed loading existing training data for {}. Generating new models".format(self.name))
|
| 56 |
-
logger.debug("Exception: %s", str(err))
|
| 57 |
-
return False, -1
|
| 58 |
-
except RuntimeError as err:
|
| 59 |
-
logger.warning("{} model has corrupted, try to load earlier one".format(self.name))
|
| 60 |
-
logger.debug("Exception: %s", str(err))
|
| 61 |
-
return False, -1
|
| 62 |
-
except:
|
| 63 |
-
logger.error(traceback.format_exc())
|
| 64 |
-
raise
|
| 65 |
-
|
| 66 |
-
return True, loaded_epoch
|
| 67 |
-
|
| 68 |
-
def get_checkpoint_path(self, epoch):
|
| 69 |
-
"""" returning the checkpoint path w.r.t epoch which should be {name}_{epoch}.pth"""
|
| 70 |
-
return os.path.join(self.model_dir, self.name + '_' +str(epoch) + '.pth')
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
def find_last(self, epoch=-1, model_dir=None):
|
| 74 |
-
"""Finds the last checkpoint file of the last trained model in the
|
| 75 |
-
model directory.
|
| 76 |
-
Returns:
|
| 77 |
-
checkpoint :The path of the last checkpoint file
|
| 78 |
-
|
| 79 |
-
"""
|
| 80 |
-
if model_dir is None:
|
| 81 |
-
model_dir = self.model_dir
|
| 82 |
-
if not os.path.exists(model_dir):
|
| 83 |
-
logger.info("model dir not exists {} ".format(model_dir))
|
| 84 |
-
return None, -1
|
| 85 |
-
#assert os.path.exists(self.model_dir), "model dir not exists {}".format(self.model_dir)
|
| 86 |
-
checkpoints = glob.glob(os.path.join(model_dir, '*.pth'))
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
checkpoints = list(filter(lambda x: os.path.basename(x).startswith(self.name), checkpoints))
|
| 90 |
-
if len(checkpoints) == 0:
|
| 91 |
-
return None, -1
|
| 92 |
-
checkpoints = {int(os.path.basename(x).split('.')[0].split('_')[-1]):x for x in checkpoints}
|
| 93 |
-
|
| 94 |
-
start = min(checkpoints.keys())
|
| 95 |
-
end = max(checkpoints.keys())
|
| 96 |
-
|
| 97 |
-
if epoch == -1:
|
| 98 |
-
return checkpoints[end], end
|
| 99 |
-
elif epoch < start :
|
| 100 |
-
raise RuntimeError(
|
| 101 |
-
"model for epoch {} has been deleted as we only keep {} models".format(epoch,self.max_to_keep))
|
| 102 |
-
elif epoch > end:
|
| 103 |
-
raise RuntimeError(
|
| 104 |
-
"epoch {} is bigger than all exist checkpoints".format(epoch))
|
| 105 |
-
else:
|
| 106 |
-
return checkpoints[epoch], epoch
|
| 107 |
-
|
| 108 |
-
target.find_last = types.MethodType(find_last, target)
|
| 109 |
-
target.get_checkpoint_path = types.MethodType(get_checkpoint_path, target)
|
| 110 |
-
target.load = types.MethodType(load, target)
|
| 111 |
-
target.save_models = types.MethodType(save_models, target)
|
| 112 |
-
target.get_rank = types.MethodType(get_rank, target)
|
| 113 |
-
|
| 114 |
-
target.max_to_keep = max_to_keep
|
| 115 |
-
target.name = name
|
| 116 |
-
target.model_dir = os.path.join(cfg.path.model_dir, cfg.setting_name)
|
| 117 |
-
return target
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/utils/plugin_loader.py
DELETED
|
@@ -1,69 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/python
|
| 2 |
-
# -*- coding: UTF-8 -*-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
""" Plugin loader for extract, training and model tasks """
|
| 7 |
-
|
| 8 |
-
from utils import logger
|
| 9 |
-
import os
|
| 10 |
-
from importlib import import_module
|
| 11 |
-
from typing import Type
|
| 12 |
-
from trainer._base import TrainerBase
|
| 13 |
-
from torch.utils.data import Dataset
|
| 14 |
-
from model._base import ModelBase
|
| 15 |
-
|
| 16 |
-
class PluginLoader():
|
| 17 |
-
"""
|
| 18 |
-
Plugin loader for extract, training and model tasks
|
| 19 |
-
function: get_{model_type}
|
| 20 |
-
args: {model_name}
|
| 21 |
-
will return the a class named model_type under model_type.model_name.py
|
| 22 |
-
|
| 23 |
-
as it return a class you should also annotate the returning classtype to make
|
| 24 |
-
code linting avaliable in some IDE
|
| 25 |
-
"""
|
| 26 |
-
@staticmethod
|
| 27 |
-
def get_classifier(name) -> Type[ModelBase]:
|
| 28 |
-
""" Return requested attribute encoder plugin """
|
| 29 |
-
return PluginLoader._import("model.classifier", name)
|
| 30 |
-
|
| 31 |
-
@staticmethod
|
| 32 |
-
def get_trainer(name) -> Type[TrainerBase]:
|
| 33 |
-
""" Return requested trainer plugin """
|
| 34 |
-
return PluginLoader._import("trainer", name)
|
| 35 |
-
|
| 36 |
-
@staticmethod
|
| 37 |
-
def get_dataset(name) -> Type[Dataset]:
|
| 38 |
-
""" Return requested trainer plugin """
|
| 39 |
-
return PluginLoader._import("dataset", name)
|
| 40 |
-
|
| 41 |
-
@staticmethod
|
| 42 |
-
def _import(attr, name):
|
| 43 |
-
""" Import the plugin's module """
|
| 44 |
-
name = name.replace("-", "_")
|
| 45 |
-
ttl = attr.split(".")[-1].title()
|
| 46 |
-
logger.info("Loading %s from %s plugin...", ttl, name.title())
|
| 47 |
-
attr = "model" if attr == "Trainer" else attr.lower()
|
| 48 |
-
mod = ".".join((attr, name))
|
| 49 |
-
module = import_module(mod)
|
| 50 |
-
logger.info(str(module) + str(ttl))
|
| 51 |
-
return getattr(module, ttl)
|
| 52 |
-
|
| 53 |
-
@staticmethod
|
| 54 |
-
def get_available_trainer():
|
| 55 |
-
""" Return a list of available models """
|
| 56 |
-
modelpath = os.path.join(os.path.dirname(__file__), "trainer")
|
| 57 |
-
models = sorted(item.name.replace(".py", "").replace("_", "-")
|
| 58 |
-
for item in os.scandir(modelpath)
|
| 59 |
-
if not item.name.startswith("_")
|
| 60 |
-
and item.name.endswith(".py"))
|
| 61 |
-
return models
|
| 62 |
-
|
| 63 |
-
@staticmethod
|
| 64 |
-
def get_default_model():
|
| 65 |
-
""" Return the default model """
|
| 66 |
-
models = PluginLoader.get_available_models()
|
| 67 |
-
return 'original' if 'original' in models else models[0]
|
| 68 |
-
|
| 69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/inference/utils/torch_save.py
DELETED
|
@@ -1,9 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
|
| 3 |
-
def torch_save(arr,file):
|
| 4 |
-
if torch.__version__>="1.6.0":
|
| 5 |
-
torch.save(arr, file, _use_new_zipfile_serialization=False)
|
| 6 |
-
else:
|
| 7 |
-
torch.save(arr, file)
|
| 8 |
-
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/preprocess.py
DELETED
|
@@ -1,258 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
from os.path import join
|
| 3 |
-
import argparse
|
| 4 |
-
import glob
|
| 5 |
-
import subprocess
|
| 6 |
-
import cv2
|
| 7 |
-
from tqdm import tqdm
|
| 8 |
-
import numpy as np
|
| 9 |
-
import logging
|
| 10 |
-
import torch
|
| 11 |
-
from test_tools.common import detect_all, grab_all_frames
|
| 12 |
-
from test_tools.faster_crop_align_xray import FasterCropAlignXRay
|
| 13 |
-
from test_tools.warp_for_xray import (
|
| 14 |
-
estimiate_batch_transform,
|
| 15 |
-
transform_landmarks,
|
| 16 |
-
std_points_256,
|
| 17 |
-
)
|
| 18 |
-
from test_tools.ct.operations import find_longest, multiple_tracking
|
| 19 |
-
from test_tools.utils import get_crop_box
|
| 20 |
-
import datetime
|
| 21 |
-
# from FaceForensics.face_detection_save import get_boundingbox
|
| 22 |
-
|
| 23 |
-
os.environ['CUDA_LAUNCH_BLOCKING'] = "1"
|
| 24 |
-
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
|
| 25 |
-
|
| 26 |
-
device=torch.device('cuda')
|
| 27 |
-
#Date
|
| 28 |
-
now = datetime.datetime.now()
|
| 29 |
-
logger = logging.getLogger("main") #Logger 선언
|
| 30 |
-
stream_handler = logging.StreamHandler() # Logger output 방법 선언
|
| 31 |
-
formatter = logging.Formatter('[%(asctime)s][%(levelname)s|%(filename)s:%(lineno)s] >> %(message)s')
|
| 32 |
-
stream_handler.setFormatter(formatter)
|
| 33 |
-
logger.addHandler(stream_handler)
|
| 34 |
-
logger.setLevel(logging.DEBUG)
|
| 35 |
-
|
| 36 |
-
crop_align_func = FasterCropAlignXRay(256)
|
| 37 |
-
max_frame= 10000
|
| 38 |
-
|
| 39 |
-
### video path를 받으면 crop된 face를 저장하는 함수 ###
|
| 40 |
-
def crop_face_from_video(video_path,cache_path,crop_path,clip_size):
|
| 41 |
-
# mp4 파일이 아니면 return
|
| 42 |
-
if 'mp4' not in video_path : return
|
| 43 |
-
# video name
|
| 44 |
-
video_name = video_path.split('/')[-1].replace('.mp4','')
|
| 45 |
-
# 만약 crop image path에 crop된 이미지가 110개 이상이면 return
|
| 46 |
-
|
| 47 |
-
if os.path.exists(crop_path):
|
| 48 |
-
if len(os.listdir(crop_path))>clip_limit:
|
| 49 |
-
logger.info(f'{video_name} already exists')
|
| 50 |
-
return
|
| 51 |
-
|
| 52 |
-
##########################################
|
| 53 |
-
# detect_res : list, 전체 frame, whole frame
|
| 54 |
-
# detect_res [] : list, len = 사람 수로 예상 the number of detected face in a frame
|
| 55 |
-
# detect_res [] [] : tuple, length = 3
|
| 56 |
-
# detect_res [] [] 의 각 요소는 각각 box, lm5 : landmark (5,2) , score
|
| 57 |
-
##########################################
|
| 58 |
-
# all_lm68 : list, 전체 frame, whole frame
|
| 59 |
-
# all_lm68 : list, len = 사람 수로 예상, the number of detected face in a frame
|
| 60 |
-
# all_lm68 : np.array : landmark 68개 (68,2)
|
| 61 |
-
##########################################
|
| 62 |
-
# frames : each frame's np.array
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
# cache_file : cache file path
|
| 66 |
-
# landmark와 box를 저장하는 cache file
|
| 67 |
-
cache_file = f"{cache_path}.pth"
|
| 68 |
-
|
| 69 |
-
if os.path.exists(cache_file):
|
| 70 |
-
# cache file이 존재하면 load하고 frame만 불러옴
|
| 71 |
-
detect_res, all_lm68 = torch.load(cache_file)
|
| 72 |
-
frames = grab_all_frames(video_path, max_size=max_frame, cvt=True)
|
| 73 |
-
logger.info("detection result loaded from cache")
|
| 74 |
-
else:
|
| 75 |
-
# cache file이 존재하지 않으면 detect_all 함수를 통해 detect_res, all_lm68, frames를 불러옴
|
| 76 |
-
# detection_all 함수는 retina_face를 이용해서 box와 landmark를 찾는 함수
|
| 77 |
-
detect_res, all_lm68, frames = detect_all(
|
| 78 |
-
video_path, return_frames=True, max_size=10000
|
| 79 |
-
)
|
| 80 |
-
torch.save((detect_res, all_lm68), cache_file)
|
| 81 |
-
try:
|
| 82 |
-
shape = frames[0].shape[:2]
|
| 83 |
-
except IndexError: # if there is no frame in the video, error list에 저장
|
| 84 |
-
f = open("./indexerror.txt", 'a')
|
| 85 |
-
f.write("{}\n".format(video_path))
|
| 86 |
-
f.close()
|
| 87 |
-
return
|
| 88 |
-
|
| 89 |
-
# 모든 detect_res
|
| 90 |
-
all_detect_res = []
|
| 91 |
-
|
| 92 |
-
assert len(all_lm68) == len(detect_res)
|
| 93 |
-
# in each frame, save the detected face's bounding box, landmark(5, 68), score as a tuple and save it in a list
|
| 94 |
-
for faces, faces_lm68 in zip(detect_res, all_lm68):
|
| 95 |
-
new_faces = []
|
| 96 |
-
for (box, lm5, score), face_lm68 in zip(faces, faces_lm68):
|
| 97 |
-
new_face = (box, lm5, face_lm68, score)
|
| 98 |
-
new_faces.append(new_face)
|
| 99 |
-
all_detect_res.append(new_faces)
|
| 100 |
-
detect_res = all_detect_res
|
| 101 |
-
# SORT tracking
|
| 102 |
-
# tracks : list, len = 사람 수로 예상, the number of detected face in a frame
|
| 103 |
-
# tracks [] : list, len = 프레임 수, the number of frames
|
| 104 |
-
# tracks [] [] : tuple, length = 4, 각각 box, lm5 : landmark (5,2) , lm68 : landmark (68,2), score
|
| 105 |
-
tracks = multiple_tracking(detect_res)
|
| 106 |
-
# tuples : list, len = 사람 수로 예상, the number of detected face in a frame
|
| 107 |
-
# tuples [] : tuple, length = 2, 각각 0, 프레임 수 the number of frames
|
| 108 |
-
tuples = [(0, len(detect_res))] * len(tracks)
|
| 109 |
-
# if there is no face detected, find the longest face in the video
|
| 110 |
-
if len(tracks) == 0:
|
| 111 |
-
tuples, tracks = find_longest(detect_res)
|
| 112 |
-
data_storage = {}
|
| 113 |
-
frame_boxes = {}
|
| 114 |
-
super_clips = []
|
| 115 |
-
frame_res = {}
|
| 116 |
-
super_clips_start_end = []
|
| 117 |
-
# super_clips : tracking된 face들을 의미하는 것으로 보임
|
| 118 |
-
for track_i, ((start, end), track) in enumerate(zip(tuples, tracks)): # each track(=face)
|
| 119 |
-
|
| 120 |
-
# if detect_res's length is not equal to track's length, raise error
|
| 121 |
-
assert len(detect_res[start:end]) == len(track)
|
| 122 |
-
|
| 123 |
-
super_clips.append(len(track))
|
| 124 |
-
super_clips_start_end.append((start, end))
|
| 125 |
-
for face, frame_idx, j in zip(track, range(start, end), range(len(track))): # frame에서 각각의 face
|
| 126 |
-
box,lm5,lm68 = face[:3] # box, lm5, lm68
|
| 127 |
-
big_box = get_crop_box(shape, box, scale=0.5) # get crop box
|
| 128 |
-
|
| 129 |
-
top_left = big_box[:2][None, :] # top left point
|
| 130 |
-
|
| 131 |
-
new_lm5 = lm5 - top_left
|
| 132 |
-
new_lm68 = lm68 - top_left
|
| 133 |
-
|
| 134 |
-
new_box = (box.reshape(2, 2) - top_left).reshape(-1)
|
| 135 |
-
|
| 136 |
-
info = (new_box, new_lm5, new_lm68, big_box) # face info
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
x1, y1, x2, y2 = big_box
|
| 140 |
-
cropped = frames[frame_idx][y1:y2, x1:x2]
|
| 141 |
-
# cropped = cv2.resize(cropped, (512, 512))
|
| 142 |
-
# face들을 tracking한 박스들로 crop함
|
| 143 |
-
# landmark들도 box에 맞게 변환
|
| 144 |
-
# data_storage에 저장 i는 face id, j는 frame을 의미
|
| 145 |
-
base_key = f"{track_i}_{j}_" # i : face, j : frame
|
| 146 |
-
data_storage[base_key + "img"] = cropped
|
| 147 |
-
data_storage[base_key + "ldm"] = info
|
| 148 |
-
data_storage[base_key + "idx"] = frame_idx
|
| 149 |
-
frame_boxes[frame_idx] = np.rint(box).astype(np.int64)
|
| 150 |
-
# 총 crop된 face들과 그 face들의 frame 수를 알려줌
|
| 151 |
-
logger.info(f"{crop_path} : sampling clips from super clips {super_clips}")
|
| 152 |
-
clips_for_video = []
|
| 153 |
-
clip_size = clip_size
|
| 154 |
-
pad_length = clip_size - 1
|
| 155 |
-
|
| 156 |
-
# 각 face id 별로 clip을 만듦
|
| 157 |
-
# 아래의 영어 표기로는 8clip을 의미하지만 정확하겐 clip size 만큼 함
|
| 158 |
-
for super_clip_idx, super_clip_size in enumerate(super_clips): # cut the super clip into clips, overlap 7frames, 8frames per clip
|
| 159 |
-
inner_index = list(range(super_clip_size))
|
| 160 |
-
|
| 161 |
-
if super_clip_size < clip_size: # if there is not enough frames to make a clip, pad the frames
|
| 162 |
-
# to do : how to operate the padding
|
| 163 |
-
# 정확하게 이 코드가 어떻게 동작하는지 모르겠지만
|
| 164 |
-
# 대략적으로 frame들을 clipsize로 나눌때 부족하면
|
| 165 |
-
# clip size만큼의 frame이 되도록 padding을 함
|
| 166 |
-
if super_clip_size < clip_size//2 : continue
|
| 167 |
-
post_module = inner_index[1:-1][::-1] + inner_index
|
| 168 |
-
|
| 169 |
-
l_post = len(post_module)
|
| 170 |
-
post_module = post_module * (pad_length // l_post + 1)
|
| 171 |
-
post_module = post_module[:pad_length]
|
| 172 |
-
assert len(post_module) == pad_length
|
| 173 |
-
|
| 174 |
-
pre_module = inner_index + inner_index[1:-1][::-1]
|
| 175 |
-
l_pre = len(post_module)
|
| 176 |
-
pre_module = pre_module * (pad_length // l_pre + 1)
|
| 177 |
-
pre_module = pre_module[-pad_length:]
|
| 178 |
-
assert len(pre_module) == pad_length
|
| 179 |
-
|
| 180 |
-
inner_index = pre_module + inner_index + post_module
|
| 181 |
-
|
| 182 |
-
super_clip_size = len(inner_index)
|
| 183 |
-
|
| 184 |
-
frame_range = [
|
| 185 |
-
inner_index[i : i + clip_size] for i in range(super_clip_size) if i + clip_size <= super_clip_size
|
| 186 |
-
]
|
| 187 |
-
for indices in frame_range:
|
| 188 |
-
clip = [(super_clip_idx, t) for t in indices]
|
| 189 |
-
clips_for_video.append(clip)
|
| 190 |
-
|
| 191 |
-
# landmarks, images = crop_align_func(landmarks, images) # i : face, j : frame
|
| 192 |
-
processed_clips = 0 # Track number of processed clips
|
| 193 |
-
for clip in clips_for_video:
|
| 194 |
-
# Check if we've reached the clip limit
|
| 195 |
-
if processed_clips >= clip_limit:
|
| 196 |
-
logger.info(f"Reached clip limit of {clip_limit}, stopping processing")
|
| 197 |
-
break
|
| 198 |
-
|
| 199 |
-
# 각 자른 clip에 대해서 진행
|
| 200 |
-
images = [data_storage[f"{i}_{j}_img"] for i, j in clip] # call cropped face images from data_storage, i : face, j : frame
|
| 201 |
-
landmarks = [data_storage[f"{i}_{j}_ldm"] for i, j in clip] # call landmarks from data_storage, i : face, j : frame
|
| 202 |
-
# landmark를 기준으로 crop align func을 진행
|
| 203 |
-
# 해당 함수가 clip에 있는 얼굴들의 landmark 평균을 기준으로 박스를 설정하고
|
| 204 |
-
# 박스를 기준으로 crop align을 진행
|
| 205 |
-
# 다르게 말하면, landmark 평균을 기준으로 박스의 geometry를 설정하고
|
| 206 |
-
# 박스의 geometry는 고정한체로 얼굴이 움직이는 걸 찍었다 생각하면 됨
|
| 207 |
-
# 다시 또 말하면, 카메라를 고정하고 사람이 움직이는 것을 찍은것처럼
|
| 208 |
-
# PPT 참조
|
| 209 |
-
landmarks, images = crop_align_func(landmarks, images) # align the face images by landmarks in the clip
|
| 210 |
-
i, j = clip[-1]
|
| 211 |
-
k = super_clips[i]%clip_size
|
| 212 |
-
|
| 213 |
-
##########################################################################
|
| 214 |
-
# 코드 변경시 이 함수에서는 이부분만 변경할 것을 권고 !!!!!!!!!!!!!!!!!!!!!!!!
|
| 215 |
-
# 특히, cv2.imwrite함수만 변경할 것을 추천
|
| 216 |
-
##########################################################################
|
| 217 |
-
if (j+1)%clip_size==0: # if last frame number of the clip is multiple of clip_size, save all images in the clip
|
| 218 |
-
# it means save face alignments in 8 frames in the video so that they don't overlap
|
| 219 |
-
for f, (i,j) in enumerate(clip) :
|
| 220 |
-
cv2.imwrite(join(crop_path, f'{i:02}_{j:04}.png'), cv2.cvtColor(images[f], cv2.COLOR_BGR2RGB))
|
| 221 |
-
if j == super_clips[i]-1: # if the clip have last frame image, save all images in the clip
|
| 222 |
-
if k!=0 : # if the clip is not multiple of clip_size, save the last k images in the clip
|
| 223 |
-
# k is the number of frames that are not overlapped
|
| 224 |
-
for l in range(clip_size-k,clip_size):
|
| 225 |
-
ci,cj = clip[l]
|
| 226 |
-
cv2.imwrite(join(crop_path, f'{ci:02}_{cj:04}.png'), cv2.cvtColor(images[l], cv2.COLOR_BGR2RGB)) # clip means face alignment images in 8 frames, non-overlap
|
| 227 |
-
|
| 228 |
-
processed_clips += 1 # Increment processed clip counter
|
| 229 |
-
##########################################################################
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
if __name__ == '__main__':
|
| 233 |
-
p = argparse.ArgumentParser(
|
| 234 |
-
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
| 235 |
-
)
|
| 236 |
-
p.add_argument('--video_path','-i', type=str, default='/videos.mp4', help='path to input video')
|
| 237 |
-
p.add_argument('--save_path','-s', type=str, default='/data/crop_face', help='path to save cropped faces')
|
| 238 |
-
p.add_argument('--cachepath', '-c', type=str, default='/data/cache', help='path to cache detection results')
|
| 239 |
-
p.add_argument('--clipsize','-l',type=int,default=32, help='number of frames in a clip')
|
| 240 |
-
args = p.parse_args()
|
| 241 |
-
video_path = args.video_path
|
| 242 |
-
save_path = args.save_path
|
| 243 |
-
cache_path = args.cachepath
|
| 244 |
-
clip_size = args.clipsize
|
| 245 |
-
|
| 246 |
-
crop_face_from_video(video_path, cache_path, crop_path, clip_size)
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
###################### reference ######################
|
| 252 |
-
|
| 253 |
-
# this code reference from FTCN Official Code in git hub
|
| 254 |
-
# link is https://github.com/yinglinzheng/FTCN
|
| 255 |
-
|
| 256 |
-
# - Zheng, Y., Bao, J., Chen, D., Zeng, M., & Wen, F. (2021). Exploring Temporal Coherence for More General Video Face Forgery Detection. In Proceedings of the IEEE/CVF International Conference on Computer Vision (pp. 15044–15054).
|
| 257 |
-
|
| 258 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/__init__.py
DELETED
|
File without changes
|
video/pwtf-dvd/model_code/preprocessing/test_tools/common.py
DELETED
|
@@ -1,122 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
|
| 3 |
-
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
|
| 4 |
-
|
| 5 |
-
from .ct.detection.utils import grab_all_frames, get_valid_faces, sample_chunks
|
| 6 |
-
from .ct.operations import multiple_tracking
|
| 7 |
-
import numpy as np
|
| 8 |
-
from .ct.face_alignment import LandmarkPredictor
|
| 9 |
-
from .ct.detection import FaceDetector
|
| 10 |
-
import cv2
|
| 11 |
-
from .utils import flatten,partition
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
detector = FaceDetector(0)
|
| 15 |
-
predictor = LandmarkPredictor(0)
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def get_five(ldm68):
|
| 19 |
-
groups = [range(36, 42), range(42, 48), [30], [48], [54]]
|
| 20 |
-
points = []
|
| 21 |
-
for group in groups:
|
| 22 |
-
points.append(ldm68[group].mean(0))
|
| 23 |
-
return np.array(points)
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
def get_bbox(mask):
|
| 27 |
-
try:
|
| 28 |
-
y, x = np.nonzero(mask[..., 0])
|
| 29 |
-
return x.min() - 1, y.min() - 1, x.max() + 1, y.max() + 1
|
| 30 |
-
except:
|
| 31 |
-
return None
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
def get_bigger_box(image, box, scale=0.5):
|
| 35 |
-
height, width = image.shape[:2]
|
| 36 |
-
box = np.rint(box).astype(np.int)
|
| 37 |
-
new_box = box.reshape(2, 2)
|
| 38 |
-
size = new_box[1] - new_box[0]
|
| 39 |
-
diff = scale * size
|
| 40 |
-
diff = diff[None, :] * np.array([-1, 1])[:, None]
|
| 41 |
-
new_box = new_box + diff
|
| 42 |
-
new_box[:, 0] = np.clip(new_box[:, 0], 0, width - 1)
|
| 43 |
-
new_box[:, 1] = np.clip(new_box[:, 1], 0, height - 1)
|
| 44 |
-
new_box = np.rint(new_box).astype(np.int)
|
| 45 |
-
return new_box.reshape(-1)
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
def process_bigger_clips(clips, dete_res, clip_size, step, scale=0.5):
|
| 49 |
-
assert len(clips) % clip_size == 0
|
| 50 |
-
detect_results = sample_chunks(dete_res, clip_size, step)
|
| 51 |
-
clips = sample_chunks(clips, clip_size, step)
|
| 52 |
-
new_clips = []
|
| 53 |
-
for i, (frame_clip, record_clip) in enumerate(zip(clips, detect_results)):
|
| 54 |
-
tracks = multiple_tracking(record_clip)
|
| 55 |
-
for j, track in enumerate(tracks):
|
| 56 |
-
new_images = []
|
| 57 |
-
for (box, ldm, _), frame in zip(track, frame_clip):
|
| 58 |
-
big_box = get_bigger_box(frame, box, scale)
|
| 59 |
-
x1, y1, x2, y2 = big_box
|
| 60 |
-
top_left = big_box[:2][None, :]
|
| 61 |
-
new_ldm5 = ldm - top_left
|
| 62 |
-
box = np.rint(box).astype(np.int)
|
| 63 |
-
new_box = (box.reshape(2, 2) - top_left).reshape(-1)
|
| 64 |
-
feed = LandmarkPredictor.prepare_feed(frame, box)
|
| 65 |
-
ldm68 = predictor(feed) - top_left
|
| 66 |
-
new_images.append(
|
| 67 |
-
(frame[y1:y2, x1:x2], big_box, new_box, new_ldm5, ldm68)
|
| 68 |
-
)
|
| 69 |
-
new_clips.append(new_images)
|
| 70 |
-
return new_clips
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
def post(detected_faces):
|
| 74 |
-
return [[face[:4], None, face[-1]] for face in detected_faces]
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
def check(detect_res):
|
| 78 |
-
return min([len(faces) for faces in detect_res]) != 0
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
def detect_all(file, sfd_only=False, return_frames=False, max_size=None):
|
| 82 |
-
frames = grab_all_frames(file, max_size=max_size, cvt=True)
|
| 83 |
-
if not sfd_only:
|
| 84 |
-
detect_res = flatten(
|
| 85 |
-
[detector.detect(item) for item in partition(frames, 50)]
|
| 86 |
-
)
|
| 87 |
-
detect_res = get_valid_faces(detect_res, thres=0.5)
|
| 88 |
-
else:
|
| 89 |
-
raise NotImplementedError
|
| 90 |
-
|
| 91 |
-
all_68 = get_lm68(frames, detect_res)
|
| 92 |
-
if not return_frames:
|
| 93 |
-
return detect_res, all_68
|
| 94 |
-
else:
|
| 95 |
-
return detect_res, all_68, frames
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
def get_lm68(frames, detect_res):
|
| 99 |
-
assert len(frames) == len(detect_res)
|
| 100 |
-
frame_count = len(frames)
|
| 101 |
-
all_68 = []
|
| 102 |
-
for i in range(frame_count):
|
| 103 |
-
frame = frames[i]
|
| 104 |
-
faces = detect_res[i]
|
| 105 |
-
if len(faces) == 0:
|
| 106 |
-
res_68 = []
|
| 107 |
-
else:
|
| 108 |
-
feeds = []
|
| 109 |
-
for face in faces:
|
| 110 |
-
assert len(face) == 3
|
| 111 |
-
box = face[0]
|
| 112 |
-
feed = LandmarkPredictor.prepare_feed(frame, box)
|
| 113 |
-
feeds.append(feed)
|
| 114 |
-
res_68 = predictor(feeds)
|
| 115 |
-
assert len(res_68) == len(faces)
|
| 116 |
-
for face, l_68 in zip(faces, res_68):
|
| 117 |
-
if face[1] is None:
|
| 118 |
-
face[1] = get_five(l_68)
|
| 119 |
-
all_68.append(res_68)
|
| 120 |
-
|
| 121 |
-
assert len(all_68) == len(detect_res)
|
| 122 |
-
return all_68
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/detection/__init__.py
DELETED
|
@@ -1,56 +0,0 @@
|
|
| 1 |
-
import cv2
|
| 2 |
-
from .detector import RetinaFace
|
| 3 |
-
from .utils import *
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
def assert_bounded(val, low, up):
|
| 7 |
-
return val >= low and val < up
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def check_valid(face, w, h):
|
| 11 |
-
box = face[0]
|
| 12 |
-
if box[0] > box[2]:
|
| 13 |
-
return False
|
| 14 |
-
if box[1] > box[3]:
|
| 15 |
-
return False
|
| 16 |
-
for idx, bound in zip([0, 1, 2, 3], [w, h, w, h]):
|
| 17 |
-
if not assert_bounded(box[idx], 0, bound):
|
| 18 |
-
return False
|
| 19 |
-
pts = face[1]
|
| 20 |
-
for p in pts:
|
| 21 |
-
for idx, bound in zip([0, 1], [w, h]):
|
| 22 |
-
if not assert_bounded(p[idx], 0, bound):
|
| 23 |
-
return False
|
| 24 |
-
return True
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
def post_detect(detect_results, scale, w, h):
|
| 28 |
-
new_results = []
|
| 29 |
-
for frame_faces in detect_results:
|
| 30 |
-
new_frame_faces = []
|
| 31 |
-
for box, ldm, score in frame_faces:
|
| 32 |
-
box = box * scale
|
| 33 |
-
ldm = ldm * scale
|
| 34 |
-
face = (box, ldm, score)
|
| 35 |
-
if check_valid(face, w=w, h=h):
|
| 36 |
-
new_frame_faces.append(face)
|
| 37 |
-
new_results.append(new_frame_faces)
|
| 38 |
-
return new_results
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
class FaceDetector(RetinaFace):
|
| 42 |
-
def scale_detect(self, images):
|
| 43 |
-
max_res = 1920
|
| 44 |
-
h, w = images[0].shape[:2]
|
| 45 |
-
if max(h, w) > max_res:
|
| 46 |
-
init_scale = max(h, w) / max_res
|
| 47 |
-
else:
|
| 48 |
-
init_scale = 1
|
| 49 |
-
resize_scale = 2 * init_scale
|
| 50 |
-
resize_w = int(w / resize_scale)
|
| 51 |
-
resize_h = int(h / resize_scale)
|
| 52 |
-
detect_input = [cv2.resize(frame, (resize_w, resize_h)) for frame in images]
|
| 53 |
-
detect_results = post_detect(
|
| 54 |
-
self.detect(detect_input), scale=resize_scale, w=w, h=h,
|
| 55 |
-
)
|
| 56 |
-
return detect_results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/detection/alignment.py
DELETED
|
@@ -1,608 +0,0 @@
|
|
| 1 |
-
from itertools import product as product
|
| 2 |
-
from math import ceil
|
| 3 |
-
|
| 4 |
-
import numpy as np
|
| 5 |
-
import torch
|
| 6 |
-
import torch.backends.cudnn as cudnn
|
| 7 |
-
import torch.nn as nn
|
| 8 |
-
import torch.nn.functional as F
|
| 9 |
-
import torchvision.models._utils as _utils
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
def conv_bn(inp, oup, stride=1, leaky=0):
|
| 13 |
-
return nn.Sequential(
|
| 14 |
-
nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
|
| 15 |
-
nn.BatchNorm2d(oup),
|
| 16 |
-
nn.LeakyReLU(negative_slope=leaky, inplace=True),
|
| 17 |
-
)
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
def conv_bn_no_relu(inp, oup, stride):
|
| 21 |
-
return nn.Sequential(
|
| 22 |
-
nn.Conv2d(inp, oup, 3, stride, 1, bias=False), nn.BatchNorm2d(oup),
|
| 23 |
-
)
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
def conv_bn1X1(inp, oup, stride, leaky=0):
|
| 27 |
-
return nn.Sequential(
|
| 28 |
-
nn.Conv2d(inp, oup, 1, stride, padding=0, bias=False),
|
| 29 |
-
nn.BatchNorm2d(oup),
|
| 30 |
-
nn.LeakyReLU(negative_slope=leaky, inplace=True),
|
| 31 |
-
)
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
def conv_dw(inp, oup, stride, leaky=0.1):
|
| 35 |
-
return nn.Sequential(
|
| 36 |
-
nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),
|
| 37 |
-
nn.BatchNorm2d(inp),
|
| 38 |
-
nn.LeakyReLU(negative_slope=leaky, inplace=True),
|
| 39 |
-
nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
|
| 40 |
-
nn.BatchNorm2d(oup),
|
| 41 |
-
nn.LeakyReLU(negative_slope=leaky, inplace=True),
|
| 42 |
-
)
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
class SSH(nn.Module):
|
| 46 |
-
def __init__(self, in_channel, out_channel):
|
| 47 |
-
super(SSH, self).__init__()
|
| 48 |
-
assert out_channel % 4 == 0
|
| 49 |
-
leaky = 0
|
| 50 |
-
if out_channel <= 64:
|
| 51 |
-
leaky = 0.1
|
| 52 |
-
self.conv3X3 = conv_bn_no_relu(in_channel, out_channel // 2, stride=1)
|
| 53 |
-
|
| 54 |
-
self.conv5X5_1 = conv_bn(in_channel, out_channel // 4, stride=1, leaky=leaky)
|
| 55 |
-
self.conv5X5_2 = conv_bn_no_relu(out_channel // 4, out_channel // 4, stride=1)
|
| 56 |
-
|
| 57 |
-
self.conv7X7_2 = conv_bn(
|
| 58 |
-
out_channel // 4, out_channel // 4, stride=1, leaky=leaky
|
| 59 |
-
)
|
| 60 |
-
self.conv7x7_3 = conv_bn_no_relu(out_channel // 4, out_channel // 4, stride=1)
|
| 61 |
-
|
| 62 |
-
def forward(self, input):
|
| 63 |
-
conv3X3 = self.conv3X3(input)
|
| 64 |
-
|
| 65 |
-
conv5X5_1 = self.conv5X5_1(input)
|
| 66 |
-
conv5X5 = self.conv5X5_2(conv5X5_1)
|
| 67 |
-
|
| 68 |
-
conv7X7_2 = self.conv7X7_2(conv5X5_1)
|
| 69 |
-
conv7X7 = self.conv7x7_3(conv7X7_2)
|
| 70 |
-
|
| 71 |
-
out = torch.cat([conv3X3, conv5X5, conv7X7], dim=1)
|
| 72 |
-
out = F.relu(out)
|
| 73 |
-
return out
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
class FPN(nn.Module):
|
| 77 |
-
def __init__(self, in_channels_list, out_channels):
|
| 78 |
-
super(FPN, self).__init__()
|
| 79 |
-
leaky = 0
|
| 80 |
-
if out_channels <= 64:
|
| 81 |
-
leaky = 0.1
|
| 82 |
-
self.output1 = conv_bn1X1(
|
| 83 |
-
in_channels_list[0], out_channels, stride=1, leaky=leaky
|
| 84 |
-
)
|
| 85 |
-
self.output2 = conv_bn1X1(
|
| 86 |
-
in_channels_list[1], out_channels, stride=1, leaky=leaky
|
| 87 |
-
)
|
| 88 |
-
self.output3 = conv_bn1X1(
|
| 89 |
-
in_channels_list[2], out_channels, stride=1, leaky=leaky
|
| 90 |
-
)
|
| 91 |
-
|
| 92 |
-
self.merge1 = conv_bn(out_channels, out_channels, leaky=leaky)
|
| 93 |
-
self.merge2 = conv_bn(out_channels, out_channels, leaky=leaky)
|
| 94 |
-
|
| 95 |
-
def forward(self, input):
|
| 96 |
-
# names = list(input.keys())
|
| 97 |
-
input = list(input.values())
|
| 98 |
-
|
| 99 |
-
output1 = self.output1(input[0])
|
| 100 |
-
output2 = self.output2(input[1])
|
| 101 |
-
output3 = self.output3(input[2])
|
| 102 |
-
|
| 103 |
-
up3 = F.interpolate(
|
| 104 |
-
output3, size=[output2.size(2), output2.size(3)], mode="nearest"
|
| 105 |
-
)
|
| 106 |
-
output2 = output2 + up3
|
| 107 |
-
output2 = self.merge2(output2)
|
| 108 |
-
|
| 109 |
-
up2 = F.interpolate(
|
| 110 |
-
output2, size=[output1.size(2), output1.size(3)], mode="nearest"
|
| 111 |
-
)
|
| 112 |
-
output1 = output1 + up2
|
| 113 |
-
output1 = self.merge1(output1)
|
| 114 |
-
|
| 115 |
-
out = [output1, output2, output3]
|
| 116 |
-
return out
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
class MobileNetV1(nn.Module):
|
| 120 |
-
def __init__(self):
|
| 121 |
-
super(MobileNetV1, self).__init__()
|
| 122 |
-
self.stage1 = nn.Sequential(
|
| 123 |
-
conv_bn(3, 8, 2, leaky=0.1), # 3
|
| 124 |
-
conv_dw(8, 16, 1), # 7
|
| 125 |
-
conv_dw(16, 32, 2), # 11
|
| 126 |
-
conv_dw(32, 32, 1), # 19
|
| 127 |
-
conv_dw(32, 64, 2), # 27
|
| 128 |
-
conv_dw(64, 64, 1), # 43
|
| 129 |
-
)
|
| 130 |
-
self.stage2 = nn.Sequential(
|
| 131 |
-
conv_dw(64, 128, 2), # 43 + 16 = 59
|
| 132 |
-
conv_dw(128, 128, 1), # 59 + 32 = 91
|
| 133 |
-
conv_dw(128, 128, 1), # 91 + 32 = 123
|
| 134 |
-
conv_dw(128, 128, 1), # 123 + 32 = 155
|
| 135 |
-
conv_dw(128, 128, 1), # 155 + 32 = 187
|
| 136 |
-
conv_dw(128, 128, 1), # 187 + 32 = 219
|
| 137 |
-
)
|
| 138 |
-
self.stage3 = nn.Sequential(
|
| 139 |
-
conv_dw(128, 256, 2), # 219 +3 2 = 241
|
| 140 |
-
conv_dw(256, 256, 1), # 241 + 64 = 301
|
| 141 |
-
)
|
| 142 |
-
self.avg = nn.AdaptiveAvgPool2d((1, 1))
|
| 143 |
-
self.fc = nn.Linear(256, 1000)
|
| 144 |
-
|
| 145 |
-
def forward(self, x):
|
| 146 |
-
x = self.stage1(x)
|
| 147 |
-
x = self.stage2(x)
|
| 148 |
-
x = self.stage3(x)
|
| 149 |
-
x = self.avg(x)
|
| 150 |
-
# x = self.model(x)
|
| 151 |
-
x = x.view(-1, 256)
|
| 152 |
-
x = self.fc(x)
|
| 153 |
-
return x
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
class ClassHead(nn.Module):
|
| 157 |
-
def __init__(self, inchannels=512, num_anchors=3):
|
| 158 |
-
super(ClassHead, self).__init__()
|
| 159 |
-
self.num_anchors = num_anchors
|
| 160 |
-
self.conv1x1 = nn.Conv2d(
|
| 161 |
-
inchannels, self.num_anchors * 2, kernel_size=(1, 1), stride=1, padding=0
|
| 162 |
-
)
|
| 163 |
-
|
| 164 |
-
def forward(self, x):
|
| 165 |
-
out = self.conv1x1(x)
|
| 166 |
-
out = out.permute(0, 2, 3, 1).contiguous()
|
| 167 |
-
|
| 168 |
-
return out.view(out.shape[0], -1, 2)
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
class BboxHead(nn.Module):
|
| 172 |
-
def __init__(self, inchannels=512, num_anchors=3):
|
| 173 |
-
super(BboxHead, self).__init__()
|
| 174 |
-
self.conv1x1 = nn.Conv2d(
|
| 175 |
-
inchannels, num_anchors * 4, kernel_size=(1, 1), stride=1, padding=0
|
| 176 |
-
)
|
| 177 |
-
|
| 178 |
-
def forward(self, x):
|
| 179 |
-
out = self.conv1x1(x)
|
| 180 |
-
out = out.permute(0, 2, 3, 1).contiguous()
|
| 181 |
-
|
| 182 |
-
return out.view(out.shape[0], -1, 4)
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
class LandmarkHead(nn.Module):
|
| 186 |
-
def __init__(self, inchannels=512, num_anchors=3):
|
| 187 |
-
super(LandmarkHead, self).__init__()
|
| 188 |
-
self.conv1x1 = nn.Conv2d(
|
| 189 |
-
inchannels, num_anchors * 10, kernel_size=(1, 1), stride=1, padding=0
|
| 190 |
-
)
|
| 191 |
-
|
| 192 |
-
def forward(self, x):
|
| 193 |
-
out = self.conv1x1(x)
|
| 194 |
-
out = out.permute(0, 2, 3, 1).contiguous()
|
| 195 |
-
|
| 196 |
-
return out.view(out.shape[0], -1, 10)
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
class RetinaFace(nn.Module):
|
| 200 |
-
def __init__(self, cfg=None, phase="train"):
|
| 201 |
-
"""
|
| 202 |
-
:param cfg: Network related settings.
|
| 203 |
-
:param phase: train or test.
|
| 204 |
-
"""
|
| 205 |
-
super(RetinaFace, self).__init__()
|
| 206 |
-
self.phase = phase
|
| 207 |
-
backbone = None
|
| 208 |
-
if cfg["name"] == "mobilenet0.25":
|
| 209 |
-
backbone = MobileNetV1()
|
| 210 |
-
elif cfg["name"] == "Resnet50":
|
| 211 |
-
import torchvision.models as models
|
| 212 |
-
|
| 213 |
-
backbone = models.resnet50(pretrained=cfg["pretrain"])
|
| 214 |
-
|
| 215 |
-
self.body = _utils.IntermediateLayerGetter(backbone, cfg["return_layers"])
|
| 216 |
-
in_channels_stage2 = cfg["in_channel"]
|
| 217 |
-
in_channels_list = [
|
| 218 |
-
in_channels_stage2 * 2,
|
| 219 |
-
in_channels_stage2 * 4,
|
| 220 |
-
in_channels_stage2 * 8,
|
| 221 |
-
]
|
| 222 |
-
out_channels = cfg["out_channel"]
|
| 223 |
-
self.fpn = FPN(in_channels_list, out_channels)
|
| 224 |
-
self.ssh1 = SSH(out_channels, out_channels)
|
| 225 |
-
self.ssh2 = SSH(out_channels, out_channels)
|
| 226 |
-
self.ssh3 = SSH(out_channels, out_channels)
|
| 227 |
-
|
| 228 |
-
self.ClassHead = self._make_class_head(fpn_num=3, inchannels=cfg["out_channel"])
|
| 229 |
-
self.BboxHead = self._make_bbox_head(fpn_num=3, inchannels=cfg["out_channel"])
|
| 230 |
-
self.LandmarkHead = self._make_landmark_head(
|
| 231 |
-
fpn_num=3, inchannels=cfg["out_channel"]
|
| 232 |
-
)
|
| 233 |
-
|
| 234 |
-
def _make_class_head(self, fpn_num=3, inchannels=64, anchor_num=2):
|
| 235 |
-
classhead = nn.ModuleList()
|
| 236 |
-
for i in range(fpn_num):
|
| 237 |
-
classhead.append(ClassHead(inchannels, anchor_num))
|
| 238 |
-
return classhead
|
| 239 |
-
|
| 240 |
-
def _make_bbox_head(self, fpn_num=3, inchannels=64, anchor_num=2):
|
| 241 |
-
bboxhead = nn.ModuleList()
|
| 242 |
-
for i in range(fpn_num):
|
| 243 |
-
bboxhead.append(BboxHead(inchannels, anchor_num))
|
| 244 |
-
return bboxhead
|
| 245 |
-
|
| 246 |
-
def _make_landmark_head(self, fpn_num=3, inchannels=64, anchor_num=2):
|
| 247 |
-
landmarkhead = nn.ModuleList()
|
| 248 |
-
for i in range(fpn_num):
|
| 249 |
-
landmarkhead.append(LandmarkHead(inchannels, anchor_num))
|
| 250 |
-
return landmarkhead
|
| 251 |
-
|
| 252 |
-
def forward(self, inputs):
|
| 253 |
-
out = self.body(inputs)
|
| 254 |
-
|
| 255 |
-
# FPN
|
| 256 |
-
fpn = self.fpn(out)
|
| 257 |
-
|
| 258 |
-
# SSH
|
| 259 |
-
feature1 = self.ssh1(fpn[0])
|
| 260 |
-
feature2 = self.ssh2(fpn[1])
|
| 261 |
-
feature3 = self.ssh3(fpn[2])
|
| 262 |
-
features = [feature1, feature2, feature3]
|
| 263 |
-
|
| 264 |
-
bbox_regressions = torch.cat(
|
| 265 |
-
[self.BboxHead[i](feature) for i, feature in enumerate(features)], dim=1
|
| 266 |
-
)
|
| 267 |
-
classifications = torch.cat(
|
| 268 |
-
[self.ClassHead[i](feature) for i, feature in enumerate(features)], dim=1
|
| 269 |
-
)
|
| 270 |
-
ldm_regressions = torch.cat(
|
| 271 |
-
[self.LandmarkHead[i](feature) for i, feature in enumerate(features)], dim=1
|
| 272 |
-
)
|
| 273 |
-
|
| 274 |
-
if self.phase == "train":
|
| 275 |
-
output = (bbox_regressions, classifications, ldm_regressions)
|
| 276 |
-
else:
|
| 277 |
-
output = (
|
| 278 |
-
bbox_regressions,
|
| 279 |
-
F.softmax(classifications, dim=-1),
|
| 280 |
-
ldm_regressions,
|
| 281 |
-
)
|
| 282 |
-
return output
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
# Adapted from https://github.com/Hakuyume/chainer-ssd
|
| 286 |
-
def decode(loc, priors, variances):
|
| 287 |
-
boxes = torch.cat(
|
| 288 |
-
(
|
| 289 |
-
priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:],
|
| 290 |
-
priors[:, 2:] * torch.exp(loc[:, 2:] * variances[1]),
|
| 291 |
-
),
|
| 292 |
-
1,
|
| 293 |
-
)
|
| 294 |
-
boxes[:, :2] -= boxes[:, 2:] / 2
|
| 295 |
-
boxes[:, 2:] += boxes[:, :2]
|
| 296 |
-
return boxes
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
def decode_landm(pre, priors, variances):
|
| 300 |
-
landms = torch.cat(
|
| 301 |
-
(
|
| 302 |
-
priors[:, :2] + pre[:, :2] * variances[0] * priors[:, 2:],
|
| 303 |
-
priors[:, :2] + pre[:, 2:4] * variances[0] * priors[:, 2:],
|
| 304 |
-
priors[:, :2] + pre[:, 4:6] * variances[0] * priors[:, 2:],
|
| 305 |
-
priors[:, :2] + pre[:, 6:8] * variances[0] * priors[:, 2:],
|
| 306 |
-
priors[:, :2] + pre[:, 8:10] * variances[0] * priors[:, 2:],
|
| 307 |
-
),
|
| 308 |
-
dim=1,
|
| 309 |
-
)
|
| 310 |
-
return landms
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
def py_cpu_nms(dets, thresh):
|
| 314 |
-
"""Pure Python NMS baseline."""
|
| 315 |
-
x1 = dets[:, 0]
|
| 316 |
-
y1 = dets[:, 1]
|
| 317 |
-
x2 = dets[:, 2]
|
| 318 |
-
y2 = dets[:, 3]
|
| 319 |
-
scores = dets[:, 4]
|
| 320 |
-
|
| 321 |
-
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
|
| 322 |
-
order = scores.argsort()[::-1]
|
| 323 |
-
|
| 324 |
-
keep = []
|
| 325 |
-
while order.size > 0:
|
| 326 |
-
i = order[0]
|
| 327 |
-
keep.append(i)
|
| 328 |
-
xx1 = np.maximum(x1[i], x1[order[1:]])
|
| 329 |
-
yy1 = np.maximum(y1[i], y1[order[1:]])
|
| 330 |
-
xx2 = np.minimum(x2[i], x2[order[1:]])
|
| 331 |
-
yy2 = np.minimum(y2[i], y2[order[1:]])
|
| 332 |
-
|
| 333 |
-
w = np.maximum(0.0, xx2 - xx1 + 1)
|
| 334 |
-
h = np.maximum(0.0, yy2 - yy1 + 1)
|
| 335 |
-
inter = w * h
|
| 336 |
-
ovr = inter / (areas[i] + areas[order[1:]] - inter)
|
| 337 |
-
|
| 338 |
-
inds = np.where(ovr <= thresh)[0]
|
| 339 |
-
order = order[inds + 1]
|
| 340 |
-
|
| 341 |
-
return keep
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
class PriorBox(object):
|
| 345 |
-
def __init__(self, cfg, image_size=None, phase="train"):
|
| 346 |
-
super(PriorBox, self).__init__()
|
| 347 |
-
self.min_sizes = cfg["min_sizes"]
|
| 348 |
-
self.steps = cfg["steps"]
|
| 349 |
-
self.clip = cfg["clip"]
|
| 350 |
-
self.image_size = image_size
|
| 351 |
-
self.feature_maps = [
|
| 352 |
-
[ceil(self.image_size[0] / step), ceil(self.image_size[1] / step)]
|
| 353 |
-
for step in self.steps
|
| 354 |
-
]
|
| 355 |
-
self.name = "s"
|
| 356 |
-
|
| 357 |
-
def forward(self):
|
| 358 |
-
anchors = []
|
| 359 |
-
for k, f in enumerate(self.feature_maps):
|
| 360 |
-
min_sizes = self.min_sizes[k]
|
| 361 |
-
for i, j in product(range(f[0]), range(f[1])):
|
| 362 |
-
for min_size in min_sizes:
|
| 363 |
-
s_kx = min_size / self.image_size[1]
|
| 364 |
-
s_ky = min_size / self.image_size[0]
|
| 365 |
-
dense_cx = [
|
| 366 |
-
x * self.steps[k] / self.image_size[1] for x in [j + 0.5]
|
| 367 |
-
]
|
| 368 |
-
dense_cy = [
|
| 369 |
-
y * self.steps[k] / self.image_size[0] for y in [i + 0.5]
|
| 370 |
-
]
|
| 371 |
-
for cy, cx in product(dense_cy, dense_cx):
|
| 372 |
-
anchors += [cx, cy, s_kx, s_ky]
|
| 373 |
-
|
| 374 |
-
# back to torch land
|
| 375 |
-
output = torch.Tensor(anchors).view(-1, 4)
|
| 376 |
-
if self.clip:
|
| 377 |
-
output.clamp_(max=1, min=0)
|
| 378 |
-
return output
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
cfg_mnet = {
|
| 382 |
-
"name": "mobilenet0.25",
|
| 383 |
-
"min_sizes": [[16, 32], [64, 128], [256, 512]],
|
| 384 |
-
"steps": [8, 16, 32],
|
| 385 |
-
"variance": [0.1, 0.2],
|
| 386 |
-
"clip": False,
|
| 387 |
-
"loc_weight": 2.0,
|
| 388 |
-
"gpu_train": True,
|
| 389 |
-
"batch_size": 32,
|
| 390 |
-
"ngpu": 1,
|
| 391 |
-
"epoch": 250,
|
| 392 |
-
"decay1": 190,
|
| 393 |
-
"decay2": 220,
|
| 394 |
-
"image_size": 640,
|
| 395 |
-
"pretrain": True,
|
| 396 |
-
"return_layers": {"stage1": 1, "stage2": 2, "stage3": 3},
|
| 397 |
-
"in_channel": 32,
|
| 398 |
-
"out_channel": 64,
|
| 399 |
-
}
|
| 400 |
-
|
| 401 |
-
cfg_re50 = {
|
| 402 |
-
"name": "Resnet50",
|
| 403 |
-
"min_sizes": [[16, 32], [64, 128], [256, 512]],
|
| 404 |
-
"steps": [8, 16, 32],
|
| 405 |
-
"variance": [0.1, 0.2],
|
| 406 |
-
"clip": False,
|
| 407 |
-
"loc_weight": 2.0,
|
| 408 |
-
"gpu_train": True,
|
| 409 |
-
"batch_size": 24,
|
| 410 |
-
"ngpu": 4,
|
| 411 |
-
"epoch": 100,
|
| 412 |
-
"decay1": 70,
|
| 413 |
-
"decay2": 90,
|
| 414 |
-
"image_size": 840,
|
| 415 |
-
"pretrain": False,
|
| 416 |
-
"return_layers": {"layer2": 1, "layer3": 2, "layer4": 3},
|
| 417 |
-
"in_channel": 256,
|
| 418 |
-
"out_channel": 256,
|
| 419 |
-
}
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
def check_keys(model, pretrained_state_dict):
|
| 423 |
-
ckpt_keys = set(pretrained_state_dict.keys())
|
| 424 |
-
model_keys = set(model.state_dict().keys())
|
| 425 |
-
used_pretrained_keys = model_keys & ckpt_keys
|
| 426 |
-
assert len(used_pretrained_keys) > 0, "load NONE from pretrained checkpoint"
|
| 427 |
-
return True
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
def remove_prefix(state_dict, prefix):
|
| 431 |
-
""" Old style model is stored with all names of parameters sharing common prefix 'module.' """
|
| 432 |
-
f = lambda x: x.split(prefix, 1)[-1] if x.startswith(prefix) else x
|
| 433 |
-
return {f(key): value for key, value in state_dict.items()}
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
def load_model(model, pretrained_path, load_to_cpu):
|
| 437 |
-
if load_to_cpu:
|
| 438 |
-
if pretrained_path is None:
|
| 439 |
-
url = "https://github.com/yinglinzheng/face_weights/releases/download/v1/mobilenet0.25_Final.pth"
|
| 440 |
-
pretrained_dict = torch.utils.model_zoo.load_url(url)
|
| 441 |
-
else:
|
| 442 |
-
pretrained_dict = torch.load(
|
| 443 |
-
pretrained_path, map_location=lambda storage, loc: storage
|
| 444 |
-
)
|
| 445 |
-
else:
|
| 446 |
-
device = torch.cuda.current_device()
|
| 447 |
-
pretrained_dict = torch.load(
|
| 448 |
-
pretrained_path, map_location=lambda storage, loc: storage.cuda(device)
|
| 449 |
-
)
|
| 450 |
-
if "state_dict" in pretrained_dict.keys():
|
| 451 |
-
pretrained_dict = remove_prefix(pretrained_dict["state_dict"], "module.")
|
| 452 |
-
else:
|
| 453 |
-
pretrained_dict = remove_prefix(pretrained_dict, "module.")
|
| 454 |
-
check_keys(model, pretrained_dict)
|
| 455 |
-
model.load_state_dict(pretrained_dict, strict=False)
|
| 456 |
-
return model
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
def load_net(model_path, device, network="mobilenet"):
|
| 460 |
-
if network == "mobilenet":
|
| 461 |
-
cfg = cfg_mnet
|
| 462 |
-
elif network == "resnet50":
|
| 463 |
-
cfg = cfg_re50
|
| 464 |
-
# net and model
|
| 465 |
-
net = RetinaFace(cfg=cfg, phase="test")
|
| 466 |
-
net = load_model(net, model_path, True)
|
| 467 |
-
net.eval()
|
| 468 |
-
cudnn.benchmark = True
|
| 469 |
-
net = net.to(device)
|
| 470 |
-
return net
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
def parse_det(det):
|
| 474 |
-
landmarks = det[5:].reshape(5, 2)
|
| 475 |
-
box = det[:4]
|
| 476 |
-
score = det[4]
|
| 477 |
-
return box, landmarks, score
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
def post_process(
|
| 481 |
-
loc,
|
| 482 |
-
conf,
|
| 483 |
-
landms,
|
| 484 |
-
prior_data,
|
| 485 |
-
cfg,
|
| 486 |
-
scale,
|
| 487 |
-
scale1,
|
| 488 |
-
resize,
|
| 489 |
-
confidence_threshold,
|
| 490 |
-
top_k,
|
| 491 |
-
nms_threshold,
|
| 492 |
-
keep_top_k,
|
| 493 |
-
):
|
| 494 |
-
boxes = decode(loc, prior_data, cfg["variance"])
|
| 495 |
-
boxes = boxes * scale / resize
|
| 496 |
-
boxes = boxes.cpu().numpy()
|
| 497 |
-
scores = conf.cpu().numpy()[:, 1]
|
| 498 |
-
landms_copy = decode_landm(landms, prior_data, cfg["variance"])
|
| 499 |
-
|
| 500 |
-
landms_copy = landms_copy * scale1 / resize
|
| 501 |
-
landms_copy = landms_copy.cpu().numpy()
|
| 502 |
-
|
| 503 |
-
# ignore low scores
|
| 504 |
-
inds = np.where(scores > confidence_threshold)[0]
|
| 505 |
-
boxes = boxes[inds]
|
| 506 |
-
landms_copy = landms_copy[inds]
|
| 507 |
-
scores = scores[inds]
|
| 508 |
-
|
| 509 |
-
# keep top-K before NMS
|
| 510 |
-
order = scores.argsort()[::-1][:top_k]
|
| 511 |
-
boxes = boxes[order]
|
| 512 |
-
landms_copy = landms_copy[order]
|
| 513 |
-
scores = scores[order]
|
| 514 |
-
|
| 515 |
-
# do NMS
|
| 516 |
-
dets = np.hstack((boxes, scores[:, np.newaxis])).astype(np.float32, copy=False)
|
| 517 |
-
keep = py_cpu_nms(dets, nms_threshold)
|
| 518 |
-
# keep = nms(dets, args.nms_threshold,force_cpu=args.cpu)
|
| 519 |
-
dets = dets[keep, :]
|
| 520 |
-
landms_copy = landms_copy[keep]
|
| 521 |
-
|
| 522 |
-
# keep top-K faster NMS
|
| 523 |
-
dets = dets[:keep_top_k, :]
|
| 524 |
-
landms_copy = landms_copy[:keep_top_k, :]
|
| 525 |
-
|
| 526 |
-
dets = np.concatenate((dets, landms_copy), axis=1)
|
| 527 |
-
# show image
|
| 528 |
-
dets = sorted(dets, key=lambda x: x[4], reverse=True)
|
| 529 |
-
dets = [parse_det(x) for x in dets]
|
| 530 |
-
|
| 531 |
-
return dets
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
def batch_detect(net, images, device, is_tensor=False, normalized=False):
|
| 535 |
-
with torch.no_grad():
|
| 536 |
-
confidence_threshold = 0.02
|
| 537 |
-
cfg = cfg_mnet
|
| 538 |
-
top_k = 5000
|
| 539 |
-
nms_threshold = 0.4
|
| 540 |
-
keep_top_k = 750
|
| 541 |
-
resize = 1
|
| 542 |
-
if not is_tensor:
|
| 543 |
-
try:
|
| 544 |
-
img = np.float32(images)
|
| 545 |
-
except ValueError:
|
| 546 |
-
raise NotImplementedError("Input images must of same size")
|
| 547 |
-
img = torch.from_numpy(img)
|
| 548 |
-
else:
|
| 549 |
-
img = images.float()
|
| 550 |
-
img = img.to(device)
|
| 551 |
-
mean = (
|
| 552 |
-
torch.as_tensor([104, 117, 123], dtype=img.dtype, device=img.device)
|
| 553 |
-
.unsqueeze(0)
|
| 554 |
-
.unsqueeze(0)
|
| 555 |
-
.unsqueeze(0)
|
| 556 |
-
)
|
| 557 |
-
img -= mean
|
| 558 |
-
img = img.permute(0, 3, 1, 2)
|
| 559 |
-
(batch_size, _, im_height, im_width,) = img.shape
|
| 560 |
-
scale = torch.as_tensor(
|
| 561 |
-
[im_width, im_height, im_width, im_height],
|
| 562 |
-
dtype=img.dtype,
|
| 563 |
-
device=img.device,
|
| 564 |
-
)
|
| 565 |
-
scale = scale.to(device)
|
| 566 |
-
|
| 567 |
-
loc, conf, landms = net(img) # forward pass
|
| 568 |
-
|
| 569 |
-
priorbox = PriorBox(cfg, image_size=(im_height, im_width))
|
| 570 |
-
priors = priorbox.forward()
|
| 571 |
-
prior_data = priors.to(device)
|
| 572 |
-
scale1 = torch.as_tensor(
|
| 573 |
-
[
|
| 574 |
-
img.shape[3],
|
| 575 |
-
img.shape[2],
|
| 576 |
-
img.shape[3],
|
| 577 |
-
img.shape[2],
|
| 578 |
-
img.shape[3],
|
| 579 |
-
img.shape[2],
|
| 580 |
-
img.shape[3],
|
| 581 |
-
img.shape[2],
|
| 582 |
-
img.shape[3],
|
| 583 |
-
img.shape[2],
|
| 584 |
-
],
|
| 585 |
-
dtype=img.dtype,
|
| 586 |
-
device=img.device,
|
| 587 |
-
)
|
| 588 |
-
scale1 = scale1.to(device)
|
| 589 |
-
|
| 590 |
-
all_dets = [
|
| 591 |
-
post_process(
|
| 592 |
-
loc_i,
|
| 593 |
-
conf_i,
|
| 594 |
-
landms_i,
|
| 595 |
-
prior_data,
|
| 596 |
-
cfg,
|
| 597 |
-
scale,
|
| 598 |
-
scale1,
|
| 599 |
-
resize,
|
| 600 |
-
confidence_threshold,
|
| 601 |
-
top_k,
|
| 602 |
-
nms_threshold,
|
| 603 |
-
keep_top_k,
|
| 604 |
-
)
|
| 605 |
-
for loc_i, conf_i, landms_i in zip(loc, conf, landms)
|
| 606 |
-
]
|
| 607 |
-
|
| 608 |
-
return all_dets
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/detection/detector.py
DELETED
|
@@ -1,46 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
|
| 3 |
-
import numpy as np
|
| 4 |
-
import torch
|
| 5 |
-
|
| 6 |
-
from .alignment import load_net, batch_detect
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
def get_project_dir():
|
| 10 |
-
current_path = os.path.abspath(os.path.join(__file__, "../"))
|
| 11 |
-
return current_path
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
def relative(path):
|
| 15 |
-
path = os.path.join(get_project_dir(), path)
|
| 16 |
-
return os.path.abspath(path)
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
class RetinaFace:
|
| 20 |
-
def __init__(
|
| 21 |
-
self, gpu_id=-1, model_path=None, network="mobilenet",
|
| 22 |
-
):
|
| 23 |
-
self.gpu_id = gpu_id
|
| 24 |
-
self.device = (
|
| 25 |
-
torch.device("cpu") if gpu_id == -1 else torch.device("cuda", gpu_id)
|
| 26 |
-
)
|
| 27 |
-
self.model = load_net(model_path, self.device, network)
|
| 28 |
-
|
| 29 |
-
def detect(self, images):
|
| 30 |
-
if isinstance(images, np.ndarray):
|
| 31 |
-
if len(images.shape) == 3:
|
| 32 |
-
return batch_detect(self.model, [images], self.device)[0]
|
| 33 |
-
elif len(images.shape) == 4:
|
| 34 |
-
return batch_detect(self.model, images, self.device)
|
| 35 |
-
elif isinstance(images, list):
|
| 36 |
-
return batch_detect(self.model, np.array(images), self.device)
|
| 37 |
-
elif isinstance(images, torch.Tensor):
|
| 38 |
-
if len(images.shape) == 3:
|
| 39 |
-
return batch_detect(self.model, images.unsqueeze(0), self.device)[0]
|
| 40 |
-
elif len(images.shape) == 4:
|
| 41 |
-
return batch_detect(self.model, images, self.device)
|
| 42 |
-
else:
|
| 43 |
-
raise NotImplementedError()
|
| 44 |
-
|
| 45 |
-
def __call__(self, images):
|
| 46 |
-
return self.detect(images)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/detection/utils.py
DELETED
|
@@ -1,147 +0,0 @@
|
|
| 1 |
-
import cv2
|
| 2 |
-
# from test_tools.utils import flatten
|
| 3 |
-
import numpy as np
|
| 4 |
-
def flatten(l):
|
| 5 |
-
return [item for sublist in l for item in sublist]
|
| 6 |
-
|
| 7 |
-
def chunks(l, n, step=None):
|
| 8 |
-
if step is None:
|
| 9 |
-
step = n
|
| 10 |
-
return [l[i : i + n] for i in range(0, len(l), step)]
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
def sample_chunks(l, n, step=None):
|
| 14 |
-
return [l[i : i + n] for i in range(0, len(l), step) if i + n <= len(l)]
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
def grab_all_frames(path, max_size, cvt=False):
|
| 18 |
-
capture = cv2.VideoCapture(path)
|
| 19 |
-
ret = True
|
| 20 |
-
frames = []
|
| 21 |
-
while ret:
|
| 22 |
-
ret, frame = capture.read()
|
| 23 |
-
if ret:
|
| 24 |
-
if cvt:
|
| 25 |
-
frame = frame[..., ::-1]
|
| 26 |
-
frames.append(frame)
|
| 27 |
-
if len(frames) == max_size:
|
| 28 |
-
break
|
| 29 |
-
capture.release()
|
| 30 |
-
return frames
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
def get_clips_uniform(path, count, clip_size):
|
| 34 |
-
capture = cv2.VideoCapture(path)
|
| 35 |
-
n_frames = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
|
| 36 |
-
max_clip_available = n_frames + 1 - clip_size
|
| 37 |
-
if count > max_clip_available:
|
| 38 |
-
count = max_clip_available
|
| 39 |
-
final_start = max_clip_available - 1
|
| 40 |
-
start_indices = np.linspace(0, final_start, count, endpoint=True, dtype=np.int)
|
| 41 |
-
all_clip_idx = [list(range(start, start + clip_size)) for start in start_indices]
|
| 42 |
-
valid = set(flatten(all_clip_idx))
|
| 43 |
-
max_idx = max(valid)
|
| 44 |
-
|
| 45 |
-
frames = {}
|
| 46 |
-
for idx in range(max_idx + 1):
|
| 47 |
-
# Get the next frame, but don't decode if we're not using it.
|
| 48 |
-
ret = capture.grab()
|
| 49 |
-
if not ret:
|
| 50 |
-
continue
|
| 51 |
-
|
| 52 |
-
if idx in valid:
|
| 53 |
-
ret, frame = capture.retrieve()
|
| 54 |
-
if not ret or frame is None:
|
| 55 |
-
continue
|
| 56 |
-
else:
|
| 57 |
-
# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 58 |
-
frames[idx] = frame
|
| 59 |
-
|
| 60 |
-
capture.release()
|
| 61 |
-
clips = []
|
| 62 |
-
for clip_idx in all_clip_idx:
|
| 63 |
-
clip = []
|
| 64 |
-
flag = True
|
| 65 |
-
for idx in clip_idx:
|
| 66 |
-
if idx not in frames:
|
| 67 |
-
flag = False
|
| 68 |
-
break
|
| 69 |
-
clip.append(frames[idx])
|
| 70 |
-
if flag:
|
| 71 |
-
clips.append(clip)
|
| 72 |
-
return clips
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
def get_valid_faces(detect_results, max_count=10, thres=0.5, at_least=False):
|
| 76 |
-
new_results = []
|
| 77 |
-
for i, faces in enumerate(detect_results):
|
| 78 |
-
if len(faces) > max_count:
|
| 79 |
-
faces = faces[:max_count]
|
| 80 |
-
l = []
|
| 81 |
-
for j, face in enumerate(faces):
|
| 82 |
-
if face[-1] < thres and not (j == 0 and at_least):
|
| 83 |
-
continue
|
| 84 |
-
box, lm, score = face
|
| 85 |
-
box = box.astype(np.float64)
|
| 86 |
-
lm = lm.astype(np.float64)
|
| 87 |
-
l.append((box, lm, score))
|
| 88 |
-
new_results.append(l)
|
| 89 |
-
return new_results
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
def scale_box(box, scale_h, scale_w, h, w):
|
| 93 |
-
x1, y1, x2, y2 = box.astype(np.int32)
|
| 94 |
-
center_x = (x1 + x2) // 2
|
| 95 |
-
center_y = (y1 + y2) // 2
|
| 96 |
-
box_h = int((y2 - y1) * scale_h)
|
| 97 |
-
box_w = int((x2 - x1) * scale_w)
|
| 98 |
-
new_x1 = center_x - box_w // 2
|
| 99 |
-
new_x2 = new_x1 + box_w
|
| 100 |
-
new_y1 = center_y - box_h // 2
|
| 101 |
-
new_y2 = new_y1 + box_h
|
| 102 |
-
new_x1 = max(new_x1, 0)
|
| 103 |
-
new_y1 = max(new_y1, 0)
|
| 104 |
-
new_y2 = min(new_y2, h)
|
| 105 |
-
new_x2 = min(new_x2, w)
|
| 106 |
-
return new_x1, new_y1, new_x2, new_y2
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
def get_bbox(detect_res):
|
| 110 |
-
tmp_detect_res = get_valid_faces(detect_res, max_count=4, thres=0.5)
|
| 111 |
-
all_face_bboxs = []
|
| 112 |
-
for faces in tmp_detect_res:
|
| 113 |
-
all_face_bboxs.extend([face[0] for face in faces])
|
| 114 |
-
all_face_bboxs = np.array(all_face_bboxs).astype(np.int)
|
| 115 |
-
x1 = all_face_bboxs[:, 0].min()
|
| 116 |
-
x2 = all_face_bboxs[:, 2].max()
|
| 117 |
-
y1 = all_face_bboxs[:, 1].min()
|
| 118 |
-
y2 = all_face_bboxs[:, 3].max()
|
| 119 |
-
|
| 120 |
-
return x1, y1, x2, y2
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
def delta_detect_res(detect_res, x1, y1):
|
| 124 |
-
diff = np.array([[x1, y1]])
|
| 125 |
-
new_detect_res = []
|
| 126 |
-
for faces in detect_res:
|
| 127 |
-
f = []
|
| 128 |
-
for face in faces:
|
| 129 |
-
box, lm, score = face
|
| 130 |
-
box = box.astype(np.float64)
|
| 131 |
-
box[[0, 2]] -= x1
|
| 132 |
-
box[[1, 3]] -= y1
|
| 133 |
-
lm = lm.astype(np.float64) - diff
|
| 134 |
-
f.append((box, lm, score))
|
| 135 |
-
new_detect_res.append(f)
|
| 136 |
-
return new_detect_res
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
def pre_crop(clips, detect_res):
|
| 140 |
-
box = np.array(get_bbox(detect_res))
|
| 141 |
-
w = box[2] - box[0]
|
| 142 |
-
h = box[3] - box[1]
|
| 143 |
-
x1, y1, x2, y2 = scale_box(
|
| 144 |
-
box, 1.5, 1.2 if w > 2 * h else 1.5, clips[0].shape[0], clips[0].shape[1]
|
| 145 |
-
)
|
| 146 |
-
clips = np.array(clips)
|
| 147 |
-
return clips[:, y1:y2, x1:x2], delta_detect_res(detect_res, x1, y1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/face_alignment/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
from .predictor import LandmarkPredictor
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/face_alignment/basenet.py
DELETED
|
@@ -1,107 +0,0 @@
|
|
| 1 |
-
# Backbone networks used for face landmark detection
|
| 2 |
-
# Cunjian Chen (cunjian@msu.edu)
|
| 3 |
-
|
| 4 |
-
import torch.nn as nn
|
| 5 |
-
import torchvision.models as models
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
class ConvBlock(nn.Module):
|
| 9 |
-
def __init__(self, inp, oup, k, s, p, dw=False, linear=False):
|
| 10 |
-
super(ConvBlock, self).__init__()
|
| 11 |
-
self.linear = linear
|
| 12 |
-
if dw:
|
| 13 |
-
self.conv = nn.Conv2d(inp, oup, k, s, p, groups=inp, bias=False)
|
| 14 |
-
else:
|
| 15 |
-
self.conv = nn.Conv2d(inp, oup, k, s, p, bias=False)
|
| 16 |
-
self.bn = nn.BatchNorm2d(oup)
|
| 17 |
-
if not linear:
|
| 18 |
-
self.prelu = nn.PReLU(oup)
|
| 19 |
-
|
| 20 |
-
def forward(self, x):
|
| 21 |
-
x = self.conv(x)
|
| 22 |
-
x = self.bn(x)
|
| 23 |
-
if self.linear:
|
| 24 |
-
return x
|
| 25 |
-
else:
|
| 26 |
-
return self.prelu(x)
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
# SE module
|
| 30 |
-
# https://github.com/wujiyang/Face_Pytorch/blob/master/backbone/cbam.py
|
| 31 |
-
class SEModule(nn.Module):
|
| 32 |
-
"""Squeeze and Excitation Module"""
|
| 33 |
-
|
| 34 |
-
def __init__(self, channels, reduction):
|
| 35 |
-
super(SEModule, self).__init__()
|
| 36 |
-
self.avg_pool = nn.AdaptiveAvgPool2d(1)
|
| 37 |
-
self.fc1 = nn.Conv2d(
|
| 38 |
-
channels, channels // reduction, kernel_size=1, padding=0, bias=False
|
| 39 |
-
)
|
| 40 |
-
self.relu = nn.ReLU(inplace=True)
|
| 41 |
-
self.fc2 = nn.Conv2d(
|
| 42 |
-
channels // reduction, channels, kernel_size=1, padding=0, bias=False
|
| 43 |
-
)
|
| 44 |
-
self.sigmoid = nn.Sigmoid()
|
| 45 |
-
|
| 46 |
-
def forward(self, x):
|
| 47 |
-
input = x
|
| 48 |
-
x = self.avg_pool(x)
|
| 49 |
-
x = self.fc1(x)
|
| 50 |
-
x = self.relu(x)
|
| 51 |
-
x = self.fc2(x)
|
| 52 |
-
x = self.sigmoid(x)
|
| 53 |
-
|
| 54 |
-
return input * x
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
# USE global depthwise convolution layer. Compatible with MobileNetV2 (224×224), MobileNetV2_ExternalData (224×224)
|
| 58 |
-
class MobileNet_GDConv(nn.Module):
|
| 59 |
-
def __init__(self, num_classes):
|
| 60 |
-
super(MobileNet_GDConv, self).__init__()
|
| 61 |
-
self.pretrain_net = models.mobilenet_v2(pretrained=False)
|
| 62 |
-
self.base_net = nn.Sequential(*list(self.pretrain_net.children())[:-1])
|
| 63 |
-
self.linear7 = ConvBlock(1280, 1280, (7, 7), 1, 0, dw=True, linear=True)
|
| 64 |
-
self.linear1 = ConvBlock(1280, num_classes, 1, 1, 0, linear=True)
|
| 65 |
-
|
| 66 |
-
def forward(self, x):
|
| 67 |
-
x = self.base_net(x)
|
| 68 |
-
x = self.linear7(x)
|
| 69 |
-
x = self.linear1(x)
|
| 70 |
-
x = x.view(x.size(0), -1)
|
| 71 |
-
return x
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
# USE global depthwise convolution layer. Compatible with MobileNetV2 (56×56)
|
| 75 |
-
class MobileNet_GDConv_56(nn.Module):
|
| 76 |
-
def __init__(self, num_classes):
|
| 77 |
-
super(MobileNet_GDConv_56, self).__init__()
|
| 78 |
-
self.pretrain_net = models.mobilenet_v2(pretrained=False)
|
| 79 |
-
self.base_net = nn.Sequential(*list(self.pretrain_net.children())[:-1])
|
| 80 |
-
self.linear7 = ConvBlock(1280, 1280, (2, 2), 1, 0, dw=True, linear=True)
|
| 81 |
-
self.linear1 = ConvBlock(1280, num_classes, 1, 1, 0, linear=True)
|
| 82 |
-
|
| 83 |
-
def forward(self, x):
|
| 84 |
-
x = self.base_net(x)
|
| 85 |
-
x = self.linear7(x)
|
| 86 |
-
x = self.linear1(x)
|
| 87 |
-
x = x.view(x.size(0), -1)
|
| 88 |
-
return x
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
# MobileNetV2 with SE; Compatible with MobileNetV2_SE (224×224) and MobileNetV2_SE_RE (224×224)
|
| 92 |
-
class MobileNet_GDConv_SE(nn.Module):
|
| 93 |
-
def __init__(self, num_classes):
|
| 94 |
-
super(MobileNet_GDConv_SE, self).__init__()
|
| 95 |
-
self.pretrain_net = models.mobilenet_v2(pretrained=True)
|
| 96 |
-
self.base_net = nn.Sequential(*list(self.pretrain_net.children())[:-1])
|
| 97 |
-
self.linear7 = ConvBlock(1280, 1280, (7, 7), 1, 0, dw=True, linear=True)
|
| 98 |
-
self.linear1 = ConvBlock(1280, num_classes, 1, 1, 0, linear=True)
|
| 99 |
-
self.attention = SEModule(1280, 8)
|
| 100 |
-
|
| 101 |
-
def forward(self, x):
|
| 102 |
-
x = self.base_net(x)
|
| 103 |
-
x = self.attention(x)
|
| 104 |
-
x = self.linear7(x)
|
| 105 |
-
x = self.linear1(x)
|
| 106 |
-
x = x.view(x.size(0), -1)
|
| 107 |
-
return x
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/face_alignment/predictor.py
DELETED
|
@@ -1,143 +0,0 @@
|
|
| 1 |
-
# Face alignment demo
|
| 2 |
-
# Uses MTCNN as face detector
|
| 3 |
-
# Cunjian Chen (ccunjian@gmail.com)
|
| 4 |
-
import torch
|
| 5 |
-
import cv2
|
| 6 |
-
import numpy as np
|
| 7 |
-
from torch.utils.data import DataLoader
|
| 8 |
-
from .basenet import MobileNet_GDConv
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
def get_device(gpu_id):
|
| 12 |
-
if gpu_id > -1:
|
| 13 |
-
return torch.device(f"cuda:{str(gpu_id)}")
|
| 14 |
-
else:
|
| 15 |
-
return torch.device("cpu")
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def load_model(file):
|
| 19 |
-
model = MobileNet_GDConv(136)
|
| 20 |
-
if file is not None:
|
| 21 |
-
model.load_state_dict(torch.load(file, map_location="cpu"))
|
| 22 |
-
else:
|
| 23 |
-
url = "https://github.com/yinglinzheng/face_weights/releases/download/v1/mobilenet_224_model_best_gdconv_external.pth"
|
| 24 |
-
model.load_state_dict(torch.utils.model_zoo.load_url(url))
|
| 25 |
-
return model
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
# landmark of (5L, 2L) from [0,1] to real range
|
| 29 |
-
def reproject(bbox, landmark):
|
| 30 |
-
landmark_ = landmark.clone()
|
| 31 |
-
x1, y1, x2, y2 = bbox
|
| 32 |
-
w = x2 - x1
|
| 33 |
-
h = y2 - y1
|
| 34 |
-
landmark_[:, 0] *= w
|
| 35 |
-
landmark_[:, 0] += x1
|
| 36 |
-
landmark_[:, 1] *= h
|
| 37 |
-
landmark_[:, 1] += y1
|
| 38 |
-
return landmark_
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
def prepare_feed(img, face):
|
| 42 |
-
height, width, _ = img.shape
|
| 43 |
-
mean = np.asarray([0.485, 0.456, 0.406])
|
| 44 |
-
std = np.asarray([0.229, 0.224, 0.225])
|
| 45 |
-
out_size = 224
|
| 46 |
-
x1, y1, x2, y2 = face[:4]
|
| 47 |
-
|
| 48 |
-
w = x2 - x1 + 1
|
| 49 |
-
h = y2 - y1 + 1
|
| 50 |
-
size = int(min([w, h]) * 1.2)
|
| 51 |
-
cx = x1 + w // 2
|
| 52 |
-
cy = y1 + h // 2
|
| 53 |
-
x1 = cx - size // 2
|
| 54 |
-
x2 = x1 + size
|
| 55 |
-
y1 = cy - size // 2
|
| 56 |
-
y2 = y1 + size
|
| 57 |
-
|
| 58 |
-
dx = max(0, -x1)
|
| 59 |
-
dy = max(0, -y1)
|
| 60 |
-
x1 = max(0, x1)
|
| 61 |
-
y1 = max(0, y1)
|
| 62 |
-
|
| 63 |
-
edx = max(0, x2 - width)
|
| 64 |
-
edy = max(0, y2 - height)
|
| 65 |
-
x2 = min(width, x2)
|
| 66 |
-
y2 = min(height, y2)
|
| 67 |
-
new_bbox = torch.Tensor([x1, y1, x2, y2]).int()
|
| 68 |
-
x1, y1, x2, y2 = new_bbox
|
| 69 |
-
cropped = img[y1:y2, x1:x2]
|
| 70 |
-
if dx > 0 or dy > 0 or edx > 0 or edy > 0:
|
| 71 |
-
cropped = cv2.copyMakeBorder(
|
| 72 |
-
cropped, int(dy), int(edy), int(dx), int(edx), cv2.BORDER_CONSTANT, 0
|
| 73 |
-
)
|
| 74 |
-
cropped_face = cv2.resize(cropped, (out_size, out_size))
|
| 75 |
-
|
| 76 |
-
if cropped_face.shape[0] <= 0 or cropped_face.shape[1] <= 0:
|
| 77 |
-
return None
|
| 78 |
-
test_face = cropped_face.copy()
|
| 79 |
-
test_face = test_face / 255.0
|
| 80 |
-
test_face = (test_face - mean) / std
|
| 81 |
-
test_face = test_face.transpose((2, 0, 1))
|
| 82 |
-
data = torch.from_numpy(test_face).float()
|
| 83 |
-
return dict(data=data, bbox=new_bbox)
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
@torch.no_grad()
|
| 87 |
-
def single_predict(model, feed, device):
|
| 88 |
-
landmark = model(feed["data"].unsqueeze(0).to(device)).cpu()
|
| 89 |
-
landmark = landmark.reshape(-1, 2)
|
| 90 |
-
landmark = reproject(feed["bbox"], landmark)
|
| 91 |
-
return landmark.numpy()
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
@torch.no_grad()
|
| 95 |
-
def batch_predict(model, feeds, device):
|
| 96 |
-
if not isinstance(feeds, list):
|
| 97 |
-
feeds = [feeds]
|
| 98 |
-
# loader = DataLoader(FeedDataset(feeds), batch_size=50, shuffle=False)
|
| 99 |
-
data = []
|
| 100 |
-
for feed in feeds:
|
| 101 |
-
data.append(feed["data"].unsqueeze(0))
|
| 102 |
-
data = torch.cat(data, 0).to(device)
|
| 103 |
-
results = []
|
| 104 |
-
|
| 105 |
-
landmarks = model(data).cpu()
|
| 106 |
-
for landmark, feed in zip(landmarks, feeds):
|
| 107 |
-
landmark = landmark.reshape(-1, 2)
|
| 108 |
-
landmark = reproject(feed["bbox"], landmark)
|
| 109 |
-
results.append(landmark.numpy())
|
| 110 |
-
return results
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
@torch.no_grad()
|
| 114 |
-
def batch_predict2(model, feeds, device, batch_size=None):
|
| 115 |
-
if not isinstance(feeds, list):
|
| 116 |
-
feeds = [feeds]
|
| 117 |
-
if batch_size is None:
|
| 118 |
-
batch_size = len(feeds)
|
| 119 |
-
loader = DataLoader(feeds, batch_size=len(feeds), shuffle=False)
|
| 120 |
-
results = []
|
| 121 |
-
for feed in loader:
|
| 122 |
-
landmarks = model(feed["data"].to(device)).cpu()
|
| 123 |
-
for landmark, bbox in zip(landmarks, feed["bbox"]):
|
| 124 |
-
landmark = landmark.reshape(-1, 2)
|
| 125 |
-
landmark = reproject(bbox, landmark)
|
| 126 |
-
results.append(landmark.numpy())
|
| 127 |
-
return results
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
class LandmarkPredictor:
|
| 131 |
-
def __init__(self, gpu_id=0, file=None):
|
| 132 |
-
self.device = get_device(gpu_id)
|
| 133 |
-
self.model = load_model(file).to(self.device).eval()
|
| 134 |
-
|
| 135 |
-
def __call__(self, feeds):
|
| 136 |
-
results = batch_predict2(self.model, feeds, self.device)
|
| 137 |
-
if not isinstance(feeds, list):
|
| 138 |
-
results = results[0]
|
| 139 |
-
return results
|
| 140 |
-
|
| 141 |
-
@staticmethod
|
| 142 |
-
def prepare_feed(img, face):
|
| 143 |
-
return prepare_feed(img, face)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/face_alignment/utils.py
DELETED
|
@@ -1,17 +0,0 @@
|
|
| 1 |
-
import cv2
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
def drawLandmark_multiple(img, bbox, landmark):
|
| 5 |
-
"""
|
| 6 |
-
Input:
|
| 7 |
-
- img: gray or RGB
|
| 8 |
-
- bbox: type of BBox
|
| 9 |
-
- landmark: reproject landmark of (5L, 2L)
|
| 10 |
-
Output:
|
| 11 |
-
- img marked with landmark and bbox
|
| 12 |
-
"""
|
| 13 |
-
x1, y1, x2, y2 = bbox
|
| 14 |
-
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
|
| 15 |
-
for x, y in landmark:
|
| 16 |
-
cv2.circle(img, (int(x), int(y)), 2, (0, 255, 0), -1)
|
| 17 |
-
return img
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/operations.py
DELETED
|
@@ -1,79 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
|
| 3 |
-
import os
|
| 4 |
-
import cv2
|
| 5 |
-
import numpy as np
|
| 6 |
-
from .tracking.sort import iou
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
def face_iou(f1, f2):
|
| 10 |
-
return iou(f1[0], f2[0])
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
def simple_tracking(batch_landmarks, index=0, thres=0.5):
|
| 14 |
-
track = []
|
| 15 |
-
|
| 16 |
-
for i, faces in enumerate(batch_landmarks):
|
| 17 |
-
if i == 0:
|
| 18 |
-
if len(faces) <= index or faces[index][-1] < 0.8:
|
| 19 |
-
return None
|
| 20 |
-
if index != 0:
|
| 21 |
-
for idx in range(index):
|
| 22 |
-
if face_iou(faces[idx], faces[index]) > thres:
|
| 23 |
-
return None
|
| 24 |
-
track.append(faces[index])
|
| 25 |
-
else:
|
| 26 |
-
last = track[i - 1]
|
| 27 |
-
if len(faces) == 0:
|
| 28 |
-
return None
|
| 29 |
-
sorted_faces = sorted(faces, key=lambda x: face_iou(x, last), reverse=True)
|
| 30 |
-
if face_iou(sorted_faces[0], last) < thres:
|
| 31 |
-
return None
|
| 32 |
-
track.append(sorted_faces[0])
|
| 33 |
-
return track
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
def multiple_tracking(batch_landmarks):
|
| 37 |
-
tracks = []
|
| 38 |
-
for i in range(len(batch_landmarks[0])):
|
| 39 |
-
track = simple_tracking(batch_landmarks, index=i)
|
| 40 |
-
if track is None:
|
| 41 |
-
continue
|
| 42 |
-
tracks.append(track)
|
| 43 |
-
return tracks
|
| 44 |
-
|
| 45 |
-
def find_longest(detect_res):
|
| 46 |
-
fc = len(detect_res)
|
| 47 |
-
tuples = []
|
| 48 |
-
start = 0
|
| 49 |
-
end = 0
|
| 50 |
-
previous_count = -1
|
| 51 |
-
all_tracks = []
|
| 52 |
-
# start 取得到,end 取不到
|
| 53 |
-
while start < (fc - 1):
|
| 54 |
-
for end in range(start + 2, fc + 1):
|
| 55 |
-
tracks = multiple_tracking(detect_res[start:end])
|
| 56 |
-
if (len(tracks) != previous_count and previous_count != -1) or len(
|
| 57 |
-
tracks
|
| 58 |
-
) == 0:
|
| 59 |
-
break
|
| 60 |
-
previous_count = len(tracks)
|
| 61 |
-
if end - start > 2:
|
| 62 |
-
if end != fc:
|
| 63 |
-
un_reach_end = end - 1
|
| 64 |
-
else:
|
| 65 |
-
un_reach_end = end
|
| 66 |
-
sub_tracks = multiple_tracking(detect_res[start:un_reach_end])
|
| 67 |
-
if end == fc and len(sub_tracks) == 0:
|
| 68 |
-
un_reach_end = end - 1
|
| 69 |
-
sub_tracks = multiple_tracking(detect_res[start:un_reach_end])
|
| 70 |
-
if len(sub_tracks) > 0:
|
| 71 |
-
tpl = (start, un_reach_end)
|
| 72 |
-
tuples.append(tpl)
|
| 73 |
-
all_tracks.append(sub_tracks[0])
|
| 74 |
-
else:
|
| 75 |
-
raise NotImplementedError
|
| 76 |
-
previous_count = -1
|
| 77 |
-
end = un_reach_end
|
| 78 |
-
start = end
|
| 79 |
-
return tuples, all_tracks
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/tracking/__init__.py
DELETED
|
File without changes
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/tracking/sort.py
DELETED
|
@@ -1,285 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
SORT: A Simple, Online and Realtime Tracker
|
| 3 |
-
Copyright (C) 2016 Alex Bewley alex@dynamicdetection.com
|
| 4 |
-
|
| 5 |
-
This program is free software: you can redistribute it and/or modify
|
| 6 |
-
it under the terms of the GNU General Public License as published by
|
| 7 |
-
the Free Software Foundation, either version 3 of the License, or
|
| 8 |
-
(at your option) any later version.
|
| 9 |
-
|
| 10 |
-
This program is distributed in the hope that it will be useful,
|
| 11 |
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 12 |
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 13 |
-
GNU General Public License for more details.
|
| 14 |
-
|
| 15 |
-
You should have received a copy of the GNU General Public License
|
| 16 |
-
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
| 17 |
-
"""
|
| 18 |
-
from __future__ import print_function
|
| 19 |
-
import os.path
|
| 20 |
-
import numpy as np
|
| 21 |
-
import matplotlib.pyplot as plt
|
| 22 |
-
import matplotlib.patches as patches
|
| 23 |
-
from scipy.optimize import linear_sum_assignment
|
| 24 |
-
import glob
|
| 25 |
-
import time
|
| 26 |
-
import argparse
|
| 27 |
-
from filterpy.kalman import KalmanFilter
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
def iou(bb_test, bb_gt):
|
| 31 |
-
"""
|
| 32 |
-
Computes IUO between two bboxes in the form [x1,y1,x2,y2]
|
| 33 |
-
"""
|
| 34 |
-
xx1 = np.maximum(bb_test[0], bb_gt[0])
|
| 35 |
-
yy1 = np.maximum(bb_test[1], bb_gt[1])
|
| 36 |
-
xx2 = np.minimum(bb_test[2], bb_gt[2])
|
| 37 |
-
yy2 = np.minimum(bb_test[3], bb_gt[3])
|
| 38 |
-
w = np.maximum(0.0, xx2 - xx1)
|
| 39 |
-
h = np.maximum(0.0, yy2 - yy1)
|
| 40 |
-
wh = w * h
|
| 41 |
-
o = wh / (
|
| 42 |
-
(bb_test[2] - bb_test[0]) * (bb_test[3] - bb_test[1])
|
| 43 |
-
+ (bb_gt[2] - bb_gt[0]) * (bb_gt[3] - bb_gt[1])
|
| 44 |
-
- wh
|
| 45 |
-
)
|
| 46 |
-
return o
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
def convert_bbox_to_z(bbox):
|
| 50 |
-
"""
|
| 51 |
-
Takes a bounding box in the form [x1,y1,x2,y2] and returns z in the form
|
| 52 |
-
[x,y,s,r] where x,y is the centre of the box and s is the scale/area and r is
|
| 53 |
-
the aspect ratio
|
| 54 |
-
"""
|
| 55 |
-
w = bbox[2] - bbox[0]
|
| 56 |
-
h = bbox[3] - bbox[1]
|
| 57 |
-
x = bbox[0] + w / 2.0
|
| 58 |
-
y = bbox[1] + h / 2.0
|
| 59 |
-
s = w * h # scale is just area
|
| 60 |
-
r = w / float(h)
|
| 61 |
-
return np.array([x, y, s, r]).reshape((4, 1))
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
def convert_x_to_bbox(x, score=None):
|
| 65 |
-
"""
|
| 66 |
-
Takes a bounding box in the centre form [x,y,s,r] and returns it in the form
|
| 67 |
-
[x1,y1,x2,y2] where x1,y1 is the top left and x2,y2 is the bottom right
|
| 68 |
-
"""
|
| 69 |
-
w = np.sqrt(x[2] * x[3])
|
| 70 |
-
h = x[2] / w
|
| 71 |
-
if score == None:
|
| 72 |
-
return np.array(
|
| 73 |
-
[x[0] - w / 2.0, x[1] - h / 2.0, x[0] + w / 2.0, x[1] + h / 2.0]
|
| 74 |
-
).reshape((1, 4))
|
| 75 |
-
else:
|
| 76 |
-
return np.array(
|
| 77 |
-
[x[0] - w / 2.0, x[1] - h / 2.0, x[0] + w / 2.0, x[1] + h / 2.0, score]
|
| 78 |
-
).reshape((1, 5))
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
class KalmanBoxTracker(object):
|
| 82 |
-
"""
|
| 83 |
-
This class represents the internel state of individual tracked objects observed as bbox.
|
| 84 |
-
"""
|
| 85 |
-
|
| 86 |
-
count = 0
|
| 87 |
-
|
| 88 |
-
def __init__(self, bbox):
|
| 89 |
-
"""
|
| 90 |
-
Initialises a tracker using initial bounding box.
|
| 91 |
-
"""
|
| 92 |
-
# define constant velocity model
|
| 93 |
-
self.kf = KalmanFilter(dim_x=7, dim_z=4)
|
| 94 |
-
self.kf.F = np.array(
|
| 95 |
-
[
|
| 96 |
-
[1, 0, 0, 0, 1, 0, 0],
|
| 97 |
-
[0, 1, 0, 0, 0, 1, 0],
|
| 98 |
-
[0, 0, 1, 0, 0, 0, 1],
|
| 99 |
-
[0, 0, 0, 1, 0, 0, 0],
|
| 100 |
-
[0, 0, 0, 0, 1, 0, 0],
|
| 101 |
-
[0, 0, 0, 0, 0, 1, 0],
|
| 102 |
-
[0, 0, 0, 0, 0, 0, 1],
|
| 103 |
-
]
|
| 104 |
-
)
|
| 105 |
-
self.kf.H = np.array(
|
| 106 |
-
[
|
| 107 |
-
[1, 0, 0, 0, 0, 0, 0],
|
| 108 |
-
[0, 1, 0, 0, 0, 0, 0],
|
| 109 |
-
[0, 0, 1, 0, 0, 0, 0],
|
| 110 |
-
[0, 0, 0, 1, 0, 0, 0],
|
| 111 |
-
]
|
| 112 |
-
)
|
| 113 |
-
|
| 114 |
-
self.kf.R[2:, 2:] *= 10.0
|
| 115 |
-
self.kf.P[
|
| 116 |
-
4:, 4:
|
| 117 |
-
] *= 1000.0 # give high uncertainty to the unobservable initial velocities
|
| 118 |
-
self.kf.P *= 10.0
|
| 119 |
-
self.kf.Q[-1, -1] *= 0.01
|
| 120 |
-
self.kf.Q[4:, 4:] *= 0.01
|
| 121 |
-
|
| 122 |
-
self.kf.x[:4] = convert_bbox_to_z(bbox)
|
| 123 |
-
self.time_since_update = 0
|
| 124 |
-
self.id = KalmanBoxTracker.count
|
| 125 |
-
KalmanBoxTracker.count += 1
|
| 126 |
-
self.history = []
|
| 127 |
-
self.hits = 0
|
| 128 |
-
self.hit_streak = 0
|
| 129 |
-
self.age = 0
|
| 130 |
-
|
| 131 |
-
def update(self, bbox):
|
| 132 |
-
"""
|
| 133 |
-
Updates the state vector with observed bbox.
|
| 134 |
-
"""
|
| 135 |
-
self.time_since_update = 0
|
| 136 |
-
self.history = []
|
| 137 |
-
self.hits += 1
|
| 138 |
-
self.hit_streak += 1
|
| 139 |
-
self.kf.update(convert_bbox_to_z(bbox))
|
| 140 |
-
|
| 141 |
-
def predict(self):
|
| 142 |
-
"""
|
| 143 |
-
Advances the state vector and returns the predicted bounding box estimate.
|
| 144 |
-
"""
|
| 145 |
-
if (self.kf.x[6] + self.kf.x[2]) <= 0:
|
| 146 |
-
self.kf.x[6] *= 0.0
|
| 147 |
-
self.kf.predict()
|
| 148 |
-
self.age += 1
|
| 149 |
-
if self.time_since_update > 0:
|
| 150 |
-
self.hit_streak = 0
|
| 151 |
-
self.time_since_update += 1
|
| 152 |
-
self.history.append(convert_x_to_bbox(self.kf.x))
|
| 153 |
-
return self.history[-1]
|
| 154 |
-
|
| 155 |
-
def get_state(self):
|
| 156 |
-
"""
|
| 157 |
-
Returns the current bounding box estimate.
|
| 158 |
-
"""
|
| 159 |
-
return convert_x_to_bbox(self.kf.x)
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
def associate_detections_to_trackers(detections, trackers, iou_threshold=0.3):
|
| 163 |
-
"""
|
| 164 |
-
Assigns detections to tracked object (both represented as bounding boxes)
|
| 165 |
-
|
| 166 |
-
Returns 3 lists of matches, unmatched_detections and unmatched_trackers
|
| 167 |
-
"""
|
| 168 |
-
if len(trackers) == 0:
|
| 169 |
-
return (
|
| 170 |
-
np.empty((0, 2), dtype=int),
|
| 171 |
-
np.arange(len(detections)),
|
| 172 |
-
np.empty((0, 5), dtype=int),
|
| 173 |
-
)
|
| 174 |
-
iou_matrix = np.zeros((len(detections), len(trackers)), dtype=np.float32)
|
| 175 |
-
|
| 176 |
-
for d, det in enumerate(detections):
|
| 177 |
-
for t, trk in enumerate(trackers):
|
| 178 |
-
iou_matrix[d, t] = iou(det, trk)
|
| 179 |
-
|
| 180 |
-
matched_indices = linear_sum_assignment(-iou_matrix)
|
| 181 |
-
matched_indices = np.array(list(zip(*matched_indices)), dtype=np.int)
|
| 182 |
-
matched_indices.shape = (-1, 2)
|
| 183 |
-
# print(matched_indices)
|
| 184 |
-
# print(type(matched_indices))
|
| 185 |
-
|
| 186 |
-
unmatched_detections = []
|
| 187 |
-
for d, det in enumerate(detections):
|
| 188 |
-
if d not in matched_indices[:, 0]:
|
| 189 |
-
unmatched_detections.append(d)
|
| 190 |
-
unmatched_trackers = []
|
| 191 |
-
for t, trk in enumerate(trackers):
|
| 192 |
-
if t not in matched_indices[:, 1]:
|
| 193 |
-
unmatched_trackers.append(t)
|
| 194 |
-
|
| 195 |
-
# filter out matched with low IOU
|
| 196 |
-
matches = []
|
| 197 |
-
for m in matched_indices:
|
| 198 |
-
if iou_matrix[m[0], m[1]] < iou_threshold:
|
| 199 |
-
unmatched_detections.append(m[0])
|
| 200 |
-
unmatched_trackers.append(m[1])
|
| 201 |
-
else:
|
| 202 |
-
matches.append(m.reshape(1, 2))
|
| 203 |
-
if len(matches) == 0:
|
| 204 |
-
matches = np.empty((0, 2), dtype=int)
|
| 205 |
-
else:
|
| 206 |
-
matches = np.concatenate(matches, axis=0)
|
| 207 |
-
|
| 208 |
-
return matches, np.array(unmatched_detections), np.array(unmatched_trackers)
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
class Sort(object):
|
| 212 |
-
def __init__(self, max_age=1, min_hits=3):
|
| 213 |
-
"""
|
| 214 |
-
Sets key parameters for SORT
|
| 215 |
-
"""
|
| 216 |
-
self.max_age = max_age
|
| 217 |
-
self.min_hits = min_hits
|
| 218 |
-
self.trackers = []
|
| 219 |
-
self.frame_count = 0
|
| 220 |
-
|
| 221 |
-
def update(self, dets):
|
| 222 |
-
"""
|
| 223 |
-
Params:
|
| 224 |
-
dets - a numpy array of detections in the format [[x1,y1,x2,y2,score],[x1,y1,x2,y2,score],...]
|
| 225 |
-
Requires: this method must be called once for each frame even with empty detections.
|
| 226 |
-
Returns the a similar array, where the last column is the object ID.
|
| 227 |
-
|
| 228 |
-
NOTE: The number of objects returned may differ from the number of detections provided.
|
| 229 |
-
"""
|
| 230 |
-
self.frame_count += 1
|
| 231 |
-
# get predicted locations from existing trackers.
|
| 232 |
-
trks = np.zeros((len(self.trackers), 5))
|
| 233 |
-
to_del = []
|
| 234 |
-
ret = []
|
| 235 |
-
for t, trk in enumerate(trks):
|
| 236 |
-
pos = self.trackers[t].predict()[0]
|
| 237 |
-
trk[:] = [pos[0], pos[1], pos[2], pos[3], 0]
|
| 238 |
-
if np.any(np.isnan(pos)):
|
| 239 |
-
to_del.append(t)
|
| 240 |
-
trks = np.ma.compress_rows(np.ma.masked_invalid(trks))
|
| 241 |
-
for t in reversed(to_del):
|
| 242 |
-
self.trackers.pop(t)
|
| 243 |
-
matched, unmatched_dets, unmatched_trks = associate_detections_to_trackers(
|
| 244 |
-
dets, trks
|
| 245 |
-
)
|
| 246 |
-
|
| 247 |
-
# update matched trackers with assigned detections
|
| 248 |
-
for t, trk in enumerate(self.trackers):
|
| 249 |
-
if t not in unmatched_trks:
|
| 250 |
-
d = matched[np.where(matched[:, 1] == t)[0], 0]
|
| 251 |
-
trk.update(dets[d, :][0])
|
| 252 |
-
|
| 253 |
-
# create and initialise new trackers for unmatched detections
|
| 254 |
-
for i in unmatched_dets:
|
| 255 |
-
trk = KalmanBoxTracker(dets[i, :])
|
| 256 |
-
self.trackers.append(trk)
|
| 257 |
-
i = len(self.trackers)
|
| 258 |
-
for trk in reversed(self.trackers):
|
| 259 |
-
d = trk.get_state()[0]
|
| 260 |
-
if (trk.time_since_update < 1) and (
|
| 261 |
-
trk.hit_streak >= self.min_hits or self.frame_count <= self.min_hits
|
| 262 |
-
):
|
| 263 |
-
ret.append(
|
| 264 |
-
np.concatenate((d, [trk.id + 1])).reshape(1, -1)
|
| 265 |
-
) # +1 as MOT benchmark requires positive
|
| 266 |
-
i -= 1
|
| 267 |
-
# remove dead tracklet
|
| 268 |
-
if trk.time_since_update > self.max_age:
|
| 269 |
-
self.trackers.pop(i)
|
| 270 |
-
if len(ret) > 0:
|
| 271 |
-
return np.concatenate(ret)
|
| 272 |
-
return np.empty((0, 5))
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
def parse_args():
|
| 276 |
-
"""Parse input arguments."""
|
| 277 |
-
parser = argparse.ArgumentParser(description="SORT demo")
|
| 278 |
-
parser.add_argument(
|
| 279 |
-
"--display",
|
| 280 |
-
dest="display",
|
| 281 |
-
help="Display online tracker output (slow) [False]",
|
| 282 |
-
action="store_true",
|
| 283 |
-
)
|
| 284 |
-
args = parser.parse_args()
|
| 285 |
-
return args
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/tracking/tracker.py
DELETED
|
@@ -1,27 +0,0 @@
|
|
| 1 |
-
from .sort import Sort
|
| 2 |
-
import numpy as np
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
def get_detections(faces):
|
| 6 |
-
detections = []
|
| 7 |
-
for face in faces:
|
| 8 |
-
x1, y1, x2, y2 = face[0]
|
| 9 |
-
detections.append((x1, y1, x2, y2, face[-1]))
|
| 10 |
-
return np.array(detections)
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
def get_tracks(detect_results):
|
| 14 |
-
tracks = {}
|
| 15 |
-
mot_tracker = Sort()
|
| 16 |
-
for faces in detect_results:
|
| 17 |
-
detections = get_detections(faces)
|
| 18 |
-
track_bbs_ids = mot_tracker.update(detections)
|
| 19 |
-
for track in track_bbs_ids: # 单独框出每一张人脸
|
| 20 |
-
id = int(track[-1])
|
| 21 |
-
box = track[:4]
|
| 22 |
-
if id in tracks:
|
| 23 |
-
tracks[id].append(box)
|
| 24 |
-
else:
|
| 25 |
-
tracks[id] = [box]
|
| 26 |
-
|
| 27 |
-
return [track for id, track in tracks.items() if len(track) == len(detect_results)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/ct/utils.py
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
import cv2
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
def write_img(file, img):
|
| 5 |
-
cv2.imwrite(file, img, [cv2.IMWRITE_PNG_COMPRESSION, 0])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
video/pwtf-dvd/model_code/preprocessing/test_tools/faster_crop_align_xray.py
DELETED
|
@@ -1,73 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
import cv2
|
| 3 |
-
from .warp_for_xray import (
|
| 4 |
-
estimiate_batch_transform,
|
| 5 |
-
transform_landmarks,
|
| 6 |
-
std_points_256,
|
| 7 |
-
)
|
| 8 |
-
import numpy as np
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
class FasterCropAlignXRay:
|
| 12 |
-
"""
|
| 13 |
-
修正到统一坐标系,统一图像大小到标准尺寸
|
| 14 |
-
"""
|
| 15 |
-
|
| 16 |
-
def __init__(self, size=256):
|
| 17 |
-
self.image_size = size
|
| 18 |
-
self.std_points = std_points_256 * size / 256.0
|
| 19 |
-
|
| 20 |
-
def __call__(self, landmarks, images=None, jitter=False):
|
| 21 |
-
landmarks = [landmark[:4] for landmark in landmarks]
|
| 22 |
-
ori_boxes = np.array([ori_box for _, _, _, ori_box in landmarks])
|
| 23 |
-
five_landmarks = np.array([ldm5 for _, ldm5, _, _ in landmarks])
|
| 24 |
-
landmarks68 = np.array([ldm68 for _, _, ldm68, _ in landmarks])
|
| 25 |
-
# assert landmarks68.min() > 0
|
| 26 |
-
|
| 27 |
-
left_top = ori_boxes[:, :2].min(0)
|
| 28 |
-
|
| 29 |
-
right_bottom = ori_boxes[:, 2:].max(0)
|
| 30 |
-
|
| 31 |
-
size = right_bottom - left_top
|
| 32 |
-
|
| 33 |
-
w, h = size
|
| 34 |
-
|
| 35 |
-
diff = ori_boxes[:, :2] - left_top[None, ...]
|
| 36 |
-
|
| 37 |
-
new_five_landmarks = five_landmarks + diff[:, None, :]
|
| 38 |
-
new_landmarks68 = landmarks68 + diff[:, None, :]
|
| 39 |
-
|
| 40 |
-
landmark_for_estimiate = new_five_landmarks.copy()
|
| 41 |
-
if jitter:
|
| 42 |
-
landmark_for_estimiate += np.random.uniform(
|
| 43 |
-
-4, 4, landmark_for_estimiate.shape
|
| 44 |
-
)
|
| 45 |
-
|
| 46 |
-
tfm, trans = estimiate_batch_transform(
|
| 47 |
-
landmark_for_estimiate, tgt_pts=self.std_points
|
| 48 |
-
)
|
| 49 |
-
|
| 50 |
-
transformed_landmarks68 = np.array(
|
| 51 |
-
[transform_landmarks(ldm68, trans) for ldm68 in new_landmarks68]
|
| 52 |
-
)
|
| 53 |
-
|
| 54 |
-
if images is not None:
|
| 55 |
-
transformed_images = [
|
| 56 |
-
self.process_sinlge(tfm, image, d, h, w)
|
| 57 |
-
for image, d in zip(images, diff)
|
| 58 |
-
] # 拼接 func 的参数
|
| 59 |
-
transformed_images = np.stack(transformed_images)
|
| 60 |
-
return transformed_landmarks68, transformed_images
|
| 61 |
-
else:
|
| 62 |
-
return transformed_landmarks68
|
| 63 |
-
|
| 64 |
-
def process_sinlge(self, tfm, image, d, h, w):
|
| 65 |
-
assert isinstance(image, np.ndarray)
|
| 66 |
-
new_image = np.zeros((h, w, 3), dtype=np.uint8)
|
| 67 |
-
x, y = d
|
| 68 |
-
ih, iw, _ = image.shape
|
| 69 |
-
new_image[y : y + ih, x : x + iw] = image
|
| 70 |
-
transformed_image = cv2.warpAffine(
|
| 71 |
-
new_image, tfm, (self.image_size, self.image_size)
|
| 72 |
-
)
|
| 73 |
-
return transformed_image
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|