当给自己拍一张美美的自拍照时,却发现照片中自己的脸不够瘦、眼睛不够大、表情不够丰富可爱…如果此时能够一键美颜瘦脸并且添加可爱的贴纸的话,是不是很棒?

当家里的小孩观看iPad屏幕时间过长或者眼睛离屏幕距离过近,家长没能时刻关注到时,如果有一款可以实现parent control的应用,那是不是很方便?面对以上问题,华为机器学习服务(ML Kit)的人脸检测功能轻松帮你搞定!

华为机器学习服务的人脸检测功能可以对人脸多达855个关键点进行检测,从而返回人脸的轮廓、眉毛、眼睛、鼻子、嘴巴、耳朵等部位的坐标以及人脸偏转角度等信息。集成人脸检测服务后开发者可以根据这些信息快速构建人脸美化的应用,或者在脸上加一些有趣可爱的贴纸元素,增加图片的趣味性。除了这个强大的功能外,人脸检测服务还可以识别人脸中包括眼睛是否睁开、是否戴眼镜或帽子、性别、年龄、是否有胡子等特征。除此之外,人脸检测功能可以识别人脸多达七种表情,包括微笑、无表情、愤怒、厌恶、惊恐、悲伤和惊讶。



“瘦脸大眼”开发实战

1. 开发准备

详细的准备步骤可以参考华为开发者联盟

这里列举关键的开发步骤。

1.1 项目级gradle里配置Maven仓地址

buildscript {
repositories {
...
maven {url 'https://developer.huawei.com/repo/'}
}
}
dependencies {
...
classpath 'com.huawei.agconnect:agcp:1.3.1.300'
}
allprojects {
repositories {
...
maven {url 'https://developer.huawei.com/repo/'}
}
}

1.2 文件头增加配置

集成SDK后,在文件头添加配置

apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'

1.3 应用级gradle里配置SDK依赖

dependencies{
// 引入基础SDK
implementation 'com.huawei.hms:ml-computer-vision-face:2.0.1.300'
// 引入人脸轮廓+关键点检测模型包
implementation 'com.huawei.hms:ml-computer-vision-face-shape-point-model:2.0.1.300'
// 引入表情检测模型包
implementation 'com.huawei.hms:ml-computer-vision-face-emotion-model:2.0.1.300'
// 引入特征检测模型包
implementation 'com.huawei.hms:ml-computer-vision-face-feature-model:2.0.1.300'
}

1.4 将以下语句添加到AndroidManifest.xml文件中,用于自动更新机器学习模型

<manifest
...
<meta-data
android:name="com.huawei.hms.ml.DEPENDENCY"
android:value= "face"/>
...
</manifest> 1.3 申请摄像头权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

2. 代码开发

2.1 使用默认参数配置,创建人脸分析器

analyzer =   MLAnalyzerFactory.getInstance().getFaceAnalyzer();

2.2  通过android.graphics.Bitmap创建MLFrame对象用于分析器检测图片
MLFrame frame = MLFrame.fromBitmap(bitmap); 2.3 调用“asyncAnalyseFrame”方法进行人脸检测
Task<List<MLFace>> task = analyzer.asyncAnalyseFrame(frame);
task.addOnSuccessListener(new OnSuccessListener<List<MLFace>>() {
@Override
public void onSuccess(List<MLFace> faces) {
// 检测成功,获取脸部关键点信息。
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// 检测失败。
}
});

2.4 通过进度条进行不同程度的大眼瘦脸处理。分别调用magnifyEye方法和smallFaceMesh方法实现大眼算法和瘦脸算法

private SeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
switch (seekBar.getId()) {
case R.id.seekbareye: // 当大眼进度条变化时,…
case R.id.seekbarface: // 当瘦脸进度条变化时,…
}
}
}
2.5 检测完成,释放分析器
try {
if (analyzer != null) {
analyzer.stop();
}
} catch (IOException e) {
Log.e(TAG, "e=" + e.getMessage());
}

Demo效果

“有趣可爱贴纸”开发实战

开发前准备

在项目级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文件里面申请相机、访问网络和存储权限

<!--相机权限-->
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA" />
<!--写权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--读权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

代码开发关键步骤

设置人脸检测器

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<MLFace> 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<MLPosition> 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数据定义介绍如何设计贴纸,首先看一下贴纸数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效果

欲了解更多详情,请参阅:华为开发者联盟官网、开发指导文档

参与开发者讨论请到Reddit: https://www.reddit.com/r/HuaweiDevelopers/

下载demo和示例代码请到Github:https://github.com/HMS-Core

