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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.simprints.face.capture.screens.livefeedback

import android.graphics.Bitmap
import android.graphics.RectF
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import com.simprints.face.capture.screens.livefeedback.views.CameraTargetOverlay
import kotlin.also
import kotlin.math.max
import kotlin.math.min
import kotlin.takeUnless

internal class CropToTargetOverlayAnalyzer(
private val targetOverlay: CameraTargetOverlay,
private val onImageCropped: (Bitmap) -> Unit
) : ImageAnalysis.Analyzer {

private var cachedRect: RectF? = null

override fun analyze(image: ImageProxy) {
val previewRect = cachedRect
?: targetOverlay.rectInCanvas.takeUnless { it.isEmpty }?.also { cachedRect = it }
?: return

// Adjust overlay size to be fit-center with the image size
val scale = getSmallerRatio(
image.width, image.height,
targetOverlay.width, targetOverlay.height,
)
val scaledWidth = (targetOverlay.width * scale).toInt()
val scaledHeight = (targetOverlay.height * scale).toInt()

// Find the offsets caused by fit-center scaling
val offsetX = (max(image.width, scaledWidth) - min(image.width, scaledWidth)) / 2
val offsetY = (max(image.height, scaledHeight) - min(image.height, scaledHeight)) / 2

// Scale the preview target to the new scale and offset
val cropLeft = offsetX + (previewRect.left * scale).toInt()
val cropWidth = (previewRect.width() * scale).toInt()
val cropTop = offsetY + (previewRect.top * scale).toInt()
val cropHeight = (previewRect.height() * scale).toInt()

onImageCropped(image.use {
Bitmap.createBitmap(
it.toBitmap(),
cropLeft,
cropTop,
cropWidth,
cropHeight
)
})
}

private fun getSmallerRatio(
cameraWidth: Int,
cameraHeight: Int,
screenWidth: Int,
screenHeight: Int,
): Float {
val widthRatio = cameraWidth / screenWidth.toFloat()
val heightRatio = cameraHeight / screenHeight.toFloat()
return min(widthRatio, heightRatio)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.simprints.face.capture.screens.livefeedback

import android.Manifest
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
Expand All @@ -11,7 +12,6 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.camera.core.CameraSelector.DEFAULT_BACK_CAMERA
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
Expand Down Expand Up @@ -73,17 +73,11 @@ internal class LiveFeedbackFragment : Fragment(R.layout.fragment_live_feedback)
}
}


override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initFragment()
}

override fun onDestroyView() {
vm.clearFrameProcessor()
super.onDestroyView()
}

private fun initFragment() {
screenSize = with(resources.displayMetrics) { Size(widthPixels, widthPixels) }
bindViewModel()
Expand All @@ -94,11 +88,7 @@ internal class LiveFeedbackFragment : Fragment(R.layout.fragment_live_feedback)
//Wait till the views gets its final size then init frame processor and setup the camera
binding.faceCaptureCamera.post {
if (view != null) {
vm.initFrameProcessor(
mainVm.samplesToCapture, mainVm.attemptNumber,
binding.captureOverlay.rectInCanvas,
Size(binding.captureOverlay.width, binding.captureOverlay.height),
)
vm.initCapture(mainVm.samplesToCapture, mainVm.attemptNumber)
}
}
}
Expand All @@ -115,9 +105,16 @@ internal class LiveFeedbackFragment : Fragment(R.layout.fragment_live_feedback)
if (!::targetResolution.isInitialized) {
targetResolution = Size(binding.captureOverlay.width, binding.captureOverlay.height)
}
val imageAnalyzer = ImageAnalysis.Builder().setTargetResolution(targetResolution)
.setOutputImageFormat(OUTPUT_IMAGE_FORMAT_RGBA_8888).build()
imageAnalyzer.setAnalyzer(cameraExecutor, ::analyze)

val imageAnalyzer = ImageAnalysis.Builder()
.setTargetResolution(targetResolution)
.setOutputImageRotationEnabled(true)
.setOutputImageFormat(OUTPUT_IMAGE_FORMAT_RGBA_8888)
.build()
val cropAnalyzer = CropToTargetOverlayAnalyzer(binding.captureOverlay, ::analyze)

imageAnalyzer.setAnalyzer(cameraExecutor, cropAnalyzer)

// Preview
val preview = Preview.Builder().setTargetResolution(targetResolution).build()
val cameraProvider = ProcessCameraProvider.getInstance(requireContext()).await()
Expand All @@ -143,19 +140,9 @@ internal class LiveFeedbackFragment : Fragment(R.layout.fragment_live_feedback)
launchPermissionRequest.launch(Manifest.permission.CAMERA)
}
}

else -> mainVm.shouldCheckCameraPermissions.set(true)
}
// if (mainVm.shouldCheckCameraPermissions.getAndSet(false)) {
// // Check permission in onResume() so that if user left the app to go to Settings
// // and give the permission, it's reflected when they come back to SID
// if (requireActivity().hasPermission(Manifest.permission.CAMERA)) {
// setUpCamera()
// } else {
// launchPermissionRequest.launch(Manifest.permission.CAMERA)
// }
// } else {
// mainVm.shouldCheckCameraPermissions.set(true)
// }
}

override fun onStop() {
Expand Down Expand Up @@ -189,12 +176,9 @@ internal class LiveFeedbackFragment : Fragment(R.layout.fragment_live_feedback)
}
}

private fun analyze(image: ImageProxy) {
private fun analyze(image: Bitmap) {
try {
vm.process(
image = image,
screenOrientation = ScreenOrientation.getCurrentOrientation(resources)
)
vm.process(croppedBitmap = image)
} catch (t: Throwable) {
Simber.e(t)
// Image analysis is running in bg thread
Expand Down
Loading