@@ -7,6 +7,8 @@ import android.renderscript.Element
77import android.renderscript.RenderScript
88import android.renderscript.ScriptIntrinsicBlur
99import androidx.core.graphics.scale
10+ import com.google.mlkit.vision.face.Face
11+ import com.google.mlkit.vision.face.FaceLandmark
1012import java.security.SecureRandom
1113
1214enum class MaskMode {
@@ -39,12 +41,13 @@ fun coerceRectToBitmap(rect: Rect, bitmap: Bitmap): Rect {
3941 return Rect (left, top, right, bottom)
4042}
4143
42- fun maskFace (bitmap : Bitmap , rect : Rect , context : Context , vararg modes : MaskMode ) {
44+ fun maskFace (bitmap : Bitmap , face : Face , context : Context , vararg modes : MaskMode ) {
45+ val rect = face.boundingBox
4346 val safeRect = coerceRectToBitmap(rect, bitmap)
4447 modes.forEach { mode ->
4548 when (mode) {
4649 MaskMode .BLACKOUT -> blackout(bitmap, safeRect)
47- MaskMode .PIXELATE -> pixelate(bitmap, safeRect)
50+ MaskMode .PIXELATE -> pixelate(bitmap, safeRect, face )
4851 MaskMode .NOISE -> noise(bitmap, safeRect)
4952 MaskMode .BLUR -> blur(bitmap, safeRect, context)
5053 }
@@ -58,19 +61,18 @@ private fun blackout(bitmap: Bitmap, rect: Rect) {
5861 canvas.drawRect(safeRect, paint)
5962}
6063
61- private fun pixelate (bitmap : Bitmap , rect : Rect , targetBlockSize : Int = 8, addNoise : Boolean = true) {
64+ private fun pixelate (bitmap : Bitmap , rect : Rect , face : Face , targetBlockSize : Int = 8, addNoise : Boolean = true) {
6265 val faceBitmap = Bitmap .createBitmap(bitmap, rect.left, rect.top, rect.width(), rect.height())
6366
6467 val small = faceBitmap.scale(targetBlockSize, targetBlockSize, false )
65- val pixelated = small.scale(rect.width(), rect.height(), false )
6668
6769 if (addNoise) {
6870 val random = SecureRandom .getInstanceStrong()
69- val noiseCanvas = Canvas (pixelated )
71+ val noiseCanvas = Canvas (small )
7072 val paint = Paint ()
7173
7274 // Replace random pixels entirely
73- val noiseProbability = 0.33f
75+ val noiseProbability = 0.25f
7476 for (y in 0 until small.height) {
7577 for (x in 0 until small.width) {
7678 if (random.nextFloat() <= noiseProbability) {
@@ -85,11 +87,64 @@ private fun pixelate(bitmap: Bitmap, rect: Rect, targetBlockSize: Int = 8, addNo
8587 }
8688 }
8789
90+ val leftEye = face.allLandmarks.find { it.landmarkType == FaceLandmark .LEFT_EYE }
91+ val rightEye = face.allLandmarks.find { it.landmarkType == FaceLandmark .RIGHT_EYE }
92+ if (leftEye != null && rightEye != null ) {
93+ val leftEyePosition = leftEye.position
94+ val rightEyePosition = rightEye.position
95+
96+ val leftEyeInFace = PointF (
97+ leftEyePosition.x - rect.left,
98+ leftEyePosition.y - rect.top
99+ )
100+ val rightEyeInFace = PointF (
101+ rightEyePosition.x - rect.left,
102+ rightEyePosition.y - rect.top
103+ )
104+
105+ val leftEyeInSmall = PointF (
106+ leftEyeInFace.x * small.width / faceBitmap.width,
107+ leftEyeInFace.y * small.height / faceBitmap.height
108+ )
109+ val rightEyeInSmall = PointF (
110+ rightEyeInFace.x * small.width / faceBitmap.width,
111+ rightEyeInFace.y * small.height / faceBitmap.height
112+ )
113+
114+ // Blackout the eyes
115+ val paint = Paint ().apply { color = Color .BLACK }
116+ val noiseCanvas = Canvas (small)
117+
118+ if (leftEyeInSmall.x >= 0 && leftEyeInSmall.x < small.width &&
119+ leftEyeInSmall.y >= 0 && leftEyeInSmall.y < small.height &&
120+ rightEyeInSmall.x >= 0 && rightEyeInSmall.x < small.width &&
121+ rightEyeInSmall.y >= 0 && rightEyeInSmall.y < small.height
122+ ) {
123+ noiseCanvas.drawLine(
124+ 0f , leftEyeInSmall.y, 7f , rightEyeInSmall.y, paint,
125+ )
126+ } else {
127+ if (leftEyeInSmall.x >= 0 && leftEyeInSmall.x < small.width &&
128+ leftEyeInSmall.y >= 0 && leftEyeInSmall.y < small.height
129+ ) {
130+ noiseCanvas.drawPoint(leftEyeInSmall.x, leftEyeInSmall.y, paint)
131+ }
132+
133+ if (rightEyeInSmall.x >= 0 && rightEyeInSmall.x < small.width &&
134+ rightEyeInSmall.y >= 0 && rightEyeInSmall.y < small.height
135+ ) {
136+ noiseCanvas.drawPoint(rightEyeInSmall.x, rightEyeInSmall.y, paint)
137+ }
138+ }
139+ }
140+
141+ val pixelated = small.scale(rect.width(), rect.height(), false )
142+
88143 val canvas = Canvas (bitmap)
89144 canvas.drawBitmap(pixelated, rect.left.toFloat(), rect.top.toFloat(), null )
90145}
91146
92- private fun blur (bitmap : Bitmap , rect : Rect , context : Context , radius : Float = 25f, rounds : Int = 3 ) {
147+ private fun blur (bitmap : Bitmap , rect : Rect , context : Context , radius : Float = 25f, rounds : Int = 10 ) {
93148 val safeRect = coerceRectToBitmap(rect, bitmap)
94149 val faceBitmap = Bitmap .createBitmap(bitmap, safeRect.left, safeRect.top, safeRect.width(), safeRect.height())
95150
0 commit comments