人臉對齊是大多數人臉分析算法中的一個關鍵模塊,在人臉識別、表情識別、人臉建模等領域有着廣泛的應用。人臉對齊獲取圖像中人臉的幾何結構,基於平移、縮放和旋轉得到對齊後的標準人臉。
在歐式幾何中,如果兩個物體具有相同的形狀,或者其中一個物體的形狀與另一個物體的鏡像相同,那麼這兩個物體是相似的。更準確地說,可以通過均勻縮放(放大或縮小)併疊加必要的平移、旋轉和反射來獲得另一個。這意味着任意物體都可以重新縮放、重新定位和反射,以便與另一物體精確重合。如果兩個物體相似,則每個物體都與另一個物體的特定均勻縮放結果一致。
目前大多數人臉對齊方案在定位關鍵點後採用 Umeyama 對齊算法進行處理。例如 insightface 中的 similarTransform,dlib 中的 get_face_chip_details。相似性變換由平移變換、旋轉變換以及尺度變換組合而成。下面以 skimage.transform.SimilarityTransform 爲例進行介紹。
SimilarityTransform
2D 相似性變換。具有以下形式:
X = a0 * x - b0 * y + a1 =
= s * x * cos(rotation) - s * y * sin(rotation) + a1
Y = b0 * x + a0 * y + b1 =
= s * x * sin(rotation) + s * y * cos(rotation) + b1
其中s
是比例因子,齊次變換矩陣爲:
除了旋轉和平移參數外,相似性變換還使用單個縮放因子擴展了歐幾里德變換。
參數:
matrix
:(3,3)數組,可選。齊次變換矩陣。scale
:float,可選。比例因子。rotation
:float,可選。逆時針旋轉角度(以弧度表示)。translation
:(tx, ty)
作爲數組、列表或元組,可選。x,y 方向平移參數。
屬性:
params
:(3,3)數組,齊次變換矩陣。
def __init__(self, matrix=None, scale=None, rotation=None,
translation=None):
params = any(param is not None
for param in (scale, rotation, translation))
if params and matrix is not None:
raise ValueError("You cannot specify the transformation matrix and"
" the implicit parameters at the same time.")
elif matrix is not None:
if matrix.shape != (3, 3):
raise ValueError("Invalid shape of transformation matrix.")
self.params = matrix
elif params:
if scale is None:
scale = 1
if rotation is None:
rotation = 0
if translation is None:
translation = (0, 0)
self.params = np.array([
[math.cos(rotation), - math.sin(rotation), 0],
[math.sin(rotation), math.cos(rotation), 0],
[ 0, 0, 1]
])
self.params[0:2, 0:2] *= scale
self.params[0:2, 2] = translation
else:
# default to an identity transform
self.params = np.eye(3)
estimate
從一組對應點估計變換。可以使用總體最小二乘法確定過定、確定和欠定的參數。源座標和目標座標的數量必須匹配。
參數:
src
:(N,2)數組,源座標。dst
:(N,2)數組,目標座標。
返回值:
success
:布爾型,如果模型估計成功,則返回True
。
self.params = _umeyama(src, dst, True)
return True
scale
@property
def scale(self):
if abs(math.cos(self.rotation)) < np.spacing(1):
# sin(self.rotation) == 1
scale = self.params[1, 0]
else:
scale = self.params[0, 0] / math.cos(self.rotation)
return scale
_umeyama
估算是否具有縮放比例的N-D相似度變換。
估計有或無標度的N-D相似變換。
參數:
src
:(M,N)數組,源座標。dst
:(M,N)數組,目標座標。estimate_scale
:布爾,是否估計比例因子。
返回值:
T
:(N + 1,N + 1),齊次相似性變化矩陣。僅當問題條件不完善時,矩陣纔會包含NaN
值。
參考文獻
- [1] “Least-squares estimation of transformation parameters between two point patterns”, Shinji Umeyama, PAMI 1991, :DOI:
10.1109/34.88573
是 和 的協方差矩陣, 和 分別是 和 的均值向量, 和 是 和 的方差。
num
是點的數量,dim
爲點的座標維度。
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 = dst_demean.T @ src_demean / num
對協方差矩陣 進行奇異值分解 ,其中 。
numpy.linalg.det 計算數組的行列式(determinant)。
numpy.linalg.svd 對數組進行奇異值分解。
S
對應分解的奇異值 。
d
爲行向量,對應公式中的 。
# 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)
numpy.linalg.matrix_rank 使用 SVD 方法返回數組的矩陣秩。
當 時,
當 時,最變換參數爲:
s
臨時保存。
# 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] = U @ V
else:
s = d[dim - 1]
d[dim - 1] = -1
T[:dim, :dim] = U @ np.diag(d) @ V
d[dim - 1] = s
else:
T[:dim, :dim] = U @ np.diag(d) @ V
- 首先計算尺度參數 (公式42);
- 然後計算平移參數 (公式41);
- 最後旋轉參數 和尺度參數 融合到一起。
T
的形式爲:
if estimate_scale:
# Eq. (41) and (42).
scale = 1.0 / src_demean.var(axis=0).sum() * (S @ d)
else:
scale = 1.0
T[:dim, dim] = dst_mean - scale * (T[:dim, :dim] @ src_mean.T)
T[:dim, :dim] *= scale
return T
參考資料:
- What does the “at” (@) symbol do in Python?
- face alignment[Ordinary Procrustes Analysis]
- Similarity (geometry)
- How does dlib face aligment works? #1382
- 傳統算法和深度學習的結合和實踐,解讀與優化 deepfake
- Umeyama算法
- similarity transform matrix in c++ is different from python #481
- C++ using
- 4.3 Planar Graphs
- Find All Cycles (Faces) In a Graph
- Chinese Whispers
- Chinese Whispers - an Efficient Graph Clustering Algorithm and its Application to Natural Language Processing Problems
- Applying Affine transform on an image using dlib
- dlib人臉關鍵點代碼解析
- Get Face Landmarks
- dlib人臉對齊源碼詳解
- dlib 人臉對齊 基本原理
- 對mtcnn的人臉對齊的理解
- Review of similarity transformation and Singular Value Decomposition
- 圖像的等距變換,相似變換,仿射變換,射影變換及其matlab實現
- skimage庫的transform.SimilarityTransform()用法
- How to compute the similarity transformation matrix
- Average Face : OpenCV ( C++ / Python ) Tutorial
- SimilarityTransform
- Aligning Face Images
- Face Alignment
- face alignment algorithm on images
- dlib數據結構matrix
- Dlib源碼解析之一 matrix和array2d和image_view
- Dlib Element Specific Operations
- maketform
- dlib.net/matrix_ex.cpp
- deepfakes/faceswap/lib/umeyama.py
- ethz-asl/maplab/test/end-to-end-common/python/end_to_end_common/umeyama.py
- CarloNicolini/ralign
- Singular Value Decomposition (SVD) tutorial