Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions yoonit-facefy/src/main/java/ai/cyberlabs/yoonit/facefy/Facefy.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package ai.cyberlabs.yoonit.facefy

import ai.cyberlabs.yoonit.facefy.model.FaceDetected
import ai.cyberlabs.yoonit.facefy.model.FacefyOptions
import android.graphics.RectF
import com.google.mlkit.vision.common.InputImage
import java.lang.Exception

class Facefy {

Expand All @@ -27,7 +27,37 @@ class Facefy {
field = value
}

fun detect(image: InputImage, onFaceDetected: (FaceDetected) -> Unit, onFaceUndetected: (Exception) -> Unit) {
facefyController.detect(image, onFaceDetected, onFaceUndetected)
var roiEnable: Boolean = FacefyOptions.faceROI.enable
set(value) {
FacefyOptions.faceROI.enable = value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think to change the faceROI to roi?

field = value
}

var roiRect: RectF = FacefyOptions.faceROI.rectOffset
set(value) {
FacefyOptions.faceROI.rectOffset = value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think to change the faceROI to roi?

field = value
}

var roiDetectMinSize: Float = FacefyOptions.faceROI.minimumSize
set(value) {
FacefyOptions.faceROI.minimumSize = value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think to change the faceROI to roi?

field = value
}

var detectMinSize: Float = FacefyOptions.faceCaptureMinSize
set(value) {
FacefyOptions.faceCaptureMinSize = value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change faceCaptureMinSize to detectMinSize .

field = value
}

var detectMaxSize: Float = FacefyOptions.faceCaptureMaxSize
set(value) {
FacefyOptions.faceCaptureMaxSize = value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change faceCaptureMaxSize to detectMaxSize .

field = value
}

fun detect(image: InputImage, onFaceDetected: (FaceDetected) -> Unit, onMessage: (String) -> Unit) {
facefyController.detect(image, onFaceDetected, onMessage)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package ai.cyberlabs.yoonit.facefy

import ai.cyberlabs.yoonit.facefy.model.FaceDetected
import ai.cyberlabs.yoonit.facefy.model.FacefyOptions
import ai.cyberlabs.yoonit.facefy.model.Message
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import androidx.core.graphics.toRectF
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.Face
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions
import java.lang.Exception

internal class FacefyController {

Expand All @@ -28,7 +31,7 @@ internal class FacefyController {
fun detect(
inputImage: InputImage,
onFaceDetected: (FaceDetected) -> Unit,
onFaceUndetected: (Exception) -> Unit
onMessage: (String) -> Unit
) {
this.detector
.process(inputImage)
Expand All @@ -42,6 +45,7 @@ internal class FacefyController {
closestFace?.let { face ->

val faceContours = mutableListOf<PointF>()
var roi = Rect()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think to change from roi to roiRect, because this is coordinates converted in pixel. Right?

var leftEyeOpenProbability: Float? = null
var rightEyeOpenProbability: Float? = null
var smilingProbability: Float? = null
Expand All @@ -60,6 +64,27 @@ internal class FacefyController {
}
}

if (FacefyOptions.faceROI.enable) {
val rect = Rect(1,2,4,5)
rect.top = 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?


roi = Rect(
(inputImage.width * FacefyOptions.faceROI.rectOffset.left).toInt(),
(inputImage.height * FacefyOptions.faceROI.rectOffset.top).toInt(),
(inputImage.width - (inputImage.width * FacefyOptions.faceROI.rectOffset.right)).toInt(),
(inputImage.height - (inputImage.height * FacefyOptions.faceROI.rectOffset.bottom)).toInt()
)
}

val boundingBox: RectF = face.boundingBox.toRectF()

val message = this.getMessage(boundingBox, inputImage.width, inputImage.height)

if (message.isNotEmpty()) {
onMessage(message)
return@addOnSuccessListener
}
Comment on lines +78 to +83
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(In first place, this is self criticism)

We must change, don't you agree?

  • getMessage is not a good function name for handling the face bounding box verifications;
  • The face bounding box verification is not explicit;
  • We (that includes me) can do it much better, right?


onFaceDetected(
FaceDetected(
leftEyeOpenProbability,
Expand All @@ -69,12 +94,57 @@ internal class FacefyController {
face.headEulerAngleY,
face.headEulerAngleZ,
faceContours,
face.boundingBox
face.boundingBox,
roi
)
)
}
}
.addOnFailureListener { e -> onFaceUndetected(e) }
.addOnFailureListener { e ->
e.message?.let { message -> onMessage(message) }
}
.addOnCompleteListener { detector.close() }
}

private fun getMessage(boundingBox: RectF, imageWidth: Int, imageHeight: Int): String {
val boundingBoxWidthRelatedWithImage = boundingBox.width() / imageWidth

if (boundingBoxWidthRelatedWithImage < FacefyOptions.faceCaptureMinSize) {
return Message.INVALID_CAPTURE_FACE_MIN_SIZE
}

if (boundingBoxWidthRelatedWithImage > FacefyOptions.faceCaptureMaxSize) {
return Message.INVALID_CAPTURE_FACE_MAX_SIZE
}

val topOffset: Float = boundingBox.top / imageHeight
val rightOffset: Float = (imageWidth - boundingBox.right) / imageWidth
val bottomOffset: Float = (imageHeight - boundingBox.bottom) / imageHeight
val leftOffset: Float = boundingBox.left / imageWidth

if (FacefyOptions.faceROI.isOutOf(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think to change the faceROI to roi?

topOffset,
rightOffset,
bottomOffset,
leftOffset)
) {
return Message.INVALID_CAPTURE_FACE_OUT_OF_ROI
}

if (FacefyOptions.faceROI.hasChanges) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think to change the faceROI to roi?


// Face is inside the region of interest and faceROI is setted.
// Face is smaller than the defined "minimumSize".
val roiWidth: Float =
imageWidth -
((FacefyOptions.faceROI.rectOffset.right + FacefyOptions.faceROI.rectOffset.left) * imageWidth)
val faceRelatedWithROI: Float = boundingBox.width() / roiWidth

if (FacefyOptions.faceROI.minimumSize > faceRelatedWithROI) {
return Message.INVALID_CAPTURE_FACE_ROI_MIN_SIZE
}
}

return ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ data class FaceDetected(
var headEulerAngleY: Float,
var headEulerAngleZ: Float,
var contours: MutableList<PointF> = mutableListOf(),
var boundingBox: Rect
var boundingBox: Rect,
var roi: Rect
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think to change from roi to roiRect, because this is coordinates converted in pixel. Right?

)
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package ai.cyberlabs.yoonit.facefy.model

import android.graphics.Color
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import.

import android.graphics.RectF

/**
* Model to set face region of interest.
*/
class FaceROI {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think to change the FaceROI to ROI?

// Enable or disable ROI.
var enable: Boolean = false

// Region of interest in percentage.
// Values valid [0, 1].
var rectOffset: RectF = RectF()

// Minimum face size in percentage in relation of the ROI.
var minimumSize: Float = 0.0f
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must be detectMinSize, right?


// Return if any attributes has modifications.
val hasChanges: Boolean
get() {
return (this.rectOffset.top != 0.0f ||
this.rectOffset.right != 0.0f ||
this.rectOffset.bottom != 0.0f ||
this.rectOffset.left != 0.0f)
}

/**
* Current offsets is out of the offset parameters.
*
* @param topOffset top offset.
* @param rightOffset right offset.
* @param bottomOffset bottom offset.
* @param leftOffset left offset.
* @return is out of the offset parameters.
*/
fun isOutOf(
topOffset: Float,
rightOffset: Float,
bottomOffset: Float,
leftOffset: Float
): Boolean {
return (this.rectOffset.top > topOffset ||
this.rectOffset.right > rightOffset ||
this.rectOffset.bottom > bottomOffset ||
this.rectOffset.left > leftOffset)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,25 @@ package ai.cyberlabs.yoonit.facefy.model

object FacefyOptions {

var faceROI: FaceROI = FaceROI()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think to change the faceROI to roi?


var classification: Boolean = true

var contours: Boolean = true

var boundingBox: Boolean = true

Comment on lines +6 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sincerely, do you think that we need this variables of control (classification, contours and boundingBox)?

/**
* * Limit the minimum face capture size.
* This variable is the face detection box percentage in relation with the UI graphic view.
* The value must be between 0 and 1.
*/
var faceCaptureMinSize: Float = 0.0f
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change faceCaptureMinSize to detectMinSize.


/**
* Limit the maximum face capture size.
* This variable is the face detection box percentage in relation with the UI graphic view.
* The value must be between 0 and 1.
*/
var faceCaptureMaxSize: Float = 1.0f
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change faceCaptureMaxSize to detectMaxSize.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ai.cyberlabs.yoonit.facefy.model

object Message {
// Face width percentage in relation of the screen width is less than the CaptureOptions.faceCaptureMinSize
const val INVALID_CAPTURE_FACE_MIN_SIZE = "INVALID_CAPTURE_FACE_MIN_SIZE"

// Face width percentage in relation of the screen width is more than the CaptureOptions.faceCaptureMinSize
const val INVALID_CAPTURE_FACE_MAX_SIZE = "INVALID_CAPTURE_FACE_MAX_SIZE"

// Face bounding box is out of the setted region of interest.
const val INVALID_CAPTURE_FACE_OUT_OF_ROI = "INVALID_CAPTURE_FACE_OUT_OF_ROI"

// Face width percentage in relation of the screen width is less than the CaptureOptions.FaceROI.minimumSize.
const val INVALID_CAPTURE_FACE_ROI_MIN_SIZE = "INVALID_CAPTURE_FACE_ROI_MIN_SIZE"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. All comments is referencing the CaptureOptions;
  2. Is it not missing the message for face undetected?