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

当家里的小孩观看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. 全程图解 手把手教您开启windows终端服务

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

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

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

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

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

  4. 虹软 Android 人脸检测与人脸识别集成分享

    目前我们的应用内使用了 ArcFace 的人脸检测功能,其他的我们并不了解,所以这里就和大家分享一下我们的集成过程和一些使用心得 集成ArcFace FD 的集成过程非常简单 在 ArcFace FD ...

  5. ArcFace Android 人脸检测与人脸识别集成分享

    目前我们的应用内使用了 ArcFace 的人脸检测功能,其他的我们并不了解,所以这里就和大家分享一下我们的集成过程和一些使用心得集成ArcFace FD 的集成过程非常简单在 ArcFace FD 的 ...

  6. 超简单集成华为HMS ML Kit文本识别SDK,一键实现账单号自动录入

    前言   在之前的文章<超简单集成华为HMS Core MLKit通用卡证识别SDK,一键实现各种卡绑定>中我们给大家介绍了华为HMS ML Kit通用卡证识别技术是如何通过拍照自动识别卡 ...

  7. 庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境

    庐山真面目之十一微服务架构手把手教你搭建基于Jenkins的企业级CI/CD环境 一.介绍 说起微服务架构来,有一个环节是少不了的,那就是CI/CD持续集成的环境.当然,搭建CI/CD环境的工具很多, ...

  8. 用华为HMS ML kit人体骨骼识别技术,Android快速实现人体姿势动作抓拍&amp;nbsp;

      你有没有过这种体验,拍照时对着镜头,脑子一片空白.表情僵硬.手和脚无处安放,最后拍出来的照片很是奇怪.拍照软件中的固定姿势抓拍功能可以帮助你:选择一个你想要的姿势模板,当你摆出同款姿势时,软件会进 ...

  9. 超简单集成HMS ML Kit文字超分能力,一键提升文本分辨率

    前言 大家有没有遇到过这种情况,在浏览微博或者公众号时看到一段有趣的文字,于是截图发到朋友圈想和好友分享.但是在发布图片时,软件会对图片强制进行压缩,导致图片分辨率下降,文字变得模糊难以阅读.那么有没 ...

随机推荐

  1. Ubuntu操作系统相关

    1.安装 三种网络类型 修改密码 重启unbuntu系统,出现starting启动界面后,长按shift键. 出现如下引导界面: (注意:这里保持默认的选项就行,即白色横条选择在*Ubuntu上,不要 ...

  2. 想抛就抛:Application_Error中统一处理ajax请求执行中抛出的异常

    女朋友不是想抛就抛,但异常却可以,不信请往下看. 今天在MVC Controller中写代码时,纠结了一下: public async Task<ActionResult> Save(in ...

  3. CSS简写指南

    高效的css写法中的一条就是使用简写.通过简写可以让你的CSS文件更小,更易读.而了解CSS属性简写也是前端开发工程师的基本功之一.今天我们系统地总结一下CSS属性的缩写. 色彩缩写 色彩的缩写最简单 ...

  4. 查看现有运行的linux服务器有多少内存条

    i161 admin # ssh 192.168.5.209 dmidecode | grep 'Ending Address' -B1 -A2    Starting Address: 0x0000 ...

  5. mysql 根据某个字段将多条记录的某个字段拼接成一个字段

    未合并情况 SELECT a.id, b.name AS "role" FROM sys_user a INNER JOIN sys_user_role c ON a.id=c.u ...

  6. 【js】操作checkbox radio 的操作总结

    摘要 总是忘记checkbox radio 的具体操作,总是坑自己,总结下记下来 html <input type="checkbox" value="1" ...

  7. href和src的区别(小计)

    1.Src 是指向物件的来源地址,请求src资源时会将其指向的资源下载并应用文档中 src的内容是页面上比不可少的一部分,是引入.在 img.script.iframe 等元素上使用. 2.href ...

  8. centos7系统优化定制

    #!/bin/bash #author junxi by #this script is only for CentOS 7.x #check the OS platform=`uname -i` i ...

  9. Python: 正则表达式匹配多行,实现多行匹配模式

    1) 点 (.)去匹配任意字符的时候,不能匹配换行符 在这个模式中(?:.|\n)是指定了一个非捕获组(仅仅用来做匹配,部能通过单独捕获或者编号的组) 2) re.DOTALL re.compile( ...

  10. C语言学习记录_2019.02.10

    sizeof:给出某个类型或某个变量在内存中占据的字节数:(1个字节8位,即8比特) 格式符 (1)%ld表示数据按十进制有符号长型整数输入或输出. (2)%d表示数据按十进制有符号整型数输入或输出. ...