跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • 尝试 AI 人脸关键点算法实现一下 Android 人脸匿名功能
未分類
20 7 月 2020

尝试 AI 人脸关键点算法实现一下 Android 人脸匿名功能

尝试 AI 人脸关键点算法实现一下 Android 人脸匿名功能

資深大佬 : exmorning 8

什么是人脸匿名( Face Anonymization )

随着人脸识别技术的普及,人脸数据的隐私问题也得到越来越多关注,针对隐私保护的研究也陆续出现。目前大致有下面几个方向

  1. 篡改输入人脸识别系统的图像。
  2. 生成式对抗网络(GAN)来匿名某人的照片或视频。
  3. 直接模糊人脸识别到的人脸

本文主要讲第 3 点,讲讲怎么使用移动端人脸关键点算法实现人脸匿名功能。这种方法对设备要求低,代码简单易懂,修改后就可直接落地。

下图就是最终想实现的功能
尝试 AI 人脸关键点算法实现一下 Android 人脸匿名功能

什么是人脸关键点算法( Face Landmarks )

人脸关键点检测是人脸相关算法中的关键一环,它是人脸识别、表情分析、3D 人脸重建,表情驱动 3D 动画等一系列人脸相关问题的前提。

尝试 AI 人脸关键点算法实现一下 Android 人脸匿名功能

我们将使用 TengineKit 来实现人脸匿名功能

TengineKit

免费移动端实时人脸 212 关键点 SDK 。是一个易于集成的人脸检测和人脸关键点 SDK 。它可以在各种手机上以非常低的延迟运行。
https://github.com/OAID/TengineKit

TengineKit 效果图

尝试 AI 人脸关键点算法实现一下 Android 人脸匿名功能

实现

配置 Gradle

Project 中的 build.gradle 添加

    repositories {         ...         mavenCentral()         ...     }      allprojects {         repositories {             ...             mavenCentral()             ...         }     } 

主 Module 中的 build.gradle 添加

    dependencies {         ...         implementation 'com.tengine.android:tenginekit:1.0.3'         ...     } 

配置 manifests

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />     <uses-permission android:name="android.permission.INTERNET"/>      <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>     <uses-permission android:name="android.permission.READ_PHONE_STATE"/>      <uses-permission android:name="android.permission.CAMERA"/>     <uses-permission android:name="android.permission.FLASHLIGHT" />      <uses-feature android:name = "android.hardware.camera" android:required="true"/>     <uses-feature android:name = "android.hardware.camera.autofocus" /> 

初始化 Android Camera

为 App 创建自定义摄像头界面的步骤如下:

  1. 检测和访问 Camera
  2. 创建预览 TextureView
  3. 构建预览 TextureView 布局
  4. 将 Camera 和 TextureView 绑定
  5. 启动预览

我们先 new 一个 TextureView.SurfaceTextureListener,在里面完成 camera 的初始配置,当 TextureView 可用的时候,onSurfaceTextureAvailable 中的代码将被调用

   private final TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {         @Override         public void onSurfaceTextureAvailable(final SurfaceTexture texture, final int width, final int height) {             int index = getCameraId();             camera = Camera.open(index);              try {                 Camera.Parameters parameters = camera.getParameters();                 List<String> focusModes = parameters.getSupportedFocusModes();                 if (focusModes != null && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {                     parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);                 }                 List<Camera.Size> cameraSizes = parameters.getSupportedPreviewSizes();                 Size[] sizes = new Size[cameraSizes.size()];                 int i = 0;                 for (Camera.Size size : cameraSizes) {                     sizes[i++] = new Size(size.width, size.height);                 }                 Size previewSize = CameraConnectionFragment.chooseOptimalSize(sizes, desiredSize.getWidth(), desiredSize.getHeight());                 parameters.setPreviewSize(previewSize.getWidth(), previewSize.getHeight());                 camera.setDisplayOrientation(90);                 camera.setParameters(parameters);                 camera.setPreviewTexture(texture);             } catch (IOException exception) {                 camera.release();             }              camera.setPreviewCallbackWithBuffer(imageListener);             Camera.Size s = camera.getParameters().getPreviewSize();             camera.addCallbackBuffer(new byte[ImageUtils.getYUVByteSize(s.height, s.width)]);              textureView.setAspectRatio(s.height, s.width);             camera.startPreview();         }          @Override         public void onSurfaceTextureSizeChanged(final SurfaceTexture texture, final int width, final int height) {         }          @Override         public boolean onSurfaceTextureDestroyed(final SurfaceTexture texture) {             return true;         }          @Override         public void onSurfaceTextureUpdated(final SurfaceTexture texture) {         }     }; 

此处将 textureView 和 camera 联系起来

    textureView.setSurfaceTextureListener(surfaceTextureListener); 

当 camera 启动预览,textureView 得到真实的 size 后。我们得到了 camera 的输出视频流的宽高和预览 textureView,将其保存起来,后续有用到。

    textureView.setRealSizeListener(new AutoFitTextureView.RealSizeListener() {         @Override         public void onRealSizeMeasure(int w, int h) {             if(!isReady){                 isReady = true;                 Camera.Size s = camera.getParameters().getPreviewSize();                 cameraReadyListener.onCameraReady(                         s.width, s.height,w, h                 );             }         }     }); 

处理 Camera 传过来的视频流

