三维人脸实践:基于Face3D的渲染、生成与重构 <一>
face3d: Python tools for processing 3D face
git code: https://github.com/yfeng95/face3d
paper list: PaperWithCode
该方法广泛用于基于三维人脸关键点的人脸生成、属性检测(如位姿、深度、PNCC等),能够快速实现人脸建模与渲染。推荐!!!
目录
- face3d: Python tools for processing 3D face
- 一、介绍
- 1.1 目录
- 1.2 构建
- 1.2.1先决条件
- 1.3 使用
- 1.3.1 克隆
- 1.3.2 编译C++文件为.so文件,用于python;如果使用numpy版本,则忽略此步。
- 1.3.3 准备BFM数据(如果不使用3dmm可跳过此步)
- 1 下载原始的BFM模型
- 2 下载额外的BFM信息:
- 3 下载STN中的UV坐标
- 1.3.4 运行
- 1.4 运行例子
- 2 pipeline源码解读
- 2.0导入相关库文件
- 2.1 加载网格数据(mesh data)
- 2.2 修改顶点(vertices)
- 2.3 修改颜色/纹理(添加光照)
- 2.4 修改顶点(映射,改变相机位置)
- 2.5 转化为2D图像
- 总结
相机坐标下的人脸变换


光照渲染


3DMM模型


提示:对于初学者来说,作者强烈建议按照这个顺序来运行样例,然后再看mesh_numpy中的代码和读每个文件中的注释。
一、介绍
这里尝试去实现有关三维人脸的一些基础功能,如处理网格数据mesh data、基于morphable model的人脸生成,基于单张人脸图片及其关键点的三维人脸重构,带有不同光照效果的人脸渲染等操作。
该工程大部分代码基于python,但有些功能如rasterization使用C++实现循环渲染会快很多,并使用Cython编译供python环境使用,该工具轻量而运行快。
1.1 目录
三维网络数据,是最流行的三维人脸表征方法;3DMM模型广泛用于产生和重构三维人脸。
# Since triangle mesh is the most popular representation of 3D face,
# the main part is mesh processing.
mesh/ # written in python and c++
| cython/ # c++ files, use cython to compile
| io.py # read & write obj
| vis.py # plot mesh
| transform.py # transform mesh & estimate matrix
| light.py # add light & estimate light(to do)
| render.py # obj to image using rasterization rendermesh_numpy/ # the same with mesh/, with each part written in numpy# slow but easy to learn and modify# 3DMM is one of the most popular methods to generate & reconstruct 3D face.
morphable_model/
| morphable_model.py # morphable model class: generate & fit
| fit.py # estimate shape&expression parameters. 3dmm fitting.
| load.py # load 3dmm data
1.2 构建
1.2.1先决条件
Python 2 or Python 3
Python packages:numpyskimage (for reading&writing image)scipy (for loading mat)matplotlib (for show)Cython (for compiling c++ file)%可参考pip3命令行下载国内源:
pip3 install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install scikit-image -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install scipy -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install Cython -i https://pypi.tuna.tsinghua.edu.cn/simple
1.3 使用
1.3.1 克隆
git clone https://github.com/YadiraF/face3d
cd face3d
1.3.2 编译C++文件为.so文件,用于python;如果使用numpy版本,则忽略此步。
cd face3d/mesh/cython
python setup.py build_ext -i
1.3.3 准备BFM数据(如果不使用3dmm可跳过此步)
1 下载原始的BFM模型
链接:https://faces.dmi.unibas.ch/bfm/main.php?nav=1-2&id=downloads

