做网站是要收费的吗,网站架构的组成部分,人寿保险网站,wordpress看文1.一个开源的可以运行的项目#xff08;face3d/README.md at master yfeng95/face3d GitHub#xff09;
在配置好环境后#xff0c;让我们一个一个py文件运行它#xff08;我将给出中文注释#xff09; 1#xff09;1_pipeline.py 将一个3d头像的mat文件转换为jpg…1.一个开源的可以运行的项目face3d/README.md at master · yfeng95/face3d · GitHub
在配置好环境后让我们一个一个py文件运行它我将给出中文注释 11_pipeline.py 将一个3d头像的mat文件转换为jpg文件 import os import sys import numpy as np import scipy.io as sio from skimage import io import matplotlib.pyplot as plt # 将项目路径添加到系统路径中 sys.path.append(..) import face3d from face3d import mesh # 1. 加载网格数据 C sio.loadmat(Data/example1.mat) vertices C[vertices] colors C[colors] triangles C[triangles] colors colors / np.max(colors) # 归一化颜色值 # 2. 变换顶点缩放、旋转、平移 # 缩放目标大小为180 s 180 / (np.max(vertices[:, 1]) - np.min(vertices[:, 1])) # 旋转30度 R mesh.transform.angle2matrix([0, 30, 0]) # 平移中心为[0, 0] t [0, 0, 0] transformed_vertices mesh.transform.similarity_transform(vertices, s, R, t) # 3. 添加光照 # 设置光源 light_positions np.array([[-128, -128, 300]]) light_intensities np.array([[1, 1, 1]]) lit_colors mesh.light.add_light(transformed_vertices, triangles, colors, light_positions, light_intensities) # 4. 变换顶点相机变换和投影 # 从世界坐标系转换到相机坐标系 camera_vertices mesh.transform.lookat_camera(transformed_vertices, eye[0, 0, 200], atnp.array([0, 0, 0]), upNone) # 正交投影 projected_vertices mesh.transform.orthographic_project(camera_vertices) # 5. 渲染生成2D图像 # 设置图像高度和宽度 h w 256 # 转换为图像坐标 image_vertices mesh.transform.to_image(projected_vertices, h, w) # 渲染 rendering mesh.render.render_colors(image_vertices, triangles, lit_colors, h, w) # 保存渲染结果 save_folder results/pipeline if not os.path.exists(save_folder): os.mkdir(save_folder) io.imsave(f{save_folder}/rendering2222.jpg, rendering) # 显示网格可选 # mesh.vis.plot_mesh(camera_vertices, triangles) # plt.show() #这里的mat文件就是特定示例的数据的MATLAB文件在这里是一个人头像的mat文件
注释和说明 步骤 1加载网格数据 使用 scipy.io.loadmat 加载来自 Data/example1.mat 文件的顶点、颜色和三角形数据。 步骤 2变换顶点 计算缩放比例 s使得顶点在 y 轴上的范围缩放到目标大小 180。根据角度 [0, 30, 0] 创建旋转矩阵 R。应用 similarity_transform 函数对顶点进行缩放、旋转和平移变换。 步骤 3添加光照 定义单个光源的位置和强度。使用 add_light 函数为顶点添加光照效果生成 lit_colors。 步骤 4变换顶点相机变换和投影 使用 lookat_camera 函数将顶点从世界坐标系转换到相机坐标系。使用 orthographic_project 函数对相机坐标系中的顶点进行正交投影。 步骤 5渲染生成2D图像 设定图像的高度和宽度为 256 像素。将投影后的顶点转换为图像坐标。使用 render_colors 函数基于三角形网格和光照处理后的颜色数据生成最终的渲染图像 rendering。 22_3dmm.py 3D可变形模型示例 3DMM参数 -- 网格 拟合: 2D图像 3DMM - 3D人脸 import os, sys import subprocess import numpy as np import scipy.io as sio from skimage import io from time import time import matplotlib.pyplot as plt # 添加上一级目录到系统路径以便导入face3d库 sys.path.append(..) import face3d from face3d import mesh from face3d.morphable_model import MorphabelModel # --------------------- 前向传播参数(形状表情姿势) -- 3D对象 -- 2D图像 --------------- # --- 1. 加载模型 bfm MorphabelModel(Data/BFM/Out/BFM.mat) print(初始化BFM模型成功) # --- 2. 生成人脸网格顶点(表示形状)和颜色(表示纹理) sp bfm.get_shape_para(random) # 获取随机形状参数 ep bfm.get_exp_para(random) # 获取随机表情参数 vertices bfm.generate_vertices(sp, ep) # 生成顶点 tp bfm.get_tex_para(random) # 获取随机纹理参数 colors bfm.generate_colors(tp) # 生成颜色 colors np.minimum(np.maximum(colors, 0), 1) # 将颜色限制在0到1之间 # --- 3. 将顶点转换到合适的位置 s 8e-04 # 缩放系数 angles [10, 30, 20] # 旋转角度 t [0, 0, 0] # 平移向量 transformed_vertices bfm.transform(vertices, s, angles, t) # 变换顶点 projected_vertices transformed_vertices.copy() # 使用标准相机和正交投影 # --- 4. 渲染(3D对象 -- 2D图像) # 设置渲染属性 h w 256; c 3 # 图像高度、宽度和通道数 image_vertices mesh.transform.to_image(projected_vertices, h, w) # 将顶点转换到图像坐标系 image mesh.render.render_colors(image_vertices, bfm.triangles, colors, h, w) # 渲染颜色图像 # -------------------- 反向传播 2D图像点和对应的3D顶点索引 -- 参数(姿势, 形状, 表情) ------ ## 只使用68个关键点进行拟合 x projected_vertices[bfm.kpt_ind, :2] # 2D关键点可以从图像中检测到 X_ind bfm.kpt_ind # 3DMM中关键点的索引固定的 # 拟合 fitted_sp, fitted_ep, fitted_s, fitted_angles, fitted_t bfm.fit(x, X_ind, max_iter 3) # 拟合形状、表情、缩放、角度和平移参数 # 验证拟合参数 fitted_vertices bfm.generate_vertices(fitted_sp, fitted_ep) # 生成拟合后的顶点 transformed_vertices bfm.transform(fitted_vertices, fitted_s, fitted_angles, fitted_t) # 变换顶点 image_vertices mesh.transform.to_image(transformed_vertices, h, w) # 将顶点转换到图像坐标系 fitted_image mesh.render.render_colors(image_vertices, bfm.triangles, colors, h, w) # 渲染拟合后的图像 # ------------- 打印和显示结果 print(姿势真实值: \n, s, angles[0], angles[1], angles[2], t[0], t[1]) print(姿势拟合值: \n, fitted_s, fitted_angles[0], fitted_angles[1], fitted_angles[2], fitted_t[0], fitted_t[1]) # 保存结果图像 save_folder results/3dmm if not os.path.exists(save_folder): os.mkdir(save_folder) io.imsave({}/generated.jpg.format(save_folder), image) # 保存生成的图像 io.imsave({}/fitted.jpg.format(save_folder), fitted_image) # 保存拟合的图像 ### ----------------- 可视化拟合过程 # 拟合 fitted_sp, fitted_ep, fitted_s, fitted_angles, fitted_t bfm.fit(x, X_ind, max_iter 3, isShow True) # 拟合并显示过程 # 验证拟合参数 for i in range(fitted_sp.shape[0]): fitted_vertices bfm.generate_vertices(fitted_sp[i], fitted_ep[i]) # 生成拟合后的顶点 transformed_vertices bfm.transform(fitted_vertices, fitted_s[i], fitted_angles[i], fitted_t[i]) # 变换顶点 image_vertices mesh.transform.to_image(transformed_vertices, h, w) # 将顶点转换到图像坐标系 fitted_image mesh.render.render_colors(image_vertices, bfm.triangles, colors, h, w) # 渲染拟合后的图像 io.imsave({}/show_{:02d}.jpg.format(save_folder, i), fitted_image) # 保存每一步的图像 # 生成GIF动画 options -delay 20 -loop 0 -layers optimize # GIF动画选项需要ImageMagick subprocess.call(convert {} {}/show_*.jpg {}.format(options, save_folder, save_folder /3dmm.gif), shellTrue) # 调用ImageMagick生成GIF subprocess.call(rm {}/show_*.jpg.format(save_folder), shellTrue) # 删除临时图像文件 这段代码是一个关于3D可变形模型3DMM的示例它展示了如何利用该模型从参数形状、表情、姿势生成3D人脸并将其渲染成2D图像。它与之前提到的 1_pipeline.py 文件是相关联的因为它们都涉及到了使用 face3d 库中的模型和函数来处理和渲染3D人脸数据。
3DMM3D Morphable Model是一种用于生成和表示3D人脸模型的统计模型。它基于统计学原理通过对大量3D人脸数据进行分析和学习构建出一个能够表示多种不同人脸形状、表情和纹理的参数化模型。
主要步骤和功能解析 导入和设置 导入必要的库和模块包括 os、sys、subprocess、numpy、scipy.io、skimage.io 和 matplotlib.pyplot。将上级目录添加到系统路径以便能够导入 face3d 库和其子模块。 初始化和加载模型 使用 MorphableModel 类加载预训练的3DMM模型文件 BFM.mat。通过随机参数获取初始的形状参数 (sp)、表情参数 (ep) 和纹理参数 (tp)。 生成人脸网格 使用获取的随机参数生成顶点 (vertices) 和颜色信息 (colors)。将颜色值限制在 [0, 1] 范围内。 顶点变换和投影 定义缩放系数 s 和旋转角度 angles。使用 transform 方法对顶点进行变换然后复制到 projected_vertices表示标准相机和正交投影后的顶点位置。 渲染生成的3D人脸 设定图像的大小为 256x256 像素。将变换后的顶点投影到图像坐标系。使用 render_colors 方法根据三角形网格和颜色信息生成渲染图像 image。 拟合过程 从生成的2D图像中提取关键点坐标 x 和对应的3DMM关键点索引 X_ind。使用 fit 方法对关键点进行拟合得到拟合后的形状参数 (fitted_sp)、表情参数 (fitted_ep)、缩放、旋转角度和平移参数。 验证拟合参数 根据拟合后的参数生成新的顶点并进行变换。将变换后的顶点投影到图像坐标系并渲染成图像保存为 fitted_image。 结果保存和可视化 打印并比较真实的姿势参数和拟合后的参数。将生成的图像和拟合后的图像保存到指定的文件夹 results/3dmm 中。 可视化拟合过程 如果需要可以将拟合过程中每一步的图像保存下来生成一个 GIF 动画展示拟合的过程。 33_transform.py 3D对象变换和相机模型示例 import os, sys import numpy as np import math import scipy.io as sio from skimage import io from time import time import subprocess # 添加上一级目录到系统路径以便导入face3d库 sys.path.append(..) import face3d from face3d import mesh # 定义变换测试函数 def transform_test(vertices, obj, camera, h256, w256): Args: vertices: 顶点数组 obj: 包含对象变换参数的字典 camera: 包含相机参数的字典 h: 图像高度 w: 图像宽度 # 获取旋转矩阵 R mesh.transform.angle2matrix(obj[angles]) # 相似变换旋转、缩放、平移 transformed_vertices mesh.transform.similarity_transform(vertices, obj[s], R, obj[t]) # 如果是正交投影 if camera[proj_type] orthographic: projected_vertices transformed_vertices image_vertices mesh.transform.to_image(projected_vertices, h, w) else: # 世界坐标系转换到相机坐标系 camera_vertices mesh.transform.lookat_camera(transformed_vertices, camera[eye], camera[at], camera[up]) # 相机坐标系转换到图像坐标系投影 projected_vertices mesh.transform.perspective_project(camera_vertices, camera[fovy], nearcamera[near], farcamera[far]) # 转换为图像坐标 image_vertices mesh.transform.to_image(projected_vertices, h, w, True) # 渲染图像 rendering mesh.render.render_colors(image_vertices, triangles, colors, h, w) rendering np.minimum(np.maximum(rendering, 0), 1) return rendering # 加载网格数据 C sio.loadmat(Data/example1.mat) vertices C[vertices] colors C[colors] triangles C[triangles] colors colors / np.max(colors) # 将顶点的中心移动到[0, 0, 0] vertices vertices - np.mean(vertices, 0)[np.newaxis, :] # 创建保存文件夹 save_folder results/transform if not os.path.exists(save_folder): os.mkdir(save_folder) options -delay 10 -loop 0 -layers optimize # GIF选项需要安装ImageMagick # 开始处理 obj {} camera {} # 将面部模型缩放到真实大小约18cm scale_init 180 / (np.max(vertices[:, 1]) - np.min(vertices[:, 1])) # 1. 固定相机模型标准相机和正交投影改变对象位置 camera[proj_type] orthographic # 缩放 for factor in np.arange(0.5, 1.2, 0.1): obj[s] scale_init * factor obj[angles] [0, 0, 0] obj[t] [0, 0, 0] image transform_test(vertices, obj, camera) io.imsave({}/1_1_{:.2f}.jpg.format(save_folder, factor), image) # 旋转角度 for i in range(3): for angle in np.arange(-50, 51, 10): obj[s] scale_init obj[angles] [0, 0, 0] obj[angles][i] angle obj[t] [0, 0, 0] image transform_test(vertices, obj, camera) io.imsave({}/1_2_{}_{}.jpg.format(save_folder, i, angle), image) # 使用ImageMagick将图片转换为GIF subprocess.call(convert {} {}/1_*.jpg {}.format(options, save_folder, save_folder /obj.gif), shellTrue) # 2. 固定对象位置中心为[0,0,0]正面姿态改变相机位置和方向使用透视投影固定视场角 obj[s] scale_init obj[angles] [0, 0, 0] obj[t] [0, 0, 0] camera[proj_type] perspective camera[at] [0, 0, 0] camera[near] 1000 camera[far] -100 camera[fovy] 30 camera[up] [0, 1, 0] # 沿Z轴移动相机从远到近始终看向面部中心 for p in np.arange(500, 250-1, -40): camera[eye] [0, 0, p] image transform_test(vertices, obj, camera) io.imsave({}/2_eye_1_{}.jpg.format(save_folder, 1000-p), image) # 沿Y轴移动相机从下到上始终看向面部中心 for p in np.arange(-300, 301, 60): camera[eye] [0, p, 250] image transform_test(vertices, obj, camera) io.imsave({}/2_eye_2_{}.jpg.format(save_folder, p/6), image) # 沿X轴移动相机从左到右始终看向面部中心 for p in np.arange(-300, 301, 60): camera[eye] [p, 0, 250] image transform_test(vertices, obj, camera) io.imsave({}/2_eye_3_{}.jpg.format(save_folder, -p/6), image) # 调整相机的上方向 camera[eye] [0, 0, 250] for p in np.arange(-50, 51, 10): world_up np.array([0, 1, 0]) # 默认方向 z np.deg2rad(p) Rz np.array([[math.cos(z), -math.sin(z), 0], [math.sin(z), math.cos(z), 0], [0, 0, 1]]) up Rz.dot(world_up[:, np.newaxis]) camera[up] np.squeeze(up) image transform_test(vertices, obj, camera) io.imsave({}/2_eye_4_{}.jpg.format(save_folder, -p), image) # 使用ImageMagick将图片转换为GIF subprocess.call(convert {} {}/2_*.jpg {}.format(options, save_folder, save_folder /camera.gif), shellTrue) # 删除生成的JPG文件 print(GIF已经生成现在删除JPG文件) subprocess.call(rm {}/*.jpg.format(save_folder), shellTrue) 该代码展示了如何通过改变3D对象的变换参数如缩放、旋转和相机的参数如位置、方向、投影类型生成对应的2D图像并将这些图像合成为GIF动画。这些步骤对于理解3D对象在不同变换下的表现以及相机模型的应用非常有帮助。 4_light.py import os, sys import numpy as np import scipy.io as sio from skimage import io from time import time import subprocess sys.path.append(..) import face3d from face3d import mesh def light_test(vertices, light_positions, light_intensities, h 256, w 256): # 添加光源并渲染 lit_colors mesh.light.add_light(vertices, triangles, colors, light_positions, light_intensities) image_vertices mesh.transform.to_image(vertices, h, w) rendering mesh.render.render_colors(image_vertices, triangles, lit_colors, h, w) rendering np.minimum((np.maximum(rendering, 0)), 1) return rendering # --------- 加载网格数据 C sio.loadmat(Data/example1.mat) vertices C[vertices] global colors global triangles colors C[colors] triangles C[triangles] colors colors / np.max(colors) # 归一化颜色 # 将顶点中心移动到[0,0,0] vertices vertices - np.mean(vertices, 0)[np.newaxis, :] # 缩放因子使人脸模型高度为18cm s 180 / (np.max(vertices[:, 1]) - np.min(vertices[:, 1])) R mesh.transform.angle2matrix([0, 0, 0]) t [0, 0, 0] vertices mesh.transform.similarity_transform(vertices, s, R, t) # 变换后的顶点 # 保存设置 save_folder results/light if not os.path.exists(save_folder): os.mkdir(save_folder) options -delay 12 -loop 0 -layers optimize # GIF选项需要ImageMagick # ---- 开始 # 1. 固定光源强度改变光源位置 # x轴光从左到右 light_intensities np.array([[1, 1, 1]]) for i, p in enumerate(range(-200, 201, 40)): light_positions np.array([[p, 0, 300]]) image light_test(vertices, light_positions, light_intensities) io.imsave({}/1_1_{:02d}.jpg.format(save_folder, i), image) # y轴光从上到下 for i, p in enumerate(range(200, -201, -40)): light_positions np.array([[0, p, 300]]) image light_test(vertices, light_positions, light_intensities) io.imsave({}/1_2_{:02d}.jpg.format(save_folder, i), image) # z轴光从近到远 for i, p in enumerate(range(100, 461, 40)): light_positions np.array([[0, 0, p]]) image light_test(vertices, light_positions, light_intensities) io.imsave({}/1_3_{:02d}.jpg.format(save_folder, i), image) subprocess.call(convert {} {}/1_*.jpg {}.format(options, save_folder, save_folder /position.gif), shellTrue) # 2. 固定光源位置改变光源强度 light_positions np.array([[0, 0, 300]]) for k in range(3): for i, p in enumerate(np.arange(0.4, 1.1, 0.2)): light_intensities np.array([[0, 0, 0]], dtypenp.float32) light_intensities[0, k] p image light_test(vertices, light_positions, light_intensities) io.imsave({}/2_{}_{:02d}.jpg.format(save_folder, k, i), image) subprocess.call(convert {} {}/2_*.jpg {}.format(options, save_folder, save_folder /intensity.gif), shellTrue) # -- 删除jpg文件 print(GIF已生成现在删除jpg文件) subprocess.call(rm {}/*.jpg.format(save_folder), shellTrue) 5_render.py 测试渲染速度。 import os, sys import numpy as np import scipy.io as sio from skimage import io from time import time import matplotlib.pyplot as plt np.set_printoptions(suppressTrue) sys.path.append(..) import face3d from face3d import mesh from face3d import mesh_numpy from face3d.morphable_model import MorphabelModel # 加载数据 C sio.loadmat(Data/example1.mat) vertices C[vertices]; colors C[colors]; triangles C[triangles] colors colors / np.max(colors) # 将顶点中心移动到[0,0,0] vertices vertices - np.mean(vertices, 0)[np.newaxis, :] # 缩放因子使人脸模型高度为18cm s 180 / (np.max(vertices[:,1]) - np.min(vertices[:,1])) R mesh.transform.angle2matrix([0, 0, 0]) t [0, 0, 0] vertices mesh.transform.similarity_transform(vertices, s, R, t) # 渲染的图像大小 h w 256 image_vertices mesh.transform.to_image(vertices, h, w) # ----------------------------------------- 渲染颜色 # 使用Python numpy渲染颜色 st time() rendering_cp mesh_numpy.render.render_colors(image_vertices, triangles, colors, h, w) print(----------colors python: , time() - st) # 使用Python numpy Rasterize方式渲染颜色 st time() rendering_cpr mesh_numpy.render.render_colors_ras(image_vertices, triangles, colors, h, w) print(----------colors python ras: , time() - st) # 使用C渲染颜色 st time() rendering_cc mesh.render.render_colors(image_vertices, triangles, colors, h, w) print(----------colors c: , time() - st) # ----------------------------------------- 渲染纹理 # 当面部纹理保存为纹理映射时推荐使用此方法。 # 加载纹理 texture io.imread(Data/uv_texture_map.jpg) / 255. tex_h, tex_w, _ texture.shape # 加载纹理坐标(uv坐标) uv_coords face3d.morphable_model.load.load_uv_coords(Data/BFM/Out/BFM_UV.mat) # 一般的UV坐标范围为[0-1] # 转换到纹理大小 texcoord np.zeros_like(uv_coords) texcoord[:, 0] uv_coords[:, 0] * (tex_h - 1) texcoord[:, 1] uv_coords[:, 1] * (tex_w - 1) texcoord[:, 1] tex_w - texcoord[:, 1] - 1 texcoord np.hstack((texcoord, np.zeros((texcoord.shape[0], 1)))) # 添加z坐标 tex_triangles triangles # 使用Python numpy渲染纹理 st time() rendering_tp face3d.mesh_numpy.render.render_texture(image_vertices, triangles, texture, texcoord, tex_triangles, h, w) print(----------texture python: , time() - st) # 使用C渲染纹理 st time() rendering_tc face3d.mesh.render.render_texture(image_vertices, triangles, texture, texcoord, tex_triangles, h, w) print(----------texture c: , time() - st) # 绘图如果需要查看效果取消注释以下代码 # plt.subplot(2, 2, 1) # plt.imshow(rendering_cp) # plt.subplot(2, 2, 2) # plt.imshow(rendering_cc) # plt.subplot(2, 2, 3) # plt.imshow(rendering_tp) # plt.subplot(2, 2, 4) # plt.imshow(rendering_tc) # plt.show() ## --------------------------- 写入OBJ文件 save_folder os.path.join(results, io) if not os.path.exists(save_folder): os.mkdir(save_folder) # 将顶点、颜色和纹理坐标转换为浮点数格式 vertices, colors, uv_coords image_vertices.astype(np.float32).copy(), colors.astype(np.float32).copy(), uv_coords.astype(np.float32).copy() # 使用Python numpy写入OBJ文件 st time() mesh_numpy.io.write_obj_with_colors_texture(os.path.join(save_folder, numpy.obj), vertices, triangles, colors, texture, uv_coords) print(----------write obj numpy: , time() - st) # 使用Cython写入OBJ文件 st time() mesh.io.write_obj_with_colors_texture(os.path.join(save_folder, cython.obj), vertices, triangles, colors, texture, uv_coords) print(----------write obj cython: , time() - st) 6_generate_image_map.py 生成表示不同属性颜色、深度、PNCC等的2D图像映射 : 将属性渲染到图像空间。 import os, sys import numpy as np import scipy.io as sio from skimage import io from time import time import matplotlib.pyplot as plt sys.path.append(..) import face3d from face3d import mesh # ------------------------------ 加载网格数据 C sio.loadmat(Data/example1.mat) vertices C[vertices]; colors C[colors]; triangles C[triangles] colors colors / np.max(colors) # ------------------------------ 修改顶点位置变换改变物体的位置 # 缩放。目标大小为200例如 s 180 / (np.max(vertices[:,1]) - np.min(vertices[:,1])) # 旋转30度例如 R mesh.transform.angle2matrix([0, 30, 0]) # 不进行平移。物体中心为[0,0] t [0, 0, 0] transformed_vertices mesh.transform.similarity_transform(vertices, s, R, t) # ------------------------------ 渲染设置转换为2D图像 # 设置渲染的高度和宽度 h w 256 # 转换为图像坐标进行渲染 image_vertices mesh.transform.to_image(transformed_vertices, h, w) ## --- 开始生成图像 save_folder results/image_map if not os.path.exists(save_folder): os.mkdir(save_folder) ## 0. 颜色映射 attribute colors color_image mesh.render.render_colors(image_vertices, triangles, attribute, h, w, c3) io.imsave({}/color.jpg.format(save_folder), np.squeeze(color_image)) ## 1. 深度图 z image_vertices[:, 2:] z z - np.min(z) z z / np.max(z) attribute z depth_image mesh.render.render_colors(image_vertices, triangles, attribute, h, w, c1) io.imsave({}/depth.jpg.format(save_folder), np.squeeze(depth_image)) ## 2. PNCC局部法线一致性约束编码图像 pncc face3d.morphable_model.load.load_pncc_code(Data/BFM/Out/pncc_code.mat) attribute pncc pncc_image mesh.render.render_colors(image_vertices, triangles, attribute, h, w, c3) io.imsave({}/pncc.jpg.format(save_folder), np.squeeze(pncc_image)) ## 3. UV坐标图像用于密集对应 uv_coords face3d.morphable_model.load.load_uv_coords(Data/BFM/Out/BFM_UV.mat) # attribute uv_coords # 注意原始论文使用量化的坐标这里未进行量化 uv_coords_image mesh.render.render_colors(image_vertices, triangles, attribute, h, w, c2) # 两个通道u, v # 添加一个通道以便显示 uv_coords_image np.concatenate((np.zeros((h, w, 1)), uv_coords_image), 2) io.imsave({}/uv_coords.jpg.format(save_folder), np.squeeze(uv_coords_image)) 7_generate_uv_map.py 生成表示不同属性颜色、深度、图像位置等的2D UV映射图 : 将属性渲染到UV空间。 import os, sys import numpy as np import scipy.io as sio from skimage import io import skimage.transform from time import time import matplotlib.pyplot as plt sys.path.append(..) import face3d from face3d import mesh from face3d.morphable_model import MorphabelModel def process_uv(uv_coords, uv_h256, uv_w256): uv_coords[:, 0] uv_coords[:, 0] * (uv_w - 1) uv_coords[:, 1] uv_coords[:, 1] * (uv_h - 1) uv_coords[:, 1] uv_h - uv_coords[:, 1] - 1 uv_coords np.hstack((uv_coords, np.zeros((uv_coords.shape[0], 1)))) # 添加z坐标 return uv_coords # -- 加载网格数据 C sio.loadmat(Data/example1.mat) vertices C[vertices]; colors C[colors]; triangles C[full_triangles] colors colors / np.max(colors) # -- 修改顶点位置进行变换改变物体的位置 s 180 / (np.max(vertices[:, 1]) - np.min(vertices[:, 1])) R mesh.transform.angle2matrix([-10, -35, 20]) t [0, 0, 0] transformed_vertices mesh.transform.similarity_transform(vertices, s, R, t) # -- 加载UV坐标 uv_coords face3d.morphable_model.load.load_uv_coords(Data/BFM/Out/BFM_UV.mat) # -- 开始生成图像 save_folder results/uv_map if not os.path.exists(save_folder): os.mkdir(save_folder) uv_h uv_w 256 image_h image_w 256 uv_coords process_uv(uv_coords, uv_h, uv_w) #-- 1. UV纹理映射 attribute colors uv_texture_map mesh.render.render_colors(uv_coords, triangles, attribute, uv_h, uv_w, c3) io.imsave({}/uv_texture_map.jpg.format(save_folder), np.squeeze(uv_texture_map)) #-- 2. UV位置图用于面部重建和对齐的密集对应 # 对于面部重建和对齐UV位置图记录了顶点在UV坐标系中的位置信息 # 注意UV位置图依赖于对应的渲染图像 projected_vertices transformed_vertices.copy() # 使用标准相机和正交投影 image_vertices mesh.transform.to_image(projected_vertices, image_h, image_w) position image_vertices.copy() position[:, 2] position[:, 2] - np.min(position[:, 2]) # 将Z坐标平移到非负数 attribute position image mesh.render.render_colors(image_vertices, triangles, colors, image_h, image_w, c3) uv_position_map mesh.render.render_colors(uv_coords, triangles, attribute, uv_h, uv_w, c3) io.imsave({}/image.jpg.format(save_folder), np.squeeze(image)) np.save({}/uv_position_map.npy.format(save_folder), uv_position_map) io.imsave({}/uv_position_map.jpg.format(save_folder), (uv_position_map) / max(image_h, image_w)) # 仅用于显示 # -- 3. 一般的几何图像。属性为顶点或转换后的顶点 # TODO # -- 4. 法线属性 # TODO 8_generate_posmap_300WLP.py 生成300W_LP数据集的UV位置映射图 import os, sys import numpy as np import scipy.io as sio from skimage import io import skimage.transform from time import time import matplotlib.pyplot as plt sys.path.append(..) import face3d from face3d import mesh from face3d.morphable_model import MorphabelModel def process_uv(uv_coords, uv_h256, uv_w256): uv_coords[:, 0] uv_coords[:, 0] * (uv_w - 1) uv_coords[:, 1] uv_coords[:, 1] * (uv_h - 1) uv_coords[:, 1] uv_h - uv_coords[:, 1] - 1 uv_coords np.hstack((uv_coords, np.zeros((uv_coords.shape[0], 1)))) # 添加z坐标 return uv_coords def run_posmap_300W_LP(bfm, image_path, mat_path, save_folder, uv_h256, uv_w256, image_h256, image_w256): # 1. 加载图像和拟合参数 image_name image_path.strip().split(/)[-1] image io.imread(image_path) / 255. [h, w, c] image.shape info sio.loadmat(mat_path) pose_para info[Pose_Para].T.astype(np.float32) shape_para info[Shape_Para].astype(np.float32) exp_para info[Exp_Para].astype(np.float32) # 2. 生成网格模型 # 生成形状 vertices bfm.generate_vertices(shape_para, exp_para) # 变换网格 s pose_para[-1, 0] angles pose_para[:3, 0] t pose_para[3:6, 0] transformed_vertices bfm.transform_3ddfa(vertices, s, angles, t) projected_vertices transformed_vertices.copy() # 使用标准相机和正交投影 image_vertices projected_vertices.copy() image_vertices[:, 1] h - image_vertices[:, 1] - 1 # 3. 使用关键点裁剪图像 kpt image_vertices[bfm.kpt_ind, :].astype(np.int32) left np.min(kpt[:, 0]) right np.max(kpt[:, 0]) top np.min(kpt[:, 1]) bottom np.max(kpt[:, 1]) center np.array([right - (right - left) / 2.0, bottom - (bottom - top) / 2.0]) old_size (right - left bottom - top) / 2 size int(old_size * 1.5) # 随机扰动可以根据需要调整这些参数 marg old_size * 0.1 t_x np.random.rand() * marg * 2 - marg t_y np.random.rand() * marg * 2 - marg center[0] center[0] t_x; center[1] center[1] t_y size size * (np.random.rand() * 0.2 0.9) # 裁剪并记录变换参数 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, image_h - 1], [image_w - 1, 0]]) tform skimage.transform.estimate_transform(similarity, src_pts, DST_PTS) cropped_image skimage.transform.warp(image, tform.inverse, output_shape(image_h, image_w)) # 将面部位置图像顶点转换为UV空间 position image_vertices.copy() position[:, 2] 1 position np.dot(position, tform.params.T) position[:, 2] image_vertices[:, 2] * tform.params[0, 0] # 缩放Z坐标 position[:, 2] position[:, 2] - np.min(position[:, 2]) # 平移Z坐标 # 4. 生成UV位置映射图 uv_position_map mesh.render.render_colors(uv_coords, bfm.full_triangles, position, uv_h, uv_w, c3) # 5. 保存文件 io.imsave({}/{}.format(save_folder, image_name), np.squeeze(cropped_image)) np.save({}/{}.format(save_folder, image_name.replace(jpg, npy)), uv_position_map) io.imsave({}/{}.format(save_folder, image_name.replace(.jpg, _posmap.jpg)), (uv_position_map) / max(image_h, image_w)) # 仅用于显示 # -- 验证 # import cv2 # uv_texture_map_rec cv2.remap(cropped_image, uv_position_map[:,:,:2].astype(np.float32), None, interpolationcv2.INTER_LINEAR, borderModecv2.BORDER_CONSTANT,borderValue(0)) # io.imsave({}/{}.format(save_folder, image_name.replace(.jpg, _tex.jpg)), np.squeeze(uv_texture_map_rec)) if __name__ __main__: save_folder results/posmap_300WLP if not os.path.exists(save_folder): os.mkdir(save_folder) # 设置参数 uv_h uv_w 256 image_h image_w 256 # 加载UV坐标 uv_coords face3d.morphable_model.load.load_uv_coords(Data/BFM/Out/BFM_UV.mat) uv_coords process_uv(uv_coords, uv_h, uv_w) # 加载BFM模型 bfm MorphabelModel(Data/BFM/Out/BFM.mat) # 运行生成UV位置映射图 image_path Data/IBUG_image_008_1_0.jpg mat_path Data/IBUG_image_008_1_0.mat run_posmap_300W_LP(bfm, image_path, mat_path, save_folder) 2. 链接https://pan.baidu.com/s/1s3NjgxuuCnB238GpzN-6Pw 提取码ucqj
test.py
import numpy as np
import os
from skimage.transform import estimate_transform, warp
import cv2
from predictor import PosPrediction
import matplotlib.pyplot as plt# 将图像从浮点数转换为无符号8位整数将关键点绘制为绿色的圆圈并返回绘制了关键点的图像。
def draw_kps(img, kps, point_size2):# 将图像从浮点数范围[0,1]转换为无符号8位整数范围[0,255]img np.array(img * 255, np.uint8)# 遍历每个关键点for i in range(kps.shape[0]):# 获取当前关键点的坐标x, yx, y int(kps[i, 0]), int(kps[i, 1])# 在图像上绘制圆形关键点圆心为 (x, y)颜色为绿色 (0, 255, 0)半径为 point_sizecv2.circle(img, (x, y), point_size, (0, 255, 0), -1)# 返回绘制了关键点的图像return img# 找到旋转矩阵参考https://github.com/YadiraF/face3d
def angle2matrix(angles):x, y, z np.deg2rad(angles[0]), np.deg2rad(angles[1]), np.deg2rad(angles[2])# xRxnp.array([[1, 0, 0],[0, np.math.cos(x), -np.math.sin(x)],[0, np.math.sin(x), np.math.cos(x)]])# yRynp.array([[ np.math.cos(y), 0, np.math.sin(y)],[ 0, 1, 0],[-np.math.sin(y), 0, np.math.cos(y)]])# zRznp.array([[np.math.cos(z), -np.math.sin(z), 0],[np.math.sin(z), np.math.cos(z), 0],[ 0, 0, 1]])RRz.dot(Ry.dot(Rx))return R.astype(np.float32)# 主要功能代码
# 加载预训练的 Haar 特征级联分类器用于人脸检测
cas cv2.CascadeClassifier(./Data/cv-data/haarcascade_frontalface_alt2.xml)
#加载测试图像
img plt.imread(./images/test.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]])
#在图像上绘制人脸检测框并显示
plt.imshow(cv2.rectangle(img.copy(),(bbox[0],bbox[1]),(bbox[2],bbox[3]),(0,255,0),2))
plt.axis(off)
plt.show()
#确定人脸区域的边界框位置
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]]) #图像大小256*256
#进行仿射变换使用 skimage.transform.estimate_transform 函数进行相似性仿射变换将原始图像中的人脸区域调整到目标大小256x256像素
tform estimate_transform(similarity, src_pts, DST_PTS)
#应用仿射变换并显示结果
img img/255.
cropped_img warp(img, tform.inverse, output_shape(256, 256))plt.imshow(cropped_img)
plt.axis(off)
plt.show()#使用一个预训练的神经网络模型来预测裁剪后人脸图像的关键点位置
#初始化关键点预测器
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])
#显示深度图像
plt.imshow(pos[...,2],cmapgray)
plt.axis(off)
plt.show()#人脸关键点
#加载关键点索引和UV映射图
uv_kpt_ind np.loadtxt(./Data/uv-data/uv_kpt_ind.txt).astype(np.int32)
uv_face plt.imread(./Data/uv-data/uv_face.png)
#绘制UV映射图中的关键点
plt.imshow(draw_kps(uv_face,uv_kpt_ind.T))
plt.axis(off)
plt.show()
#从三维坐标中提取人脸关键点
face_kps pos[uv_kpt_ind[1,:],uv_kpt_ind[0,:],:]
plt.imshow(draw_kps(img.copy(),face_kps))
plt.axis(off)
plt.show()
#加载人脸点云索引和绘制
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, :]
plt.figure(figsize(8,8))
plt.imshow(draw_kps(img.copy(),vertices[:,:2],1))
plt.axis(off)
plt.show()
#纹理映射
texture cv2.remap(img, pos[:,:,:2].astype(np.float32), None, interpolationcv2.INTER_NEAREST, borderModecv2.BORDER_CONSTANT,borderValue(0))
plt.imshow(draw_kps(texture,uv_kpt_ind.T))
plt.axis(off)
plt.show()
#加载三角形索引和纹理
triangles np.loadtxt(./Data/uv-data/triangles.txt).astype(np.int32)
all_colors np.reshape(texture, [256*256, -1])
colors all_colors[face_ind, :]
#计算三角形的深度和纹理
tri_depth (vertices[triangles[:,0],2 ] vertices[triangles[:,1],2] vertices[triangles[:,2],2])/3.
tri_tex (colors[triangles[:,0] ,:] colors[triangles[:,1],:] colors[triangles[:,2],:])/3.
tri_tex tri_tex*255
#绘制三维纹理贴图
img_3D np.zeros_like(img,dtypenp.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])],dtypenp.int32)img_3D cv2.drawContours(img_3D,[cnt],0,tri_tex[i],-1)
plt.imshow(img_3D/255.0)
plt.show()
函数左右
旋转坐标使用旋转矩阵 trans_mat 将原始顶点 vertices 进行旋转得到 rotated_vertices。
拉到画布上计算旋转后顶点的平移量将顶点平移至画布中心。
生成图像创建空白的 img_3D 和 mask用于绘制旋转后的三维纹理贴图。
循环每个三角形遍历所有三角形 (triangles.shape[0])并绘制其轮廓。根据轮廓面积选择填充颜色使得图像具有最大填充面积。
保存和显示保存旋转后的图像并显示在屏幕上。
def trans_angle(p, trans_mat):# 旋转坐标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_xshift_y ori_y-rot_yrotated_vertices[:,0] rotated_vertices[:,0] shift_xrotated_vertices[:,1] rotated_vertices[:,1] shift_yimg_3D np.zeros_like(img, dtypenp.uint8)mask np.zeros_like(img, dtypenp.uint8)fill_area 0for 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])], dtypenp.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.imwrite(img/str(p).jpg, img_3D)cv2.imwrite(img/str(60-p) .jpg, img_3D)plt.imshow(img_3D)plt.show()
#第一个 for 循环旋转角度从 0 到 29每次增加 2*i 度。对每个角度调用 trans_angle 函数生成旋转后的图像。
for i in range(30):print(i)trans_mat angle2matrix((0,2*i,0))trans_angle(i, trans_mat)
#第二个 for 循环旋转角度从 -30 到 -1每次减小 2*i 度。对每个角度调用 trans_angle 函数生成旋转后的图像并以 60-p 的格式保存。
for i in range(-30,0):i -31 - ip abs(i) 60print(p)trans_mat angle2matrix((0,2*i,0))trans_angle(p, trans_mat)人脸检测与裁剪使用 Haar 特征级联分类器检测人脸并裁剪出检测到的第一个人脸区域。图像仿射变换将裁剪后的人脸图像进行仿射变换调整到固定大小256x256的图像。人脸关键点预测使用神经网络预测人脸关键点位置。三维重建 将预测得到的关键点位置映射到三维空间。使用三角网格和颜色信息对人脸进行纹理贴图和渲染。最后生成旋转后不同角度的人脸图像。