前言

在这个美即真理、全民娱乐的时代,可爱有趣的人脸贴纸在各大美颜软件中得到了广泛的应用,现在已经不仅局限于相机美颜类软件中,在社交、娱乐类的app中对人脸贴纸、AR贴纸的需求也非常广泛。本文详细介绍了集成华为HMS ML kit人脸识别实现2d贴纸的集成过程,在后面的文章中我们还会介绍3D贴纸的开发过程,欢迎大家关注哦~

场景

在美颜相机、美图app以及社交类app(如抖音、微博、微信)等需要对拍照,或者对照片进行处理的app都会构建自己特有的贴纸的需求。

开发前准备

在项目级gradle里添加华为maven仓

打开AndroidStudio项目级build.gradle文件

增量添加如下maven地址:buildscript {

{

maven {url 'http://developer.huawei.com/repo/'}

}

}

allprojects {

repositories {

maven { url 'http://developer.huawei.com/repo/'}

}

}

在应用级的build.gradle里面加上SDK依赖

// Face detection SDK.

implementation 'com.huawei.hms:ml-computer-vision-face:2.0.1.300'

// Face detection model.

implementation 'com.huawei.hms:ml-computer-vision-face-shape-point-model:2.0.1.300'

在AndroidManifest.xml文件里面申请相机、访问网络和存储权限

代码开发关键步骤

设置人脸检测器MLFaceAnalyzerSetting detectorOptions;

detectorOptions = new MLFaceAnalyzerSetting.Factory()

.setFeatureType(MLFaceAnalyzerSetting.TYPE_UNSUPPORT_FEATURES)

.setShapeType(MLFaceAnalyzerSetting.TYPE_SHAPES)

.allowTracing(MLFaceAnalyzerSetting.MODE_TRACING_FAST)

.create();

detector = MLAnalyzerFactory.getInstance().getFaceAnalyzer(detectorOptions);

这里我们通过相机回调拿到相机帧数据,并通过调用人脸检测器拿到人脸轮廓点后写入FacePointEngine供贴纸滤镜使用@Override

public void onPreviewFrame(final byte[] imgData, final Camera camera) {

int width = mPreviewWidth;

int height = mPreviewHeight;

long startTime = System.currentTimeMillis();

//设置前后摄方向一致

if (isFrontCamera()){

mOrientation = 0;

}else {

mOrientation = 2;

}

MLFrame.Property property =

new MLFrame.Property.Creator()

.setFormatType(ImageFormat.NV21)

.setWidth(width)

.setHeight(height)

.setQuadrant(mOrientation)

.create();

ByteBuffer data = ByteBuffer.wrap(imgData);

// 调用人脸检测接口

SparseArray faces = detector.analyseFrame(MLFrame.fromByteBuffer(data,property));

//判断是否获取到人脸信息

if(faces.size()>0){

MLFace mLFace = faces.get(0);

EGLFace EGLFace = FacePointEngine.getInstance().getOneFace(0);

EGLFace.pitch = mLFace.getRotationAngleX();

EGLFace.yaw = mLFace.getRotationAngleY();

EGLFace.roll = mLFace.getRotationAngleZ() - 90;

if (isFrontCamera())

EGLFace.roll = -EGLFace.roll;

if (EGLFace.vertexPoints == null) {

EGLFace.vertexPoints = new PointF[131];

}

int index = 0;

// 获取一个人的轮廓点坐标并转化到openGL归一化坐标系下的浮点值

for (MLFaceShape contour : mLFace.getFaceShapeList()) {

if (contour == null) {

continue;

}

List points = contour.getPoints();

for (int i = 0; i < points.size(); i++) {

MLPosition point = points.get(i);

float x = ( point.getY() / height) * 2 - 1;

float y = ( point.getX() / width ) * 2 - 1;

if (isFrontCamera())

x = -x;

PointF Point = new PointF(x,y);

EGLFace.vertexPoints[index] = Point;

index++;

}

}

// 插入人脸对象

FacePointEngine.getInstance().putOneFace(0, EGLFace);

// 设置人脸个数

FacePointEngine.getInstance().setFaceSize(faces!= null ? faces.size() : 0);

}else{

FacePointEngine.getInstance().clearAll();

}

long endTime = System.currentTimeMillis();

Log.d("TAG","Face detect time: " + String.valueOf(endTime - startTime));

}

ML kit接口返回的人脸轮廓点情况如图所示:

介绍如何设计贴纸,首先看一下贴纸数JSON数据定义public class FaceStickerJson {

public int[] centerIndexList; // 中心坐标索引列表,有可能是多个关键点计算中心点

public float offsetX; // 相对于贴纸中心坐标的x轴偏移像素

public float offsetY; // 相对于贴纸中心坐标的y轴偏移像素

public float baseScale; // 贴纸基准缩放倍数

public int startIndex; // 人脸起始索引,用于计算人脸的宽度

public int endIndex; // 人脸结束索引,用于计算人脸的宽度

public int width; // 贴纸宽度

public int height; // 贴纸高度

public int frames; // 贴纸帧数

public int action; // 动作,0表示默认显示,这里用来处理贴纸动作等

public String stickerName; // 贴纸名称,用于标记贴纸所在文件夹以及png文件的

public int duration; // 贴纸帧显示间隔

public boolean stickerLooping; // 贴纸是否循环渲染

public int maxCount; // 最大贴纸渲染次数

...

}