将所有框都勾上,填写对应的信息后会收到一个下载link,将下载后的文件拷贝:
copy 01_MorphabelModel.mat to raw/
2 下载额外的BFM信息:
链接:3DFFA
下载【face profiling】和【3DFFA】
链接:HPEN
下载HPEN
将下载好的三个压缩包解压,分别在里面找到如下文件:
model_info.mat Model_Expression.mat Model_face_contour_trimed.mat Model_tri_mouth.mat Modelplus_nose_hole.mat Modelplus_parallel.mat vertex_code.mat
然后在face3d/examples/Data/BFM目录下新建一个文件夹3ddfa,将上述文件拷贝进去。
3DDFA(Face Alignment Across Large Poses: A 3D Solution) HFPE(High-Fidelity Pose and Expression Normalization for Face Recognition in the Wild)
3 下载STN中的UV坐标
链接:BFM_UV
点击download下载后解压,在face3d/examples/Data/BFM目录下新建一个文件夹stn,再将BFM_UV.mat复制到stn/
1.3.4 运行
在BFM目录下创建Out文件夹
运行Matlab中的generate.m,产生的文件将会保存在 Out/
提示:一些空文件夹可能需要通过mkdir创建
1.4 运行例子
examples使用cython版本,如果使用numpy,将mesh替换为mesh_numpy即可
cd examples
python 1_pipeline.py
如果得到如下输出,并且在pipeline下有生产的照片说明运行成功

2 pipeline源码解读
提示:为了方便理解,源码解读可能会使用numpy版本;而示例使用的是cython版本。
Pipeline:将3D目标转化为2D图像
2.0导入相关库文件
''' Simple example of pipeline
3D obj(process) --> 2d image
'''
import os, sys
import numpy as np
import scipy.io as sio
from skimage import io
from time import time
import matplotlib.pyplot as pltsys.path.append('..')
import face3d
from face3d import mesh
2.1 加载网格数据(mesh data)
网格数据包含:顶点,三角网格数据,颜色(可选),纹理(可选)。这里使用颜色来表征人脸面部的纹理
# ------------------------------ 1. load mesh data
# -- mesh data consists of: vertices, triangles, color(optinal), texture(optional)
# -- here use colors to represent the texture of face surface
C = sio.loadmat('Data/example1.mat')
vertices = C['vertices']; colors = C['colors']; triangles = C['triangles']
colors = colors/np.max(colors)
这里示例的网格数据来自.mat文件,分别获取其中的vertices、color和triangles数据,并将颜色归一化。