解决集成问题请到Stack Overflow:https://stackoverflow.com/questions/tagged/huawei-mobile-services?tab=Newest

手把手教你集成华为机器学习服务(ML Kit)人脸检测功能的更多相关文章

  1. 手把手教你学Dapr - 4. 服务调用

    上一篇:手把手教你学Dapr - 3. 使用Dapr运行第一个.Net程序 介绍 通过使用服务调用,您的应用程序可以使用标准的gRPC或HTTP协议与其他应用程序可靠.安全地通信. 为什么不直接用Ht ...

  2. 全程图解 手把手教您开启windows终端服务

    一.什么是远程桌面? 远程桌面是微软公司为了方便网络管理员管理维护服务器而推出的一项服务.从windows 2000 server版本开始引入,网络管理员使用远程桌面连接程序连接到网络任意一台开启了远 ...

  3. 手把手教用C#编写Windows服务 并控制服务 安装、启动、停止、卸载

    Windows服务 Microsoft Windows 服务(即,以前的 NT 服务)使您能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序.这些服务可以在计算机启动时自动启动, ...

  4. 手把手教你写一个windows服务 【基于.net】 附实用小工具{注册服务/开启服务/停止服务/删除服务}

    1,本文适用范围 语言:.net 服务类型:windows服务,隔一段时间执行 2,服务搭建: 1,在vs中创建 console程序 2,在console项目所在类库右键 添加-新建项-选择Windo ...

  5. 超简单集成华为 HMS MLKit 机器学习服务:银行卡识别 SDK,一键实现银行卡绑定

    前言 小编前面几期文章分别给大家介绍了使用 HMS ML Kit SDK 实现微笑抓拍.证件照 DIY.拍照翻译的功能开发(链接见文章末尾),本次小编给大家带来的是使用 HMS 机器学习服务(ML K ...

  6. 超简单集成 HMS ML Kit 实现最大脸微笑抓拍

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

  7. 手把手教你学Dapr - 9. 可观测性

    目录 手把手教你学Dapr - 1. .Net开发者的大时代 手把手教你学Dapr - 2. 必须知道的概念 手把手教你学Dapr - 3. 使用Dapr运行第一个.Net程序 手把手教你学Dapr ...

  8. 手把手教你学Dapr - 5. 状态管理

    上一篇:手把手教你学Dapr - 4. 服务调用 介绍 使用状态管理,您的应用程序可以将数据作为键/值对存储在支持的状态存储中. 您的应用程序可以使用 Dapr 的状态管理 API 使用状态存储组件来 ...

  9. 手把手教你学Dapr - 8. 绑定

    目录 手把手教你学Dapr - 1. .Net开发者的大时代 手把手教你学Dapr - 2. 必须知道的概念 手把手教你学Dapr - 3. 使用Dapr运行第一个.Net程序 手把手教你学Dapr ...

随机推荐

  1. HDU - 2159 dp

    题目: 最近xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务.久而久之xhd开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级.现在的问题是,xhd升掉最后一级还 ...

  2. Python小练习批量爬取下载歌曲

    import requests import os headers={ 'Cookie': '_ga=GA1.2.701818100.1612092981; _gid=GA1.2.748589379. ...

  3. OpenStack Train版-11.安装horizon服务(计算节点)

    OpenStack仪表板Dashboard服务的项目名称是Horizon,它所需的唯一服务是身份服务keystone,开发语言是python的web框架Django. 安装Train版本的Horizo ...

  4. Kubernetes安装EFK教程(非存储持久化方式部署)

    1.简介 这里所指的EFK是指:ElasticSearch,Fluentd,Kibana ElasticSearch Elasticsearch是一个基于Apache Lucene的开源搜索和数据分析 ...

  5. git branch & git remote branch

    git branch & git remote branch $ git branch -h usage: git branch [<options>] [-r | -a] [-- ...

  6. 如何在 macOS 上搭建 PHP 开发环境

    如何在 macOS 上搭建 PHP 开发环境 Linux, Nginx, MySQL, PHP $ php --version $ php -v # PHP 7.3.11 (cli) (built: ...

  7. select notes mark

    select notes mark mark-line https://time.geekbang.org/column/article/224545

  8. Flutter Navigator2.0

    Example 1 import 'package:dart_printf/dart_printf.dart'; import 'package:flutter/material.dart'; cla ...

  9. js generator和yield

    function co<T>(fn: () => Generator<any, any, any>): Promise<T> { const g: Gener ...

  10. NGK每日快讯」2021.1.27日NGK公链第85期官方快讯!