在这个美即真理、全民娱乐的时代,可爱有趣的人脸贴纸在各大美颜软件中获得了普遍的应用,如今已经不只局限于相机美颜类软件中,在社交、娱乐类的app中对人脸贴纸、AR贴纸的需求也很是普遍。本文详细介绍了集成华为HMS ML kit人脸识别实现2d贴纸的集成过程,在后面的文章中咱们还会介绍3D贴纸的开发过程,欢迎你们关注哦~android
在美颜相机、美图app以及社交类app(如抖音、微博、微信)等须要对拍照,或者对照片进行处理的app都会构建本身特有的贴纸的需求。缓存
打开AndroidStudio项目级build.gradle文件微信
增量添加以下maven地址:网络
buildscript { { maven {url 'http://developer.huawei.com/repo/'} } } allprojects { repositories { maven { url 'http://developer.huawei.com/repo/'} } }
// 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'
<!--相机权限--> <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);
@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接口返回的人脸轮廓点状况如图所示:app
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; // 最大贴纸渲染次数 ... }
{ "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 }] }
@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(); }
@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); }
@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); } }
这样咱们的贴纸就画到人脸上了.maven
原文连接:https://developer.huawei.com/consumer/cn/forum/topicview?tid=0203324526929930082&fid=18ide
原做者:旭小夜oop