python两张图⽚融合_两张脸融合在⼀起长啥样?Opencv-
Python来告诉你!
1,Image Morphing 介绍
图像融合简单来说,通过把图像设置为不同的透明度,把两张图像融合为⼀张图像(⼀般要求图像需要等尺⼨),公式如下:
可以根据这个公式尝试实现⼀下融合技术,利⽤ OpenCV 的 cv2.addWeighted() 函数,代码如下:
import cv2
import  numpy as np
file_path1 = "E:/data_ceshi/1.jpg"
file_path2 = "E:/data_ceshi/2.jpg"
img1 = cv2.imread(file_path1)
img2 = cv2.imread(file_path2)
morph_img = cv2.addWeighted(img1,0.5,img2,0.5,0)
save_img = np.hstack((img1,morph_img,img2))
cv2.imwrite("E:/data_ceshi/save.jpg",save_img)
cv2.imshow("morph_img",save_img)
cv2.waitKey(0)
这⾥ alpha 设置为 0.5, 最终结果如下图:
左右两边分别为融合之前的两张图⽚,中间为融合结果,看起来⾮常不好,图⽚中脸的部分的确融合了,但是给我们的感觉就是明显的失真效果,太假了
对于上⾯提到的融合想法,若想要达到不错的效果需要对⼈脸区域进⾏对齐操作,⽽这⼀步就需要⽤到之前介绍的技术:⼈脸68个特征点提取,Delaunay 三⾓剖分
2,特征点提取
在做⼈脸对齐时,不仅需要考虑⼈脸部分需要对齐,这⾥也需要考虑图⽚的整体性(例如头发、脖⼦、
肩膀等部位),这⾥除去 dlib 提取 68个特征点之外,⼜加⼊了12个特征点(⼈⼯标记)分别为图像四⾓、四边中点、肩膀处,右⽿边缘、脖⼦等
3,Delaunay 三⾓剖分
三⾓剖分⽬的⽹格化图像脸部区域,⽅便寻特征对应点,为后⾯使⽤仿射变换进⾏对齐操作:
从三⾓剖分图上来看,⼈脸区域轮廓是⾮常相似的,⼈脸融合时需要把脸部每⼀个对应的⼩三⾓区域事先⼀⼀对齐,然后利⽤设置的透明度参数来做最终的效果融合。这样结果就显得不那么失真。
4,Face Morph(脸部融合)
下⾯将脸部融合技术拆解为⼏部分:
1,脸部特长点提取、三⾓剖分(前⾯已经详细介绍了,这⾥就不再⼀⼀展开了),详情参考这篇⽂章:
2,对 1 中的三⾓剖分每个顶点做对应点衔接并记录下来,对应点记录的是三⾓形三顶点的索引数,如下图所⽰:
3,图⽚中对每⼀个三⾓剖分区域做放射变换,⽤到的函数:getAffineTransform() 得到仿射变换矩阵,warpAffine() 进⾏放射变换,最终得到两个变换图像,
4,对 3 中得到的两图像中像素值调整透明度参数,来进⾏图像融合
最终结果如下:
out_img1.jpg
out_img2.jpg
out_img3.jpg
结果来看,脸部区域能够取得不错的结果,但整体来看仍然有很⼤的瑕疵,但是我们可以通过⼿动选择更多特征对应点来改善这种效果,最后附上完整代码
import cv2
import numpy as np
import sys
#Read points from  text file
def readPoints(path):
# Create an array of points
points = []
# Read points
with open(path) as file:
for line in file:
x,y = line.split()
points.append((int(x),int(y)))
return points
# Apply affine tranform calculated using srcTri and sdtTri to src and output an image of size
def applyAffineTransform(src,srcTri,dstTri,size):
#Given a pair of triangles,find the affine transform.
warpMat = AffineTransform(np.float32(srcTri),np.float32(dstTri))
#Apply the Affine Transform just foundto the src image
dst = cv2.warpAffine(src,warpMat,
(size[0],size[1]),None,flags=cv2.INTER_LINEAR,borderMode=cv2.BORDER_REFLECT_101)
return dst
# Warps and alpha blends triangular regions from img1 and img2 to img
def morphTriangle(img1,img2,img,t1,t2,t,alpha):
#Find bounding rectangle for each triangle
r1 = cv2.boundingRect(np.float32([t1]))
r2 = cv2.boundingRect(np.float32([t2]))
r = cv2.boundingRect(np.float32([t]))
# Offset points by left top corner of the respective rectangles
t1Rect = []
t2Rect = []
tRect = []
rectangle函数opencvfor i in range(0,3):
tRect.append(((t[i][0] - r[0]),(t[i][1]-r[1])))
t1Rect.append(((t1[i][0]-r1[0]),(t1[i][1]-r1[1])))
t2Rect.append(((t2[i][0] -r2[0]),(t2[i][1]-r2[1])))
# Get mask by filling triangles
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 patched
img1Rect = img1[r1[1]:r1[1]+r1[3],r1[0]:r1[0]+r1[2]]
img2Rect = img2[r2[1]:r2[1]+r2[3],r2[0]:r2[0]+r2[2]]
size = (r[2],r[3])
warpImage1 = applyAffineTransform(img1Rect,t1Rect,tRect,size)
warpImage2 = applyAffineTransform(img2Rect,t2Rect,tRect,size)
# Alpha blend rectangular patches
imgRect = (1.0-alpha) *warpImage1 +alpha*warpImage2
# Copy triangular region of rectangular patch to tje output image
print(r[1],r[3],r[0],r[2])
print(imgRect.shape)
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 if __name__ =='__main__':
filename1 = "E:/data_ceshi/2.jpg"
filename2 = "E:/data_ceshi/3.jpg"
points_txt1 = "E:/data_"
points_txt2  ="E:/data_"
alpha = 0.5
# Read images
img1 = cv2.imread(filename1)
img2 = cv2.imread(filename2)
# Convertat to float data type
img1 = np.float32(img1)
img2 = np.float32(img2)
# Read array of corresponding points
points1 = readPoints(points_txt1)
points2 = readPoints(points_txt2)
points = []
# Compute weighted average point coordinate
for i in range(0,len(points1)):
x = (1-alpha) *points1[i][0] +alpha *points2[i][0]
y = (1-alpha)*points1[i][1] + alpha*points2[i][1]
points.append((x,y))
imgMorph = np.zeros(img1.shape,dtype = img1.dtype)
# Read triangles
with open("E:/data_") as file:
for line in file:
x,y,z = line.split()
x = int(x)
y = int(y)
z = int(z)
t1 = [points1[x],points1[y],points1[z]]
t2 = [points2[x],points2[y],points2[z]]
t = [points[x],points[y],points[z]]
# Morph one triangle at a time
morphTriangle(img1,img2,imgMorph,t1,t2,t,alpha)
# Display Results
out_img = np.hstack((img1,imgMorph,img2))
cv2.imwrite("E:/data_ceshi/out_img.jpg",out_img)
cv2.imshow("Morphed Face",np.uint8(imgMorph))
cv2.waitKey(0)
5,⼩总结
虽然本次⾯向对象是⼈脸,但相同技术原理也可以运⽤到其他物体上⾯,⽐如把苹果和橘⼦部分融合、⼈脸区域更换等功能,如果有更好的idea 的话,可能会得到意想不到的结果,也可以在下⽅留⾔!
最后⽂章中完整源码和⽂件都已经打包到 Github 上去了,后台回复关键词 ⼈脸融合 即可获取;下⼀篇 将 利⽤ OpenCV  进⾏换脸操作,感兴趣的⼩伙伴们可以提起关注⼀下。