转载:https://blog.csdn.net/torchlight2012/article/details/76066790

在 AR Camera开发记录(一) – Rajawali的使用末尾的部分,介绍了如何动态修改模型的顶点坐标。 
那个例子是用一个最简单的矩形平面模型来说明的。 
说白了就是找到需要修改的顶点,然后设置新的坐标值。 
而对于3D人脸模型来说,情况要复杂一些,想找到需要的顶点相对来说比较麻烦。 
本文主要说一下动态修改3D人脸模型会遇到的问题,以及在了解了这些问题之后如何自己创建3D人脸模型。

要注意的问题

首先说明下,笔者的项目中用到的3D人脸模型的格式为Obj。 
在Rajawali库的wiki页面中有如下说明:

The most important thing is that the model should be triangulated. 
Rajawali doesn’t accept quads, only tris. In Blender, this is an option you can select in the exporter. 
In a program like MeshLab, this is done automatically.

意思就是在Rajawali中,Obj模型只支持由三角形的面组成,不支持四边形的面。

而上一篇文章用到的矩形平面模型不同,它是Rajawali内置的,支持四边形。 
我们把它的顶点坐标读出来看,也确实是一个用4个顶点组成的四边形的面。 
因此在处理3D人脸模型的顶点的时候就有差异了。

再来看看这张来自opengl-tutorial的教程图例: 

这张图左边部分,说的是OpenGL在没有使用索引缓冲(index buffer)的情况下,每个三角形需要由3个顶点组成,两个三角形总共需要6个顶点。而右边部分使用索引缓冲,复用了公共点v1和v2,两个三角形只需4个顶点。

Rajawali解析出来的Obj模型的顶点组成是按左边的方式。 
那么在修改Obj模型顶点坐标的时候就要注意了,模型中有些线段相交的地方,看上去是一个公共顶点,实际上不止一个。

下面来看看具体示例。 
在Blender中创建一个由两个三角形组成的四边形: 

在Rajawali中加载这个模型,然后读取各个顶点坐标,得到如下位置关系: 
 
这里把两个三角形分开一些是为了看清楚,实际上v0和v4、v2和v5是重合在一起的。

如果像上一篇文章那样,想让左上角的顶点坐标动态变化,只修改v0是不对的。 
下面看看代码:

@Override
protected void onRender(long ellapsedRealtime, double deltaTime) {long time = ellapsedRealtime / 10000000;double amp = 0.5 * Math.sin(Math.PI * time / 180.0);FloatBuffer vertBuffer = mGeometry3D.getVertices();// 修改v0的y坐标,即第1个数据,赋值为ampvertBuffer.put(1, (float) (amp));mGeometry3D.changeBufferData(mGeometry3D.getVertexBufferInfo(), vertBuffer, 0, vertBuffer.limit());
}

效果如下: 

可以明显看到,只修改v0的话,只影响了左下方的三角形,右上方的三角形没有变化。 
因此还得把v4也一起修改,才能达到预想的效果:

    FloatBuffer vertBuffer = mGeometry3D.getVertices();// 修改v0的y坐标,即第1个数据,赋值为ampvertBuffer.put(1, (float) (amp));// 修改v4的y坐标,即第13个数据,赋值为ampvertBuffer.put(13, (float) (amp));

修改后的效果如下: 

创建3D人脸模型

搞清楚上面的问题后,我们开始创建3D人脸模型。 
如何建这个人脸模型,跟使用到的人脸检测技术有关。 
比如Dlib返回68个关键点,Face++或SenseTime等返回106个关键点: 

至于怎么根据这些关键点去构成人脸模型,估计是跟面部肌肉的牵拉有关,笔者也没搞懂,就找了别人的来参考: 
 
视频地址

然后就是依样画葫芦,对关键点进行连线,线与线再组成面: 

如何获得顶点顺序

从前文可知,在Rajawali中加载这个人脸模型的话,每个相邻的三角形面相交的地方,不共用顶点。 
如果想修改这个相交点的话,必须把组成这些相邻的三角形的点都找出来。人工去找显然不实际。因此本节内容主要是要解决这个问题。

