Skip to content
Open
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ object ApiConfig {
.build()
val retrofit = Retrofit.Builder()
// .baseUrl("http://192.168.0.107:3000/")
.baseUrl("http://192.168.0.244:3000/")
// .baseUrl("http://192.168.0.244:3000/")
.baseUrl("http://10.0.4.144:3000/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
Expand Down
79 changes: 79 additions & 0 deletions AndroidApps/app/src/main/java/com/mfa/facedetector/BlurDetector.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.mfa.facedetector

import android.graphics.Bitmap
import com.mfa.preprocessor.PreprocessingUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext

class BlurDetector {
fun isBlurry(pixels: Array<IntArray>): Boolean {
val threshold = 1.2
val variance = calculateLaplaceScore(pixels)
return variance < threshold
}

fun isBlurry(bitmap: Bitmap) : Boolean {
val p = PreprocessingUtils()
val array = p.convertRawGreyImg(bitmap)
return isBlurry(array)
}

private fun calculateLaplaceScore(pixels: Array<IntArray>): Double {
val p = PreprocessingUtils()
var laplaceKernel : FloatArray = arrayOf(
1f, 1f, 1f,
1f, -8f, 1f,
1f, 1f, 1f
).toFloatArray();
var newPixels : Array<IntArray>
val gaussianKernel = p.generateGaussianKernel(9, 1.5f);
newPixels = p.convolve(pixels, gaussianKernel, 9)
newPixels = p.convolve(newPixels, laplaceKernel, 3)

val mean = calculateAverageBrightness(newPixels)
val variance = calculateVariance(newPixels)
return Math.sqrt(variance) / mean;
}

fun calculateVariance(array: Array<IntArray>): Double = runBlocking {
val rows = array.size
val cols = array[0].size
val totalElements = rows * cols

if (totalElements == 0) return@runBlocking 0.0
val sum = withContext(Dispatchers.Default) {
array.map { row ->
async { row.sum() }
}.awaitAll().sum()
}
val mean = sum.toDouble() / totalElements
val sumSquaredDifferences = withContext(Dispatchers.Default) {
array.map { row ->
async {
row.sumOf { value ->
val diff = value - mean
diff * diff
}
}
}.awaitAll().sum()
}
sumSquaredDifferences / totalElements
}


private fun calculateAverageBrightness(pixels: Array<IntArray>): Double {
if (pixels.isEmpty() || pixels[0].isEmpty()) return 0.0 // Edge case
var totalBrightness = 0.0
val width = pixels.size
val height = pixels[0].size
for (x in 0 until width) {
for (y in 0 until height) {
totalBrightness += pixels[x][y] // Clamp to valid range
}
}
return totalBrightness / (width * height)
}
}
127 changes: 117 additions & 10 deletions AndroidApps/app/src/main/java/com/mfa/preprocessor/Convolution.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,40 @@
package com.mfa.preprocessor

import android.graphics.Bitmap
import android.graphics.Color
import kotlinx.coroutines.*
import kotlin.math.abs

class Convolution {
fun padWithReplicate(input: Array<IntArray>, padY: Int, padX: Int): Array<IntArray> {
val inputRows = input.size
val inputCols = input[0].size
val paddedRows = inputRows + 2 * padY
val paddedCols = inputCols + 2 * padX

val padded = Array(paddedRows) { IntArray(paddedCols) }

for (i in 0 until paddedRows) {
for (j in 0 until paddedCols) {
val srcI = when {
i < padY -> 0
i >= padY + inputRows -> inputRows - 1
else -> i - padY
}

val srcJ = when {
j < padX -> 0
j >= padX + inputCols -> inputCols - 1
else -> j - padX
}

padded[i][j] = input[srcI][srcJ]
}
}

return padded
}

fun convolve(
input: Array<IntArray>,
kernel: FloatArray,
Expand All @@ -12,27 +43,103 @@ class Convolution {
): Array<IntArray> = runBlocking {
val inputRows = input.size
val inputCols = input[0].size
val outputRows = inputRows - kernelRows + 1
val outputCols = inputCols - kernelCols + 1
val output = Array(outputRows) { IntArray(outputCols) }

// Use coroutines for actual parallelism
val padY = kernelRows / 2
val padX = kernelCols / 2

val paddedInput = padWithReplicate(input, padY, padX)
val output = Array(inputRows) { IntArray(inputCols) }

coroutineScope {
(0 until outputRows).map { i ->
launch(Dispatchers.Default) { // Launch parallel coroutines
for (j in 0 until outputCols) {
(0 until inputRows).map { i ->
launch(Dispatchers.Default) {
for (j in 0 until inputCols) {
var sum = 0f
for (ki in 0 until kernelRows) {
for (kj in 0 until kernelCols) {
sum += input[i + ki][j + kj] * kernel[ki * kernelCols + kj]
val pi = i + ki
val pj = j + kj
sum += paddedInput[pi][pj] * kernel[ki * kernelCols + kj]
}
}
output[i][j] = abs(sum.toInt()).coerceIn(0, 255) // More readable
output[i][j] = abs(sum.toInt()).coerceIn(0, 255)
}
}
}.joinAll() // Wait for all coroutines to finish
}.joinAll()
}

output
}

fun convolveBitmap(
input: Bitmap,
kernel: FloatArray,
kernelRows: Int,
kernelCols: Int
): Bitmap = runBlocking {
val width = input.width
val height = input.height
val outputWidth = width - kernelCols + 1
val outputHeight = height - kernelRows + 1
val rChannel = Array(height) { IntArray(width) }
val gChannel = Array(height) { IntArray(width) }
val bChannel = Array(height) { IntArray(width) }

for (y in 0 until height) {
for (x in 0 until width) {
val color = input.getPixel(x, y)
rChannel[y][x] = Color.red(color)
gChannel[y][x] = Color.green(color)
bChannel[y][x] = Color.blue(color)
}
}

val channels = arrayOf(rChannel, gChannel, bChannel)
val outputChannels = Array(3) { Array(outputHeight) { IntArray(outputWidth) } }

coroutineScope {
for (c in 0 until 3) {
(0 until outputHeight).map { i ->
launch(Dispatchers.Default) {
for (j in 0 until outputWidth) {
var sum = 0f
for (ki in 0 until kernelRows) {
for (kj in 0 until kernelCols) {
sum += channels[c][i + ki][j + kj] * kernel[ki * kernelCols + kj]
}
}
outputChannels[c][i][j] = abs(sum.toInt()).coerceIn(0, 255)
}
}
}.joinAll()
}
}

// Combine RGB back into bitmap
val outputBitmap = Bitmap.createBitmap(outputWidth, outputHeight, Bitmap.Config.ARGB_8888)
for (i in 0 until outputHeight) {
for (j in 0 until outputWidth) {
val r = outputChannels[0][i][j]
val g = outputChannels[1][i][j]
val b = outputChannels[2][i][j]
val color = Color.rgb(r, g, b)
outputBitmap.setPixel(j, i, color)
}
}

outputBitmap
}

fun convolveBitmapGrayScale(
input: Bitmap,
kernel: FloatArray,
kernelRows: Int,
kernelCols: Int
): Bitmap {
val p = PreprocessingUtils()
val inputArray = p.bitmapToGrayIntArray(input);
val outputArray = convolve(inputArray, kernel, kernelRows, kernelCols);
val out = p.grayIntArrayToBitmap(outputArray)
return out;
}
}
Loading