记录--从AI到美颜全流程讲解
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

美颜和短视频
美颜相关APP可以说是现在手机上的必备的软件,例如抖音,快手,拍出的“照骗”和视频不加美颜效果,估计没有人敢传到网上。很多人一直好奇美颜类APP是如何开发出来的。本文就大致讲一下在Android上如何实现实时修改唇色效果。其它功能例如美白,腮红都是类似的原理
下图的唇色修改效果就是想实现的功能
美颜原理
美颜是的基本原理就是深度学习加计算机图形学。深度学习用来人脸检测和人脸关键点检测。计算机图形学用来磨皮,瘦脸和画妆容。一般在Android上使用OpenGLES,IOS为Metal。由于计算机图形学概念较多和复杂,本文中用Android的Canvas替代。
人脸检测 & 人脸关键点
- 人脸检测指的是对图片或者视频流中的人脸进行检测,并定位到图片中的人脸。
- 人脸关键点检测是对人脸中五官和脸的轮廓进行关键点定位,一般情况下它紧接在人脸检测后。
我们将使用TengineKit来实现实时大红唇效果。
TengineKit
免费移动端实时人脸212关键点SDK。是一个易于集成的人脸检测和人脸关键点SDK。它可以在各种手机上以非常低的延迟运行。
实现口红效果
配置 Gradle
Project中的build.gradle添加
    repositories {
        ...
        mavenCentral()
        ...
    }
    allprojects {
        repositories {
            ...
            mavenCentral()
            ...
        }
    }
    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人脸实时打码 zhuanlan.zhihu.com/p/161038093
处理Gif传过来的图片流
首先我们先初始化TengineKit:
- 选用normal处理模式
- 打开人脸检测和人脸关键点功能
- 设置图片流格式为RGBA
- 设置输入图片流的宽高,此处为gif图的预览宽高
- 设置输出图片流的宽高,此处为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);
    }
此代码来源于 github.com/DingProg/Ma…
渲染
传过来的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;
        }
    });
效果对比
https://juejin.cn/post/6855129006367309832
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

记录--从AI到美颜全流程讲解的更多相关文章
- 【拖拽可视化大屏】全流程讲解用python的pyecharts库实现拖拽可视化大屏的背后原理,简单粗暴!
		"整篇文章较长,干货很多!建议收藏后,分章节阅读." 一.设计方案 整体设计方案思维导图: 整篇文章,也将按照这个结构来讲解. 若有重点关注部分,可点击章节目录直接跳转! 二.项目 ... 
- linux Qt编译自己的动态库(.so),详细全流程
		本篇记录Qt编译动态库全流程 1. 建立工程 首先,打开Qt,新建C++ Library 工程 点击choose之后,输入项目名称为Example,一直下一步即可 生成的项目里边有三个文件,分别是ex ... 
- CentOS7+CDH5.14.0安装全流程记录,图文详解全程实测-总目录
		CentOS7+CDH5.14.0安装全流程记录,图文详解全程实测-总目录: 0.Windows 10本机下载Xshell,以方便往Linux主机上上传大文件 1.CentOS7+CDH5.14.0安 ... 
- AI全流程开发难题破解之钥
		摘要:通过对ModelArts.盘古大模型.ModelBox产品技术的解读,帮助开发者更好的了解AI开发生产线. 本文分享自华为云社区<[大厂内参]第16期:华为云AI开发生产线,破解AI全流程 ... 
- MindStudio模型训练场景精度比对全流程和结果分析
		摘要:MindStudio是一套基于华为昇腾AI处理器开发的AI全栈开发平台 本文分享自华为云社区<MindStudio模型训练场景精度比对全流程和结果分析>,作者:yd_24730208 ... 
- canvas 制作flappy bird(像素小鸟)全流程
		flappy bird制作全流程: 一.前言 像素小鸟这个简单的游戏于2014年在网络上爆红,游戏上线一段时间内appleStore上的下载量一度达到5000万次,风靡一时, 近年来移动web的普及为 ... 
