探索MediaPipe检测人脸关键点

news2024/11/17 1:42:56

MediaPipe是Google开源的计算机视觉处理框架,基于TensorFlow来训练模型,支持人脸识别、人脸关键点、物体检测追踪、图像分类、人像分割、手势识别、文本分类、语音分类等。我们可以使用CPU来推理,也可以选择GPU加速推理。在滤镜特效场景,经常需要用到人脸关键点。

目录

一、配置参数与模型

1、配置参数

2、检测模型 

二、工程配置

三、初始化工作

1、初始化模型

2、初始化Camera

四、检测实时流

1、检测人脸关键点

2、绘制人脸关键点

五、检测结果​​​​​​​

一、配置参数与模型

1、配置参数

检测人脸关键点的配置参数有运行模式、人脸数、最小的检测人脸置信度、最小的显示人脸置信度、最小的追踪人脸置信度、结果回调,具体如下表所示:

选项描述取值范围默认值
running_mode

IMAGE: 单个图像 

VIDEO: 视频帧

LIVE_STREAM: 实时流

{IMAGE,VIDEO,

LIVE_STREAM}

IMAGE
num_faces最多检测的人脸数大于01

min_face_detection

_confidence

人脸检测最小置信度[0.0, 1.0]0.5

min_face_presence

_confidence

人脸显示最小置信度[0.0, 1.0]0.5
min_tracking_confidence人脸追踪最小置信度[0.0, 1.0]0.5
output_face_blendshapes是否输出混合形状(用于3D人脸模型)Booleanfalse

output_facial_transformation

_matrixes

是否输出变换矩阵(用于滤镜特效)Booleanfalse
result_callback异步回调结果(LIVE_STREAM模式)ResultListener   /

2、检测模型 

检测人间关键点分为三步:首先检测人脸,然后定位关键点,最后识别面部特征。使用到的模型如下:

  • 人脸检测模型:根据人脸关键点特征来检测人脸;
  • 人脸网格模型:包含478个坐标点的3D人脸标识;
  • 混合形状模型:预测52个混合形状的分数,表示不同表情的系数; 

二、工程配置

以Android平台为例,在gradle导入MediaPipe相关包:

implementation 'com.google.mediapipe:tasks-vision:0.10.0'

然后运行下载模型的task,并且指定模型保存路径:

project.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'
apply from: 'download_tasks.gradle'

这里用到的模型是face_landmarker,设置src和dest:

task downloadTaskFile(type: Download) {
    src 'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task'
    dest project.ext.ASSET_DIR + '/face_landmarker.task'
    overwrite false
}

preBuild.dependsOn downloadTaskFile

三、初始化工作

1、初始化模型

模型的初始化包括:设置运行模式、模型路径、检测人脸数、回调结果等,示例代码如下:

    fun setupFaceLandmark() {
        val baseOptionBuilder = BaseOptions.builder()

        // 设置运行模式,默认CPU
        when (currentDelegate) {
            DELEGATE_CPU -> {
                baseOptionBuilder.setDelegate(Delegate.CPU)
            }
            DELEGATE_GPU -> {
                baseOptionBuilder.setDelegate(Delegate.GPU)
            }
        }
        // 设置模型路径
        baseOptionBuilder.setModelAssetPath(MP_FACE_LANDMARKER_TASK)

        try {
            val baseOptions = baseOptionBuilder.build()
            // 设置检测的人脸数、最小的检测人脸置信度
            val optionsBuilder =
                FaceLandmarker.FaceLandmarkerOptions.builder()
                    .setBaseOptions(baseOptions)
                    .setMinFaceDetectionConfidence(minFaceDetectionConfidence)
                    .setMinTrackingConfidence(minFaceTrackingConfidence)
                    .setMinFacePresenceConfidence(minFacePresenceConfidence)
                    .setNumFaces(maxNumFaces)
                    .setRunningMode(runningMode)

            // LIVE_STREAM模式:设置回调结果
            if (runningMode == RunningMode.LIVE_STREAM) {
                optionsBuilder
                    .setResultListener(this::returnLivestreamResult)
                    .setErrorListener(this::returnLivestreamError)
            }

            val options = optionsBuilder.build()
            faceLandmarker =
                FaceLandmarker.createFromOptions(context, options)
        } catch (e: IllegalStateException) {
            faceLandmarkerHelperListener?.onError(
                "Face Landmark failed to initialize, error: " + e.message)
        } catch (e: RuntimeException) {
            faceLandmarkerHelperListener?.onError(
                "Face Landmark failed to initialize. See error logs for details", GPU_ERROR)
        }
    }

