在現在這個靠臉吃飯的時代,如果你沒有一張瓜子臉一雙大眼睛,那還怎麼去吃飯呢,而現在一些直播視頻App相機應用基本都會有瘦臉大眼效果.本文是在OpenGl環境下,在shader中通過對像素位置進行偏移來實現放大縮小的,實現起來快速簡單,也是各大主流應用裁劉德基本方式.

舉個栗子

首先在這裏給大家看一個效果圖

臉型微調之後

細心的朋友應該可以看出來兩張圖片的區別,圖二明顯臉更尖了,更趨近於瓜子臉,眼睛也稍微大了點.這裏其實就是用到了最常見的拉伸和縮放處理.

原理解析

其實整個過程可以分成一下三種類型的處理

  • 圓內放大
  • 圓內縮小
  • 向某一點拉伸

其實拉伸縮放理解並不困難,大家都對Bitmap有進行拉伸或者縮放過,但是這裏的拉伸和縮放同Bitmap的拉伸縮放最大的區別就是要考慮到處理範圍的周邊像素,要使整個圖片看起來過度正常,如果只是使用Bitmap的拉伸縮放必然會在邊界處像素差別很大,能看到一條很明顯的像素分割線,而且這樣還會丟失一些像素,我們來看一個簡單的放大操作效果.

原圖

放大後

這是普通的放大操作,存在很明顯的一條圓形邊界,而且放大前的好多像素直接被放大後的像素給替代(覆蓋)了,像素丟失了,眼睛的其他部位都沒有了,這顯然不是我們想要的結果.這種簡單的放大操作實現起來也很簡單,但是他的適用也很侷限

float dis=distance(vec2(gPosition.x,gPosition.y/uXY),vec2(centerPoint.x,centerPoint.y)); if(dis< RADIUS){ gl_FragColor=texture2D(vTexture,vec2(aCoordinate.x/2.0+0.25,aCoordinate.y/2.0+0.25)); } gl_FragColor=orignalColor;

而我們這裏用到的是局部拉伸和縮放,顯然用這種算法是不合理的.

而局部微調中用到的算法原理更多是把局部區域進行微小的擠壓,將一些像素進行縮放拉伸的同時會對另一些像素進行擠壓,基本不會產生像素的丟失和明顯的分界線.本質是對像素的座標值進行偏移(或者說是對每個位置上的像素進行偏移).

一個形象的比如:我們可以將一幅圖像的所有像素點映射到一張面片上,面片的厚度表示單位面積上原始像素的多少,開始時面片厚度是一致的,我們可以設爲1,越厚則便是這裏堆積的原始像素的密度越大,局部放大就是將面片局部進行擀薄,且厚薄區域的過度圓滑,該區域的原始像素值變少並且向外擴張,這樣原本離中心距離爲1的像素跑到距離爲3的位置,距離爲2的像素跑到距離爲5的位置,以此類推,就會產生放大的效果;但是這種放大並不會有明顯的過度.假設我們的放大半徑爲100,這樣離中心35的像素移動到離中心71的位置,而後面的29個距離上卻堆積了原來從36到100的像素,並且產生一種平穩的過度.

下面看一下具體的實現.

圓內放大

//圓內放大
vec2 enlargeFun(vec2 curCoord,vec2 circleCenter,float radius,float intensity,float curve)
{
    float currentDistance = distance(curCoord,circleCenter);

{
        float weight = currentDistance/radius;

weight = 1.0-intensity*(1.0-pow(weight,curve));//默認curve 爲2 ,當 curve 越大時, 會放大得越大的,
        weight = clamp(weight,0.0,1.0);
        curCoord = circleCenter+(curCoord-circleCenter)*weight;
    }
    return curCoord;
}

這是圓內放大算法, 
輸入:座標,放大中心座標,放大半徑,放大比例係數,放大算法參數. 
返回:放大之後應該取的像素的位置.

爲了分析這個算法到底是怎麼實現的,我們先可以簡單的另 intensity = 1.0,curve = 2.0;此時再看這個算法其實就是pow函數,也就是平方函數了,這個平方函數是怎麼實現放大的呢.

