-
-
Notifications
You must be signed in to change notification settings - Fork 84
/
FileReader.kt
124 lines (109 loc) · 4.84 KB
/
FileReader.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/*
* Copyright 2023 Shubham Panchal
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http:https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ml.quaterion.facenetdetection
import android.graphics.Bitmap
import android.graphics.Rect
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions
import com.ml.quaterion.facenetdetection.model.FaceNetModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
// Utility class to read images from internal storage
class FileReader( private var faceNetModel: FaceNetModel ) {
private val realTimeOpts = FaceDetectorOptions.Builder()
.setPerformanceMode( FaceDetectorOptions.PERFORMANCE_MODE_FAST )
.build()
private val detector = FaceDetection.getClient( realTimeOpts )
private val defaultScope = CoroutineScope( Dispatchers.Default )
private val mainScope = CoroutineScope( Dispatchers.Main )
private var numImagesWithNoFaces = 0
private var imageCounter = 0
private var numImages = 0
private var data = ArrayList<Pair<String,Bitmap>>()
private lateinit var callback: ProcessCallback
// imageData will be provided to the MainActivity via ProcessCallback ( see the run() method below ) and finally,
// used by the FrameAnalyser class.
private val imageData = ArrayList<Pair<String,FloatArray>>()
// Given the Bitmaps, extract face embeddings from then and deliver the processed embedding to ProcessCallback.
fun run( data : ArrayList<Pair<String,Bitmap>> , callback: ProcessCallback ) {
numImages = data.size
this.data = data
this.callback = callback
scanImage( data[ imageCounter ].first , data[ imageCounter ].second )
}
interface ProcessCallback {
fun onProcessCompleted( data : ArrayList<Pair<String,FloatArray>> , numImagesWithNoFaces : Int )
}
// Crop faces and produce embeddings ( using FaceNet ) from given image.
// Store the embedding in imageData
private fun scanImage( name : String , image : Bitmap ) {
mainScope.launch {
val inputImage = InputImage.fromByteArray(
BitmapUtils.bitmapToNV21ByteArray(image),
image.width,
image.height,
0,
InputImage.IMAGE_FORMAT_NV21
)
detector.process(inputImage)
.addOnSuccessListener { faces ->
if (faces.size != 0) {
mainScope.launch {
val embedding = getEmbedding(image, faces[0].boundingBox)
imageData.add(Pair(name, embedding))
// Embedding stored, now proceed to the next image.
if (imageCounter + 1 != numImages) {
imageCounter += 1
scanImage(data[imageCounter].first, data[imageCounter].second)
} else {
// Processing done, reset the file reader.
callback.onProcessCompleted(imageData, numImagesWithNoFaces)
reset()
}
}
}
else {
// The image contains no faces, proceed to the next one.
numImagesWithNoFaces += 1
if (imageCounter + 1 != numImages) {
imageCounter += 1
scanImage(data[imageCounter].first, data[imageCounter].second)
} else {
callback.onProcessCompleted(imageData, numImagesWithNoFaces)
reset()
}
}
}
}
}
// Suspend function for running the FaceNet model
private suspend fun getEmbedding(image: Bitmap, bbox : Rect ) : FloatArray = withContext( Dispatchers.Default ) {
return@withContext faceNetModel.getFaceEmbedding(
BitmapUtils.cropRectFromBitmap(
image,
bbox
)
)
}
private fun reset() {
imageCounter = 0
numImages = 0
numImagesWithNoFaces = 0
data.clear()
}
}