- gitbook 入门教程之小白都能看懂的 Gitbook 插件开发全流程
		什么是插件 Gitbook 插件是扩展 GitBook 功能(电子书和网站)的最佳方式. 只要是 Gitbook 默认没有提供的功能,基于插件机制都可以自行扩展,是插件让 Gitbook 变得更加强大 ... 
- 【CKB.DEV 茶话会】第二期:聊聊 CKB 钱包和 Nervos DAO 全流程
		CKB.DEV 茶话会第二期:聊聊 CKB 钱包和 Nervos DAO 全流程 为了鼓励更多优秀的开发者和研究人员参与到 CKB 的开发和生态建设中去,我们希望组织一系列 CKB Developer ... 
- 如何使用 CODING 实践 DevOps 全流程
		你好,欢迎使用 CODING!这份最佳实践将帮助你通过 CODING 研发管理系统来更好地实践 DevOps 流程. DevOps 的本质是打破各个部门之间的隔阂,打通企业的前中后台,推进跨部门协作. ... 
- 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_1-2.中大型公司里面项目开发流程讲解
		笔记 2.中大型公司里面项目开发流程讲解 简介:讲解一个项目如何从零到上线,经历过怎样的步骤和流程 1.一个中大型项目的开发流程,从需求调研到项目上线 ... 
随机推荐
- NC17315 背包
			题目链接 题目 题目描述 Applese有 \(1\) 个容量为 \(v\) 的背包,有 \(n\) 个物品,每一个物品有一个价值 \(a_i\) ,以及一个大小 \(b_i\) 然后他对此提出了自己 ... 
- tmpwatch命令
			tmpwatch命令 tmpwatch递归删除给定时间内未访问的文件,通常用于清理用于临时存放空间的目录,并可以设置文件超期时间,默认单位以小时计算. 语法 tmpwatch [-u | -m | - ... 
- Java I/O 教程(十 一) BufferedWriter和BufferedReader
			Java BufferedWriter 类 Java BufferedWriter class 继承了Writer类,为Writer实例提供缓冲. 提升了写字符和字符串性能. 类定义: public ... 
- VS2019 添加三方文件夹遇到的坑
			在开发新项目时需要用到一些三方 API,这些三方 API 没有生成 lib,所以我们在 VS 编译器中添加这些三方文件夹的头文件路径后 会出现 ERROR LNK2019 的错误提示,这些提示通常都是 ... 
- Jenkins流水线使用@Grab 导入Maven库
			有个需求需要在pipeline中调用Java的SDK去执行业务 使用 @Grab 注解可以在Maven中导入Java 库, @Grab('org.apache.commons:commons-math ... 
- 硬件开发笔记(八): 硬件开发基本流程,制作一个USB转RS232的模块(七):创建基础DIP元器件(晶振)封装并关联原理图元器件
			前言 有了原理图,可以设计硬件PCB,在设计PCB之间还有一个协同优先动作,就是映射封装,原理图库的元器件我们是自己设计的.为了更好的表述封装设计过程,本文描述了创建晶振封装(DIP),将原理图的 ... 
- Ubuntu18.04搭建Vue开发环境
			更新软件源列表 sudo apt update nodejs安装 sudo apt install nodejs nodejs -v #查看版本 npm安装 sudo apt install npm ... 
- K6 在 Nebula Graph 上的压测实践
			背景 对于数据库来说,性能测试是一个非常频繁的事情.优化查询引擎的规则,调整存储引擎的参数等,都需要通过性能测试,查看系统在不同场景下的影响. 即便是同样的代码,同样的参数配置,在不同的机器资源配置, ... 
- Java 常用类 String类与其他结构之间的转换-----String 与 char[]之间的转换
			1 /* 2 String 与 char[]之间的转换 3 4 String----> char[]:调用String的toCharArray() 5 char[] ---->String ... 
- 面试准备不充分,被Java守护线程干懵了,面试官主打一个东西没用但你得会
			写在开头 面试官:小伙子请聊一聊Java中的精灵线程? 我:什么?精灵线程?啥时候精灵线程? 面试官:精灵线程没听过?那守护线程呢? 我:守护线程知道,就是为普通线程服务的线程嘛. 面试官:没了?守护 ... 
