抖音美颜效果开源实现,从 AI 到美颜全流程讲解 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
exmorning

抖音美颜效果开源实现,从 AI 到美颜全流程讲解

  •  
  •   exmorning 2020 年 7 月 27 日 7553 次点击
    这是一个创建于 2098 天前的主题,其中的信息可能已经有所发展或是发生改变。

    美颜和短视频

    美颜相关 APP 可以说是现在手机上的必备的软件,例如抖音,快手,拍出的“照骗”和视频不加美颜效果,估计没有人敢传到网上。很多人一直好奇美颜类 APP 是如何开发出来的。本文就大致讲一下在 Android 上如何实现实时修改唇色效果。

    下图的红唇效果就是想实现的功能
    demo1

    美颜原理

    美颜是的基本原理就是深度学习加计算机图形学。深度学习用来人脸检测和人脸关键点检测。计算机图形学用来磨皮,瘦脸和画妆容。一般在 Android 上使用 OpenGLES,IOS 为 Metal 。由于计算机图形学概念较多和复杂,本文中用 Android 的 Canvas 替代。

    人脸检测 & 人脸关键点

    1. 人脸检测指的是对图片或者视频流中的人脸进行检测,并定位到图片中的人脸。
    2. 人脸关键点检测是对人脸中五官和脸的轮廓进行关键点定位,一般情况下它紧接在人脸检测后。

    face landmarks

    我们将使用 TengineKit 来实现实时大红唇效果。

    TengineKit

    免费移动端实时人脸 212 关键点 SDK 。是一个易于集成的人脸检测和人脸关键点 SDK 。它可以在各种手机上以非常低的延迟运行。
    https://github.com/OAID/TengineKit

    TengineKit 效果图

    demo1

    实现口红效果

    配置 Gradle

    Project 中的 build.gradle 添加

     repositories { ... mavenCentral() ... } allprojects { repositories { ... mavenCentral() ... } } 

    主 Module 中的 build.gradle 添加

     dependencies { ... implementation 'com.tengine.android:tenginekit:1.0.5' ... } 

    配置 manifests

     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> 

    相对于上篇用摄像头来做效果,本文用 gif 图来代替摄像头的输入的视频流,如果想用摄像头实现,可以参考:

    用开源 212 点人脸关键点实现 Android 人脸实时打码 https://zhuanlan.zhihu.com/p/161038093

    处理 Gif 传过来的图片流

    首先我们先初始化 TengineKit:

    1. 选用 normal 处理模式
    2. 打开人脸检测和人脸关键点功能
    3. 设置图片流格式为 RGBA
    4. 设置输入图片流的宽高,此处为 gif 图的预览宽高
    5. 设置输出图片流的宽高,此处为 GifImageView 的宽高,此处和 gif 一致,所以用 gif 图的宽高代替
     com.tenginekit.Face.init(getBaseContext(), AndroidConfig.create() .setNormalMode() .openFunc(AndroidConfig.Func.Detect) .openFunc(AndroidConfig.Func.Landmark) .setInputImageFormat(AndroidConfig.ImageFormat.RGBA) .setInputImageSize(facingGif.getGifWidth(), facingGif.getGifHeight()) .setOutputImageSize(facingGif.getGifWidth(), facingGif.getGifHeight()) ); 

    通过关键点得到嘴唇的形状

     Path getMouthLandmarks(FaceLandmarkInfo fi){ Path outPath = new Path(); outPath.moveTo(fi.landmarks.get(180).X,fi.landmarks.get(180).Y); for(int i = 180; i < 189; i++){ outPath.lineTo( fi.landmarks.get(i).X, fi.landmarks.get(i).Y ); } for(int i = 204; i >= 196; i--){ outPath.lineTo( fi.landmarks.get(i).X, fi.landmarks.get(i).Y ); } outPath.close(); Path inPath = new Path(); inPath.moveTo(fi.landmarks.get(180).X,fi.landmarks.get(180).Y); for(int i = 195; i >= 188; i--){ inPath.lineTo( fi.landmarks.get(i).X, fi.landmarks.get(i).Y ); } for(int i = 204; i <= 211; i++){ inPath.lineTo( fi.landmarks.get(i).X, fi.landmarks.get(i).Y ); } outPath.op(inPath, Path.Op.DIFFERENCE); return outPath; } 

    给嘴唇涂上颜色

     public static void drawLipPerfect(Canvas canvas, Path lipPath, int color, int alpha) { //most 70% alpha if (alpha > 80) { alpha = (int) (alpha * 0.9f + 0.5f); } alpha = (int) (Color.alpha(color) * ((float) alpha / 255)) << 24; color = alphaColor(color, alpha); final PointF position = new PointF(); float blur_radius = 5; Bitmap mask = createMask(lipPath, color, blur_radius, position); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); canvas.drawBitmap(mask, position.x, position.y, paint); } 

    此代码来源于 https://github.com/DingProg/Makeup

    渲染

    传过来的 bitmap 为 RGB_565,需要转为标准的 RGBA 格式

     facingGif.setOnFrameAvailable(new GifImageView.OnFrameAvailable() { @Override public Bitmap onFrameAvailable(Bitmap bitmap) { // bitmap RGB_565 Bitmap out_bitmap = Bitmap.createBitmap( facingGif.getGifWidth(), facingGif.getGifHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(out_bitmap); canvas.drawBitmap(bitmap, 0, 0, null); bitmap.recycle(); byte[] bytes = bitmap2Bytes(out_bitmap); Face.FaceDetect faceDetect = com.tenginekit.Face.detect(bytes); if(faceDetect.getFaceCount() > 0){ faceLandmarks = faceDetect.landmark2d(); if(faceLandmarks != nul){ for (int i = 0; i < faceLandmarks.size(); i++) { Path m_p = getMouthLandmarks(faceLandmarks.get(i)); LipDraw.drawLipPerfect(canvas, m_p, Color.WHITE, 100); } } } return out_bitmap; } }); 

    效果对比

    demo demo

    建议

    有兴趣的同学可以在当前项目的基础上面深化,具体可以参考
    https://github.com/DingProg/Makeup

    更进一步想尝试商业级的美颜效果可以参考
    https://github.com/CainKernel/CainCamera

    参考

    1. TengineKit - Free, Fast, Easy, Real-Time FaceDetection & FaceLandmark SDK on Mobile.

    2. Makeup - 让你的“女神”逆袭,代码撸彩妆(画妆)

    3. CainCamera - CainCamera is an Android Project to learn about development of beauty camera, image and short video

    源码

    https://github.com/jiangzhongbo/TengineKit_Demo_Face_Beauty

    知乎

    https://zhuanlan.zhihu.com/p/163604590

    系列

    https://v2ex.com/t/690842
    https://v2ex.com/t/687534

    19 条回复    2020-08-11 13:10:24 +08:00
    KalaSearch
        1
    KalaSearch  
       2020 年 7 月 27 日
    做得挺棒的,感谢 lz 分享

    这个 gif 选得也好 :D
    96412hj
        2
    96412hj  
       2020 年 7 月 27 日
    美颜和人脸识别技术是不是有点类似?
    exmorning
        3
    exmorning  
    OP
       2020 年 7 月 27 日
    exmorning
        4
    exmorning  
    OP
       2020 年 7 月 27 日
    @96412hj 差不多,人脸识别还要一步人脸特征值检测
    dying4death
        5
    dying4death  
       2020 年 7 月 27 日
    男人看的颜值和女人看的颜值什么意思
    exmorning
        6
    exmorning  
    OP
       2020 年 7 月 27 日
    @dying4death 男性审美和女性审美不一样,所以打分也不一致,不过这个算法做的有问题,作为附带功能带上去
    glumess
        7
    glumess  
       2020 年 7 月 27 日
    大佬优秀,感谢分享,请问文章可以在公众号 《音视频开发进阶》中进行转载吗?
    exmorning
        8
    exmorning  
    OP
       2020 年 7 月 27 日
    @glumess 可以的,注明来源(知乎地址)就行
    Veneris
        9
    Veneris  
       2020 年 7 月 27 日
    感谢分享,蛮清晰的文章。
    exmorning
        10
    exmorning  
    OP
       2020 年 7 月 27 日
    @Veneris ;-)
    rayhy
        11
    rayhy  
       2020 年 7 月 28 日 via Android
    请问楼主,这些算法在桌面端或者 Web 端实现有什么辅助用的基础库吗?就是不太想从底层写起,比 opencv 再高一个层次的。
    exmorning
        12
    exmorning  
    OP
       2020 年 7 月 28 日
    @rayhy web 用 three.js+tf.js
    dream4ever
        13
    dream4ever  
       2020 年 7 月 28 日
    这个妹子不错,哈哈
    exmorning
        14
    exmorning  
    OP
       2020 年 7 月 28 日
    @dream4ever 欢迎骚扰~
    dream4ever
        15
    dream4ever  
       2020 年 7 月 29 日
    @exmorning 咦,莫非你就是动图里的妹子?
    AmberJiang
        16
    AmberJiang  
       2020 年 7 月 31 日 via iPad
    卧槽 一开始看这妹子 我还以为是王鸥
    duanxiaoyu
        17
    duanxiaoyu  
       2020 年 8 月 10 日
    学会了,去哪找妹子来测试
    firefox12
        18
    firefox12  
       2020 年 8 月 11 日
    大佬牛逼, 但是如果我现在想从零开始 实现这些 该如何入门,目前 你描述的 只不过是 用个库 调调 api,没有核心技术。
    firefox12
        19
    firefox12  
       2020 年 8 月 11 日
    还有你的简书都挂了, 能不能把文章放到 github 上?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2716 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 367ms UTC 10:14 PVG 18:14 LAX 03:14 JFK 06:14
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86