跳转至

图像分析

前言

人脸检测

google ml-kit教程:

ml-kit的检测器

  • 定义一个类,在类里加载ml-kit的检测器,然后,提供图像即可进行处理,本示例不包含连接到相机分析流部分
val process = BaseImageAnalyzer()
//传入bitmap调用分析
process.processBitmap(bitmap) { list ->

}
//这个类里维护了ml-kit 分析器
class BaseImageAnalyzer {
    private val executor = ScopedExecutor(TaskExecutors.MAIN_THREAD)

    //ml-kit的人脸检测器
    private val detector: FaceDetector

    init {
        //初始化检测器
        val options = FaceDetectorOptions.Builder()
            .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE)//在检测人脸时更注重速度还是准确性,精确模式会检测到比快速模式更少的人脸
            .setContourMode(FaceDetectorOptions.CONTOUR_MODE_NONE)//轮廓检测
            .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_NONE)//面部特征点
            //.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)//是否将人脸分为不同类别(例如“微笑”和“眼睛睁开”)。
            .setMinFaceSize(1.0f)//人脸最小占图片的百分比
            //.enableTracking() //disable when contour is enable https://developers.google.com/ml-kit/vision/face-detection/android
            .build()

        detector = FaceDetection.getClient(options)
        Log.v(TAG, "Face detector options: $options")

    }

    fun stop() {
        detector.close()
    }

    //使用检测器开始处理图片
    fun processBitmap(bitmap: Bitmap, listener: OnSuccessListener<List<Face>>) {
        detector.process(InputImage.fromBitmap(bitmap, 0))
            .addOnSuccessListener(executor, listener)
            .addOnFailureListener(
                executor,
                OnFailureListener { e: Exception ->
                    val error = "Failed to process. Error: " + e.localizedMessage
                    Log.d(TAG, error)
                    e.printStackTrace()
                }
            )
    }
}

连接到相机分析流

需要使类继承自ImageAnalysis.Analyzer,重写ImageAnalysis.Analyzer 的analyze方法,将此类作为analyzer usecase 绑定到相机后,相机自动调用其analyze方法,提供分析数据

  • camerax绑定用例示例(在CameraXManager类中,不需要手动调用):
cameraProvider.bindToLifecycle(lifeOwner, cameraSelector, preview, imageAnalyzer)
  • 继承ImageAnalysis.Analyzer,并调用ml-kit示例: 继承ImageAnalysis.Analyzer,在analyze方法中调用ml-kit的检测器,将相机数据交给ml-kit做人脸检测,检测器部分和上面是一样的。
class BaseImageAnalyzer : ImageAnalysis.Analyzer {
    private val executor = ScopedExecutor(TaskExecutors.MAIN_THREAD)

    //mlkit的人脸检测器
    private val detector: FaceDetector
    //...省略初始化detector和其他东西

    //重写ImageAnalysis.Analyzer 的analyze方法
    @SuppressLint("UnsafeExperimentalUsageError", "UnsafeOptInUsageError")
    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image
        val rotationDegrees = imageProxy.imageInfo.rotationDegrees

        mediaImage?.let {
            //检测
            detector.detectInImage(InputImage.fromMediaImage(it, rotationDegrees))
                .addOnSuccessListener { results ->
                    onSuccess(
                        imageProxy,
                        results,
                        graphicOverlay,
                    )
                }
                .addOnFailureListener {
                    graphicOverlay.clear()
                    graphicOverlay.postInvalidate()
                    onFailure(it)
                }
                .addOnCompleteListener {
                    imageProxy.close()
                }
        }
    }
}

ml-kit使用:

  • 使用google ml-kit 并连接到相机分析流示例: 还是以CameraExampleActivity 为例:

  • 需要在配置相机时,指定模式为CaptureMode.imageAnalysis

    override fun configAll(intent: Intent): ManagerConfig {
    //....省略
    return ManagerConfig().apply {
        this.captureMode = CaptureMode.imageAnalysis//设置为分析图像模式
        this.size = Size(1920, 1080)//拍照,预览的分辨率,期望值,不一定会用这个值
    }
}
  1. 在cameraHolderInitStart方法中,调用 cameraHolder.changeAnalyzer()设置图像分析器,这样即可完成上面所说将分析器绑定到相机

  2. 示例1 使用ml-kit处理图像,绘制人脸边框和“特征点”的连线 FaceContourDetectionProcessor继承自BaseImageAnalyzer,实现了绘制人脸图像框体,点位连线等的内容

    override fun cameraHolderInitStart(cameraHolder: CameraHolder) {
    super.cameraHolderInitStart(cameraHolder)
    val cameraPreview = cameraHolder.cameraPreview

    //使用mlkit进行人脸检测,并绘制人脸框体和点位
    val analyzer = FaceContourDetectionProcessor(
        cameraPreview,
        page.graphicOverlayFinder,
    ).also {
        cameraHolder.changeAnalyzer(it)//设置图像分析器
    }
    //监听分析结果
    (analyzer as FaceContourDetectionProcessor).analyzeListener =
        AnalyzeResultListener {bitmap:Bitmap?,faces:List<Face> ->
            // when analyze success
        }
}
  • 示例2 还是使用ml-kit处理图像,得到包含人脸信息的照片,然后将其交给tensorflow lite模型处理,得到面部特征点。
    override fun cameraHolderInitStart(cameraHolder: CameraHolder) {
    super.cameraHolderInitStart(cameraHolder)
    //加载tensorflow lite模型,这里仅做演示
    val model = FaceDetection.create(
        this.assets,
        TestFileDecActivity.TF_OD_API_MODEL_FILE,
        TestFileDecActivity.TF_OD_API_LABELS_FILE,
        TestFileDecActivity.TF_OD_API_IS_QUANTIZED
    )
    //TensorFlowLink继承自ImageAnalysis.Analyzer,将其作为分析器设置给相机即可拿到分析流
    val tensorFlowLink = TensorFlowLink { image: ImageProxy ->
        //获取图像
        val bitmap = image.toBitmap()
        //这里使用了ml-kit分析是否包含面部数据,如果包含,则将面部图像裁剪下来
        MyFileProcessor.process(bitmap) {
            it?.let {
                //将裁剪后的面部图像转换成特定尺寸bitmap
                val tmp = FaceDetection.convertBitmap(it)
                //将处理好的面部图像交给模型处理,获取特征点
                val masks = model.detectionBitmap(tmp)
            }
        }

    }.also {
        cameraHolder.changeAnalyzer(it)//设置图像分析器
    }

}

