mirror of
https://github.com/iperov/DeepFaceLive
synced 2025-08-14 02:37:01 -07:00
code release
This commit is contained in:
parent
b941ba41a3
commit
a902f11f74
354 changed files with 826570 additions and 1 deletions
205
xlib/math/Affine2DMat.py
Normal file
205
xlib/math/Affine2DMat.py
Normal file
|
@ -0,0 +1,205 @@
|
|||
import cv2
|
||||
import numpy as np
|
||||
import numpy.linalg as npla
|
||||
|
||||
|
||||
class Affine2DMat(np.ndarray):
|
||||
"""
|
||||
affine transformation matrix for 2D
|
||||
shape is (2,3)
|
||||
"""
|
||||
|
||||
def __new__(cls, values):
|
||||
values = np.array(values)
|
||||
if values.shape != (2,3):
|
||||
raise ValueError('values must have shape (2,3)')
|
||||
|
||||
obj = super().__new__(cls, shape=(2,3), dtype=np.float32, buffer=None, offset=0, strides=None, order=None)
|
||||
obj[:] = values
|
||||
|
||||
return obj
|
||||
|
||||
def __init__(self, values):
|
||||
super().__init__()
|
||||
|
||||
@staticmethod
|
||||
def umeyama(src, dst, estimate_scale=True):
|
||||
"""
|
||||
Estimate N-D similarity transformation with or without scaling.
|
||||
Parameters
|
||||
----------
|
||||
src : (M, N) array
|
||||
Source coordinates.
|
||||
dst : (M, N) array
|
||||
Destination coordinates.
|
||||
estimate_scale : bool
|
||||
Whether to estimate scaling factor.
|
||||
|
||||
Returns
|
||||
-------
|
||||
The homogeneous similarity transformation matrix. The matrix contains
|
||||
NaN values only if the problem is not well-conditioned.
|
||||
|
||||
Reference
|
||||
Least-squares estimation of transformation parameters between two point patterns", Shinji Umeyama, PAMI 1991, DOI: 10.1109/34.88573
|
||||
"""
|
||||
num = src.shape[0]
|
||||
dim = src.shape[1]
|
||||
|
||||
# Compute mean of src and dst.
|
||||
src_mean = src.mean(axis=0)
|
||||
dst_mean = dst.mean(axis=0)
|
||||
|
||||
# Subtract mean from src and dst.
|
||||
src_demean = src - src_mean
|
||||
dst_demean = dst - dst_mean
|
||||
|
||||
# Eq. (38).
|
||||
A = np.dot(dst_demean.T, src_demean) / num
|
||||
|
||||
# Eq. (39).
|
||||
d = np.ones((dim,), dtype=np.double)
|
||||
if np.linalg.det(A) < 0:
|
||||
d[dim - 1] = -1
|
||||
|
||||
T = np.eye(dim + 1, dtype=np.double)
|
||||
|
||||
U, S, V = np.linalg.svd(A)
|
||||
|
||||
# Eq. (40) and (43).
|
||||
rank = np.linalg.matrix_rank(A)
|
||||
if rank == 0:
|
||||
return np.nan * T
|
||||
elif rank == dim - 1:
|
||||
if np.linalg.det(U) * np.linalg.det(V) > 0:
|
||||
T[:dim, :dim] = np.dot(U, V)
|
||||
else:
|
||||
s = d[dim - 1]
|
||||
d[dim - 1] = -1
|
||||
T[:dim, :dim] = np.dot(U, np.dot(np.diag(d), V))
|
||||
d[dim - 1] = s
|
||||
else:
|
||||
T[:dim, :dim] = np.dot(U, np.dot(np.diag(d), V))
|
||||
|
||||
if estimate_scale:
|
||||
# Eq. (41) and (42).
|
||||
scale = 1.0 / src_demean.var(axis=0).sum() * np.dot(S, d)
|
||||
else:
|
||||
scale = 1.0
|
||||
|
||||
T[:dim, dim] = dst_mean - scale * np.dot(T[:dim, :dim], src_mean.T)
|
||||
T[:dim, :dim] *= scale
|
||||
|
||||
return Affine2DMat(T[:2])
|
||||
|
||||
|
||||
@staticmethod
|
||||
def from_3_pairs(src_pts, dst_pts) -> 'Affine2DMat':
|
||||
"""
|
||||
calculates Affine2DMat from three pairs of the corresponding points.
|
||||
"""
|
||||
return Affine2DMat(cv2.getAffineTransform(np.float32(src_pts), np.float32(dst_pts)))
|
||||
|
||||
def invert(self):
|
||||
"""
|
||||
returns inverted Affine2DMat
|
||||
"""
|
||||
affine_mat = np.concatenate( (self.copy(), [[0,0,1]]), 0 )
|
||||
affine_mat = npla.inv(affine_mat)
|
||||
return Affine2DMat( affine_mat[:2,:] )
|
||||
|
||||
|
||||
# def scaled(self, sw : float, sh: float, tw: float, th: float) -> 'Affine2DMat':
|
||||
# """
|
||||
|
||||
# sw, sh source width/height scale
|
||||
# tw, th target width/height scale
|
||||
# """
|
||||
# src_pts = np.float32([(0,0),(1,0),(0,1),(0.5,0.5)])
|
||||
# src_pts -= 0.5
|
||||
|
||||
# dst_pts = self.transform_points(src_pts)
|
||||
|
||||
# print(src_pts, dst_pts)
|
||||
|
||||
# src_pts = src_pts*(sw,sh)
|
||||
|
||||
# dst_cpt = dst_pts[-1]
|
||||
|
||||
# dst_pts = (dst_pts-dst_cpt)*(tw,th) + dst_cpt*(tw,th)
|
||||
|
||||
|
||||
|
||||
# return Affine2DUniMat.from_3_pairs(src_pts[:3], dst_pts[:3] )
|
||||
|
||||
|
||||
|
||||
def transform_points(self, points):
|
||||
if not isinstance(points, np.ndarray):
|
||||
points = np.float32(points)
|
||||
|
||||
dtype = points.dtype
|
||||
|
||||
points = np.pad(points, ((0,0), (0,1) ), constant_values=(1,), mode='constant')
|
||||
|
||||
return np.matmul( np.concatenate( [ self, [[0,0,1]] ], 0), points.T).T[:,:2].astype(dtype)
|
||||
|
||||
def as_uni_mat(self) -> 'Affine2DUniMat':
|
||||
"""
|
||||
represent this mat as Affine2DUniMat
|
||||
"""
|
||||
return Affine2DUniMat(self)
|
||||
|
||||
|
||||
class Affine2DUniMat(Affine2DMat):
|
||||
"""
|
||||
same as Affine2DMat but for transformation of uniform coordinates
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def umeyama(src, dst, estimate_scale=True): return Affine2DMat.umeyama(src, dst, estimate_scale=estimate_scale).as_uni_mat()
|
||||
|
||||
@staticmethod
|
||||
def from_3_pairs(src_pts, dst_pts) -> 'Affine2DUniMat': return Affine2DMat.from_3_pairs(src_pts, dst_pts).as_uni_mat()
|
||||
|
||||
def invert(self) -> 'Affine2DUniMat': return super().invert().as_uni_mat()
|
||||
#def scaled(self, sw : float, sh: float, tw: float, th: float) -> 'Affine2DUniMat': return super().scaled(sw, sh, tw, th).as_uni_mat()
|
||||
|
||||
def source_scaled_around_center(self, sw : float, sh: float) -> 'Affine2DUniMat':
|
||||
"""
|
||||
produces scaled UniMat around center in source space
|
||||
|
||||
sw, sh source width/height scale
|
||||
"""
|
||||
src_pts = np.float32([(0,0),(1,0),(0,1)])
|
||||
|
||||
dst_pts = self.transform_points(src_pts)
|
||||
|
||||
src_pts = (src_pts-0.5)/(sw,sh)+0.5
|
||||
|
||||
return Affine2DUniMat.from_3_pairs(src_pts, dst_pts)
|
||||
|
||||
def source_translated(self, utw : float, uth: float) -> 'Affine2DUniMat':
|
||||
"""
|
||||
produces translated UniMat in source space
|
||||
|
||||
utw, uth uniform translate values
|
||||
"""
|
||||
src_pts = np.float32([(0,0),(1,0),(0,1)])
|
||||
dst_pts = self.transform_points(src_pts)
|
||||
src_pts += (utw, uth)
|
||||
|
||||
return Affine2DUniMat.from_3_pairs(src_pts, dst_pts)
|
||||
|
||||
|
||||
|
||||
def to_exact_mat(self, sw : float, sh: float, tw: float, th: float) -> 'Affine2DMat':
|
||||
"""
|
||||
calculate exact Affine2DMat using provided source and target sizes
|
||||
|
||||
sw, sh source width/height
|
||||
tw, th target width/height
|
||||
"""
|
||||
return Affine2DMat.from_3_pairs([[0,0],[sw,0],[0,sh]],
|
||||
self.transform_points( [(0,0),(1,0),(0,1)] ) * (tw,th) )
|
||||
|
4
xlib/math/__init__.py
Normal file
4
xlib/math/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
from .Affine2DMat import Affine2DMat, Affine2DUniMat
|
||||
from .math_ import (intersect_two_line, polygon_area, segment_length,
|
||||
segment_to_vector)
|
||||
from .nms import nms
|
46
xlib/math/math_.py
Normal file
46
xlib/math/math_.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
import numpy as np
|
||||
import numpy.linalg as npla
|
||||
|
||||
|
||||
def segment_length(p1 : np.ndarray, p2 : np.ndarray):
|
||||
"""
|
||||
p1 (2,)
|
||||
p2 (2,)
|
||||
"""
|
||||
return npla.norm(p2-p1)
|
||||
|
||||
def segment_to_vector(p1 : np.ndarray, p2 : np.ndarray):
|
||||
"""
|
||||
p1 (2,)
|
||||
p2 (2,)
|
||||
"""
|
||||
x = p2-p1
|
||||
x /= npla.norm(x)
|
||||
return x
|
||||
|
||||
|
||||
def intersect_two_line(a1, a2, b1, b2) -> np.ndarray:
|
||||
"""
|
||||
Returns the point of intersection of the lines (not segments) passing through a2,a1 and b2,b1.
|
||||
a1: [x, y] a point on the first line
|
||||
a2: [x, y] another point on the first line
|
||||
b1: [x, y] a point on the second line
|
||||
b2: [x, y] another point on the second line
|
||||
"""
|
||||
s = np.vstack([a1,a2,b1,b2]) # s for stacked
|
||||
h = np.hstack((s, np.ones((4, 1)))) # h for homogeneous
|
||||
l1 = np.cross(h[0], h[1]) # get first line
|
||||
l2 = np.cross(h[2], h[3]) # get second line
|
||||
x, y, z = np.cross(l1, l2) # point of intersection
|
||||
if z == 0: # lines are parallel
|
||||
return (float('inf'), float('inf'))
|
||||
return np.array( [x/z, y/z], np.float32 )
|
||||
|
||||
def polygon_area(poly : np.ndarray) -> float:
|
||||
"""
|
||||
calculate area of n-vertices polygon with non intersecting edges
|
||||
|
||||
poly np.ndarray (n,2)
|
||||
"""
|
||||
return float( np.abs(np.sum( poly[:,0] * np.roll( poly[:,1], -1 ) - poly[:,1] * np.roll( poly[:,0], -1 ) ) / 2) )
|
||||
|
30
xlib/math/nms.py
Normal file
30
xlib/math/nms.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
import numpy as np
|
||||
|
||||
def nms(x1, y1, x2, y2, scores, thresh):
|
||||
"""
|
||||
Non-Maximum Suppression
|
||||
|
||||
x1,y1,x2,y2,scores np.ndarray of box coords with the same length
|
||||
|
||||
returns indexes of boxes
|
||||
"""
|
||||
keep = []
|
||||
if len(x1) == 0:
|
||||
return keep
|
||||
|
||||
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
|
||||
order = scores.argsort()[::-1]
|
||||
|
||||
keep = []
|
||||
while order.size > 0:
|
||||
i = order[0]
|
||||
keep.append(i)
|
||||
xx_1, yy_1 = np.maximum(x1[i], x1[order[1:]]), np.maximum(y1[i], y1[order[1:]])
|
||||
xx_2, yy_2 = np.minimum(x2[i], x2[order[1:]]), np.minimum(y2[i], y2[order[1:]])
|
||||
|
||||
width, height = np.maximum(0.0, xx_2 - xx_1 + 1), np.maximum(0.0, yy_2 - yy_1 + 1)
|
||||
ovr = width * height / (areas[i] + areas[order[1:]] - width * height)
|
||||
|
||||
inds = np.where(ovr <= thresh)[0]
|
||||
order = order[inds + 1]
|
||||
return keep
|
Loading…
Add table
Add a link
Reference in a new issue