在這個美即真理、全民娛樂的時代,可愛有趣的人臉貼紙在各大美顏軟件中獲得了普遍的應用,如今已經不只侷限於相機美顏類軟件中,在社交、娛樂類的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