I'm new to mediapipe, and I used its Face Landmarks on my app, but i has an offset for all the points. as you can see, all the points have the same x offset. landmarks preview
I tried to implement it on overlay class(I checked my xml files, and It was the same with the orginal app which did not has this offset first) in various valuse:
for (landmark in faceLandmarkerResult.faceLandmarks()) {
for (normalizedLandmark in landmark) {
// Apply the X offset to the X coordinate
val adjustedX = (normalizedLandmark.x() * imageWidth * scaleFactor) - xOffset
val adjustedY = normalizedLandmark.y() * imageHeight * scaleFactor
canvas.drawPoint(adjustedX, adjustedY, pointPaint)
}
}
but still nothing has been changed. could you please guide me in the case of removing this offset? I'm showing landmarks on a fragment:
class DrowsinessFragment : Fragment(), FaceLandmarkerHelper.LandmarkerListener {
companion object {
private const val TAG = "Face Landmarker"
}
val xOffset = -20f
private var _fragmentCameraBinding: FragmentDrowsinessBinding? = null
private val fragmentCameraBinding
get() = _fragmentCameraBinding!!
private lateinit var faceLandmarkerHelper: FaceLandmarkerHelper
private val viewModel: MainViewModel by activityViewModels()
private val faceBlendshapesResultAdapter by lazy {
FaceBlendshapesResultAdapter()
}
private var preview: Preview? = null
private var imageAnalyzer: ImageAnalysis? = null
private var camera: Camera? = null
private var cameraProvider: ProcessCameraProvider? = null
private var cameraFacing = CameraSelector.LENS_FACING_FRONT
/** Blocking ML operations are performed using this executor */
private lateinit var backgroundExecutor: ExecutorService
override fun onResume() {
super.onResume()
// Make sure that all permissions are still present, since the
// user could have removed them while the app was in paused state.
// Start the FaceLandmarkerHelper again when users come back
// to the foreground.
backgroundExecutor.execute {
if (faceLandmarkerHelper.isClose()) {
faceLandmarkerHelper.setupFaceLandmarker()
}
}
}
override fun onPause() {
super.onPause()
if(this::faceLandmarkerHelper.isInitialized) {
viewModel.setMaxFaces(faceLandmarkerHelper.maxNumFaces)
viewModel.setMinFaceDetectionConfidence(faceLandmarkerHelper.minFaceDetectionConfidence)
viewModel.setMinFaceTrackingConfidence(faceLandmarkerHelper.minFaceTrackingConfidence)
viewModel.setMinFacePresenceConfidence(faceLandmarkerHelper.minFacePresenceConfidence)
viewModel.setDelegate(faceLandmarkerHelper.currentDelegate)
// Close the FaceLandmarkerHelper and release resources
backgroundExecutor.execute { faceLandmarkerHelper.clearFaceLandmarker() }
}
}
override fun onDestroyView() {
_fragmentCameraBinding = null
super.onDestroyView()
// Shut down our background executor
backgroundExecutor.shutdown()
backgroundExecutor.awaitTermination(
Long.MAX_VALUE, TimeUnit.NANOSECONDS
)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_fragmentCameraBinding =
FragmentDrowsinessBinding.inflate(inflater, container, false)
return fragmentCameraBinding.root
}
@SuppressLint("MissingPermission")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Initialize our background executor
backgroundExecutor = Executors.newSingleThreadExecutor()
// Wait for the views to be properly laid out
fragmentCameraBinding.viewFinder.post {
// Set up the camera and its use cases
setUpCamera()
}
// Create the FaceLandmarkerHelper that will handle the inference
backgroundExecutor.execute {
faceLandmarkerHelper = FaceLandmarkerHelper(
context = requireContext(),
runningMode = RunningMode.LIVE_STREAM,
minFaceDetectionConfidence = viewModel.currentMinFaceDetectionConfidence,
minFaceTrackingConfidence = viewModel.currentMinFaceTrackingConfidence,
minFacePresenceConfidence = viewModel.currentMinFacePresenceConfidence,
maxNumFaces = viewModel.currentMaxFaces,
currentDelegate = viewModel.currentDelegate,
faceLandmarkerHelperListener = this
)
}
// Attach listeners to UI control widgets
// initBottomSheetControls()
}
// Initialize CameraX, and prepare to bind the camera use cases
private fun setUpCamera() {
val cameraProviderFuture =
ProcessCameraProvider.getInstance(requireContext())
cameraProviderFuture.addListener(
{
// CameraProvider
cameraProvider = cameraProviderFuture.get()
// Build and bind the camera use cases
bindCameraUseCases()
}, ContextCompat.getMainExecutor(requireContext())
)
}
// Declare and bind preview, capture and analysis use cases
@SuppressLint("UnsafeOptInUsageError")
private fun bindCameraUseCases() {
// CameraProvider
val cameraProvider = cameraProvider
?: throw IllegalStateException("Camera initialization failed.")
val cameraSelector =
CameraSelector.Builder().requireLensFacing(cameraFacing).build()
// Preview. Only using the 4:3 ratio because this is the closest to our models
preview = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3)
.setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)
.build()
// ImageAnalysis. Using RGBA 8888 to match how our models work
imageAnalyzer =
ImageAnalysis.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3)
.setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
.build()
// The analyzer can then be assigned to the instance
.also {
it.setAnalyzer(backgroundExecutor) { image ->
detectFace(image)
}
}
// Must unbind the use-cases before rebinding them
cameraProvider.unbindAll()
try {
// A variable number of use-cases can be passed here -
// camera provides access to CameraControl & CameraInfo
camera = cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageAnalyzer
)
// Attach the viewfinder's surface provider to preview use case
preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}
private fun detectFace(imageProxy: ImageProxy) {
faceLandmarkerHelper.detectLiveStream(
imageProxy = imageProxy,
isFrontCamera = cameraFacing == CameraSelector.LENS_FACING_FRONT
)
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
imageAnalyzer?.targetRotation =
fragmentCameraBinding.viewFinder.display.rotation
}
// Update UI after face have been detected. Extracts original
// image height/width to scale and place the landmarks properly through
// OverlayView
override fun onResults(
resultBundle: FaceLandmarkerHelper.ResultBundle
) {
activity?.runOnUiThread {
// Pass necessary information to OverlayView for drawing on the canvas
fragmentCameraBinding.overlay.setResults(
resultBundle.result,
resultBundle.inputImageHeight,
resultBundle.inputImageWidth,
RunningMode.LIVE_STREAM,
xOffset // Pass the X offset
)
// Force a redraw
fragmentCameraBinding.overlay.invalidate()
}
}
}
override fun onEmpty() {
fragmentCameraBinding.overlay.clear()
activity?.runOnUiThread {
faceBlendshapesResultAdapter.updateResults(null)
faceBlendshapesResultAdapter.notifyDataSetChanged()
}
}
override fun onError(error: String, errorCode: Int) {
activity?.runOnUiThread {
Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show()
faceBlendshapesResultAdapter.updateResults(null)
faceBlendshapesResultAdapter.notifyDataSetChanged()
//
}
}
}