我们先来看看这个人脸模型的顶点顺序。 
这里笔者用了另一个软件MeshLab来查看。 
打开模型以后,点击上方工具栏的Render,再在其下拉菜单中点击ShowLabels,就会显示模型每个顶点的序号: 
 
这里的序号起始是从0开始的。从图中可知模型左眼角的顶点序号是20。

我们再用文本编辑器打开Obj模型文件,找到以“f”开头的部分:

...省略...
f 23/1/1 21/2/1 31/3/1
f 23/1/2 24/4/2 21/2/2
f 24/4/3 25/5/3 21/2/3
f 25/5/4 26/6/4 21/2/4
f 21/2/5 26/6/5 6/7/5
...省略...

每一行f描述一个面由哪些点组成的。 
f后面有三组数字,每组由三个数字组成,以斜杠隔开。 
每组的第一个数字是顶点的序号,第二个数字是贴图坐标点的序号,第三个数字是顶点法线的序号。

我们暂时只关注第一个数字,即顶点的序号。这里的序号起始是从1开始的。 
以第一行数据为例:

f 23/1/1 21/2/1 31/3/1

表示由第23、21和31的顶点组成一个三角面。 
也就是上图这个位置的面: 

以此类推,结合Obj文件和MeshLab可以看到,人脸模型的左眼角位置,是9个三角形的公共点。 
也就是说,要在Rajawali中修改这个位置的顶点的话,需要改9个点: 

先人工找一下这9个点。 
把Obj文件中以”f”开头的行找出来,去掉其他部分,只保留顶点序号:

...省略...
23
21
31
23
24
21
24
25
21
25
26
21
21
26
6
...省略...

那么含有21(左眼角位置)的点的顺序就是: 
2,6,9,12,13…省略

要人工找这9个点的顺序就太费劲了,但如果用代码来找的话,其实也很简单。 
笔者用Python写了两个方法来找:

# 获取顶点列表
# path是Obj文件的路径
def read_obj_indices(path):indices = []with open(path, 'r') as f:lines = f.readlines()for line in lines:data = line.split()prefix = data[0]# 获取以'f'开头的行if prefix.startswith('f', 0, len(prefix)):# 去掉'f'data.remove('f')for element in data:# 找到第一个'/'的位置pos = element.index('/')# 截取'/'之前的部分,即顶点的序号element = element[0:pos]indices.append(element)return indices
# 对顶点序号进行排序
# indicess是上一个方法获取的顶点列表
# size是顶点数量
def order_obj_indices(indices, size):lists = [[] for t in range(size)]i = 1for index in indices:lists[int(index) - 1].append(i)i += 1for element in lists:print element

用上面的方法排序后打印出来,找到第21行对比:

[2, 6, 9, 12, 13, 69, 70, 74, 114]

与人工找的顶点顺序是一致的。