其實從圖像中大家就能明白原理了,座標經過平方處理之後,本來A點應該取A1像素,結果取的是A2像素,也就是離中心0.25位置的像素B1被放在了離中心0.5的位置A上,這顯然就是放大的操作.由於橫縱座標一一對應,所以該算法並不會造成像素的丟失,只是會在放大區域的邊緣內有一部分像素比較密集的區域.

上面的分析是建立在特殊值下的,我們再回到這個函數本身, 
curve 是我們的pow函數的次方值,由於我們考慮的都是[0,1]區間的,所以該值越大,離中心點向外擴散也越厲害,放大效果越大. 
intensity 的取值是[0,1]當它取1時就是我們上面分析的情況,會最大化的利用pow次方產生的座標偏移來取像素,若它爲0,則不會產生任何縮放效果,intensity 是一個影響因子,一個對 pow函數產生的座標偏移的採用度,intensity越大則會更大化利用 pow 函數產生座標便宜作爲最後的座標偏移.

圓內縮小

vec2 narrowFun(vec2 curCoord,vec2 circleCenter,float radius,float intensity,float curve)
{
    float currentDistance = distance(curCoord,circleCenter);

{
        float weight = currentDistance/radius;
        weight = 1.0-intensity*(1.0-pow(weight,curve));//默認curve 爲2 ,當curve 越大時, 會縮小得越小的,
        weight = clamp(weight,0.0001,1.0);
        curCoord = circleCenter+(curCoord-circleCenter)/weight;
    }
    return curCoord;
}

上面分析了圓內放大,看一下縮小的代碼,其實也不難理解,也是利用pow函數進行像素座標的偏移.這裏就不多家分析了.

向某一點拉伸

// 拉伸
vec2 stretchFun(vec2 textureCoord, vec2 originPosition, vec2 targetPosition, float radius,float curve)
{
    vec2 offset = vec2(0.0);
    vec2 result = vec2(0.0);

vec2 direction = targetPosition - originPosition;

float infect = distance(textureCoord, originPosition)/radius;

infect = pow(infect,curve);// 默認 curve 爲1,這個值越大,拉伸到指定點越圓潤,越小越尖
    infect = 1.0-infect;
    infect = clamp(infect,0.0,1.0);
    offset = direction * infect;
    result = textureCoord - offset;

return result;
}

輸入:座標,拉伸中心座標,拉伸目標座標,拉伸半徑,拉伸算法參數. 
返回:放大之後應該取的像素的位置.

拉伸和縮放原理其實是一樣的,只是理解起來有些差距.我們還是設 curve = 2,對其原理進行分析.

上圖是我對拉伸原理的一個簡單描述,A爲拉伸中心座標,B爲拉伸目標座標,經過拉伸後,圓1上的像素會被平移到圓1’,圓2上的像素會被平移到圓2’.以A爲圓心的同心圓經過拉伸之後都會被平移,離A越近平移的距離越遠,A點直接平移到B點, 而R處則不會平移.看到這裏應該已經知道怎樣將一個圓臉變成瓜子臉了吧.當然這都是微調,如果調整過大會產生不自然的效果.curve 值越大,拉伸到指定點越圓潤,越小越尖.

上面只是我對人臉變形時的原理進行分析,要想使用變形,首先要確定人臉特徵點,有了這些特徵點,你才知道縮放中心半徑等等,而使用時往往不是一步就能達到理想效果,比如說我們大眼 一般是首先對一個比較大的包含眼睛的區域進行放大,然後再對眼睛中心瞳孔位置進行進一步放大.實際使用時爲了達到某種效果一般都是對這幾種操作進行組合使用,而且是多次操作.

附一篇优秀博客:https://www.jianshu.com/p/3334a3af331f

转载于:https://my.oschina.net/HeroOneHY/blog/3094905

