Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import com.london.imageharamblur.models.GenderDetectionModel
import com.london.imageharamblur.models.ModelDownloadManager
import com.london.imageharamblur.ui.ModerationCacheManager
import com.london.imageharamblur.utils.cropFace
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext

internal class ImageModerationProcessor(private val context: Context) {
companion object {
Expand Down Expand Up @@ -163,4 +164,4 @@ internal class ImageModerationProcessor(private val context: Context) {
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ internal fun Drawable.toBitmap(): Bitmap? {
setBounds(0, 0, canvas.width, canvas.height)
draw(canvas)
return bitmap
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.london.imageharamblur.faceDetection

import android.graphics.Rect

data class DetectedFace(
val boundingBox: Rect,
val confidence: Float
)
)
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
package com.london.imageharamblur.faceDetection

import android.content.Context
import android.graphics.*
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.graphics.Paint
import android.graphics.Rect
import androidx.core.graphics.get
import androidx.core.graphics.scale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.support.common.FileUtil
import java.nio.ByteBuffer
import java.nio.ByteOrder
import kotlin.math.exp
import kotlin.math.max
import kotlin.math.min
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext

class FaceDetector(private val context: Context) {

Expand Down Expand Up @@ -239,12 +245,14 @@ class FaceDetector(private val context: Context) {
val scale = contrast
val translate = brightness + 128f * (1 - contrast)

colorMatrix.set(floatArrayOf(
scale, 0f, 0f, 0f, translate,
0f, scale, 0f, 0f, translate,
0f, 0f, scale, 0f, translate,
0f, 0f, 0f, 1f, 0f
))
colorMatrix.set(
floatArrayOf(
scale, 0f, 0f, 0f, translate,
0f, scale, 0f, 0f, translate,
0f, 0f, scale, 0f, translate,
0f, 0f, 0f, 1f, 0f
)
)

paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
canvas.drawBitmap(bitmap, 0f, 0f, paint)
Expand Down Expand Up @@ -323,10 +331,14 @@ class FaceDetector(private val context: Context) {
val paddedW = w * padding
val paddedH = h * padding

val left = ((cx - paddedW * 0.5f) * imageWidth).coerceIn(0f, imageWidth.toFloat()).toInt()
val top = ((cy - paddedH * 0.5f) * imageHeight).coerceIn(0f, imageHeight.toFloat()).toInt()
val right = ((cx + paddedW * 0.5f) * imageWidth).coerceIn(0f, imageWidth.toFloat()).toInt()
val bottom = ((cy + paddedH * 0.5f) * imageHeight).coerceIn(0f, imageHeight.toFloat()).toInt()
val left =
((cx - paddedW * 0.5f) * imageWidth).coerceIn(0f, imageWidth.toFloat()).toInt()
val top =
((cy - paddedH * 0.5f) * imageHeight).coerceIn(0f, imageHeight.toFloat()).toInt()
val right =
((cx + paddedW * 0.5f) * imageWidth).coerceIn(0f, imageWidth.toFloat()).toInt()
val bottom =
((cy + paddedH * 0.5f) * imageHeight).coerceIn(0f, imageHeight.toFloat()).toInt()

val rect = Rect(left, top, right, bottom)

Expand Down Expand Up @@ -377,11 +389,12 @@ class FaceDetector(private val context: Context) {
bestOverlap = selectedFace
}

val adaptiveThreshold = if (face.confidence > 0.8f && selectedFace.confidence > 0.8f) {
IOU_THRESHOLD * 0.8f
} else {
IOU_THRESHOLD
}
val adaptiveThreshold =
if (face.confidence > 0.8f && selectedFace.confidence > 0.8f) {
IOU_THRESHOLD * 0.8f
} else {
IOU_THRESHOLD
}

if (iou > adaptiveThreshold) {
shouldSelect = false
Expand Down Expand Up @@ -412,7 +425,7 @@ class FaceDetector(private val context: Context) {
val intersectionBottom = min(box1.bottom, box2.bottom)

val intersectionArea = max(0, intersectionRight - intersectionLeft) *
max(0, intersectionBottom - intersectionTop)
max(0, intersectionBottom - intersectionTop)

val box1Area = box1.width() * box1.height()
val box2Area = box2.width() * box2.height()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package com.london.imageharamblur.models

import android.content.Context
import android.graphics.Bitmap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.tensorflow.lite.DataType
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.support.common.FileUtil
Expand All @@ -14,10 +18,6 @@ import java.io.File
import java.io.FileInputStream
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext

internal class ContentDetectionModel {

Expand Down Expand Up @@ -148,7 +148,8 @@ internal class ContentDetectionModel {
suspend fun detectContent(bitmap: Bitmap): ContentResult = withContext(Dispatchers.IO) {
interpreterLock.withLock {
try {
val interpreter = getOrCreateInterpreter() ?: return@withContext ContentResult(isInappropriate = false)
val interpreter =
getOrCreateInterpreter() ?: return@withContext ContentResult(isInappropriate = false)

// Validate interpreter state
if (interpreter.outputTensorCount == 0) {
Expand Down Expand Up @@ -176,18 +177,21 @@ internal class ContentDetectionModel {
outputBuffer.buffer.asFloatBuffer().get(floatArray)
floatArray
}

DataType.UINT8 -> {
val byteArray = ByteArray(outputSize)
outputBuffer.buffer.rewind()
outputBuffer.buffer.get(byteArray)
byteArray.map { (it.toInt() and 0xFF) / 255f }.toFloatArray()
}

DataType.INT8 -> {
val byteArray = ByteArray(outputSize)
outputBuffer.buffer.rewind()
outputBuffer.buffer.get(byteArray)
byteArray.map { (it.toFloat() + 128f) / 255f }.toFloatArray()
}

else -> FloatArray(outputSize)
}

Expand All @@ -210,10 +214,12 @@ internal class ContentDetectionModel {
}

fun close() {
try {0
try {
0
interpreterThreadLocal.get()?.close()
interpreterThreadLocal.remove()
} catch (e: Exception) {}
} catch (e: Exception) {
}
}

companion object {
Expand All @@ -224,4 +230,4 @@ internal class ContentDetectionModel {

internal data class ContentResult(
val isInappropriate: Boolean
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,4 @@ internal class GenderDetectionModel {
internal data class GenderResult(
val isFemale: Boolean,
val confidence: Float
)
)
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package com.london.imageharamblur.models

import android.content.Context
import androidx.core.content.edit
import com.google.firebase.ml.modeldownloader.CustomModel
import com.google.firebase.ml.modeldownloader.CustomModelDownloadConditions
import com.google.firebase.ml.modeldownloader.DownloadType
import com.google.firebase.ml.modeldownloader.FirebaseModelDownloader
import kotlinx.coroutines.*
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await
import kotlinx.coroutines.withContext
import java.io.File
import java.util.concurrent.atomic.AtomicBoolean
import androidx.core.content.edit

class ModelDownloadManager(context: Context) {

Expand Down Expand Up @@ -96,7 +103,8 @@ class ModelDownloadManager(context: Context) {

if (genderModel?.file != null && nsfwModel?.file != null) {
if (genderModel.file!!.exists() && genderModel.file!!.length() > 0 &&
nsfwModel.file!!.exists() && nsfwModel.file!!.length() > 0) {
nsfwModel.file!!.exists() && nsfwModel.file!!.length() > 0
) {

saveModelVersions(genderModel, nsfwModel)
prefs.edit { putBoolean(PREF_FIREBASE_MODELS_READY, true) }
Expand All @@ -121,7 +129,8 @@ class ModelDownloadManager(context: Context) {

if (genderModel?.file != null && nsfwModel?.file != null &&
genderModel.file!!.exists() && nsfwModel.file!!.exists() &&
genderModel.file!!.length() > 0 && nsfwModel.file!!.length() > 0) {
genderModel.file!!.length() > 0 && nsfwModel.file!!.length() > 0
) {

return@withContext ModelFiles(
genderModelFile = genderModel.file!!,
Expand Down Expand Up @@ -152,4 +161,4 @@ class ModelDownloadManager(context: Context) {
)
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import android.content.Context
import android.graphics.drawable.Drawable
import android.os.Build
import com.london.imageharamblur.ImageModerationProcessor
import com.london.imageharamblur.utils.blurBitmap
import com.london.imageharamblur.extensions.toBitmap
import com.london.imageharamblur.utils.blurBitmap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
Expand Down Expand Up @@ -101,4 +101,4 @@ internal class ImageModerationController(
fun close() {
processor?.close()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ data class ImageModerationState(
val originalBitmap: Bitmap? = null,
val blurredBitmap: Bitmap? = null,
val error: String? = null
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,11 @@ fun ImageViewFilter(
isLoading -> {
loadingContent()
}

errorState != null -> {
errorContent(errorState)
}

moderationState != null && moderationState!!.isModerated -> {
if (moderationState!!.shouldBlur && config.showCustomContentWhenBlurred) {
moderatedContent()
Expand All @@ -139,9 +141,9 @@ fun ImageViewFilter(
blurStrength = config.blurStrength,
modifier = Modifier.fillMaxSize()
)
if (moderationState!!.shouldBlur){
moderatedContent()
}
if (moderationState!!.shouldBlur) {
moderatedContent()
}

// Show text overlay when image should be blurred but showTextInsteadOfBlur is true
if (moderationState!!.shouldBlur && config.showTextInsteadOfBlur) {
Expand Down Expand Up @@ -212,4 +214,4 @@ private fun createModerationController(
cacheKey = imageKey,
enableModeration = config.enableModeration,
blurStrength = config.blurStrength
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ fun ModeratedImage(
contentScale = contentScale,
blurStrength = blurStrength
)

else -> NormalImage(
bitmap = state.originalBitmap,
contentDescription = contentDescription,
Expand All @@ -56,6 +57,7 @@ private fun BlurredImage(
contentScale = contentScale,
blurStrength = blurStrength
)

else -> PreProcessedBlurImage(
bitmap = state.blurredBitmap ?: state.originalBitmap!!,
contentDescription = contentDescription,
Expand Down Expand Up @@ -111,4 +113,4 @@ private fun NormalImage(
contentScale = contentScale,
modifier = modifier.fillMaxSize()
)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.london.imageharamblur.ui

import android.util.Log
import androidx.collection.LruCache
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -25,4 +24,4 @@ object ModerationCacheManager {
}

fun size(): Int = cache.size()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ internal suspend fun blurBitmap(input: Bitmap, radius: Int): Bitmap =
}

Bitmap.createBitmap(pixels, w, h, input.config)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ fun cropFace(bitmap: Bitmap, face: DetectedFace): Bitmap {
val bottom = rect.bottom.coerceAtMost(bitmap.height)

return Bitmap.createBitmap(bitmap, left, top, right - left, bottom - top)
}
}

This file was deleted.

Loading
Loading