You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于逆变换的人脸图像变形问题求助(非内置函数实现)

人脸图像逆变换变形问题排查与手动实现方案

核心问题定位

  • 顶点顺序不匹配:Delaunay三角网的三角顶点必须在源图和目标图中保持严格一致的顺时针/逆时针顺序,否则变换矩阵会产生镜像或扭曲。
  • 行列坐标混淆:图像的像素坐标是(行,列)对应(y,x),但变换计算通常用(x,y),如果混用会导致整体偏移或变形。
  • 未填充像素:逆变换时若未遍历目标图所有像素,或三角区域判断错误,会出现空白区域。

手动逆变换实现步骤

1. 统一三角顶点顺序

遍历所有三角对,将源三角和目标三角统一调整为顺时针顺序:

def is_clockwise(points):
    # points格式:[(x1,y1), (x2,y2), (x3,y3)]
    val = (points[1][0]-points[0][0])*(points[1][1]+points[0][1]) + \
          (points[2][0]-points[1][0])*(points[2][1]+points[1][1]) + \
          (points[0][0]-points[2][0])*(points[0][1]+points[2][1])
    return val < 0

# 遍历三角对,统一顺序
for src_tri, dst_tri in zip(source_triangles, dest_triangles):
    if not is_clockwise(src_tri):
        src_tri = src_tri[::-1]
    if not is_clockwise(dst_tri):
        dst_tri = dst_tri[::-1]

2. 手动计算仿射变换矩阵及逆矩阵

基于3组对应点构建线性方程组求解变换矩阵,再求逆用于逆变换:

import numpy as np

def compute_affine_matrix(src_pts, dst_pts):
    x0, y0 = src_pts[0]
    x1, y1 = src_pts[1]
    x2, y2 = src_pts[2]
    
    X0, Y0 = dst_pts[0]
    X1, Y1 = dst_pts[1]
    X2, Y2 = dst_pts[2]
    
    # 构建线性方程组
    A = [
        [x0, y0, 1, 0, 0, 0],
        [0, 0, 0, x0, y0, 1],
        [x1, y1, 1, 0, 0, 0],
        [0, 0, 0, x1, y1, 1],
        [x2, y2, 1, 0, 0, 0],
        [0, 0, 0, x2, y2, 1]
    ]
    B = [X0, Y0, X1, Y1, X2, Y2]
    
    coeffs = np.linalg.solve(A, B)
    a, b, c, d, e, f = coeffs
    
    # 返回3x3齐次变换矩阵
    return np.array([
        [a, b, c],
        [d, e, f],
        [0, 0, 1]
    ])

# 对每个三角对计算逆变换矩阵
inv_matrices = []
for src_tri, dst_tri in zip(source_triangles, dest_triangles):
    affine_mat = compute_affine_matrix(src_tri, dst_tri)
    inv_matrices.append(np.linalg.inv(affine_mat))

3. 逆变换遍历与像素填充

遍历目标图每个像素,判断所属三角后通过逆矩阵映射回源图取像素:

def inverse_warp(source_img, dest_img, dest_triangles, inv_matrices):
    h, w = dest_img.shape[:2]
    # 遍历目标图所有像素
    for y in range(h):
        for x in range(w):
            # 检查当前像素(x,y)属于哪个目标三角
            for idx, dst_tri in enumerate(dest_triangles):
                if point_in_triangle((x,y), dst_tri):
                    # 用逆矩阵映射回源图坐标
                    src_x, src_y, _ = inv_matrices[idx] @ np.array([x, y, 1])
                    src_x = int(round(src_x))
                    src_y = int(round(src_y))
                    # 确保坐标在源图范围内
                    if 0 <= src_x < source_img.shape[1] and 0 <= src_y < source_img.shape[0]:
                        dest_img[y, x] = source_img[src_y, src_x]
                    break
    return dest_img

# 重心坐标法判断点是否在三角形内
def point_in_triangle(p, tri):
    A, B, C = tri
    v0 = (C[0]-A[0], C[1]-A[1])
    v1 = (B[0]-A[0], B[1]-A[1])
    v2 = (p[0]-A[0], p[1]-A[1])
    
    dot00 = v0[0]*v0[0] + v0[1]*v0[1]
    dot01 = v0[0]*v1[0] + v0[1]*v1[1]
    dot02 = v0[0]*v2[0] + v0[1]*v2[1]
    dot11 = v1[0]*v1[0] + v1[1]*v1[1]
    dot12 = v1[0]*v2[0] + v1[1]*v2[1]
    
    denom = dot00 * dot11 - dot01 * dot01
    if denom == 0:
        return False
    u = (dot11 * dot02 - dot01 * dot12) / denom
    v = (dot00 * dot12 - dot01 * dot02) / denom
    
    return (u >= 0) and (v >= 0) and (u + v <= 1)

关键注意事项

  • 所有三角顶点必须统一用(x,y)格式(对应图像的列、行),禁止与(y,x)混用。
  • 逆变换必须遍历目标图的每个像素,而非源图,否则会出现未填充区域。
  • 由于Delaunay三角不会出现共线点,变换矩阵必然可逆,无需额外判断。

内容的提问来源于stack exchange,提问作者darryl247

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 07:20:39