人脸morph

  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)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容