2、初始化Camera

这里以LIVE_STREAM模式为例,Camera的初始化包括:设置像素格式、预览宽高比、绑定生命周期、关联SurfaceProvider。示例代码如下:

    private fun bindCameraUseCases() {
        val cameraProvider = cameraProvider ?: throw IllegalStateException("Camera init failed.")

        val cameraSelector =
            CameraSelector.Builder().requireLensFacing(cameraFacing).build()

        // 预览的宽高比为4:3
        preview = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3)
            .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)
            .build()

        // 设置像素格式为RGBA_8888,预览的旋转角度
        imageAnalyzer =
            ImageAnalysis.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3)
                .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
                .build()
                .also {
                    it.setAnalyzer(backgroundExecutor) { image ->
                        // 执行检测人脸关键点
                        faceLandmarkerHelper.detectLiveStream(image, cameraFacing == CameraSelector.LENS_FACING_FRONT)
                    }
                }

        // 绑定之前,先解除绑定
        cameraProvider.unbindAll()

        try {
            // 绑定Lifecycle
            camera = cameraProvider.bindToLifecycle(
                this, cameraSelector, preview, imageAnalyzer)
            // 关联SurfaceProvider
            preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)
        } catch (exc: Exception) {
            Log.e(TAG, "bind lifecycle failed", exc)
        }
    }

四、检测实时流

1、检测人脸关键点

在检测之前,先拷贝数据,图像帧预处理,然后执行检测:

    fun detectLiveStream(
        imageProxy: ImageProxy,
        isFrontCamera: Boolean) {
        val frameTime = SystemClock.uptimeMillis()

        // 拷贝RGB数据到缓冲区
        val bitmapBuffer =
            Bitmap.createBitmap(
                imageProxy.width,
                imageProxy.height,
                Bitmap.Config.ARGB_8888
            )
        imageProxy.use { bitmapBuffer.copyPixelsFromBuffer(imageProxy.planes[0].buffer) }
        imageProxy.close()

        val matrix = Matrix().apply {
            // 图像旋转
            postRotate(imageProxy.imageInfo.rotationDegrees.toFloat())

            // 如果是前置摄像头,需要左右镜像
            if (isFrontCamera) {
                postScale(-1f, 1f, imageProxy.width.toFloat(), imageProxy.height.toFloat())
            }
        }
        val rotatedBitmap = Bitmap.createBitmap(
            bitmapBuffer, 0, 0, bitmapBuffer.width, bitmapBuffer.height,
            matrix, true)

        // 转换Bitmap为MPImage
        val mpImage = BitmapImageBuilder(rotatedBitmap).build()
        // 异步检测人脸关键点
        faceLandmarker?.detectAsync(mpImage, frameTime)
    }

2、绘制人脸关键点

检测到人脸关键点结果后,然后回调到主线程:

   override fun onResults(resultBundle: FaceLandmarkerHelper.ResultBundle) {
        activity?.runOnUiThread {
            if (_fragmentCameraBinding != null) {
                // 显示推理时长
                fragmentCameraBinding.bottomSheetLayout.inferenceTimeVal.text =
                    String.format("%d ms", resultBundle.inferenceTime)

                // 传递结果给OverlayView
                fragmentCameraBinding.overlay.setResults(
                    resultBundle.result,
                    resultBundle.inputImageHeight,
                    resultBundle.inputImageWidth,
                    RunningMode.LIVE_STREAM
                )

                // 主动触发渲染
                fragmentCameraBinding.overlay.invalidate()
            }
        }
    }

最后绘制人脸关键点,包括面部表情、轮廓:

   override fun draw(canvas: Canvas) {
        super.draw(canvas)
        if(results == null || results!!.faceLandmarks().isEmpty()) {
            clear()
            return
        }

        results?.let { faceLandmarkResult ->
            // 绘制关键点
            for(landmark in faceLandmarkResult.faceLandmarks()) {
                for(normalizedLandmark in landmark) {
                    canvas.drawPoint(normalizedLandmark.x() * imageWidth * scaleFactor,
                        normalizedLandmark.y() * imageHeight * scaleFactor, pointPaint)
                }
            }
            // 绘制线条
            FaceLandmarker.FACE_LANDMARKS_CONNECTORS.forEach {
                canvas.drawLine(
                    faceLandmarkResult.faceLandmarks()[0][it!!.start()].x() * imageWidth * scaleFactor,
                    faceLandmarkResult.faceLandmarks()[0][it.start()].y() * imageHeight * scaleFactor,
                    faceLandmarkResult.faceLandmarks()[0][it.end()].x() * imageWidth * scaleFactor,
                    faceLandmarkResult.faceLandmarks()[0][it.end()].y() * imageHeight * scaleFactor,
                    linePaint)
            }
        }
    }

