(九)OpenFace解析(GPUPixel)

智码先锋说
• 阅读 66

接下来的目标就是将这些坐标抽取到OpenGL项目中了。作为landmarks,输入进来。
当然,这一点也可以先不做,可以先找到人像部位对应的位置。
landmarks信息传入之后。
3.5 OpenFace索引解析
在网上搜索资料,终于找到了这一部分的点:

(九)OpenFace解析(GPUPixel)

传入pixel中:

(九)OpenFace解析(GPUPixel)

发现不太行,之前的这个是归一化过的。

(九)OpenFace解析(GPUPixel)

得归一化才可以。
但不知道这个归一化原则是什么,按照最小值和最大值进行归一化试试。
发现不太行。

(九)OpenFace解析(GPUPixel)

描点描的非常散乱。

(九)OpenFace解析(GPUPixel)

按照图像宽度和高度归一化后,就成功了。

(九)OpenFace解析(GPUPixel)

但是OpenGL渲染出来的图像还是不太行。
3.6 OpenGL渲染问题修复
现在渲染不出来的原因是关键点没了,但是关键点没了具体是怎么让OpenGL失能的,我也是不太清楚的。目前的主要矛盾到底是不是这个?
关键点补全了,是否能渲染齐全呢,我再试试。
是可以的。

(九)OpenFace解析(GPUPixel)

那也就是意味着:
OpenGL渲染是依据着landmarks数据进行渲染的。
而我们之前有结论:

几种美颜滤镜是依据着landmarks的数据进行处理的,美颜滤镜会把最终的版本送给GLFW库让其渲染。

由此我们得出结论:

  OpenGL渲染不出来的原因是因为滤镜给不出来最终答案。
  所以主要矛盾还是在滤镜答案的给出上。修复滤镜成了重中之重。    

如何进行滤镜的修复?
弄明白滤镜是如何拿着这些点进行“美颜的”,验证能否用68个点来实现滤镜的美化。
如何弄明白滤镜是如何拿这些点美颜的呢?

就得先框定滤镜代码的范围,明白每一部分的代码是什么意思,是怎么工作起来的原理。

3.6.1 美颜滤镜代码的范围

首先在app.cc部分创建了滤镜实例:

(九)OpenFace解析(GPUPixel)

这段代码是滤镜实例。

4个滤镜就是4个对象:

(九)OpenFace解析(GPUPixel)

是个派生类,派生自FaceMakeupFilter父类。继承create、而init进行了一个重写。这段代码的主要意图在于声明一个对象,是没有具体的实现的。
接下来看看create。

(九)OpenFace解析(GPUPixel)

这段代码是create静态成员函数的实现。该函数试图创建一个LipstickFilter实例,并返回一个指向该实例的指针。接下来调用init方法来初始化这个实例。如果初始化失败,则调用reset函数来释放这个智能指针所管理的对象,使其变成nullptr。最后,返回这个智能指针。

(九)OpenFace解析(GPUPixel)

接下来就是init函数了。
1,首先通过create创建了一个mouth图像的实例对象,并将这个智能指针传给了mouth变量,此时mouth具有了那个图像的纹理数据(宽度、高度、通道数、像素信息)。
2,然后再把这个纹理数据给了全局变量,这个时候大家都能访问了。
(九)OpenFace解析(GPUPixel)
3,以下代码设置了纹理在人脸上的应用区域。
(九)OpenFace解析(GPUPixel)
其中frambounds是个结构体:
(九)OpenFace解析(GPUPixel)
用来定义坐标和长宽。然后传入到全局变量中:

(九)OpenFace解析(GPUPixel)
4,最后调用了基类的初始化函数:
(九)OpenFace解析(GPUPixel)
(1)先初始化顶点着色器、再初始化片段着色器。
(2)再设置关键的参数和引用,这些都是渲染时所用到的。
(3)设置滤镜的强度信息;
(4)设置关键点信息。
这样就初始化完一个滤镜了。
之后这些滤镜应用关键点信息。
(九)OpenFace解析(GPUPixel)
这一步很关键了,landmarks进来之后。这些滤镜修改了哪些数据?
进来之后,存储在了这个位置:
(九)OpenFace解析(GPUPixel)
这儿打印一下试试水。
(九)OpenFace解析(GPUPixel)
(九)OpenFace解析(GPUPixel)
是136...我看看landmarks是多少吧。
(九)OpenFace解析(GPUPixel)
他也是136。不过确实是136. 68*2可不就等于136吗。这没毛病。
这个回调函数的作用就是让每个滤镜都能获取关键点数据。
之后就应用这个函数对图像进行一个应用了。
(九)OpenFace解析(GPUPixel)
gpuSourceImage是图像处理的起点,接下来深入到内部看一看。
(九)OpenFace解析(GPUPixel)
最后发现,是这个函数进行的处理。
(九)OpenFace解析(GPUPixel)
就是它进行的更新。
在这其中:
(九)OpenFace解析(GPUPixel)
这一步将切换到专门用于美妆效果的渲染程序。传入的参数是之前通过顶点和片段着色器初始化的,专门用于处理美妆效果。然后将然后将landmarks作为顶点位置信息,这些关键点数据在SetFaceLandmarks方法中已经被转换和存储。通过glVertexAttribPointer将这些顶点位置信息传递给GPU,这些位置信息将决定美妆效果被应用的具体位置。
(九)OpenFace解析(GPUPixel)

