人脸对齐中的相似性变换

人脸对齐是大多数人脸分析算法中的一个关键模块,在人脸识别、表情识别、人脸建模等领域有着广泛的应用。人脸对齐获取图像中人脸的几何结构,基于平移、缩放和旋转得到对齐后的标准人脸。

在欧式几何中,如果两个物体具有相同的形状,或者其中一个物体的形状与另一个物体的镜像相同,那么这两个物体是相似的。更准确地说,可以通过均匀缩放(放大或缩小)并叠加必要的平移、旋转和反射来获得另一个。这意味着任意物体都可以重新缩放、重新定位和反射,以便与另一物体精确重合。如果两个物体相似,则每个物体都与另一个物体的特定均匀缩放结果一致。

目前大多数人脸对齐方案在定位关键点后采用 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是比例因子,齐次变换矩阵为:
[ a 0 b 0 a 1 b 0 a 0 b 1 0 0 1 ] \begin{bmatrix} a_0 & b_0 & a_1 \\ b_0 & a_0 & b_1 \\ 0 & 0 & 1 \end{bmatrix} a0b00b0a00a1b11
除了旋转和平移参数外,相似性变换还使用单个缩放因子扩展了欧几里德变换。
参数:

  • matrix:(3,3)数组,可选。齐次变换矩阵。
  • scale:float,可选。比例因子。
  • rotation:float,可选。逆时针旋转角度(以弧度表示)。
  • translation(tx, ty)作为数组、列表或元组,可选。x,y 方向平移参数。

属性:

  • params:(3,3)数组,齐次变换矩阵。

[ s cos ⁡ ( θ ) sin ⁡ ( θ ) t x sin ⁡ ( θ ) s cos ⁡ ( θ ) t y 0 0 1 ] \begin{bmatrix} s \cos(\theta) & \sin(\theta) & t_x\\ \sin(\theta) & s\cos(\theta) & t_y\\ 0 & 0 & 1 \end{bmatrix} scos(θ)sin(θ)0sin(θ)scos(θ)0txty1

    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

μ x = 1 n ∑ i = 1 n x i (34) μ y = 1 n ∑ i = 1 n y i (35) σ x 2 = 1 n ∑ i = 1 n ∥ x i − μ x ∥ 2 (36) σ y 2 = 1 n ∑ i = 1 n ∥ y i − μ y ∥ 2 (37) Σ x y = 1 n ∑ i = 1 n ( y i − μ y ) ( x i − μ x ) ⊤ (38) \begin{aligned} \mu_x &= \frac{1}{n}\sum_{i=1}^n x_i \qquad\qquad\qquad \text{(34)}\\ \mu_y &= \frac{1}{n}\sum_{i=1}^n y_i \qquad\qquad\qquad \text{(35)}\\ \sigma_x^2 &= \frac{1}{n}\sum_{i=1}^n \| x_i-\mu_x\|^2 \qquad\qquad \text{(36)}\\ \sigma_y^2 &= \frac{1}{n}\sum_{i=1}^n \| y_i-\mu_y\|^2 \qquad\qquad \text{(37)}\\ \Sigma_{xy} &= \frac{1}{n}\sum_{i=1}^n (y_i-\mu_y)(x_i-\mu_x)^\top \quad \text{(38)} \end{aligned} μxμyσx2σy2Σxy=n1i=1nxi(34)=n1i=1nyi(35)=n1i=1nxiμx2(36)=n1i=1nyiμy2(37)=n1i=1n(yiμy)(xiμx)(38)
Σ x y \Sigma_{xy} Σxy X X X Y Y Y 的协方差矩阵, μ x \mu_x μx μ y \mu_y μy 分别是 X X X Y Y Y 的均值向量, σ x 2 \sigma_x^2 σx2 σ y 2 \sigma_y^2 σy2 X X X Y Y Y 的方差。
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

对协方差矩阵 Σ x y \Sigma_{xy} Σxy 进行奇异值分解 U D V ⊤ UDV^\top UDV,其中 D = d i a g ( d i ) , d 1 ≥ d 2 ≥ ⋯ ≥ d m ≥ 0 D=\mathrm{diag}(d_i), d_1 \ge d_2 \ge\cdots \ge d_m \ge 0 D=diag(di),d1d2dm0

S = { I if  d e t ( Σ x y ) ≥ 0 d i a g ( 1 , 1 , … , 1 , − 1 ) if  d e t ( Σ x y ) < 0 (39) S = \begin{cases} I &\text{if } \mathrm{det}(\Sigma_{xy}) \ge 0 \\ \mathrm{diag}(1,1,\dots,1,-1) &\text{if } \mathrm{det}(\Sigma_{xy}) < 0 \end{cases}\quad \text{(39)} S={Idiag(1,1,,1,1)if det(Σxy)0if det(Σxy)<0(39)
numpy.linalg.det 计算数组的行列式(determinant)。
numpy.linalg.svd 对数组进行奇异值分解。
S对应分解的奇异值 d i a g ( d i ) \mathrm{diag}(d_i) diag(di)
d为行向量,对应公式中的 S S S

    # 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 方法返回数组的矩阵秩。
rank ( Σ x y ) = m \text{rank}{(\Sigma_{xy})}= m rank(Σxy)=m 时,
S = { I if  d e t ( U ) d e t ( V ) = 1 d i a g ( 1 , 1 , … , 1 , − 1 ) if  d e t ( U ) d e t ( V ) = − 1 (43) S = \begin{cases} I &\text{if } \mathrm{det}(U)\mathrm{det}(V)=1 \\ \mathrm{diag}(1,1,\dots,1,-1) &\text{if } \mathrm{det}(U)\mathrm{det}(V)=-1 \end{cases} \quad \text{(43)} S={Idiag(1,1,,1,1)if det(U)det(V)=1if det(U)det(V)=1(43)
rank ( Σ x y ) > m \text{rank}{(\Sigma_{xy})}> m rank(Σxy)>m 时,最变换参数为:
R = U S V ⊤ (40) t = μ y − c R μ x (41) c = 1 σ x 2 t r ( D S ) (42) \begin{aligned} R &= USV^\top \qquad \text{(40)}\\ t &= \mu_y - cR\mu_x \quad \text{(41)}\\ c &= \frac{1}{\sigma_x^2}\mathrm{tr}(DS) \quad \text{(42)} \end{aligned} Rtc=USV(40)=μycRμx(41)=σx21tr(DS)(42)

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
  • 首先计算尺度参数 c c c (公式42);
  • 然后计算平移参数 t t t(公式41);
  • 最后旋转参数 R R R 和尺度参数 c c c 融合到一起。

T的形式为:
[ s cos ⁡ ( θ ) sin ⁡ ( θ ) t x sin ⁡ ( θ ) s cos ⁡ ( θ ) t y 0 0 1 ] \begin{bmatrix} s \cos(\theta) & \sin(\theta) & t_x\\ \sin(\theta) & s\cos(\theta) & t_y\\ 0 & 0 & 1 \end{bmatrix} scos(θ)sin(θ)0sin(θ)scos(θ)0txty1

    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

参考资料:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值