首先我们先初始化 TengineKit:

  1. 选用 camera 处理模式
  2. 打开人脸检测和人脸关键点功能
  3. 设置视频流格式为 YUV_NV21 ( Android camera 默认格式)
  4. 设置输入视频流的宽高,此处为 camera 的预览宽高
  5. 设置输出视频流的宽高,此处为 textrureView 的宽高
  6. 设置输入视频流来自前置摄像头
    com.tenginekit.Face.init(getBaseContext(),             AndroidConfig.create()                     .setCameraMode()                     .openFunc(AndroidConfig.Func.Detect)                     .openFunc(AndroidConfig.Func.Landmark)                     .setInputImageFormat(AndroidConfig.ImageFormat.YUV_NV21)                     .setInputImageSize(previewWidth, previewHeight)                     .setOutputImageSize(outputWidth, outputHeight)     );     com.tenginekit.Face.Camera.switchCamera(false); 

处理数据

  1. 得到手机旋转角度,将其设置到 TengineKit
  2. 开始检测,当检测到人脸数目大于 0 的时候,调用 faceDetect.landmark2d(),得到人脸关键点链表
    int degree = CameraEngine.getInstance().getCameraOrientation(sensorEventUtil.orientation);      com.tenginekit.Face.Camera.setRotation(degree - 90, false,             outputWidth, outputHeight);      com.tenginekit.Face.FaceDetect faceDetect = Face.detect(data);     faceLandmarks = null;     if(faceDetect.getFaceCount() > 0){         faceLandmarks = faceDetect.landmark2d();     } 

高斯模糊和绘制

这里使用 Android 的 bitmap 来实现功能,这种做法比较粗糙,性能差,但是简单易懂,如果读者有兴趣可以使用 OpenGLES 来实现此功能。

  1. 将从摄像头中得到的 yuv 数据通过 TengineKit 的图片帮助函数转化为 Bitmap
  2. 通过人脸关键点的外接框,裁剪 bitmap 得到人脸的 bitmap 数组
  3. 将得到的人脸 bitmap 进行高斯模糊
    if(testBitmap != null){         testBitmap.recycle();     }     testBitmap = Face.Image.convertCameraYUVData(             data,             previewWidth, previewHeight,             outputWidth, outputHeight,             - 90,             true);       for(Bitmap bitmap : testFaceBitmaps){         bitmap.recycle();     }     testFaceBitmaps.clear();     if(testBitmap != null && faceDetect.getFaceCount() > 0){         if(faceLandmarks != null){             for (int i = 0; i < faceLandmarks.size(); i++) {                     Bitmap face = BitmapUtils.getDstArea(testBitmap, faceLandmarks.get(i).getBoundingBox());                     face = BitmapUtils.blurByGauss(face, 50);                     testFaceBitmaps.add(face);             }         }     }      runInBackground(new Runnable() {         @Override         public void run() {             trackingOverlay.postInvalidate();         }     }); 

trackingOverlay 为定制的 view,将 canvas 暴露出来用于画 bitmap

    trackingOverlay.addCallback(new OverlayView.DrawCallback() {         @Override         public void drawCallback(final Canvas canvas) {             if(testBitmap != null){                 canvas.drawBitmap(testBitmap, 0,0, circlePaint);             }             if(faceLandmarks != null){                 for (int i = 0; i < faceLandmarks.size(); i++) {                     Rect r = faceLandmarks.get(i).getBoundingBox();                     canvas.drawRect(r, circlePaint);                     canvas.drawBitmap(testFaceBitmaps.get(i), r.left, r.top, circlePaint);                 }             }         }     }); 

效果

尝试 AI 人脸关键点算法实现一下 Android 人脸匿名功能 尝试 AI 人脸关键点算法实现一下 Android 人脸匿名功能

Demo

尝试 AI 人脸关键点算法实现一下 Android 人脸匿名功能

参考

https://github.com/OAID/TengineKit

源码

https://github.com/jiangzhongbo/TengineKit_Demo_Identity_Protection

知乎

https://zhuanlan.zhihu.com/p/161038093

大佬有話說 (14)

  • 資深大佬 : Jirajine

    那么如何确保原始人脸数据不被这个 sdk 偷走呢。

  • 資深大佬 : aabbcc112233

    还是上次那个小姐姐吗?感觉颜值降低了 1 分。另外她的微信号你还没发给我

  • 主 資深大佬 : exmorning

    @Jirajine 目前就 auth 了一下,不上传任何数据

  • 主 資深大佬 : exmorning

    @aabbcc112233 不是给了抖音号可以发私信嘛,微信号得自己要啊

  • 資深大佬 : meilande

    用 bitmap 性能不是很好,正在商用得用 OpenGL 写

  • 主 資深大佬 : exmorning

    @meilande 如果想实现下 OpenGL,可以参考 https://github.com/CainKernel/CainCamera

  • 資深大佬 : emeab

    对于某些视频很实用啊

  • 資深大佬 : CoderGeek

    再整点换脸 QvQ

  • 主 資深大佬 : exmorning

    @emeab 你启发了我!

  • 主 資深大佬 : exmorning

    @CoderGeek 下一篇考虑写一个换脸的 demo

  • 資深大佬 : JackCui

    跟 landmark 有什么关系?不就是检测个人脸 bbox,然后加个模糊吗?

  • 主 資深大佬 : exmorning

    @JackCui 用关键点外接框做的,如果用人脸检测框,打码没有这么稳定

  • 資深大佬 : CoderGeek

    @exmorning 赞~

  • 主 資深大佬 : exmorning

    @CoderGeek 😉

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具