五、检测结果

输入数据可以是静态图像、实时视频流、文件视频帧。输出数据有人脸边界框、人脸网格、关键点坐标。其中,人脸关键点包括:脸部轮廓、嘴巴、鼻子、眼睛、眉毛、脸颊等,属于3D的landmark模型。如下图所示:

 在人脸识别、人脸关键点基础上,还支持换脸,变成可爱的卡通效果。眨眼睛、摇头、张嘴这些表情动作,都会有实时的卡通头像变化。如下图所示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/687946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【软考网络管理员】2023年软考网管初级常见知识考点(17)-数据加密与签名摘要

涉及知识点 对称加密,非对称加密,数字签名,报文摘要,数字证书 软考网络管理员常考知识点,软考网络管理员网络安全,网络管理员考点汇总。 文章目录 涉及知识点前言一、对称加密技术1.DES2.3DES3.IDEA4.AES5…

系统架构的精髓:18个必懂的设计概念一览

要想在系统设计领域中脱颖而出,深入理解一些基础的系统设计概念是必不可少的,这些概念包括负载均衡、缓存、分区、复制、数据库和代理等。 依据我自己的经验,我总结出了18个关键概念,如果能掌握这些,将极大地提升你处…

Android APT 系列 (二):APT 筑基之注解

注解介绍 元数据 元数据就是为其他数据提供信息的数据 注解 官方解释:注解用于为代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 JDK 1.5 开始添加到 Java 的。 简单…

建议收藏:OpenKruise入门与实践

简介 OpenKruise 是一个基于 Kubernetes 的扩展套件,主要聚焦于云原生应用的自动化,比如部署、发布、运维以及可用性防护。 OpenKruise 提供的绝大部分能力都是基于 CRD 扩展来定义,它们不存在于任何外部依赖,可以运行在任意纯净的…

css 背景颜色级别高于背景图