faceTextureCoordinates方法提供了美妆效果纹理的坐标,这些坐标基于面部关键点进行了调整,以确保美妆纹理正确映射到面部的特定区域(如嘴唇或眼睛周围)。
打印一下coord看是不是136个点。
答案不是:
(九)OpenFace解析(GPUPixel)
是222个点,这意味着,使用的111个点版本的,而不是222个点的版本的。
(九)OpenFace解析(GPUPixel)
打印了这个东西,发现OpenGL是按照这个来打印的。
(九)OpenFace解析(GPUPixel)
这个坐标是按着这个来的:
(九)OpenFace解析(GPUPixel)

现在弄清楚这些坐标是什么玩意儿,为什么有了landmarks,还有这个纹理坐标呢?
面部关键点(landmarks):这些是从面部检测算法得到的,标识了面部的特定特征点位置,比如眼睛、鼻子、嘴巴的轮廓等。这些关键点用于确定美妆效果应该被应用到图像的哪些具体位置上。
faceTextureCoordinates提供的一组纹理坐标,用于控制美妆纹理在面部的具体投影方式。
那现在就得重新调整这部分纹理坐标了。
(九)OpenFace解析(GPUPixel)

暂时将landmarks放上去了。现在都一样了。
然后
(九)OpenFace解析(GPUPixel)
这一步进行一个绑定并应用。
之后进入这段代码:

(九)OpenFace解析(GPUPixel)

auto face_indexs = this->getFaceIndexs();这行代码调用了getFaceIndexs函数,获取一个包含面部数据索引的数组。这些索引定义了如何将关键点连接起来形成多个三角形,这些三角形最终构成了整个面部的网格模型。
关键核心就是face_indexs.data(),这部分代码。
(九)OpenFace解析(GPUPixel)

得把它改一下。

(九)OpenFace解析(GPUPixel)
手动加完之后,再试试。

(九)OpenFace解析(GPUPixel)

还是不太行。
那这个问题就得仔细琢磨琢磨了。
可能的问题:
(1)片段着色器可能返回了高红色和绿色分量、低蓝色分量的颜色。这可能是混合函数逻辑的结果。fgColor.a(在片段着色器中的alpha通道)可能存在问题。如果alpha始终为0,bgColor可能没有正确与前景色混合。
(2)纹理可能没有正确绑定,这可能意味着inputImageTexture2(嘴唇纹理)没有被正确采样。
(3)传递给着色器的纹理坐标或顶点可能存在问题。像intensity和blendMode这样的uniform变量可能设置不正确,导致意外的混合结果。
接下来的两条路:
插值、或者改底层代码。

插值这条路线还是最简单的。还是这么来吧。

3.6.2 插值路线
这条路线要怎么走呢?

第一步,先恢复之前的正常代码吧。恢复完成之后。

下一步选择一张差异大点的图片然后将VNN的landmarks保存下来,识别每一个点的含义,然后用这个材料插值到Openface生成的这个landmarks里面去。

眉毛的地方,有差距,还有脸的位置。既然如此。
然后开始识别之前每一个点的意思和现在每一个点的意思。
先看看能不能用Opencv给每个点打一个标号。

这是Openface。
再看看VNN。

然后将VNN的值输出出来。

(九)OpenFace解析(GPUPixel)

已获得。
接下来开始插值。核心思路是将VNN的部分值用OpenFace代替。
插完值后运行,有一个不太好的结果。

我总感觉人脸识别失败了。
我先尝试下原始的数据试试。
原始的数据是正常的:

那也就意味着,只要重新编译了,这个图像识别的数据就已经定死了。
问题还是出在插值上,插值有问题。
这样,先将原有的landmarks的数值插进去试试。
有问题。

并且打印出来的这个值也对不上。
这绝对是把皇军给我的数据都吃了回扣了!!!!!
这个landmarks值出来的不太对。
(九)OpenFace解析(GPUPixel)
我发现这些数据大有玄机。
Tmd,数值重复了!!!

(九)OpenFace解析(GPUPixel)

清澈多了。
再换上试试。
(九)OpenFace解析(GPUPixel)
这......是数量出问题了,重新打印后,恢复正常了。

到此为止。重新插值。

然后创建两个文件夹。分别是插值前和插值后效果展示。
去除掉OpenGL界面,转换成图片输出的形式。