我们制作猫耳贴纸JSON文件,通过人脸索引找到眉心84号点和鼻尖85号点分别贴上耳朵和鼻子,然后把它和图片都放在assets目录下{

"stickerList": [{

"type": "sticker",

"centerIndexList": [84],

"offsetX": 0.0,

"offsetY": 0.0,

"baseScale": 1.3024,

"startIndex": 11,

"endIndex": 28,

"width": 495,

"height": 120,

"frames": 2,

"action": 0,

"stickerName": "nose",

"duration": 100,

"stickerLooping": 1,

"maxcount": 5

}, {

"type": "sticker",

"centerIndexList": [83],

"offsetX": 0.0,

"offsetY": -1.1834,

"baseScale": 1.3453,

"startIndex": 11,

"endIndex": 28,

"width": 454,

"height": 150,

"frames": 2,

"action": 0,

"stickerName": "ear",

"duration": 100,

"stickerLooping": 1,

"maxcount": 5

}]

}

这里渲染贴纸纹理我们使用GLSurfaceView,使用起来比TextureView简单, 首先在onSurfaceChanged实例化贴纸滤镜,传入贴纸路径并开启相机@Override

public void onSurfaceCreated(GL10 gl, EGLConfig config) {

GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

mTextures = new int[1];

mTextures[0] = OpenGLUtils.createOESTexture();

mSurfaceTexture = new SurfaceTexture(mTextures[0]);

mSurfaceTexture.setOnFrameAvailableListener(this);

//将samplerExternalOES 输入到纹理中

cameraFilter = new CameraFilter(this.context);

//设置assets目录下人脸贴纸路径

String folderPath ="cat";

stickerFilter = new FaceStickerFilter(this.context,folderPath);

//创建屏幕滤镜对象

screenFilter = new BaseFilter(this.context);

facePointsFilter = new FacePointsFilter(this.context);

mEGLCamera.openCamera();

}

然后在onSurfaceChanged初始化贴纸滤镜@Override

public void onSurfaceChanged(GL10 gl, int width, int height) {

Log.d(TAG, "onSurfaceChanged. width: " + width + ", height: " + height);

int previewWidth = mEGLCamera.getPreviewWidth();

int previewHeight = mEGLCamera.getPreviewHeight();

if (width > height) {

setAspectRatio(previewWidth, previewHeight);

} else {

setAspectRatio(previewHeight, previewWidth);

}

// 设置画面的大小,创建FrameBuffer,设置显示尺寸

cameraFilter.onInputSizeChanged(previewWidth, previewHeight);

cameraFilter.initFrameBuffer(previewWidth, previewHeight);

cameraFilter.onDisplaySizeChanged(width, height);

stickerFilter.onInputSizeChanged(previewHeight, previewWidth);

stickerFilter.initFrameBuffer(previewHeight, previewWidth);

stickerFilter.onDisplaySizeChanged(width, height);

screenFilter.onInputSizeChanged(previewWidth, previewHeight);

screenFilter.initFrameBuffer(previewWidth, previewHeight);

screenFilter.onDisplaySizeChanged(width, height);

facePointsFilter.onInputSizeChanged(previewHeight, previewWidth);

facePointsFilter.onDisplaySizeChanged(width, height);

mEGLCamera.startPreview(mSurfaceTexture);

}

最后通过onDrawFrame把贴纸绘制到屏幕@Override

public void onDrawFrame(GL10 gl) {

int textureId;

// 清除屏幕和深度缓存

GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);

//更新获取一张图

mSurfaceTexture.updateTexImage();

//获取SurfaceTexture转化矩阵

mSurfaceTexture.getTransformMatrix(mMatrix);

//设置相机显示转化矩阵

cameraFilter.setTextureTransformMatrix(mMatrix);

//绘制相机纹理

textureId = cameraFilter.drawFrameBuffer(mTextures[0],mVertexBuffer,mTextureBuffer);

//绘制贴纸纹理

textureId = stickerFilter.drawFrameBuffer(textureId,mVertexBuffer,mTextureBuffer);

//绘制到屏幕

screenFilter.drawFrame(textureId , mDisplayVertexBuffer, mDisplayTextureBuffer);

if(drawFacePoints){

facePointsFilter.drawFrame(textureId, mDisplayVertexBuffer, mDisplayTextureBuffer);

}

}

这样我们的贴纸就画到人脸上了.

Demo效果

源码

原作者:旭小夜