AR Camera开发记录(二) -- 3D人脸模型相关推荐

  1. 3D人脸模型Flame ----《Learning a model of facial shape and expression from 4D scans》论文讲解及代码注释

    前文 在阅读论文前,首先我们要有一定的知识储备,包括人脸建模,表情制作,旋转转换等,才能方便我们的论文理解,所以首先我会讲解一些关键的知识点. Flame模型的作用? Flame是一个3D人脸的通用模 ...

  2. 3D人脸模型建模工具,用于人脸建模,3DMM系数匹配

    TF_FLAME.rar_人脸建模_3dMM_其他下载-pudn.com 3DMM进行人脸重建中的配准方法 3DMM下载地址 GitHub - YadiraF/face3d: Python tools ...

  3. Android内嵌Unity开发简单的3D动态模型

    Unity端就不仔细说了,下面主要讲Unity打包出来在Android studio后的操作. DEMO地址:Stringf/UnityAndroid3DModel (github.com) Unit ...

  4. 车牌识别系统开发记录(二) 车牌定位

    这里面我要具体介绍的检测车牌方法的步骤如下: 首先利用Sobel滤波器对灰度图像进行滤波,突出图像中的垂直边缘信息 利用数学形态学方法: Close(先膨胀再腐蚀,填充内部空隙) 利用findCont ...

  5. 微信小程序商城开发记录二之数据表结构设计

    文章目录 前言 1.用户表 2.产品表 3.商品品牌表 4.商品分类表 5.订单表 6.订单商品信息表 7.购物车表 8.收获地址表 9.省市区地址联动表 10.广告信息表 11.优惠券表 12.优惠 ...

  6. 农场游戏的开发记录二

    决定做成控制台输入的游戏模式 因为不懂动画那些,先做一个古老的控制台输入命令的游戏吧. 今天在主类增加了一个控制台输入的功能,和一个仓库类.一个农场类.另外改成类名首字母小写的错误.很晚了才开始改,所 ...

  7. 农场游戏开发记录二十

    我又来了.刚刚还带着有点兴奋的心情,现在又有些郁闷.因为想到深入编程技能的话,还需要付出非常多的努力,而自己年纪真是不小了,真是后悔为何十几年前没有坚持下来. 最近在学习设计模式,单例模式.工厂模式等 ...

  8. 3d slicer调整窗宽窗位_3D人脸模型月销量上千单,谁在打印,谁在帮打?

    原标题:3D人脸模型月销量上千单,谁在打印,谁在帮打? 编辑导读:前段时间,一则"面具可代替人脸解解锁手机"的新闻让人们感觉不寒而栗.如果面具可以代替人脸解锁手机,那么是否也可以完 ...

  9. 3d 模型换人脸_3D人脸模型月销量上千单 谁在打印,谁在帮打?

    近日央视新闻报道了一则"面具可代替人脸解锁手机"的内容,在网上引发了广泛关注.在新闻中,有科研人员用一个面具代替人脸进行了安全测试,结果是"假脸"成功地通过脸部 ...

最新文章

  1. [收藏] Java 编程的动态性
  2. 【C 语言】文件操作 ( 配置文件读写 | 写出或更新配置文件 | 追加键值对数据 | 更新键值对数据 )
  3. 区块链100讲:据说,80%的人都搞不懂哈希算法
  4. 定义和使用含有泛型的类
  5. 《C++ Primer》14.4节练习(部分)
  6. Java 7:使用NIO.2进行文件过滤–第3部分
  7. diskfileitemfactory 需要的包_《开心枕和生气包》中班下学期社会教案
  8. LWIP源代码文件目录解析
  9. ASP.NET Core部署在IIS上
  10. helm安装istio_第五章 用Helm部署Istio
  11. 对Javascript“闭包”的简单理解
  12. 2016OSC源创会年终盛典-架构数据场-陈沙克
  13. 通过C++实现Android Native Service
  14. html5仿苹果通讯录效果,仿IOS手机通讯录效果
  15. Filter过滤指定ip地址
  16. 算法导论第22章部分答案
  17. 魔兽世界忘记账号角色服务器,魔兽世界里我知道了他的游戏角色名字怎样查到他的战网通行证...
  18. 图像检索代码python_python-图像检索
  19. 钢笔墨水能否代替打印机墨水_喷墨打印机该用染料墨水还是颜料墨水?
  20. 2019年的软件百强企业榜单

热门文章

  1. 简要概述服装设计管理
  2. 弹钢琴flash游戏
  3. web期末作业设计网页 HTML+CSS+JavaScript仿王者荣耀游戏新闻咨询(网页设计期末课程设计)...
  4. 解决CUDA out of memory. Tried to allocate 2.10 GiB (GPU 0; 14.76 GiB total capac
  5. Dubbo Cluster集群那点你不知道的事。
  6. Bluetooth技术学习笔记 ——HFP控制功能之连接管理
  7. c程序设计语言k amp rpdf6,Adaptive Interpupillary Distance Adjustment for Stereoscopic 3D Visualization...
  8. 修改北斗AR地球仪激活验证
  9. 2023年安徽省工业项目投资导向计划奖励补贴、入库申报类别范围
  10. 骑行318、 2016.7.18