在OpenGL中利用shader進行實時瘦臉大眼等臉型微調相关推荐

  1. 在OpenGL中利用shader进行实时瘦脸大眼等脸型微调

    在OpenGL中利用shader进行实时瘦脸大眼等脸型微调 在现在这个靠脸吃饭的时代,如果你没有一张瓜子脸一双大眼睛,那还怎么去吃饭呢,而现在一些直播视频App相机应用基本都会有瘦脸大眼效果.本文是在 ...

  2. html中md5如何使用方法,html中使用js進行登錄md5加密提交並重定向新頁面

    在web開發中有時需要將用戶的一些信息在客戶端加密后進行提交,然后在web后端進行驗證,可以提高頁面傳輸過程中的安全性.本例實現一個簡單的加密登錄demo,例子僅僅為了演示使用,實際使用時可以根據實際 ...

  3. [心得] 如何利用liquibase進行資料庫版本控制 - 實際練習

    透過上一篇的基本觀念介紹,希望大家應該有一點點感覺了! 這篇我們就來做個簡單的版本演練,加深印象吧! 我使用的環境如下 System : Windows 7 Database : SQL Server ...

  4. OpenGL中的Shader

    http://blog.csdn.net/huangcanjun187/article/details/52474365 学习总结自:http://learnopengl.com/#!Getting- ...

  5. Unity中利用shader将贴图变为黑白色

    使用Shader直接将贴图变为黑白色 Unity游戏开发中,有时候需要将贴图变为黑白色,以标记为两种状态,大部分时候都是美术做出两张图,一张彩色的一张黑白的,然后来回替换,最近为了省贴图资源,于是在网 ...

  6. airflow 進行後端大數據中ETL處理(草稿)

    情境說明: 公司接了外部的數據源,要把資料同步到gcp雲端來,資料表很多 ,我們使用了多種方式把資料寫入到我們公司位於gcp上的bigquery 資料表 問題 資料表很多樣,不同的資料表有不同的ETL ...

  7. mysql+分表+1168,MySQL使用MERGE進行分表實現

    使用MERGE進行分表: 發現一個MERGE表一直出現Unable to open underlying table which is differently defined or of non-My ...

  8. Python之Pandas:利用pandas实现行数据添加,即将字典格式的数据,按照行数据,从头开始循环添加到dataframe中

    Python之Pandas:利用pandas实现行数据添加,即将字典格式的数据,按照行数据,从头开始循环添加到dataframe中e中 目录 利用pandas实现行数据添加,即将字典格式的数据,按照行 ...

  9. 如何利用Shader来渲染游戏中的3D角色

    杨航最近在学Unity3D 本文主要介绍一下如何利用Shader来渲染游戏中的3D角色,以及如何利用Unity提供的Surface Shader来书写自定义Shader. 一.从Shader开始 ...

最新文章

  1. 为数据赋能:腾讯TDSQL分布式金融级数据库前沿技术
  2. 750px设计稿处理方式
  3. 左连接、右连接、交叉连接、全外连接
  4. 序列的修改、散列和切片
  5. iOS自动打开闪光灯
  6. 计算机中word音乐符号在哪里找,word音乐符号怎么打出来|word音乐符号怎么打
  7. C语言学习笔记->const和define区别
  8. 最近整理电脑硬盘,分享几个小巧实用的软件下载,持续更新
  9. 阿里云SLB负载均衡ESS弹性伸缩
  10. 腾讯云服务器价格明细表(CPU内存/带宽/磁盘)
  11. 推荐一个直接用于项目开发的PID库!很好用,很稳定
  12. 魔百和CM311-1A_YST、(YM)_安卓9_S905L3A_默认开启ADB_纯净精简语音_完美线刷包
  13. java ftp 上传下载
  14. Python 绘制遥感数字高程影像(DEM)
  15. ubuntu安装QT依赖
  16. 命令关闭所有cmd窗口
  17. C#.NET程序设计教程实验指导(清华大学 江红,余青松)实验源码
  18. 蓝桥杯C51(试题内容学习)
  19. 一个很好用的网站:大英百科全书
  20. linux中的网络命名空间的使用

热门文章

  1. C# Label 通过Panel中的ScrollBar实现滑动条
  2. Python 十六进制,十进制转换
  3. C++(面试题):给40亿个不重复的无符号整数,没排过序,如何快速判断一个数是否在这40亿个数中
  4. (十五)覆盖率数据采样、交叉覆盖率_覆盖选项
  5. 深化测试用例的覆盖率
  6. 网络电话Skype协议分析
  7. PyQt5--UI大全
  8. 麻省理工学院计算机系硕士课程,麻省理工学院计算机专业研究生申请 你听说过《算法导论吗》?...
  9. 新浪微博API做的AIR桌面应用
  10. 一个好用的在线PDF压缩工具