对于图片转3d人脸方面的研究

1.一个开源的可以运行的项目(face3d/README.md at master · yfeng95/face3d · GitHub)

在配置好环境后,让我们一个一个py文件运行它(我将给出中文注释)

1)1_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], at=np.array([0, 0, 0]), up=None)

# 正交投影

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

 2)2_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_{:0>2d}.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'), shell=True)  # 调用ImageMagick生成GIF

subprocess.call('rm {}/show_*.jpg'.format(save_folder), shell=True)  # 删除临时图像文件

这段代码是一个关于3D可变形模型(3DMM)的示例,它展示了如何利用该模型从参数(形状、表情、姿势)生成3D人脸,并将其渲染成2D图像。它与之前提到的 1_pipeline.py 文件是相关联的,因为它们都涉及到了使用 face3d 库中的模型和函数来处理和渲染3D人脸数据。

3DMM(3D Morphable Model)是一种用于生成和表示3D人脸模型的统计模型。它基于统计学原理,通过对大量3D人脸数据进行分析和学习,构建出一个能够表示多种不同人脸形状、表情和纹理的参数化模型。

主要步骤和功能解析:

  1. 导入和设置

    • 导入必要的库和模块,包括 ossyssubprocessnumpyscipy.ioskimage.iomatplotlib.pyplot
    • 将上级目录添加到系统路径,以便能够导入 face3d 库和其子模块。
  2. 初始化和加载模型

    • 使用 MorphableModel 类加载预训练的3DMM模型文件 BFM.mat
    • 通过随机参数获取初始的形状参数 (sp)、表情参数 (ep) 和纹理参数 (tp)。
  3. 生成人脸网格

    • 使用获取的随机参数生成顶点 (vertices) 和颜色信息 (colors)。
    • 将颜色值限制在 [0, 1] 范围内。
  4. 顶点变换和投影

    • 定义缩放系数 s 和旋转角度 angles
    • 使用 transform 方法对顶点进行变换,然后复制到 projected_vertices,表示标准相机和正交投影后的顶点位置。
  5. 渲染生成的3D人脸

    • 设定图像的大小为 256x256 像素。
    • 将变换后的顶点投影到图像坐标系。
    • 使用 render_colors 方法根据三角形网格和颜色信息生成渲染图像 image
  6. 拟合过程

    • 从生成的2D图像中提取关键点坐标 x 和对应的3DMM关键点索引 X_ind
    • 使用 fit 方法对关键点进行拟合,得到拟合后的形状参数 (fitted_sp)、表情参数 (fitted_ep)、缩放、旋转角度和平移参数。
  7. 验证拟合参数

    • 根据拟合后的参数生成新的顶点并进行变换。
    • 将变换后的顶点投影到图像坐标系并渲染成图像,保存为 fitted_image
  8. 结果保存和可视化

    • 打印并比较真实的姿势参数和拟合后的参数。
    • 将生成的图像和拟合后的图像保存到指定的文件夹 results/3dmm 中。
  9. 可视化拟合过程

    • 如果需要,可以将拟合过程中每一步的图像保存下来,生成一个 GIF 动画展示拟合的过程。

 3)3_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, h=256, w=256):
    '''
    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'], near=camera['near'], far=camera['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'), shell=True)

# 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'), shell=True)

# 删除生成的JPG文件
print('GIF已经生成,现在删除JPG文件')
subprocess.call('rm {}/*.jpg'.format(save_folder), shell=True)
 

该代码展示了如何通过改变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_{:0>2d}.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_{:0>2d}.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_{:0>2d}.jpg'.format(save_folder, i), image)

subprocess.call('convert {} {}/1_*.jpg {}'.format(options, save_folder, save_folder + '/position.gif'), shell=True)

# 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]], dtype=np.float32)

        light_intensities[0, k] = p

        image = light_test(vertices, light_positions, light_intensities)

        io.imsave('{}/2_{}_{:0>2d}.jpg'.format(save_folder, k, i), image)

subprocess.call('convert {} {}/2_*.jpg {}'.format(options, save_folder, save_folder + '/intensity.gif'), shell=True)

# -- 删除jpg文件

print('GIF已生成,现在删除jpg文件')

subprocess.call('rm {}/*.jpg'.format(save_folder), shell=True)

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(suppress=True)

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, c=3)

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, c=1)

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, c=3)

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, c=2)  # 两个通道: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_h=256, uv_w=256):

    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, c=3)

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, c=3)

uv_position_map = mesh.render.render_colors(uv_coords, triangles, attribute, uv_h, uv_w, c=3)

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_h=256, uv_w=256):

    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_h=256, uv_w=256, image_h=256, image_w=256):

    # 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, c=3)

    # 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, interpolation=cv2.INTER_LINEAR, borderMode=cv2.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_size=2):# 将图像从浮点数(范围[0,1])转换为无符号8位整数(范围[0,255])img = np.array(img * 255, np.uint8)# 遍历每个关键点for i in range(kps.shape[0]):# 获取当前关键点的坐标(x, y)x, 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])# xRx=np.array([[1,              0,                0],[0, np.math.cos(x),  -np.math.sin(x)],[0, np.math.sin(x),   np.math.cos(x)]])# yRy=np.array([[ np.math.cos(y), 0, np.math.sin(y)],[              0, 1,              0],[-np.math.sin(y), 0, np.math.cos(y)]])# zRz=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)# 主要功能代码
# 加载预训练的 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],cmap='gray')
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, interpolation=cv2.INTER_NEAREST, borderMode=cv2.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,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)
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, dtype=np.uint8)mask = np.zeros_like(img, dtype=np.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])], 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.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)的图像。
  • 人脸关键点预测:使用神经网络预测人脸关键点位置。
  • 三维重建
    • 将预测得到的关键点位置映射到三维空间。
    • 使用三角网格和颜色信息对人脸进行纹理贴图和渲染。
    • 最后生成旋转后不同角度的人脸图像。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/30765.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

海外仓系统能解决海外仓哪些难题?海外仓标准化管理实用指南

海外仓管理问题常常导致业务流程变慢,根据我们的调查显示,至少有48%的海外仓每周都会出现一些“小意外”。甚至这些小问题每天都在发生,问题的出现已经严重影响到了海外仓业务的进行。今天我们将重点分析海外仓比较常见的一些问题&#xff0c…

springboot vue 开源 会员收银系统 (7) 收银台的完善 新增开卡 结算

前言 完整版演示 开发版演示 在前面的开发中,我们成功完成了商品分类和商品信息的搭建,开发了收银台基础。现在,我们将进一步完善收银台的功能,添加开卡和结算功能,并在后台实现会员卡的创建和订单保存。同时&#xff…

使用 Monkey Patch 解决 Sahi 可视化的中文乱码问题

其实如果是对算法的输出结果进行可视化的话,使用 Pillow 库是完全没有问题的。但是存在着这样一种情况,我们调用的公共包当中,里面已经有了可视化的接口,但是使用的是 OpenCV 中的 cv2.putText 进行可视化的。正常来说&#xff0c…

地瓜网络技术综合助手教你一键下载腾讯会议高清视频

当您错过腾讯会议的直播课程,不必担心,地瓜网络技术综合助手帮您轻松获取视频回放。 只需几个简单步骤,即可在手头保留珍贵的学习资料。 首先,启动地瓜网络技术综合助手, 进行软件初始化并开启监测功能。 接下来&…

智慧乡村和美人家信息化系统

一、简介 智慧乡村和美人家信息化系统是一个综合管理平台,集成了首页概览、一张图可视化、数据填报、智能评估、便捷申报、公开公示、任务管理、活动发布和灵活配置等功能。该系统不仅提升了乡村管理效率,也优化了家庭生活的便捷性。通过一张图&#xf…

BEVM背靠比特大陆打造新赛道,算力RWA成下一个千亿市场?

众所周知,在加密行业,每隔一段时间就会有一个新的概念或者一个新词出现,并引来社区和资本的追捧关注,笔者近期在浏览新闻时,发现了一个特别有意思的新概念——算力RWA,在社区引起了不少讨论。 该词最早出现…

工时管理系统的优势及推荐

企业发展离不开每一个员工,而员工的工作效率高低也是影响着企业在行业内的竞争力,所以规范管理员工时间,提升员工工作效率势在必行。工时管理系统在现代企业中的应用越来越广泛,不仅是因为它能显著提高企业管理效率,更…

Flutter【组件】按钮

简介 flutter 按钮组件。提供一种封装按钮组件的思路,并不支持过多的自定义属性。根据使用场景及设计规范进行封装,使用起来比较方便。 github地址:https://github.com/ThinkerJack/jac_uikit pub地址:https://pub.dev/package…

IOS 关于Apple Pay 与内购

一、什么是Apple Pay、什么是内购 首先这两个不是一样的,很多人一看觉得这两是一回事,我之前也是这么想的。今天我来给大家阐述一下: Apple Pay:是指支付实物类。类似国内的微信、支付宝。支付超市食品类啥的。 内购&#xff1…

el-upload组件校验不通过预览列表依然显示图片问题解决

如图校验不通过的图片依然显示在预览列表了&#xff0c;需要在校验不通过的时候移除图片 <el-uploadclass"upload-cls":action"ossSignature.host":auto-upload"false"ref"upload":list-type"listType":limit"limi…

如何在React中使用CSS模块,并解释为什么使用它们比传统CSS更有益?

在React中使用CSS模块是一种将CSS类名局部化到单个组件的方法&#xff0c;从而避免了全局作用域中的类名冲突。CSS模块允许你为组件编写样式&#xff0c;并确保这些样式只应用于该组件&#xff0c;而不会影响到其他组件。 以下是在React中使用CSS模块的步骤&#xff1a; 安装C…

通过CSS样式来禁用href

<style>.disabled-link {pointer-events: none;cursor: default;text-decoration: none;color: inherit; }</style><a href"https://www.example.com" class"disabled-link">禁用链接</a> 在上述CSS样式中&#xff0c; pointer-…

前端项目如何规范文件命名

前端项目如何规范文件命名 ls-lint 是一个非常快的文件和目录名称 linter&#xff0c;可方便约束项目目录和文件的命名。 特点&#xff1a; 快速依赖少适用所有文件配置简单 安装依赖 npm install ls-lint/ls-lint -D 在 husky 加入 git hook:"husky": {"h…

医疗器械3D全景展会在线漫游创造数字化时代的展览新篇章

在数字化浪潮的引领下&#xff0c;VR虚拟网上展会正逐渐成为企业展示品牌实力、吸引潜在客户的首选平台。我们与广交会携手走过三年多的时光&#xff0c;凭借优质的服务和丰富的经验&#xff0c;赢得了客户的广泛赞誉。 面对传统展会活动繁多、企业运营繁忙的挑战&#xff0c;许…

日语 词汇

あつい 熱い さむい 寒い ひろい 広い せまい 狭い   おおき 大き  ちいさい 小さい おおい 多い しょう  少 はやい 早い 速い  おそい 遅い わるい 悪い たかい 高い  ひくい 低い つよい 強い よわい 弱い ふかい 深い うすい 薄い あつい …

深入浅出Git原理与Gitflow流程

1 Git原理 版本控制系统在软件开发和团队协作中扮演着至关重要的角色。它们帮助开发人员跟踪和管理代码的变化&#xff0c;协调多人同时编辑同一代码库&#xff0c;回溯历史版本&#xff0c;并解决代码冲突等问题。Git作为当今最流行的分布式版本控制系统&#xff0c;为开发人…

C++ 59 之 纯虚函数和抽象类

#include <iostream> #include <string> using namespace std;class Cal { // 类中有纯虚函数&#xff0c;这个类也叫做抽象类&#xff0c;无法实现实例化 public:int m_a;int m_b;// 虚函数// virtual int getRes(){// return 0;// }// 纯虚函数 作用和虚函数…

士兰微MEMS陀螺仪在电动升降桌上的应用

杭州士兰微电子股份有限公司&#xff0c;一家在国内集成电路芯片设计与制造领域内具有领先地位的高新技术企业&#xff0c;早已以其创新的半导体微电子产品和服务&#xff0c;赢得了市场的广泛认可。士兰微不仅致力于集成电路芯片的研发&#xff0c;而且涉猎于MEMS传感器技术领…

第7章 工程项目财务评价 作业

第7章 工程项目财务评价 作业 一单选题&#xff08;共23题&#xff0c;100分&#xff09; (单选题)企业缴纳所得税后的利润,按照下列哪一个顺序进行分配?() A. ①②③④ B. ①③②④ C. ④③②① D. ②④①③ 其中:①承担被没收的财物损失,支付各项税收的滞纳金和罚款; ②提…

torch.optim 之 distinct penalization

看torch.optim中介绍到distinct penalization: Remember that parameters() returns an iterable that contains all learnable parameters, including biases and other parameters that may prefer distinct penalization. To address this, one can specify individual pena…