大发龙虎首页    注册   登录
大发龙虎 = way to explore
大发龙虎 是一个大发龙虎关于 分享和探索的地方
现在注册
已注册用户请  登录
exmorning
大发龙虎  ›  程序员

抖音快手 APP"大眼特效"开源实现,换了一个甜美系小姐姐做效果演示

  •  
  •   exmorning · 13 天前 · 8567 次点击

    大眼特效

    抖音短大发龙虎视频 中的大眼特效有很多人玩,这篇就讲一下怎么实现。本文为《抖音美颜效果开源实现,从 AI 到美颜全流程讲解》姐妹篇,很多代码和内容都类似,看过的同学可以直接看效果和源码。

    demo1

    下图为演示小姐姐

    demo1

    大眼特效原理

    大眼特效原理的美颜差不多,都是 AI 和计算机图形学的结合

    美颜是的基本原理就是深度学习加计算机图形学。深度学习用来人脸检测和人脸关键点检测。计算机图形学用来磨皮,瘦脸和画妆容。一般在 Android 上使用 OpenGLES,IOS 为 Metal 。

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

    人脸检测 & 人脸关键点

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

    face landmarks

    大发龙虎大发龙虎我 们 将使用 TengineKit 来实现大眼特效。

    TengineKit

    免费移动端实时人脸 212 关键点 SDK 。是一个易于集成的人脸检测和人脸关键点 SDK 。它可以在各种手机上以非常低的延迟运行。
    http://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 传过来的图片流

    首先大发龙虎大发龙虎我 们 先初始化 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())
        );
    

    通过关键点得眼睛的中心点

        Point getLeftEyeCenter(FaceLandmarkInfo fi){
            FaceLandmarkPoint p1 = fi.landmarks.get(105);
            FaceLandmarkPoint p2 = fi.landmarks.get(113);
            return new Point((int)((p1.X + p2.X) / 2), (int)((p1.Y + p2.Y) / 2));
        }
    
        Point getRightEyeCenter(FaceLandmarkInfo fi){
            FaceLandmarkPoint p1 = fi.landmarks.get(121);
            FaceLandmarkPoint p2 = fi.landmarks.get(129);
            return new Point((int)((p1.X + p2.X) / 2), (int)((p1.Y + p2.Y) / 2));
        }
    

    眼睛放大算法

    public class MagnifyEyeUtils {
        /**
         *  眼睛放大算法
         * @param bitmap      原来的 bitmap
         * @param centerPoint 放大中心点
         * @param radius      放大半径
         * @param sizeLevel    放大力度  [0,4]
         * @return 放大眼睛后的图片
         */
        public static Bitmap magnifyEye(Bitmap bitmap, Point centerPoint, int radius, float sizeLevel) {
            Bitmap dstBitmap = bitmap.copy(Bitmap.Config.RGB_565, true);
            int left = centerPoint.x - radius < 0 ? 0 : centerPoint.x - radius;
            int top = centerPoint.y - radius < 0 ? 0 : centerPoint.y - radius;
            int right = centerPoint.x + radius > bitmap.getWidth() ? bitmap.getWidth() - 1 : centerPoint.x + radius;
            int bottom = centerPoint.y + radius > bitmap.getHeight() ? bitmap.getHeight() - 1 : centerPoint.y + radius;
            int powRadius = radius * radius;
    
            int offsetX, offsetY, powDistance, powOffsetX, powOffsetY;
    
            int disX, disY;
    
            //当为负数时,为缩小
            float strength = (5 + sizeLevel * 2) / 10;
    
            for (int i = top; i <= bottom; i++) {
                offsetY = i - centerPoint.y;
                for (int j = left; j <= right; j++) {
                    offsetX = j - centerPoint.x;
                    powOffsetX = offsetX * offsetX;
                    powOffsetY = offsetY * offsetY;
                    powDistance = powOffsetX + powOffsetY;
    
                    if (powDistance <= powRadius) {
                        double distance = Math.sqrt(powDistance);
                        double sinA = offsetX / distance;
                        double cosA = offsetY / distance;
    
                        double scaleFactor = distance / radius - 1;
                        scaleFactor = (1 - scaleFactor * scaleFactor * (distance / radius) * strength);
    
                        distance = distance * scaleFactor;
                        disY = (int) (distance * cosA + centerPoint.y + 0.5);
                        disY = checkY(disY, bitmap);
                        disX = (int) (distance * sinA + centerPoint.x + 0.5);
                        disX = checkX(disX, bitmap);
                        //中心点不做处理
                        if (!(j == centerPoint.x && i == centerPoint.y)) {
                            dstBitmap.setPixel(j, i, bitmap.getPixel(disX, disY));
                            //dstBitmap.setPixel(j, i, Color.WHITE);
                        }
                    }
                }
            }
            return dstBitmap;
        }
    
        private static int checkY(int disY, Bitmap bitmap) {
            if (disY < 0) {
                disY = 0;
            } else if (disY >= bitmap.getHeight()) {
                disY = bitmap.getHeight() - 1;
            }
            return disY;
        }
    
        private static int checkX(int disX, Bitmap bitmap) {
            if (disX < 0) {
                disX = 0;
            } else if (disX >= bitmap.getWidth()) {
                disX = bitmap.getWidth() - 1;
            }
            return disX;
        }
    
    }
    

    此代码来源于 http://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++) {
                            FaceLandmarkInfo fi = faceLandmarks.get(i);
                            out_bitmap = MagnifyEyeUtils.magnifyEye(out_bitmap, getLeftEyeCenter(fi), 40, 4);
                            out_bitmap = MagnifyEyeUtils.magnifyEye(out_bitmap, getRightEyeCenter(fi), 40, 4);
                        }
                    }
                }
                return out_bitmap;
            }
        });
    

    效果对比

    demo demo

    建议

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

    参考

    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

    源码

    http://github.com/jiangzhongbo/TengineKit_Demo_Big_Eyes

    知乎

    http://zhuanlan.zhihu.com/p/164803269

    大发龙虎 系列

    大发龙虎推荐 一个 Github 上面免费用的 Android 人脸关键点 SDK,Demo 图中的小姐姐好漂亮

    尝试 AI 人脸关键点算法实现一下 Android 人脸匿名功能

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

    40 条回复    2020-08-06 09:25:07 +08:00
    leimao
        1
    leimao   13 天前 via iPhone   ❤️ 3
    大发龙虎我 绝对不会为网红花一分钱
    nksky
        2
    nksky   13 天前   ❤️ 1
    第一张图大发龙虎我 看着有点恐怖
    littiefish
        3
    littiefish   13 天前 via iPhone   ❤️ 4
    畸形审美
    yanzhiling2001
        4
    yanzhiling2001   13 天前
    眼睛大的像 ET 了
    mugglezzz
        5
    mugglezzz   13 天前
    @nksky #2 第一张图是 阿丽塔 啦
    takemeaway
        6
    takemeaway   13 天前
    大发龙虎我 比较好奇 TengineKit 是全自己研发的算法吗?
    还是挺厉害的
    chiaf
        7
    chiaf   13 天前
    太想 ET 了😂
    exmorning
        8
    exmorning   13 天前
    @takemeaway 是啊,从端侧推理引擎到算法都是自己搞的
    mmrx
        9
    mmrx   13 天前
    哈哈哈哈大眼动图可太恐怖了

    感谢楼主分享
    exmorning
        10
    exmorning   13 天前
    @mmrx 特地做大了点,为了展示效果
    xrr2016
        11
    xrr2016   13 天前
    话说战斗天使啥时候出第二部啊?第一部才讲了漫画的一点内容...
    Soar360
        12
    Soar360   13 天前
    死鱼眼的既视感
    nguoidiqua
        13
    nguoidiqua   13 天前   ❤️ 3
    假脸时代。

    对于国内各种自拍、网上的美照什么的已经完全无感了,美颜太过了,经常挡一下脸就会看到脸忽然变大一圈大,然后马上又变小了,感觉很无语。
    revalue
        14
    revalue   13 天前
    哪有人把眼袋放大得那么大啊,混蛋
    ln1225707801
        15
    ln1225707801   13 天前 via Android
    效果蛮厉害的,就是审美需要加强
    zhangchongjie
        16
    zhangchongjie   13 天前 via Android
    opencv 可以做到吗
    wushigejiajia01
        17
    wushigejiajia01   13 天前
    @xrr2016 大发龙虎你 参考下阿凡达, 反正没看到片子, 卡梅隆这人是不能信了
    exmorning
        18
    exmorning   13 天前
    @zhangchongjie 可以的,就是 opencv 性能在 mobile 侧不行
    UnitTest
        19
    UnitTest   13 天前
    感谢大发龙虎技术 分享,学习了。
    就是效果有点 creepy 。。
    exmorning
        20
    exmorning   13 天前
    @UnitTest 为了效果牺牲了颜值~
    buliugu
        21
    buliugu   13 天前
    有点恐怖谷效应
    zhangchongjie
        22
    zhangchongjie   13 天前 via Android
    @exmorning 嗯嗯,没试过,做 web 的看看热闹😂
    exmorning
        23
    exmorning   13 天前
    @zhangchongjie 有时间,大发龙虎我 用 nodejs 包一下
    takemeaway
        24
    takemeaway   13 天前
    看起来像 ERT 算法,其实还是做得不错的。 看识别率了
    stnaw
        25
    stnaw   13 天前
    用 shader 和人脸蒙版会更好
    exmorning
        26
    exmorning   13 天前
    @takemeaway 暴力上 MobileNet
    exmorning
        27
    exmorning   13 天前
    @stnaw 这样就是不是入门级的文章了,上 canvas 为了让初学者能快速玩起来
    root8080
        28
    root8080   13 天前
    小姐姐快秃了 注意保养 😭
    exmorning
        29
    exmorning   13 天前
    @root8080 大发龙虎我 会转告的😭
    IzayakI
        30
    IzayakI   12 天前
    第一张图是铳梦里的阿丽塔吧,漫画好看,电影故事讲得稀烂。


    还有,后面的对比 gif,这个大眼好看嘛?莫名其妙的。
    exmorning
        31
    exmorning   12 天前
    @IzayakI 为了看出效果,特地做大了些
    LZSZ
        32
    LZSZ   12 天前
    有点像青蛙 原谅大发龙虎我 这么说
    vinew
        33
    vinew   12 天前
    小姐姐可能还需要植发特效,发量渐少了
    GM
        34
    GM   11 天前
    一行源码都没有,大发龙虎我 是不是理解错“开源”的意思了?
    Rh1
        35
    Rh1   10 天前 via iPhone
    真是什么玩意都可以扯到“国内”“国外”.... 滤镜美颜这种东西在全球都很火好吗?
    falcon05
        36
    falcon05   10 天前 via iPhone
    太吓人了大发龙虎我 去
    erek
        37
    erek   9 天前
    期待阿丽塔第二部
    CoderGeek
        38
    CoderGeek   7 天前
    666
    djs
        39
    djs   6 天前
    吓死大发龙虎我 了。。。那个大眼图
    minglanyu
        40
    minglanyu   5 天前
    这小姐姐... 这大眼...
    大发龙虎我 欣赏不了。
    大发龙虎关于   ·   FAQ   ·   API   ·   大发龙虎大发龙虎我 们 的愿景   ·   广告投放   ·   感谢   ·   实用小大发龙虎工具   ·   4146 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 09:31 · PVG 17:31 · LAX 02:31 · JFK 05:31
    ♥ Do have faith in what you're doing.