点赞
收藏
评论区
推荐文章
Karen110 Karen110
3年前
一篇文章带你了解JavaScript htmldom 元素
这篇文章将教会大家如何查找和访问网页中的HTML元素。一、找到HTML元素通常,使用JavaScript,想操作HTML元素。要做到这一点,必须先找到元素。有几种方法可以做到这一点。找到DOM中的HTML元素的最简单的方法,是利用元素的id。使用id"intro"找到元素:varmyElementdocument.getElementById("in
高防加速CDN 高防加速CDN
2年前
域名解析平台如何解析域名?
域名解析平台顾名思义就是帮助进行域名解析的服务管理平台,当然它的自助性对应着它的免费。在域名解析平台上,不管你的域名是在哪里申请的,都可以在这个平台上进行解析。域名解析平台域名解析平台就是提供域名解析服务,在新网注册的域名,可使用新网域名解析服务器,在域名
Wesley13 Wesley13
3年前
iOS下集成Lua与socket,cjson第三方库(非游戏引擎下)
文章首发于开源中国,这里是作者本人转载公司的项目最近要使用Lua进行全平台的解析,iOS的集成在网上已经有了教程,但是lua三方库的集成一直出现错误。在混迹github,stackoverflow许久后,终于在cocoa2dx的相关问题中找到了一个合适的方法,在此总结成篇,以供各位参考。先抛出几篇基础教程:将Lua嵌入
Stella981 Stella981
3年前
CoreGraphics 之CGAffineTransform仿射变换(3)
 CoreGraphics的仿射变换可以用于平移、旋转、缩放变换路径或者图形上下文。  (1)平移变换将路径或图形上下文中的形状的当前位置平移到另一个相对位置。举例来说,如果你在(10,20)的位置处画一个点,对它应用(30,40)的平移变换,然后绘制它,这个点将被绘制在(40,60)的位置处。为了创建一个平移变换,使用CGAffineTra
Stella981 Stella981
3年前
OpenGL NDC 左手还是右手?
之前看到一篇文章说默认情况下OpenGLNDC是基于左手坐标系。我在我之前的博文(https://my.oschina.net/iirecord/blog/824029)中也提到过。之后我在想为什么OpenGLNDC是基于左手坐标系?真的是基于左手坐标系吗?所以这篇博客就是来找到答案。再谈OpenGL坐标变换(CoordinateT
Stella981 Stella981
3年前
Http请求(Day03)
ServletRequest接口​在容器中接收到Web客户端的HTTP请求之后会先解析该请求,并创建与之相对应的请求对象和响应对象,而创建的请求对象的类型就是ServletRequest类型。1、ServletRequest​简单点说就是该接口封装了请求信息。可以从中获取
Wesley13 Wesley13
3年前
C# 曲线上的点(二) 获取距离最近的点
如何在一条曲线上,获取到距离指定点最近的点位置?!(https://oscimg.oschina.net/oscnet/6ce94b2cd799359221f22a5742193a09296.png)与上一篇C曲线上的点(一)获取指定横坐标对应的纵坐标值(https://www.oschina.net/action/GoToLink?
Stella981 Stella981
3年前
Git 版本控制管理(一)
  Git是一个分布式版本控制工具,它的作者 LinusTorvalds 是这样给我们介绍Git —— Thestupidcontenttracker(傻瓜式的内容跟踪器)关于Git的产生背景在此不做讲解,有兴趣的可以搜索一下。先介绍一下Git的特点,主要有两大特点:版本控制:可以解决多人同时开发的代码问题,也可以解决找
Wesley13 Wesley13
3年前
IDEA实用设置,提高开发速度。
1.代码提示设置FileSettingsKeymapMainmenuCodeCompletionBasic右键点击RemoveKey移除掉默认快捷键,再Add一个新的快捷键。当然Alt/已经被占用了,可以找到(搜索框右边那个放大镜,点一下按对应的快捷键就可以搜索,注意是右边)删掉,也可以设置别的。SmartType
Stella981 Stella981
3年前
Python日志库logging总结
在部署项目时,不可能直接将所有的信息都输出到控制台中,我们可以将这些信息记录到日志文件中,这样不仅方便我们查看程序运行时的情况,也可以在项目出现故障时根据运行时产生的日志快速定位问题出现的位置。1、日志级别Python标准库logging用作记录日志,默认分为六种日志级别(括号为级别对应的数值),NOTSET(0)、DEBUG(10)
贾蔷 贾蔷
1个月前
力扣746:三步通关最小花费爬楼梯
题目解析:站在楼梯的某个台阶时,需要支付当前台阶对应的体力值costi,之后可以选择向上爬1或2个台阶。最终目标是到达‌楼层顶部‌(即数组末尾之后的位置),且初始位置可选择下标0或1的台阶作为起点。要求找出到达顶部的‌最小花费路径‌。例如输入cost