2.2 修改顶点(vertices)
改变网格对象在世界坐标系中的位置。三维物体的变换方式有:缩放(scale)、旋转、平移等操作。这里在y通道上设置scale尺度为180,旋转30°,原地平移。
# ------------------------------ 2. modify vertices(transformation. change position of obj)
# -- change the position of mesh object in world space
# scale. target size=180 for example
s = 180/(np.max(vertices[:,1]) - np.min(vertices[:,1]))
# rotate 30 degree for example
R = mesh.transform.angle2matrix([0, 30, 0])
# no translation. center of obj:[0,0]
t = [0, 0, 0]
transformed_vertices = mesh.transform.similarity_transform(vertices, s, R, t)
其中,angle2matrix的源码如下:
def angle2matrix(angles):''' get rotation matrix from three rotation angles(degree). right-handed.Args:angles: [3,]. x, y, z anglesx: pitch. positive for looking down.y: yaw. positive for looking left. z: roll. positive for tilting head right. Returns:R: [3, 3]. rotation matrix.'''x, y, z = np.deg2rad(angles[0]), np.deg2rad(angles[1]), np.deg2rad(angles[2])# xRx=np.array([[1, 0, 0],[0, cos(x), -sin(x)],[0, sin(x), cos(x)]])# yRy=np.array([[ cos(y), 0, sin(y)],[ 0, 1, 0],[-sin(y), 0, cos(y)]])# zRz=np.array([[cos(z), -sin(z), 0],[sin(z), cos(z), 0],[ 0, 0, 1]])R=Rz.dot(Ry.dot(Rx))return R.astype(np.float32)
其作用是根据输入的角度生产旋转矩阵。
x:pitch 倾斜。正,向下看。
y: yaw 偏转。正,向左看。
z: roll 滚动。正,表示向右倾斜头部。
similarity_transform 的源码如下:
def similarity_transform(vertices, s, R, t3d):''' similarity transform. dof = 7.3D: s*R.dot(X) + tHomo: M = [[sR, t],[0^T, 1]]. M.dot(X)Args:(float32)vertices: [nver, 3]. s: [1,]. scale factor.R: [3,3]. rotation matrix.t3d: [3,]. 3d translation vector.Returns:transformed vertices: [nver, 3]'''t3d = np.squeeze(np.array(t3d, dtype = np.float32))transformed_vertices = s * vertices.dot(R.T) + t3d[np.newaxis, :]return transformed_vertices
输入三维顶点、缩放因子s、旋转角R和平移向量t3d,即可得到变换后的新坐标
2.3 修改颜色/纹理(添加光照)
添加点光源。光源位置在世界坐标系中定义
# ------------------------------ 3. modify colors/texture(add light)
# -- add point lights. light positions are defined in world space
# set lights
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)
其中,mesh.light.add_light定义如下
def add_light(vertices, triangles, colors, light_positions = 0, light_intensities = 0):''' Gouraud shading. add point lights.In 3d face, usually assume:1. The surface of face is Lambertian(reflect only the low frequencies of lighting)2. Lighting can be an arbitrary combination of point sources3. No specular (unless skin is oil, 23333)Ref: https://cs184.eecs.berkeley.edu/lecture/pipeline Args:vertices: [nver, 3]triangles: [ntri, 3]light_positions: [nlight, 3] light_intensities: [nlight, 3]Returns:lit_colors: [nver, 3]'''nver = vertices.shape[0]normals = get_normal(vertices, triangles) # [nver, 3]# ambient# La = ka*Ia# diffuse# Ld = kd*(I/r^2)max(0, nxl)direction_to_lights = vertices[np.newaxis, :, :] - light_positions[:, np.newaxis, :] # [nlight, nver, 3]direction_to_lights_n = np.sqrt(np.sum(direction_to_lights**2, axis = 2)) # [nlight, nver]direction_to_lights = direction_to_lights/direction_to_lights_n[:, :, np.newaxis]normals_dot_lights = normals[np.newaxis, :, :]*direction_to_lights # [nlight, nver, 3]normals_dot_lights = np.sum(normals_dot_lights, axis = 2) # [nlight, nver]diffuse_output = colors[np.newaxis, :, :]*normals_dot_lights[:, :, np.newaxis]*light_intensities[:, np.newaxis, :]diffuse_output = np.sum(diffuse_output, axis = 0) # [nver, 3]# specular# h = (v + l)/(|v + l|) bisector# Ls = ks*(I/r^2)max(0, nxh)^p# increasing p narrows the reflectionloblit_colors = diffuse_output # only diffuse part here.lit_colors = np.minimum(np.maximum(lit_colors, 0), 1)return lit_colors
Gouraud 着色法:是用于网格中插值的着色方法,可实现边缘的连续变化。在三维人脸中,通常由以下假设:
1、人脸表面是Lambertian,即朗博表面,只会反射低频的光
2、光照可以是点光源的任意组合。
3、无镜面反射。
这些参考了https://cs184.eecs.berkeley.edu/lecture/pipeline。但是这个网站好像挂掉了
get_normal函数在源码中另有定义,这里不再赘述。
输入的参数有:顶点坐标、三角网格数据、光源位置、光线强度。经过运算后输出加入点光源后的颜色数据。这里,如果没有相关知识,默认拿来使用即可。
2.4 修改顶点(映射,改变相机位置)
将对象从世界坐标系转换为相机坐标系,即观察者角度。如果使用标准相机,可忽略。
# ------------------------------ 4. modify vertices(projection. change position of camera)
# -- transform object from world space to camera space(what the world is in the eye of observer).
# -- omit if using standard camera
camera_vertices = mesh.transform.lookat_camera(transformed_vertices, eye = [0, 0, 200], at = np.array([0, 0, 0]), up = None)
# -- project object from 3d world space into 2d image plane. orthographic or perspective projection
projected_vertices = mesh.transform.orthographic_project(camera_vertices)
其中,相机坐标系lookat_camera的定义如下:
def normalize(x):epsilon = 1e-12norm = np.sqrt(np.sum(x**2, axis = 0))norm = np.maximum(norm, epsilon)return x/norm
def lookat_camera(vertices, eye, at = None, up = None):""" 'look at' transformation: from world space to camera spacestandard camera space: camera located at the origin. looking down negative z-axis. vertical vector is y-axis.Xcam = R(X - C)Homo: [[R, -RC], [0, 1]]Args:vertices: [nver, 3] eye: [3,] the XYZ world space position of the camera.5at: [3,] a position along the center of the camera's gaze.up: [3,] up direction Returns:transformed_vertices: [nver, 3]"""if at is None:at = np.array([0, 0, 0], np.float32)if up is None:up = np.array([0, 1, 0], np.float32)eye = np.array(eye).astype(np.float32)at = np.array(at).astype(np.float32)z_aixs = -normalize(at - eye) # look forwardx_aixs = normalize(np.cross(up, z_aixs)) # look righty_axis = np.cross(z_aixs, x_aixs) # look upR = np.stack((x_aixs, y_axis, z_aixs))#, axis = 0) # 3 x 3transformed_vertices = vertices - eye # translationtransformed_vertices = transformed_vertices.dot(R.T) # rotationreturn transformed_vertices
标准相机空间设定为:相机在原点;向下看,是负Z轴;垂直向量为Y轴。
输入参数为:顶点,摄像机在世界坐标系的位置,沿着相机视线中心的位置(默认为[0,0,0]),向上方向(默认为(0,1,0))
根据输入,计算出旋转矩R,并通过Xcam=R(X-C)计算出新顶点的位置。
2.5 转化为2D图像
设置图像宽高为256
# ------------------------------ 5. render(to 2d image)
# set h, w of rendering
h = w = 256
# change to image coords for rendering
image_vertices = mesh.transform.to_image(projected_vertices, h, w)
# render
rendering = mesh.render.render_colors(image_vertices, triangles, lit_colors, h, w)
mesh.transform.to_image部分的源码如下:
def to_image(vertices, h, w, is_perspective = False):''' change vertices to image coord system3d system: XYZ, center(0, 0, 0)2d image: x(u), y(v). center(w/2, h/2), flip y-axis. Args:vertices: [nver, 3]h: height of the renderingw : width of the renderingReturns:projected_vertices: [nver, 3] '''image_vertices = vertices.copy()if is_perspective:# if perspective, the projected vertices are normalized to [-1, 1]. so change it to image size first.image_vertices[:,0] = image_vertices[:,0]*w/2image_vertices[:,1] = image_vertices[:,1]*h/2# move to center of imageimage_vertices[:,0] = image_vertices[:,0] + w/2image_vertices[:,1] = image_vertices[:,1] + h/2# flip vertices along y-axis.image_vertices[:,1] = h - image_vertices[:,1] - 1return image_vertices
输入参数为:顶点坐标、图像宽高、透视选项(默认为Fals),通过计算得到二维顶点坐标。
mesh.render.render_colors的源码如下:(此为numpy版本)
def render_colors(vertices, triangles, colors, h, w, c = 3):''' render mesh with colorsArgs:vertices: [nver, 3]triangles: [ntri, 3] colors: [nver, 3]h: heightw: width Returns:image: [h, w, c]. '''assert vertices.shape[0] == colors.shape[0]# initial image = np.zeros((h, w, c))depth_buffer = np.zeros([h, w]) - 999999.for i in range(triangles.shape[0]):tri = triangles[i, :] # 3 vertex indices# the inner bounding boxumin = max(int(np.ceil(np.min(vertices[tri, 0]))), 0)umax = min(int(np.floor(np.max(vertices[tri, 0]))), w-1)vmin = max(int(np.ceil(np.min(vertices[tri, 1]))), 0)vmax = min(int(np.floor(np.max(vertices[tri, 1]))), h-1)if umax<umin or vmax<vmin:continuefor u in range(umin, umax+1):for v in range(vmin, vmax+1):if not isPointInTri([u,v], vertices[tri, :2]): continuew0, w1, w2 = get_point_weight([u, v], vertices[tri, :2])point_depth = w0*vertices[tri[0], 2] + w1*vertices[tri[1], 2] + w2*vertices[tri[2], 2]if point_depth > depth_buffer[v, u]:depth_buffer[v, u] = point_depthimage[v, u, :] = w0*colors[tri[0], :] + w1*colors[tri[1], :] + w2*colors[tri[2], :]return image
输入为顶点坐标、三角网格数据、网格颜色数据以及目标图片的长宽。
输出为目标的带纹理的二维图像数据。
# ---- show rendering
# plt.imshow(rendering)
# plt.show()
save_folder = 'results/pipeline'
if not os.path.exists(save_folder):os.mkdir(save_folder)
io.imsave('{}/rendering.jpg'.format(save_folder), rendering)
# ---- show mesh
# mesh.vis.plot_mesh(camera_vertices, triangles)
# plt.show()
这里展示二维图片效果:

也可展示出相加空间下的模型:

总结
这里主要介绍如何重现效果,以及解读如何从3D人脸数据转换为二维人脸数据的代码。那么,三维数据又包含哪些信息呢?如何实现不同的三维人脸生成呢?继续系列二。
相关文章:
三维人脸实践:基于Face3D的渲染、生成与重构 <一>
face3d: Python tools for processing 3D face git code: https://github.com/yfeng95/face3d paper list: PaperWithCode 该方法广泛用于基于三维人脸关键点的人脸生成、属性检测(如位姿、深度、PNCC等),能够快速实现人脸建模与渲染。推荐…...
Javascript 设计模式
设计模式的五大设计原则(SOLID)单一职责:一个程序只需要做好一件事。如果功能过于复杂就拆分开,保证每个部分的独立开放封闭原则:对扩展开放,对修改封闭。增加需求时,扩展新代码,而不是修改源代码。这是软件设计的终极…...
JAVA-文档工具screw-gui
前言 为什么萌生了写文档工具得想法,因为在项目开发得过程中,经常需要补充一些文档,比如数据库文档、详细设计文档等等,文档与项目相绑定,在项目需求新增或变更时,文档也需要反反复复得修改。 1. 数据库…...
开源鸿蒙南向嵌入学习笔记——NAPI框架学习(一)
开源鸿蒙南向嵌入学习笔记——NAPI框架学习(一) 前言——系列介绍 本系列文章主要是记录笔者在鸿蒙南向的学习与工作中的知识点笔记记录,其中不止会针对鸿蒙中的学习问题进行思考与记录,也会对涉及到的一些嵌入式等其他领域知识&…...
Spring - Spring框架概述面试题总结
文章目录01. 什么是Spring?02. Spring框架的设计目标,设计理念,和核心是什么?03. Spring的优点是什么?04. Spring框架中都用到了哪些设计模式?05. Spring有哪些应用场景?06. Spring由哪些模块组成…...
学习python好就业么
Python的普及与数据挖掘、人工智能和数值计算等领域的蓬勃发展相关,但同时也与普遍编程需求的增加有关。 Python作为人工智能的头号语言,一方面会吸引大量计划从事人工智能的人来学习,另一方面自然也带动了网络上对这门“新语言”的关注和讨…...
瑞幸咖啡的最终目标并不是做国内市场大哥
出品 | 何玺 排版 | 叶媛 日前,瑞幸咖啡发布2022年第四季度及全年财报。数据显示,在刚刚过去的2022年,瑞幸咖啡首次实现了营收超百亿,门店规模也超越老对手星巴克,成为了国内第一连锁咖啡品牌。 那么,瑞幸…...
GPT 模型介绍 | GPT3 / GPT3.5 + Flask | Github源码链接
1. 模型介绍 Chatgpt 使用与 InstructGPT相同的方法,使用来自人类反馈的强化学习 (RLHF) 来训练该模型,但数据收集设置略有不同。我们使用监督微调训练了一个初始模型:人类 AI 训练员提供对话,他们在对话中扮演双方——用户和 AI…...
蓝桥杯入门即劝退(二十六)组合问题(回溯算法)
-----持续更新Spring入门系列文章----- 如果你也喜欢Java和算法,欢迎订阅专栏共同学习交流! 你的点赞、关注、评论、是我创作的动力! -------希望我的文章对你有所帮助-------- 专栏:蓝桥杯系列 一、题目描述 给定两个整数 n …...
现代卷积神经网络(ResNet)
专栏:神经网络复现目录 本章介绍的是现代神经网络的结构和复现,包括深度卷积神经网络(AlexNet),VGG,NiN,GoogleNet,残差网络(ResNet),稠密连接网络…...
PTA:L1-019 谁先倒、L1-020 帅到没朋友、L1-021 重要的话说三遍(C++)
目录 L1-019 谁先倒 问题描述: L1-020 帅到没朋友 问题描述: 实现代码(只过了部分): L1-021 重要的话说三遍 问题描述: 实现代码: 无解析 L1-019 谁先倒 问题描述: 划拳是…...
STL常见容器之set/multiset、map/multimap
set/multiset—集合容器 特点 所有元素都会在插入时自动被排序 本质 set/multiset属于关联式容器,底层结构是二叉树实现 set和multiset区别 set不可以插入重复数据,而multiset可以set插入数据的同时会返回插入结果,表示插入是否成功multiset…...
ThreadLocal 实现原理
每个 Thread 中都存储着一个成员变量:ThreadLocalMap /** InheritableThreadLocal values pertaining to this thread. This map is* maintained by the InheritableThreadLocal class.*/ThreadLocal.ThreadLocalMap inheritableThreadLocals null; ThreadLocal 本…...
BUUCTF [羊城杯 2020]easyre 题解
一.查壳 64位无壳 二.主函数逻辑 可以得知flag长度为38,然后进行三次加密 第一次加密是base64加密,得到code1 第二次加密是将code1拆成四段赋给code2 第三次加密是将code2内的数字和字母移3位,其他字符不变 str2保存的是最终的加密字符 三.encode_one_base64 看到主函数…...
网络协议(十二):HTTPS(SSL/TLS、TLS1.2的连接)
网络协议系列文章 网络协议(一):基本概念、计算机之间的连接方式 网络协议(二):MAC地址、IP地址、子网掩码、子网和超网 网络协议(三):路由器原理及数据包传输过程 网络协议(四):网络分类、ISP、上网方式、公网私网、NAT 网络…...
九九乘法表--课后程序(Python程序开发案例教程-黑马程序员编著-第3章-课后作业)
实例9:九九乘法表 乘法口诀是中国古代筹算中进行乘法、除法、开方等运算的基本计算规则,沿用至今已有两千多年。古代的乘法口诀与现在使用的乘法口诀顺序相反,自上而下从“九九八十一”开始到“一一如一”为止,因此,古…...
在超算上安装文件树命令tree
超算平台使用的centos系统没有内置tree命令,需要通过源码安装。记录安装流程如下。 1. 下载源码包 下载链接如下: http://mama.indstate.edu/users/ice/tree/ 选择“Download the latest version” 如本文下载了源码包“tree-2.1.0.tgz”. 2. 源码包…...
论文投稿指南——中文核心期刊推荐(经济管理)
【前言】 🚀 想发论文怎么办?手把手教你论文如何投稿!那么,首先要搞懂投稿目标——论文期刊 🎄 在期刊论文的分布中,存在一种普遍现象:即对于某一特定的学科或专业来说,少数期刊所含…...
在vue中如果computed属性是一个异步操作怎么办?
在计算属性中使用异步方法时,可以使用async/await来处理异步操作。由于计算属性是基于它们的依赖缓存的,所以我们需要使用一个返回Promise的异步方法来确保计算属性能够正常运行。 下面是一个简单的示例,演示如何在计算属性中使用异步方法&am…...
SRP合批问题
1)SRP合批问题 2)多个Base相机渲染到同一个渲染目标,移动平台花屏的问题 3)粒子系统对GPU Instancing的支持 4)如何修改URP下场景和UI分辨率分离(不需要改颜色空间) 这是第327篇UWA技术知识分…...
2026论文降重神器盘点!毕业论文“AIGC痕迹”怎么破?
【CSDN技术引言:拒绝“开源背调”式的学术翻车】 哈喽各位同行和科研圈的战友们。最近后台私信快炸了,今年这届硕博生仿佛遭遇了“灭顶之灾”。某985高校前天出炉的抽检结果直接把大家看傻了:明明自己逐字逐句手敲的论文,知网查重…...
打破3D创作壁垒:零成本解决方案实现Blender到Unreal Engine的无缝资产迁移
打破3D创作壁垒:零成本解决方案实现Blender到Unreal Engine的无缝资产迁移 【免费下载链接】bl_datasmith Blender addon to export UE4 Datasmith format 项目地址: https://gitcode.com/gh_mirrors/bl/bl_datasmith 你是否也曾因格式转换丢失过数小时的工作…...
Spatial Audio(空间音频)与多声道环绕声:从5.1到7.1的沉浸式体验升级
1. 从立体声到环绕声:音频技术的进化之路 记得我第一次在朋友家体验5.1声道家庭影院时,那种子弹从耳边呼啸而过的感觉让我彻底震撼了。这完全颠覆了我对"好音质"的认知——原来声音可以如此立体、如此真实。要理解现代的空间音频技术…...
RK3568 NPU RKNN(五):RKNN-ToolKit2性能与内存评估实战解析
1. 环境准备与工具链搭建 在开始RKNN-ToolKit2的性能与内存评估之前,我们需要先搭建完整的开发环境。这里以野火LubanCat开发板为例,具体硬件配置为RK3568芯片4GB内存版本。开发主机建议使用Ubuntu 20.04系统,确保Python版本在3.6-3.8之间。 …...
基于MATLAB的模拟退火粒子群算法在含分布式电源配电网多目标优化中的应用
310.基于matlab的模拟退火粒子群算法对含分布式电源的配电网进行多目标优化,目标函数包括总有功网损、总投资与运行成本、电压稳定欲度。 和目标函数相关参数有单位分布式电源投资成本、运行成本,分布式电源设备使用年限、贴现率等。 经过优化得到最佳结…...
实战指南:基于快马生成电商订单自动化n8n工作流,无缝衔接shopify与crm
实战指南:基于快马生成电商订单自动化n8n工作流,无缝衔接shopify与crm 最近在帮朋友优化他们电商业务的后台流程,发现手动处理订单实在太费时间了。特别是遇到大促期间,订单量暴增,人工操作不仅效率低还容易出错。于是…...
3分钟搞定Vue时间轴组件:打造优雅时间线应用的终极指南
3分钟搞定Vue时间轴组件:打造优雅时间线应用的终极指南 【免费下载链接】timeline-vuejs Minimalist Timeline ⏳ with VueJS 💚 项目地址: https://gitcode.com/gh_mirrors/ti/timeline-vuejs 还在为Vue项目中的时间线展示而烦恼吗?t…...
HUNYUAN-MT企业级Java集成指南:构建高并发翻译微服务
HUNYUAN-MT企业级Java集成指南:构建高并发翻译微服务 1. 引言 想象一下,你负责的电商平台刚刚接到一个来自海外的百万级订单,但商品详情、用户手册全是中文。市场团队急等着把上万页的产品资料翻译成十几种语言,时间窗口只有短短…...
FreeRTOS+LwIP 2.2.0实战:手把手教你理解tcpip_thread的消息处理机制
FreeRTOSLwIP 2.2.0实战:深入解析tcpip_thread的消息驱动架构 在嵌入式网络开发中,理解协议栈的线程模型是构建稳定系统的关键。当FreeRTOS遇上LwIP,tcpip_thread就像一位不知疲倦的邮差,日夜处理着来自各方的网络报文。本文将带您…...
告别盲调:用eBPF uprobe给Go/Python应用函数调用画张“热力图”(附libbpfgo实战代码)
深度剖析eBPF uprobe技术:为Go/Python应用构建动态函数热力图 在云原生与微服务架构盛行的今天,后端服务的性能调优一直是开发者面临的挑战。传统性能分析工具往往需要重启服务或修改代码,这在生产环境中几乎不可行。而eBPF技术的出现&#x…...