<div class"bg-parent"> <img :src"employeeImg" class"bg-url" /> <div class"bg"> <el-icon class"plus-icon"> <Plus /> </el-icon> </div> </div> .bg-parent{ //父级…

Web服务器群集:Nginx之Rewrite重写

目录 一、理论 1.Nginx正则表达式 2.location匹配 3.rewrite重写 二、实验 1.基于域名的跳转 2.基于客户端 IP访问跳转 3.基于旧域名跳转到新域名后面加目录 4.基于参数匹配的跳转 5.基于目录下所有 php结尾的文件跳转 6.基于最普通一条url请求的跳转 三、总结 一、…

福昕Foxit PDF远程代码执行漏洞CVE-2023-27363分析与复现

漏洞概述 福建福昕软件开发股份有限公司是一家国际化运营的PDF电子文档解决方案提供厂商&#xff0c;提供文档的生成、转换、显示、编辑、搜索、打印、存储、签章、表单、保护、安全分发管理等涵盖文档生命周期的产品技术与解决方案。其下产品Foxit PDF Reader和Foxit PDF Edit…

性能测试——App性能测试需要关注的指标

目录 一、Android客户端性能测试常见指标&#xff1a; 二、预期标准指定原则 三、测试方法及工具 一.从用户角度出发 二.站在管理员的角度考虑需要关注的性能点 三.站在开发(设计)人员角度去考虑 四.站在测试工程师角度考虑 总结&#xff1a; 一、Android客户端性能测试…

无功功率补偿及电容器的简单介绍 安科瑞 许敏

摘要&#xff1a;主要对无功功率的概念、电容器的补偿方式、补偿容量的计算等方面进行了阐述&#xff0c;在此基础上介绍了电容器运行的注意事项。 关键词&#xff1a;电力电容器&#xff1b;补偿方式 &#xff1b;运行 0 引言 笔者从进入吕合煤业从事电工工作的那天起&#…

Wav2Lip原理以及训练

原理 1: 音视频同步判别器 常规SyncNet&#xff1a; 功能&#xff1a;音频和嘴唇同步 实质&#xff1a;判断音频和唇形在某个共同参数空间下的相似性。 网络结构&#xff1a; 一种伪孪生网络结构&#xff0c;分别提取嘴形特征和音频特征&#xff0c;然后通过对比损失计算两者…

VS2017+OpenCV4.5.1 安装与配置,扩展模块opencv_contrib的安装与配置

文章目录 VS2017OpenCV4.5.1 安装与配置&#xff0c;扩展模块opencv_contrib的安装与配置1、OpenCV下载&#xff1a;&#xff08;1&#xff09;下载地址&#xff1a;https://opencv.org/releases/page&#xff08;2&#xff09;解压到指定文件夹&#xff1a; 2、配置环境&#…

如何进行可视化的数据过滤?Sugar BI 的过滤组件教你快速实现

Sugar BI 中支持了 10种过滤组件&#xff0c;这些过滤组件都是让用户在浏览报表或大屏的时候&#xff0c;能够交互式的对页面上的图表进行数据的过滤。所有过滤组件对图表的数据过滤设置都是一样的&#xff0c;如下&#xff1a; 例如页面中已有两个图表&#xff08;这两个图表…

人脑髓鞘化

髓鞘化 大纲&#xff1a;髓鞘化定义&#xff1b;髓鞘化能用来干嘛&#xff1b;髓鞘化现阶段存在的痛点&#xff1b;现有方法如何解决问题&#xff1b;我们方法的优势。 定义 髓鞘化是指髓鞘发展的过程&#xff0c;它使神经兴奋在沿神经纤维传导时速度加快&#xff0c;并保证…

开窗函数分享

开窗函数定义 开窗函数&#xff1a;用于为行定义一个窗口&#xff0c;它一组值进行操作&#xff0c;不需要使用group by子句对数据进行分组&#xff0c;能够在同一行中同时返回基础行的列和聚合列。 划重点!!! 开窗函数返回&#xff1a;基础行列、聚合列 下面通过例子看一下…

OJ# 376 机器翻译

题目描述 ​ 小李的电脑上安装了一个机器翻译软件&#xff0c;他经常用这个软件来翻译英语文章。 ​这个翻译软件的原理很简单&#xff0c;它只是从头到尾&#xff0c;依次将每个英文单词用对应的中文含义来替换。对于每个英文单词&#xff0c;软件会先在内存中查找这个单词的…

简单易用多git服务器ssh密钥配置管理

文章目录 前言一、什么是ssh-key二、配置步骤添加ssh-key配置多ssh-key 总结 前言 快速理解如何配置管理多个git服务器的ssh&#xff0c;当我们有多个git帐号时会涉及如何管理不同的remote使用不同的git账户登陆推送 当前repo的origin remote是github&#xff0c;我们在推送时…

EMMC基础知识总结

1、说明 1.0 整体架构 ‘ EMMC最简单的可理解为带有控制器的FLASH&#xff0c;具体结构如下&#xff1a; EMMC&#xff1a; Embedded multiMediaCard EMMC. EMMC内部&#xff1a; host interface 、 flash controller 、 flash memory 1.1 flash memory 结构 EMMC 中一般…

学习Vue3——watchEffect(高级侦听器)

立即运行一个函数&#xff0c;同时响应式地追踪其依赖&#xff0c;并在依赖更改时重新执行。 watchEffect有两个参数 第一个参数就是要运行的副作用函数 第二个参数是一个可选的选项&#xff0c;可以用来调整副作用的刷新时机或调试副作用的依赖 API—watchEffect 基本用法 …

电脑必备,推荐几款好用的程序软件,让你工作更加高效

在当今的信息化时代&#xff0c;电脑已成为我们日常生活中必不可少的工具。而在电脑上安装一些好用的程序软件&#xff0c;能够大大提高我们的工作效率和体验。但是市面上的软件五花八门&#xff0c;要如何选择呢&#xff1f;下面将为大家推荐几款值得使用的程序软件&#xff0…

告别脚本小子系列丨JAVA安全(8)——反序列化利用链(下)

0x01 前言 在前面的文章中介绍了基于CC链的反序列化利用方式&#xff0c;并且通过最终调用Runtime类的exec方法达到命令执行的效果。在CC链中还可以通过xalan来执行命令。 xalan是java操作xml的库&#xff0c;属于java内置的官方库之一&#xff0c;在CC链中主要用到的是com.sun…