sinx/x
https://blog.csdn.net/jacke121/article/details/79846442
这个也能旋转:
https://github.com/Hangz-nju-cuhk/Rotate-and-Render
3D人脸旋转
吴尚哲的 CVPR 最佳论文:无需任何监督,即可重建三维图像
Unsupervised Learning of Probably Symmetric Deformable 3D Objects from Images in the Wild.
对称可变形三维物体的无监督学习
论文链接:https://arxiv.org/abs/1911.11130
代码地址:https://github.com/elliottwu/unsup3d
项目地址:https://elliottwu.com/projects/unsup3d/
Demo 地址:http://www.robots.ox.ac.uk/~vgg/blog/unsupervised-learning-of-probably-symmetric-deformable-3d-objects-from-images-in-the-wild.html
https://blog.csdn.net/zb1165048017/article/details/104204035
博客源码
链接: https://pan.baidu.com/s/18z2b6Sut6qFecOpGqNc8YA
提取码: ad77
https://blog.csdn.net/muyouhang/article/details/103649301
自动化所作品, 解决极端姿态下(如侧脸),一些特征点变了不可见,不同姿态下的人脸表观也存在巨大差异使得关键点定位困难等问题,本文提出一种基于3D人脸形状的定位方法3DDFA,算法框架为:
(1) 输入为100x100的RGB图像和PNCC (Projected Normalized Coordinate Code) 特征,PNCC特征的计算与当前形状相关,可以反映当前形状的信息;算法的输出为3D人脸形状模型参数
(2) 使用卷积神经网络拟合从输入到输出的映射函数,网络包含4个卷积层,3个pooling层和2个全连接层
通过级联多个卷积神经网络直至在训练集上收敛,PNCC特征会根据当前预测的人脸形状更新,并作为下一级卷积神经网络的输入。
(3) 此外,卷积神经网络的损失函数也做了精心的设计,通过引入权重,让网络优先拟合重要的形状参数,如尺度、旋转和平移;当人脸形状接近ground truth时,再考虑拟合其他形状参数
实验证明该损失函数可以提升定位模型的精度。由于参数化形状模型会限制人脸形状变形的能力,作者在使用3DDFA拟合之后,抽取HOG特征作为输入,使用线性回归来进一步提升2D特征点的定位精度。
训练3DDFA模型,需要大量的多姿态人脸样本。为此,作者基于已有的数据集如300W,利用3D信息虚拟生成不同姿态下的人脸图像,核心思想为:先预测人脸图像的深度信息,通过3D旋转来生成不同姿态下的人脸图像
链接(含源码)
http://www.greenxf.com/soft/285868.html
执行比较慢
import numpy as np
import os
from skimage.transform import estimate_transform, warp
import cv2
from predictor import PosPrediction
## 预检测人脸框或者关键点,目的是裁剪人脸
cas = cv2.CascadeClassifier('./Data/cv-data/haarcascade_frontalface_alt2.xml')
img = cv2.imread('./images/zly.jpg')
img = cv2.imread('./images/000001.jpg')
img_gray= cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
faces = cas.detectMultiScale(img_gray,2,3,0,(30,30))
bbox = np.array([faces[0,0],faces[0,1],faces[0,0]+faces[0,2],faces[0,1]+faces[0,3]])
left = bbox[0]; top = bbox[1]; right = bbox[2]; bottom = bbox[3]
old_size = (right - left + bottom - top)/2
center = np.array([right - (right - left) / 2.0, bottom - (bottom - top) / 2.0])
size = int(old_size*1.6)
src_pts = np.array([[center[0]-size/2, center[1]-size/2],
[center[0] - size/2, center[1]+size/2],
[center[0]+size/2, center[1]-size/2]])
DST_PTS = np.array([[0,0], [0,255], [255, 0]])
tform = estimate_transform('similarity', src_pts, DST_PTS)
img = img/255.
cropped_img = warp(img, tform.inverse, output_shape=(256, 256))
pos_predictor = PosPrediction(256, 256)
pos_predictor.restore('./Data/net-data/256_256_resfcn256_weight')
cropped_pos = pos_predictor.predict(cropped_img) #网络推断
#将裁剪图的结果重新调整
cropped_vertices = np.reshape(cropped_pos, [-1, 3]).T
z = cropped_vertices[2,:].copy()/tform.params[0,0]
cropped_vertices[2,:] = 1
vertices = np.dot(np.linalg.inv(tform.params), cropped_vertices)
vertices = np.vstack((vertices[:2,:], z))
pos = np.reshape(vertices.T, [256, 256, 3])
## 人脸关键点
def draw_kps(img,kps,point_size=2):
img = np.array(img*255,np.uint8)
for i in range(kps.shape[0]):
cv2.circle(img,(int(kps[i,0]),int(kps[i,1])),point_size,(0,255,0),-1)
return img
uv_kpt_ind = np.loadtxt('./Data/uv-data/uv_kpt_ind.txt').astype(np.int32)
face_kps = pos[uv_kpt_ind[1,:],uv_kpt_ind[0,:],:]
cv2.imshow('aaaa3',draw_kps(img.copy(),face_kps))
cv2.waitKey(1)
face_ind = np.loadtxt('./Data/uv-data/face_ind.txt').astype(np.int32)
all_vertices = np.reshape(pos, [256*256, -1])
vertices = all_vertices[face_ind, :]
cv2.imshow("xuan",draw_kps(img.copy(),vertices[:,:2],1))
cv2.waitKey(1)
texture = cv2.remap(img, pos[:,:,:2].astype(np.float32), None, interpolation=cv2.INTER_NEAREST, borderMode=cv2.BORDER_CONSTANT,borderValue=(0))
#找到每个三角形每个顶点的肤色
triangles = np.loadtxt('./Data/uv-data/triangles.txt').astype(np.int32)
all_colors = np.reshape(texture, [256*256, -1])
colors = all_colors[face_ind, :]
print(vertices.shape) # texutre每个像素对应的3D坐标
print(triangles.shape) #每个三角网格对应的像素索引
print(colors.shape) #每个三角形的颜色
#获取三角形每个顶点的depth,平均值作为三角形高度
tri_depth = (vertices[triangles[:,0],2 ] + vertices[triangles[:,1],2] + vertices[triangles[:,2],2])/3.
#获取三角形每个顶点的color,平均值作为三角形颜色
tri_tex = (colors[triangles[:,0] ,:] + colors[triangles[:,1],:] + colors[triangles[:,2],:])/3.
tri_tex = tri_tex*255
img_3D = np.zeros_like(img,dtype=np.uint8)
for i in range(triangles.shape[0]):
cnt = np.array([(vertices[triangles[i,0],0],vertices[triangles[i,0],1]),
(vertices[triangles[i,1],0],vertices[triangles[i,1],1]),
(vertices[triangles[i,2],0],vertices[triangles[i,2],1])],dtype=np.int32)
img_3D = cv2.drawContours(img_3D,[cnt],0,tri_tex[i],-1)
cv2.imshow("aaa1",img_3D/255.0)
cv2.waitKey(1)
# 找到旋转矩阵,参考https://github.com/YadiraF/face3d
def angle2matrix(angles):
x, y, z = np.deg2rad(angles[0]), np.deg2rad(angles[1]), np.deg2rad(angles[2])
# x
Rx=np.array([[1, 0, 0],
[0, np.math.cos(x), -np.math.sin(x)],
[0, np.math.sin(x), np.math.cos(x)]])
# y
Ry=np.array([[ np.math.cos(y), 0, np.math.sin(y)],
[0, 1, 0],
[-np.math.sin(y), 0, np.math.cos(y)]])
# z
Rz=np.array([[np.math.cos(z), -np.math.sin(z), 0],
[np.math.sin(z), np.math.cos(z), 0],
[0, 0, 1]])
R=Rz.dot(Ry.dot(Rx))
return R.astype(np.float32)
trans_mat = angle2matrix((0,15,0))
# 旋转坐标
rotated_vertices = vertices.dot(trans_mat.T)
# 把图像拉到画布上
ori_x = np.min(vertices[:,0])
ori_y = np.min(vertices[:,1])
rot_x = np.min(rotated_vertices[:,0])
rot_y = np.min(rotated_vertices[:,1])
shift_x = ori_x-rot_x
shift_y = ori_y-rot_y
rotated_vertices[:,0]=rotated_vertices[:,0]+shift_x
rotated_vertices[:,1]=rotated_vertices[:,1]+shift_y
img_3D = np.zeros_like(img,dtype=np.uint8)
mask = np.zeros_like(img,dtype=np.uint8)
fill_area=0
for i in range(triangles.shape[0]):
cnt = np.array([(rotated_vertices[triangles[i,0],0],rotated_vertices[triangles[i,0],1]),
(rotated_vertices[triangles[i,1],0],rotated_vertices[triangles[i,1],1]),
(rotated_vertices[triangles[i,2],0],rotated_vertices[triangles[i,2],1])],dtype=np.int32)
mask = cv2.drawContours(mask,[cnt],0,(255,255,255),-1)
if(np.sum(mask[...,0])>fill_area):
fill_area = np.sum(mask[...,0])
img_3D = cv2.drawContours(img_3D,[cnt],0,tri_tex[i],-1)
cv2.imshow('xuanzhuan',img_3D)
cv2.waitKey()
# plt.imshow(img_3D)