除使用相机的分析流之外,还可以手动获取相机图片进行分析

示例:CameraExampleActivity文件中

  /**
 * 每隔20ms从预览视图中获取bitmap
 * 然后运行图像分析,绘制矩形框
 * 但是这种方式分析图象后,绘制框体会有延迟、卡顿感,不如直接使用图像分析流畅
 */
suspend fun runFaceDetection(interval: Long = 20L) {
    if (cameraConfig.isUsingImageAnalyzer() || stopAnalyzer) {
        Log.d(TAG, "runFaceDetection: 已使用图像分析或stopAnalyzer==true")
        return
    } else {
        flow<Boolean> {
            while (true) {
                delay(interval)
                emit(stopAnalyzer)
                if (stopAnalyzer) {
                    break
                }
            }
        }.collect {
            cameraXF.provideBitmap()?.let { originalBitmap ->
                //识别图像
                BitmapProcessor.process(originalBitmap) { faces: List<Face> ->
                    //上面依据识别成功,得到了返回数据,我们在这里调用了一个普通方法来使用识别出来的数据
                    BitmapProcessor.onSuccess(faces, page.graphicOverlayFinder)
                }
            }

        }
    }
}

tensorflow

面部识别,特征点计算

来源

文章链接

  • TestFileDecActivity
  • 若需要连接到相机分析流,请看上面章节
  • 加载tensorflow lite模型,运行检测,请看FaceDetection.kt文件
//使用TensorFlow Lite 模型的处理器
private val model = FaceDetection.create(
        this.assets,
        TF_OD_API_MODEL_FILE,
        TF_OD_API_LABELS_FILE,
        TF_OD_API_IS_QUANTIZED
   )
StoreX.with(this).safHelper.selectFile(fileType = "image/*") { uri ->
            //选择图片后经过mlkit的处理,以及MyFileProcessor中的裁剪,得到只有面部区域的bitmap
            MyFileProcessor.process(contentResolver, uri) {
                //处理bitmap,获取面部特征点
                it?.let { it1 ->
                    //将bitmap转换成特定尺寸bitmap
                    val tmp = FaceDetection.convertBitmap(it1)
                    //获取特征点
                    val masks = model.detectionBitmap(tmp)
                }
            }
        }

活体检测

来源:模型实现 模型训练 which only supports print attack and replay attack. If you have other requirements, please use this source code to retrain.

使用方式: 首先,你需要将人脸部分裁剪下来, 然后调用FaceAntiSpoofingHolder中的anti(bitmap)方法,得到一个0-1的分数。

  1. 如果你使用了mlkit进行检测,则已经处理好了,直接设置AnalyzeResultListener,就可以在回调中得到面部区域信息和图片, 此时直接调用crop方法即可裁剪。
//监听分析结果
(analyzer as FaceContourDetectionProcessor).analyzeListener =
  AnalyzeResultListener { it, faces: List<Face> ->
    //裁剪图像,或许需要优化裁剪的尺寸
    it?.cropImage(faces)?.let { bitmap: Bitmap? ->
      if (bitmap != null) {
        // when analyze success
        lifecycleScope.launch {
          val score = antiHelper.anti(bitmap)
          tv.setText("为假的可能性:$score")
        }
      }
    }
  }
  1. 如果没使用mlkit分析,直接调用MyFileProcessor.process方法即可。
    MyFileProcessor.process(it){croped->
        croped?.let{tmp->
          lifecycleScope.launch {
            val score = FaceAntiSpoofingHolder.instance(application)
              .anti(tmp)
            page.tvAnti.setText("为假的可能性:$score")
          }
        }
    }