android 贴纸 源码,超简单集成HMS ML Kit 人脸检测实现可爱贴纸相关推荐

  1. 【转载】超简单集成HMS ML Kit 人脸检测实现可爱2D贴纸

    文章目录 前言 场景 开发前准备 在项目级gradle里添加华为maven仓 在应用级的build.gradle里面加上SDK依赖 在AndroidManifest.xml文件里面申请相机.访问网络和 ...

  2. 超简单集成HMS ML Kit二代身份证识别,一键实名认证

    前言 就在近期华为HMS ML Kit 发布了1.0.3.30版本,ML Kit在原有通用OCR功能的基础上,又新增了银行卡识别(BCR)和二代身份证识别(ICR).今天小编就给大家介绍一下其中的IC ...

  3. android自带抓拍算法,Android | 超简单集成HMS ML Kit实现最大脸微笑抓拍

    前言 如果大家对HMS ML Kit 人脸检测功能有所了解,相信已经动手调用我们提供的接口编写自己的APP啦.目前就有小伙伴在调用接口的过程中反馈,不太清楚HMS ML Kit 文档中的MLMaxSi ...

  4. 超简单集成HMS ML Kit 实现parental control

    前言   各位应用程序开发者有没有在后台收到过家长们的反馈? 希望能够提供一个开关,采取一些措施保护小孩的眼睛,因为现在小孩子的近视率越来越高,和他们长时间近距离盯着屏幕有很大的关系.最近有一个海外的 ...

  5. 美颜神器——快速集成华为HMS ML Kit人脸检测实现大眼瘦脸

    前言 生活中遇到难忘美好的瞬间,小编总是忍不住用拍照的方式来留住它,相信大家也和我一样.但我们大多数人都不是专业的摄影师或者模特,光线没选好.角度不对等等原因,导致对照片的自己不满意,该怎么办呢?这时 ...

  6. 超简单集成HMS ML套件二代身份证识别,一键实名认证

    前言 就在近期华为HMS ML Kit发布了1.0.3.30版本,ML Kit在原有通用OCR功能的基础上,又添加了银行卡识别(BCR)和二代身份识别(ICR).今天小编就给ML kit除了提供语言相 ...

  7. 超简单集成HMS Scan Kit扫码SDK,轻松实现扫码购

    前言   在前面的文章中,我们向大家介绍了HMS Scan Kit 的快速集成方法以及HMS Scan Kit和其他开源扫码工具的竞争力对比分析,如果没有看到也没关系,文章下方的往期链接中有文章入口. ...

  8. 全网最火 - 跳舞的鸭子动态源码 - 超简单

    全网最火 - 跳舞的鸭子动态源码 - 超简单 效果图: <!DOCTYPE html> <html lang="en"> <head><m ...

  9. 自己动手调试Android源码(超简单)

    在自己动手编译Android最新源码一文中,我们为自己编译了一份最新的Android源码.很多时候,我们编译源码的目的不仅仅是尝试一番,而是希望对其进行调试,并修改源码,看看其中一些关键机制的运行原理 ...

最新文章

  1. 滴滴开源在2019:十大重点项目盘点,DoKit客户端研发助手首破1万Star
  2. qtcreator安装后的设置
  3. 全球及中国卸妆条行业发展规模与营销前景分析报告2022版
  4. 云浮市云计算大数据中心预计明年6月建成
  5. 新的blog,将会记录我的成长历程
  6. linux buffer cache 过高_你真的理解Linux的内存监控吗?
  7. .net core 部署应用程序注意事项
  8. 使用jpa报No query defined for that name错误
  9. Micropython TurnipBit 旋转按钮控制直流电机转速(儿时记忆中的吊扇)
  10. php闭包 js闭包,JavaScript闭包与PHP闭包的区别是什么?
  11. C++ QT学习之路----VS2017+QT环境搭建
  12. wirelessn1000 驱动_Intel无线网卡驱动程序下载
  13. linux 多核 双系统,Linux GRUB实现双系统引导教程
  14. 信息流推广与普通搜索推广的区别与优势是什么?
  15. 问题 A: 【动态规划】机器人军团(最大不下降子序列)
  16. Java笔记(错题)
  17. 利用shrinkwrap锁定依赖版本
  18. @老徐FrankXuLei 受邀为上海师翊网络科技有限公司讲授《微软WCF分布式开发与SOA架构设计课程》
  19. excel批量提取网页标题
  20. Unity 报错之 ToLua打包:Unable to find tolua DllNotFoundException: tolua

热门文章

  1. 想要学习人工智能,有哪些大学专业可以选择?
  2. Mac 用VMware安装win7后出现鼠标指针不能点击的问题
  3. Java自学笔记之网络编程
  4. 完数什么意思_数学中合数是什么意思?
  5. 【光学】基于matlab GUI杨氏双缝干涉【含Matlab源码 001期】
  6. 【10道大厂必考的计算机网络问题】陶辉老师
  7. 《欢乐颂》:成年人世界,智商在一…
  8. Docker版Dzzoffice安装教程
  9. 卷积神经网络:填充、步幅
  10. zigbee初始化流程