V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
exmorning
V2EX  ›  程序员

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

  •  
  •   exmorning · Jul 27, 2020 · 7596 views
    This topic created in 2113 days ago, the information mentioned may be changed or developed.

    美颜和短视频

    美颜相关 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 != null){
                        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 replies    2020-08-11 13:10:24 +08:00
    KalaSearch
        1
    KalaSearch  
       Jul 27, 2020
    做得挺棒的,感谢 lz 分享

    这个 gif 选得也好 :D
    96412hj
        2
    96412hj  
       Jul 27, 2020
    美颜和人脸识别技术是不是有点类似?
    exmorning
        3
    exmorning  
    OP
       Jul 27, 2020
    exmorning
        4
    exmorning  
    OP
       Jul 27, 2020
    @96412hj 差不多,人脸识别还要一步人脸特征值检测
    dying4death
        5
    dying4death  
       Jul 27, 2020
    男人看的颜值和女人看的颜值什么意思
    exmorning
        6
    exmorning  
    OP
       Jul 27, 2020
    @dying4death 男性审美和女性审美不一样,所以打分也不一致,不过这个算法做的有问题,作为附带功能带上去
    glumess
        7
    glumess  
       Jul 27, 2020
    大佬优秀,感谢分享,请问文章可以在公众号 《音视频开发进阶》中进行转载吗?
    exmorning
        8
    exmorning  
    OP
       Jul 27, 2020
    @glumess 可以的,注明来源(知乎地址)就行
    Veneris
        9
    Veneris  
       Jul 27, 2020
    感谢分享,蛮清晰的文章。
    exmorning
        10
    exmorning  
    OP
       Jul 27, 2020
    @Veneris ;-)
    rayhy
        11
    rayhy  
       Jul 28, 2020 via Android
    请问楼主,这些算法在桌面端或者 Web 端实现有什么辅助用的基础库吗?就是不太想从底层写起,比 opencv 再高一个层次的。
    exmorning
        12
    exmorning  
    OP
       Jul 28, 2020
    @rayhy web 用 three.js+tf.js
    dream4ever
        13
    dream4ever  
       Jul 28, 2020
    这个妹子不错,哈哈
    exmorning
        14
    exmorning  
    OP
       Jul 28, 2020
    @dream4ever 欢迎骚扰~
    dream4ever
        15
    dream4ever  
       Jul 29, 2020
    @exmorning 咦,莫非你就是动图里的妹子?
    AmberJiang
        16
    AmberJiang  
       Jul 31, 2020 via iPad
    卧槽 一开始看这妹子 我还以为是王鸥
    🤣🤣
    duanxiaoyu
        17
    duanxiaoyu  
       Aug 10, 2020
    学会了,去哪找妹子来测试
    firefox12
        18
    firefox12  
       Aug 11, 2020
    大佬牛逼, 但是如果我现在想从零开始 实现这些 该如何入门,目前 你描述的 只不过是 用个库 调调 api,没有核心技术。
    firefox12
        19
    firefox12  
       Aug 11, 2020
    还有你的简书都挂了, 能不能把文章放到 github 上?
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3166 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 76ms · UTC 14:56 · PVG 22:56 · LAX 07:56 · JFK 10:56
    ♥ Do have faith in what you're doing.