使用 Google 面部检测 ML 套件在现有照片周围绘制一个框

Drawing a Box Around Face To Existed Photos with Google Face Detection ML Kit

我们在 Android 中实现了 Android 人脸检测机器学习套件。它像魅力一样工作,检测人脸。

问题:当检测到多张人脸时,我们想在检测到的人脸周围绘制矩形

我们做了什么:

已实施

 implementation 'com.google.android.gms:play-services-mlkit-face-detection:16.1.5'

创建了自定义视图:

class FaceView(val theContext : Context, val bounds : Rect) : View(theContext) {

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        val myPaint = Paint()

        myPaint.color = Color.BLACK
        myPaint.style = Paint.Style.STROKE
        myPaint.strokeWidth = 10f

        canvas?.drawRect(bounds, myPaint)
    }
}

尝试在我们从创建的人脸对象 ML 套件中获得的边界上绘制一个矩形

val result = detector.process(image).addOnSuccessListener { faces ->

for (face in faces) {
val bounds = face.boundingBox

val view = FaceView(requireContext(), bounds)
binding.actionRoot.addView(view)

val lp : ConstraintLayout.LayoutParams =
 ConstraintLayout.LayoutParams(bounds.width(),bounds.height())

lp.startToStart = binding.actionPhoto.id
lp.topToTop = binding.actionPhoto.id

lp.marginStart = bounds.right
lp.topMargin = bounds.bottom
        
view.layoutParams = lp
}}

结果:

我们如何为从 URI(而不是从 CameraX)生成的每个面绘制一个矩形并使它们可点击?

你可以参考这里的项目,但是是java代码

https://github.com/kkdroidgit/FaceDetect

-- 而 ML 工具包给出检测到的面孔的矩形。我认为 Google 开发人员让我们更容易自动绘制矩形并将面作为独立的位图对象提供。

对于我的解决方案:

我使用了@anonymous 建议在脸部周围画线的示例项目。

  • 首先,我从提供的面部矩形中得到起点、终点、底部和顶部点。 (这个矩形是从原始位图创建的,而不是从图像视图创建的。所以矩形点属于我使用的原始位图)。

  • 由于Rect点不属于ImageView而是位图本身,我们需要在ImageView上找到相关的rect点(或者创建一个缩放位图并在其上检测人脸)。我们将此点计算为原始位图中的百分比。

  • 而我们在示例项目中使用原始点来绘制线。我们实现了具有透明背景的视图,使用计算点使面部矩形可点击。

-作为最后一件事,我们为每张脸创建了位图。

    detector.process(theImage)
                    .addOnSuccessListener { faces ->
    
           val bounds = face.boundingBox
            val screenWidth = GetScreenWidth().execute()
    
            // val theImage = InputImage.fromBitmap(tempBitmap,0)
    
            val paint = Paint()
            paint.strokeWidth = 1f
            paint.color = Color.RED
            paint.style = Paint.Style.STROKE
    
            val theStartPoint = if(bounds.left < 0) 0 else{ bounds.left}
            val theEndPoint = if(bounds.right > tempBitmap.width) tempBitmap.width else { bounds.right}
            val theTopPoint = if(bounds.top < 0) 0 else { bounds.top }
            val theBottomPoint = if(bounds.bottom > tempBitmap.height) tempBitmap.height else { bounds.bottom }
    
            val faceWidth = theEndPoint - theStartPoint
            val faceHeight = theBottomPoint - theTopPoint
    
            Log.d(Statics.LOG_TAG, "Face width : ${faceWidth} Face Height $faceHeight")
    
            val startPointPercent = theStartPoint.toFloat() / tempBitmap.width.toFloat()
            val topPointPercent = theTopPoint.toFloat() / tempBitmap.height.toFloat()
    
            Log.d(Statics.LOG_TAG, "Face start point percent : ${startPointPercent} Face top percent $topPointPercent")
    
            val faceWidthPercent = faceWidth / tempBitmap.width.toFloat()
            val faceHeightPercent = faceHeight / tempBitmap.height.toFloat()
    
            Log.d(Statics.LOG_TAG, "Face width  percent: ${faceWidthPercent} Face Height Percent $faceHeightPercent")
    
            val faceImage = ConstraintLayout(requireContext())
            faceImage.setBackgroundColor(Color.TRANSPARENT)
            binding.actionRoot.addView(faceImage)
    
            val boxWidth = screenWidth.toFloat()*faceWidthPercent
            val boxHeight = screenWidth.toFloat()*faceHeightPercent
    
            Log.d(Statics.LOG_TAG, "Box width : ${boxWidth} Box Height $boxHeight")
    
            val lp : ConstraintLayout.LayoutParams =
                ConstraintLayout.LayoutParams(
                    boxWidth.toInt(),
                    boxHeight.toInt()
                )
    
    
            lp.startToStart = binding.actionPhoto.id
            lp.topToTop = binding.actionPhoto.id
    
            lp.marginStart = (screenWidth * startPointPercent).toInt()
            lp.topMargin = (screenWidth * topPointPercent).toInt()
    
            faceImage.layoutParams = lp
    
            val theFaceBitmap = Bitmap.createBitmap(
                tempBitmap,
                theStartPoint,
                theTopPoint,
                faceWidth,
                faceHeight)
    
    
            if (face.trackingId != null) {
                val id = face.trackingId
    
                faceImage.setOnClickListener {
                    
                
                    binding.actionPhoto.setImageBitmap(theFaceBitmap)
                    
                 
                }
            }
}

结果: