I have following code in place
GraphicOverlay.kt
open class GraphicOverlay(context: Context?, attrs: AttributeSet?) :
View(context, attrs) {
private val lock = Any()
private val graphics: MutableList<Graphic> = ArrayList()
var mScale: Float? = null
var mOffsetX: Float? = null
var mOffsetY: Float? = null
var cameraSelector: Int = CameraSelector.LENS_FACING_FRONT
abstract class Graphic(private val overlay: GraphicOverlay) {
abstract fun draw(canvas: Canvas?)
fun calculateRect(height: Float, width: Float, boundingBoxT: Rect): RectF {
// for land scape
fun isLandScapeMode(): Boolean {
return overlay.context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
}
fun whenLandScapeModeWidth(): Float {
return when (isLandScapeMode()) {
true -> width
false -> height
}
}
fun whenLandScapeModeHeight(): Float {
return when (isLandScapeMode()) {
true -> height
false -> width
}
}
val scaleX = overlay.width.toFloat() / whenLandScapeModeWidth()
val scaleY = overlay.height.toFloat() / whenLandScapeModeHeight()
val scale = scaleX.coerceAtLeast(scaleY)
overlay.mScale = scale
// Calculate offset (we need to center the overlay on the target)
val offsetX = (overlay.width.toFloat() - ceil(whenLandScapeModeWidth() * scale)) / 2.0f
val offsetY =
(overlay.height.toFloat() - ceil(whenLandScapeModeHeight() * scale)) / 2.0f
overlay.mOffsetX = offsetX
overlay.mOffsetY = offsetY
val mappedBox = RectF().apply {
left = boundingBoxT.right * scale + offsetX
top = boundingBoxT.top * scale + offsetY
right = boundingBoxT.left * scale + offsetX
bottom = boundingBoxT.bottom * scale + offsetY
}
// for front mode
if (overlay.isFrontMode()) {
val centerX = overlay.width.toFloat() / 2
mappedBox.apply {
left = centerX + (centerX - left)
right = centerX - (right - centerX)
}
}
return mappedBox
}
}
fun isFrontMode() = cameraSelector == CameraSelector.LENS_FACING_FRONT
fun toggleSelector() {
cameraSelector =
if (cameraSelector == CameraSelector.LENS_FACING_BACK) CameraSelector.LENS_FACING_FRONT
else CameraSelector.LENS_FACING_BACK
}
fun clear() {
synchronized(lock) { graphics.clear() }
postInvalidate()
}
fun add(graphic: Graphic) {
synchronized(lock) { graphics.add(graphic) }
}
fun remove(graphic: Graphic) {
synchronized(lock) { graphics.remove(graphic) }
postInvalidate()
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
synchronized(lock) {
for (graphic in graphics) {
graphic.draw(canvas)
}
}
}
}
FaceContourGraphic.kt
class FaceContourGraphic(
overlay: GraphicOverlay,
private val face: Face,
private val imageRect: Rect
) : GraphicOverlay.Graphic(overlay) {
private val facePositionPaint: Paint
private val idPaint: Paint
private val boxPaint: Paint
init {
val selectedColor = Color.WHITE
facePositionPaint = Paint()
facePositionPaint.color = selectedColor
idPaint = Paint()
idPaint.color = selectedColor
boxPaint = Paint()
boxPaint.color = selectedColor
boxPaint.style = Paint.Style.STROKE
boxPaint.strokeWidth = BOX_STROKE_WIDTH
}
override fun draw(canvas: Canvas?) {
val rect = calculateRect(
imageRect.height().toFloat(),
imageRect.width().toFloat(),
face.boundingBox
)
canvas?.drawRect(rect, boxPaint)
}
companion object {
private const val BOX_STROKE_WIDTH = 5.0f
}
}
SelfieAnalyzer.kt
abstract class SelfieAnalyzer<T> : ImageAnalysis.Analyzer {
abstract val graphicOverlay: GraphicOverlay
@SuppressLint("UnsafeExperimentalUsageError")
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
mediaImage?.let { image ->
detectInImage(InputImage.fromMediaImage(image, imageProxy.imageInfo.rotationDegrees))
.addOnSuccessListener { results ->
onSuccess(
results,
graphicOverlay,
image.cropRect
)
imageProxy.close()
}
.addOnFailureListener {
onFailure(it)
imageProxy.close()
}
}
}
protected abstract fun detectInImage(image: InputImage): Task<T>
abstract fun stop()
protected abstract fun onSuccess(
results: T,
graphicOverlay: GraphicOverlay,
rect: Rect
)
protected abstract fun onFailure(e: Exception)
companion object {
const val TAG: String = "SelfieAnalyzer"
}
}
FaceContourDetectionProcessor.kt
class FaceContourDetectionProcessor(private val view: GraphicOverlay) :
SelfieAnalyzer<List<Face>>() {
private val realTimeOpts = FaceDetectorOptions.Builder()
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
.setContourMode(FaceDetectorOptions.CONTOUR_MODE_NONE)
.build()
private val detector = FaceDetection.getClient(realTimeOpts)
override val graphicOverlay: GraphicOverlay
get() = view
override fun detectInImage(image: InputImage): Task<List<Face>> {
return detector.process(image)
}
override fun stop() {
try {
detector.close()
} catch (e: IOException) {
Log.e(TAG, "Exception thrown while trying to close Face Detector: $e")
}
}
override fun onSuccess(
results: List<Face>,
graphicOverlay: GraphicOverlay,
rect: Rect
) {
graphicOverlay.clear()
results.forEach { face ->
val faceGraphic = FaceContourGraphic(graphicOverlay, face, rect)
graphicOverlay.add(faceGraphic)
}
graphicOverlay.postInvalidate()
}
override fun onFailure(e: Exception) {
Log.w(TAG, "Face Detector failed.$e")
}
companion object {
private const val TAG = "FaceDetectionProcessor"
}
}
SelfieFragment.kt
class SelfieFragment : Fragment() {
// runs on camera permission grant success
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
cameraProviderFuture.addListener({
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview
val preview = Preview.Builder().build()
preview.setSurfaceProvider(binding.viewFinder.surfaceProvider)
imageCapture = ImageCapture.Builder().build()
val imageAnalyzer = ImageAnalysis.Builder()
.setBackpressureStrategy(STRATEGY_KEEP_ONLY_LATEST)
.build()
.also {
it.setAnalyzer(
cameraExecutor,
selectAnalyzer()
)
}
// Select front camera as a default
val cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture, imageAnalyzer
)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(context))
}
}
}
fragment_selfie.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".ui.fragments.SelfieFragment">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.example.appname.customviews.GraphicOverlay
android:id="@+id/graphic_overlay"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@id/viewFinder"
app:layout_constraintLeft_toLeftOf="@id/viewFinder"
app:layout_constraintRight_toRightOf="@id/viewFinder"
app:layout_constraintTop_toTopOf="@id/viewFinder" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnCamera"
style="@style/Widget.MaterialComponents.Button.OutlinedButton.Icon"
android:layout_width="wrap_content"
android:layout_height="56dp"
android:layout_marginBottom="16dp"
android:background="@android:color/transparent"
android:insetLeft="0dp"
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
app:icon="@drawable/ic_photo_camera_48"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
I want to create bitmap of detected faces but I do not know how, because Face
api does not provide an way.
Any help is highly appreciated!
from How to extract detected faces to bitmaps?
No comments:
Post a Comment