美文网首页
人脸morph

人脸morph

作者: johnzhang123 | 来源:发表于2019-01-16 09:56 被阅读0次

      morph的意思是变形,在landmark和Delaunay Triangulation的基础上,这里介绍一种融合多张脸的变形方法。我们把小布什和奥巴马的脸融合到一起去。

    1. 原理:
        在Delaunay Triangulation之后,将对应三角形仿射变换到target上,并将颜色进行融合。就两张图来说,一种简单的融合方式就是(1 - \alpha) * c1 + \alpha * c2 ,具体步骤如下:
        a). 将两张图标定landmark, 之后再在边角处标上另外几个定位点(正确的定位点越多越好),然后做Delaunay Triangulation:
      obm_xbs_delauney.png
        b). 复制第一幅图像,把第二幅图像上对应的三角形区域通过仿射变换映射过来,将其颜色进行融合,以各占一半比例为例:0.5 * c1 + 0.5 * c2,周边模糊是因为缺少定位点,缺少定位点就会导致仿射映射不准确而重影。而自动检测的landmark只位于正脸部,所以那一部分比较清晰。
      morph.png
    2. 示例代码:
    def rectContains(rect, point) :
        if point[0] < rect[0] :
            return False
        elif point[1] < rect[1] :
            return False
        elif point[0] > rect[0] + rect[2] :
            return False
        elif point[1] > rect[1] + rect[3] :
            return False
        return True
    
    def applyAffineTransform(src, srcTri, dstTri, size) :
        # Given a pair of triangles, find the affine transform.
        warpMat = cv2.getAffineTransform( np.float32(srcTri), np.float32(dstTri) )
        # Apply the Affine Transform just found to the src image
        dst = cv2.warpAffine( src, warpMat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101 )
        return dst
    
    def morphTriangle(img_list, img, t_list, t, alpha) :
        # Find bounding rectangle for each triangle
        r_list = []
        for i in range(len(t_list)):
            r_list.append(cv2.boundingRect(np.float32([t_list[i]])))
        r = cv2.boundingRect(np.float32([t]))
        # Offset points by left top corner of the respective rectangles
        tRect_list = []
        tRect = []
        for i in range(len(t_list)):
            tmp_list = []
            for j in range(0, 3):
                tmp_list.append(((t_list[i][j][0] - r_list[i][0]),(t_list[i][j][1] - r_list[i][1])))
            tRect_list.append(tmp_list)
        for j in range(0, 3):
            tRect.append(((t[j][0] - r[0]),(t[j][1] - r[1])))
        # Get mask by filling triangle
        mask = np.zeros((r[3], r[2], 3), dtype = np.float32)
        cv2.fillConvexPoly(mask, np.int32(tRect), (1.0, 1.0, 1.0), 16, 0);
        # Apply warpImage to small rectangular patches
        imgRect_list = []
        for i in range(len(r_list)):
            imgRect_list.append(img_list[i][r_list[i][1]:r_list[i][1] + r_list[i][3], r_list[i][0]:r_list[i][0] + r_list[i][2]])
        size = (r[2], r[3])
        warpImage_list = []
        for i in range(len(imgRect_list)):
            warpImage_list.append(np.float32(applyAffineTransform(imgRect_list[i], tRect_list[i], tRect, size)))
        # Alpha blend rectangular patches
        imgRect = alpha * sum(warpImage_list[:-1]) + (1 - alpha * (len(warpImage_list) - 1)) * warpImage_list[-1]
            # Copy triangular region of the rectangular patch to the output image
        img[r[1]:r[1]+r[3], r[0]:r[0]+r[2]] = img[r[1]:r[1]+r[3], r[0]:r[0]+r[2]] * ( 1 - mask ) + imgRect * mask
    
    def calculateDelaunayTriangles(rect, points):
        #create subdiv
        subdiv = cv2.Subdiv2D(rect);
        # Insert points into subdiv
        for p in points:
            subdiv.insert(p)
        triangleList = subdiv.getTriangleList();
        delaunayTri = []
        pt = []
        for t in triangleList:
            pt.append((t[0], t[1]))
            pt.append((t[2], t[3]))
            pt.append((t[4], t[5]))
            pt1 = (t[0], t[1])
            pt2 = (t[2], t[3])
            pt3 = (t[4], t[5])
            if rectContains(rect, pt1) and rectContains(rect, pt2) and rectContains(rect, pt3):
                ind = []
                #Get face-points (from 68 face detector) by coordinates
                for j in range(0, 3):
                    for k in range(0, len(points)):
                        if(abs(pt[j][0] - points[k][0]) < 1.0 and abs(pt[j][1] - points[k][1]) < 1.0):
                            ind.append(k)
                # Three points form a triangle. Triangle array corresponds to the file tri.txt in FaceMorph
                if len(ind) == 3:
                    delaunayTri.append((ind[0], ind[1], ind[2]))
            pt = []
        return delaunayTri
    
    img1 = cv2.imread('../jianshu/abm.jpg')
    keypoints1 = get_keypoints(img1)
    img2 = cv2.imread('../jianshu/xbs.jpg')
    keypoints2 = get_keypoints(img2)
    
    alpha = 0.5
    # Read images
    img_list = []
    img_list.append(img1)
    img_list.append(img2)
    points_list = []
    points_list.append(keypoints1)
    points_list.append(keypoints2)
    
    points = []
    for i in range(0, len(points_list[0])):
        x = alpha * sum(t[i][0] for t in points_list)
        y = alpha * sum(t[i][1] for t in points_list)
        points.append((x,y))
    dt = calculateDelaunayTriangles((0, 0, (img_list[0].shape)[1], (img_list[0].shape)[0]), points_list[0])
    imgMorph = np.zeros(img_list[0].shape, dtype = img_list[0].dtype)
    for line in dt:
        x,y,z = line
        x = int(x)
        y = int(y)
        z = int(z)
        t_list = []
        for i in range(len(points_list)):
            t_list.append((points_list[i][x], points_list[i][y], points_list[i][z]))
        t = [points[x], points[y], points[z]]
        # Morph one triangle at a time.
        morphTriangle(img_list, imgMorph, t_list, t, alpha)
    show_img(imgMorph)
    

    相关文章

      网友评论

          本文标题:人脸morph

          本文链接:https://www.haomeiwen.com/subject/pejpdqtx.html